Retry USB approval delivery when notifications become available

This commit is contained in:
acrogenesis committed 2026-09-15 14:06:32 -06:00
1 parent 79db1a1826
commit 9e20c2a185
2 files changed
+76 -43

No files matched your search

+44 -32
View File
@@ -16,7 +16,7 @@ IPC.Connected)
USBGUARD_DEVICE_ID="${BASH_REMATCH[1]}" \ USBGUARD_DEVICE_ID="${BASH_REMATCH[1]}" \
USBGUARD_DEVICE_TARGET_NEW=block \ USBGUARD_DEVICE_TARGET_NEW=block \
USBGUARD_DEVICE_RULE="${BASH_REMATCH[2]}" \ USBGUARD_DEVICE_RULE="${BASH_REMATCH[2]}" \
"$OMARCHY_PATH/bin/omarchy-usb-authorization-event" "$OMARCHY_PATH/bin/omarchy-usb-authorization-event" || true
fi fi
done <<<"$blocked" done <<<"$blocked"
exit 0 exit 0
@@ -44,40 +44,52 @@ request="$requests/$token.json"
# watch executes handlers concurrently. A reconnect scan and a policy event # watch executes handlers concurrently. A reconnect scan and a policy event
# can refer to the same device; publish only one pending request. # can refer to the same device; publish only one pending request.
exec 9>"$requests/.lock" exec 9>"$requests/.lock"
flock 9 # Keep retrying without needing another USB event. Handlers belong to the
[[ ! -e $request ]] || exit 0 # watcher service and are stopped with the graphical session.
while true; do
flock 9
[[ ! -e $request ]] || exit 0
# Discard events overtaken by another policy decision or device replacement. # Discard events overtaken by another policy decision or device replacement.
blocked=$(usbguard list-devices --blocked) || exit 1 blocked=$(usbguard list-devices --blocked) || exit 1
current=0 current=0
while IFS= read -r line; do while IFS= read -r line; do
if [[ $line == "$USBGUARD_DEVICE_ID: $USBGUARD_DEVICE_RULE" ]]; then if [[ $line == "$USBGUARD_DEVICE_ID: $USBGUARD_DEVICE_RULE" ]]; then
current=1 current=1
break break
fi
done <<<"$blocked"
(( current )) || exit 0
# Login and shell restarts can leave D-Bus alive before notifications are ready.
# Bound each wait so disappeared devices are rechecked between attempts.
if ! omarchy-notification-wait 1; then
flock -u 9
sleep 1
continue
fi fi
done <<<"$blocked"
(( current )) || exit 0
temporary=$(mktemp "$requests/.request.XXXXXXXXXX") temporary=$(mktemp "$requests/.request.XXXXXXXXXX")
trap 'rm -f "$temporary"' EXIT trap 'rm -f "$temporary"' EXIT
jq -n \ jq -n \
--arg id "$USBGUARD_DEVICE_ID" \ --arg id "$USBGUARD_DEVICE_ID" \
--arg rule "$USBGUARD_DEVICE_RULE" \ --arg rule "$USBGUARD_DEVICE_RULE" \
'{id: $id, rule: $rule}' >"$temporary" '{id: $id, rule: $rule}' >"$temporary"
chmod 600 "$temporary" chmod 600 "$temporary"
mv -f "$temporary" "$request" mv -f "$temporary" "$request"
if ! omarchy-notification-send \ if omarchy-notification-send \
--app-name omarchy-action \ --app-name omarchy-action \
--urgency critical \ --urgency critical \
--glyph 󰖀 \ --glyph 󰖀 \
"USB accessory blocked" \ "USB accessory blocked" \
"Click to review it. Keep it blocked unless you recognize the device." \ "Click to review it. Keep it blocked unless you recognize the device." \
--exec omarchy-launch-floating-terminal-with-presentation \ --exec omarchy-launch-floating-terminal-with-presentation \
omarchy-usb-authorization-review "$token"; then omarchy-usb-authorization-review "$token"; then
# Only a delivered notification may suppress subsequent attempts. Keep the exit 0
# lock until failure cleanup so another event cannot skip an unsent request. fi
rm -f "$request" rm -f "$request"
exit 1 flock -u 9
fi sleep 1
done
+32 -11
View File
@@ -67,6 +67,7 @@ generate-policy)
;; ;;
list-devices) list-devices)
if [[ ${2:-} == "--blocked" ]]; then if [[ ${2:-} == "--blocked" ]]; then
if [[ -n ${BLOCKED_DEVICES_FILE:-} ]]; then cat "$BLOCKED_DEVICES_FILE"; exit 0; fi
if [[ ${BLOCKED_DEVICE_PRESENT:-1} == 1 ]]; then if [[ ${BLOCKED_DEVICE_PRESENT:-1} == 1 ]]; then
rule="${BLOCKED_DEVICE_RULE:?}" rule="${BLOCKED_DEVICE_RULE:?}"
if [[ -n ${BLOCKED_DEVICE_RULE_FILE:-} && -f $BLOCKED_DEVICE_RULE_FILE ]]; then if [[ -n ${BLOCKED_DEVICE_RULE_FILE:-} && -f $BLOCKED_DEVICE_RULE_FILE ]]; then
@@ -126,7 +127,17 @@ cat >"$stub_bin/omarchy-notification-send" <<'STUB'
printf 'notification' >>"$CALLS" printf 'notification' >>"$CALLS"
printf ' <%s>' "$@" >>"$CALLS" printf ' <%s>' "$@" >>"$CALLS"
printf '\n' >>"$CALLS" printf '\n' >>"$CALLS"
exit "${NOTIFICATION_FAIL:-0}" if [[ -n ${NOTIFICATION_FAILURE_MARKER:-} && ! -e $NOTIFICATION_FAILURE_MARKER ]]; then
touch "$NOTIFICATION_FAILURE_MARKER"
exit 1
fi
STUB
cat >"$stub_bin/omarchy-notification-wait" <<'STUB'
#!/bin/bash
if [[ -n ${NOTIFICATION_READY_FILE:-} ]]; then
touch "$NOTIFICATION_READY_FILE.waited"
[[ -e $NOTIFICATION_READY_FILE ]]
fi
STUB STUB
chmod +x "$stub_bin"/* chmod +x "$stub_bin"/*
@@ -515,16 +526,26 @@ fi
pass "USB review binds approval to the device snapshot" pass "USB review binds approval to the device snapshot"
notification_count=$(grep -c '^notification' "$calls") notification_count=$(grep -c '^notification' "$calls")
if NOTIFICATION_FAIL=1 USBGUARD_IPC_SIGNAL=IPC.Connected \ printf '17: %s\n18: %s\n' "$malicious_rule" 'block id 1234:5678 name "Second blocked device" hash "second"' >"$scratch/two-blocked"
"$ROOT/bin/omarchy-usb-authorization-event"; then NOTIFICATION_READY_FILE="$scratch/notification-ready" \
fail "a failed notification must report failure" NOTIFICATION_FAILURE_MARKER="$scratch/first-send-failed" \
fi BLOCKED_DEVICES_FILE="$scratch/two-blocked" \
[[ ! -e $request ]] || fail "a failed notification must not suppress retries" USBGUARD_IPC_SIGNAL=IPC.Connected \
USBGUARD_IPC_SIGNAL=IPC.Connected "$ROOT/bin/omarchy-usb-authorization-event" timeout 10 "$ROOT/bin/omarchy-usb-authorization-event" &
[[ -f $request ]] || fail "reconnection retries a failed notification" scan_pid=$!
[[ $(grep -c '^notification' "$calls") == $((notification_count + 2)) ]] || for ((attempt=0; attempt<50; attempt++)); do
fail "reconnection must attempt delivery after an earlier send failure" [[ -e $scratch/notification-ready.waited ]] && break
pass "failed USB notifications can be retried on reconnection" sleep 0.02
done
[[ -e $scratch/notification-ready.waited ]] || fail "startup waits for notification readiness"
[[ $(grep -c '^notification' "$calls") == "$notification_count" ]] ||
fail "startup does not send before the notification server is ready"
touch "$scratch/notification-ready"
wait "$scan_pid" || fail "startup scan must retry and finish without another USB event"
[[ $(grep -c '^notification' "$calls") == $((notification_count + 3)) ]] ||
fail "two blocked devices must be notified despite a failed first send"
[[ -f $request ]] || fail "successful retry leaves a reviewable request"
pass "USB startup waits for notifications and automatically retries delivery for all devices"
sysfs="$scratch/sys/bus/usb/devices" sysfs="$scratch/sys/bus/usb/devices"
mkdir -p "$sysfs/usb1" "$sysfs/1-2" mkdir -p "$sysfs/usb1" "$sysfs/1-2"