Recover the session lock before waiting for notifications

Restarting the shell waited for the notification bus name to reappear
before it would re-acquire a lock whose client had died. A slow or
failed notification plugin then left the user stranded behind
Hyprland's failsafe even though the lock service worked.

Wait for the shell's core IPC, re-secure the lock immediately, and only
then wait for a previously running notification service, reporting it
separately if it never returns. Cover the never-returning case: the lock
comes back and the restart still reports the missing service.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Afonso OliveiraandClaude Fable 5.1 committed 2026-09-17 17:02:43 +01:00
1 parent 13a4306a8e
commit b34b117fd0
2 files changed
+80 -18

No files matched your search

+42 -18
View File
@@ -82,26 +82,50 @@ while timeout 5 quickshell kill -p "$CONFIG_DIR" --any-display >/dev/null 2>&1;
# not transient variables from a terminal, SSH connection, or development tool.
hyprctl dispatch 'hl.dsp.exec_cmd("omarchy-launch-shell")' >/dev/null
shell_ready=0
for (( attempt = 0; attempt < 20; attempt++ )); do
if OMARCHY_PATH="$session_omarchy_path" OMARCHY_SHELL_IPC_TIMEOUT=0.5s omarchy-shell shell ping >/dev/null 2>&1 &&
{ (( notifications_were_running == 0 )) || notifications_ready; }; then
# The session stays compositor-locked after the old lock client died, so
# re-acquire the lock and let the user authenticate out of it.
if (( relock )) && ! relock_session; then
echo "Omarchy shell restarted, but the session lock was not re-secured." >&2
exit 1
fi
# Invitation toasts (like Voxtype/fingerprint setup) die with the old
# shell, and their notify-send waiters hang forever: the dying server
# never emits NotificationClosed. A still-running omarchy-*-invitation
# unit is therefore an unanswered invitation — re-run it so its toast
# reappears on the new shell. Answered invitations have already exited
# and been collected, so the glob no longer matches them.
systemctl --user try-restart 'omarchy-*-invitation.service' 2>/dev/null || true
exit 0
if OMARCHY_PATH="$session_omarchy_path" OMARCHY_SHELL_IPC_TIMEOUT=0.5s omarchy-shell shell ping >/dev/null 2>&1; then
shell_ready=1
break
fi
sleep 0.1
done
if (( shell_ready == 0 )); then
echo "Omarchy shell did not become ready after restart." >&2
exit 1
fi
echo "Omarchy shell did not become ready after restart." >&2
exit 1
# The session stays compositor-locked after the old lock client died, so
# re-acquire the lock and let the user authenticate out of it. This comes
# first and depends on nothing else: a user stranded behind the failsafe must
# not wait on the notification plugin, which may be slow or absent.
if (( relock )) && ! relock_session; then
echo "Omarchy shell restarted, but the session lock was not re-secured." >&2
exit 1
fi
# Core IPC answers before the notification plugin has registered its bus
# name, so wait for it separately before one-time toasts are sent.
if (( notifications_were_running )); then
notifications_restored=0
for (( attempt = 0; attempt < 20; attempt++ )); do
if notifications_ready; then
notifications_restored=1
break
fi
sleep 0.1
done
if (( notifications_restored == 0 )); then
echo "Omarchy shell restarted, but its notification service did not become ready." >&2
exit 1
fi
fi
# Invitation toasts (like Voxtype/fingerprint setup) die with the old
# shell, and their notify-send waiters hang forever: the dying server
# never emits NotificationClosed. A still-running omarchy-*-invitation
# unit is therefore an unanswered invitation — re-run it so its toast
# reappears on the new shell. Answered invitations have already exited
# and been collected, so the glob no longer matches them.
systemctl --user try-restart 'omarchy-*-invitation.service' 2>/dev/null || true
exit 0
+38
View File
@@ -181,6 +181,12 @@ else
[[ ! -f $OMARCHY_TEST_NOTIFICATION_CHECKS ]] || read -r checks <"$OMARCHY_TEST_NOTIFICATION_CHECKS"
(( checks += 1 ))
printf '%s\n' "$checks" >"$OMARCHY_TEST_NOTIFICATION_CHECKS"
# The service was running before the restart and, when asked to, never
# comes back afterwards.
if [[ ${OMARCHY_TEST_NOTIFICATIONS_DIE:-0} == 1 ]]; then
(( checks == 1 )) && echo 'b true' || echo 'b false'
exit 0
fi
if (( checks == 1 || checks >= 4 )); then
echo 'b true'
else
@@ -285,3 +291,35 @@ restart_pid_one=""
grep -F "ipc -n -p $restart_root/shell call -- lock lock" "$ipc_log" >/dev/null || fail "dead-lock recovery re-acquires the session lock"
grep -F "ipc -n -p $restart_root/shell call -- lock status" "$ipc_log" >/dev/null || fail "dead-lock recovery waits for the lock to become secure"
pass "restart recovers a locked session whose lock client died"
# Lock recovery must not wait on the notification plugin: a stranded user gets
# the lock back even when notifications never return, and the restart then
# reports the missing service rather than claiming success.
sleep 30 &
restart_pid_one=$!
printf '%s\n' "$restart_pid_one" >"$restart_state"
rm -f "$restart_state.locked" "$test_tmp/notification-checks"
: >"$restart_log"
: >"$ipc_log"
if PATH="$restart_bin:$PATH" \
OMARCHY_PATH="$restart_root" \
XDG_RUNTIME_DIR="$runtime_dir" \
OMARCHY_TEST_SESSION_LOCKED=1 \
OMARCHY_TEST_QS_STATE="$restart_state" \
OMARCHY_TEST_QS_LOG="$restart_log" \
OMARCHY_TEST_QS_ENV_LOG="$restart_env_log" \
OMARCHY_TEST_DISPATCH_LOG="$dispatch_log" \
OMARCHY_TEST_IPC_LOG="$ipc_log" \
OMARCHY_TEST_SESSION_PATH="$restart_root" \
OMARCHY_TEST_NOTIFICATION_CHECKS="$test_tmp/notification-checks" \
OMARCHY_TEST_NOTIFICATIONS_DIE=1 \
timeout 10 "$ROOT/bin/omarchy-restart-shell" >"$test_tmp/dead-notifications.out" 2>&1; then
fail "a restart whose notification service never returns must not report success"
fi
wait "$restart_pid_one" 2>/dev/null || true
restart_pid_one=""
grep -F "ipc -n -p $restart_root/shell call -- lock lock" "$ipc_log" >/dev/null || fail "lock recovery waited on the notification service" "$(cat "$ipc_log")"
[[ -f $restart_state.locked ]] || fail "lock recovery did not re-secure the session without notifications"
grep -q "notification service did not become ready" "$test_tmp/dead-notifications.out" || fail "a missing notification service is not reported" "$(cat "$test_tmp/dead-notifications.out")"
pass "restart recovers the lock even when the notification service never returns"