Make a dead lock client diagnosable and recoverable (#6630)
* Persist the Omarchy shell log across sessions Quickshell only logs to its instance runtime dir on tmpfs, so when the shell dies the idle/lock event trail is gone after a reboot (#6628). Launch the shell through omarchy-launch-shell, which pipes stdout/stderr into the journal under the omarchy-shell tag — bounded, timestamped, and persistent — and surface that log in omarchy-debug-idle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Recover a locked session whose lock client died When the shell dies while the session is locked, Hyprland's failsafe keeps the session locked with no lock client left, and omarchy-restart-shell refused to run in exactly that state, leaving reboot as the only way back in (#6628). Gate the refusal on the lock service actually holding (or acquiring) the lock rather than on the session's LOCK state — a dead shell and a crash-handler relaunch that holds no lock both fail that check — then restart the shell, re-acquire the session lock, and wait for it to report secure, the same secure-poll omarchy-system-sleep-lock uses, so the user can authenticate out of the failsafe. Enable Hyprland's allow_session_lock_restore so the compositor accepts the replacement lock client. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3aa7c1ee13
commit
1ded25fd45
@@ -61,6 +61,7 @@ runtime_dir="$test_tmp/runtime"
|
||||
mkdir -p "$restart_root/shell" "$restart_bin" "$runtime_dir"
|
||||
touch "$restart_root/shell/shell.qml"
|
||||
ln -s "$ROOT/bin/omarchy-shell" "$restart_bin/omarchy-shell"
|
||||
ln -s "$ROOT/bin/omarchy-launch-shell" "$restart_bin/omarchy-launch-shell"
|
||||
ln -s "$ROOT/bin/omarchy-cmd-missing" "$restart_bin/omarchy-cmd-missing"
|
||||
|
||||
cat >"$restart_bin/qs" <<'SH'
|
||||
@@ -74,6 +75,17 @@ case "$*" in
|
||||
grep -Fx '303' "$OMARCHY_TEST_QS_STATE" >/dev/null &&
|
||||
printf 'ok\n'
|
||||
;;
|
||||
*'lock lock')
|
||||
touch "$OMARCHY_TEST_QS_STATE.locked"
|
||||
printf 'ok\n'
|
||||
;;
|
||||
*'lock status')
|
||||
if [[ -f $OMARCHY_TEST_QS_STATE.locked ]]; then
|
||||
printf '{"secure": true, "requested": true}\n'
|
||||
else
|
||||
printf '{"secure": false, "requested": false}\n'
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
SH
|
||||
|
||||
@@ -110,13 +122,24 @@ if [[ ${1:-} == "-j" && ${2:-} == "monitors" ]]; then
|
||||
elif [[ ${1:-} == "dispatch" && ${2:-} == hl.dsp.exec_cmd* ]]; then
|
||||
printf '%s\n' "${2:-}" >>"$OMARCHY_TEST_DISPATCH_LOG"
|
||||
OMARCHY_PATH="$OMARCHY_TEST_SESSION_PATH" \
|
||||
env -u OMARCHY_TEST_TRANSIENT_ENV quickshell -n -p "$OMARCHY_TEST_SESSION_PATH/shell"
|
||||
env -u OMARCHY_TEST_TRANSIENT_ENV omarchy-launch-shell
|
||||
printf 'ok\n'
|
||||
elif [[ ${1:-} == "dispatch" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
SH
|
||||
|
||||
# Keep the test hermetic where journald has no usable stream socket.
|
||||
cat >"$restart_bin/systemd-cat" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
while (( $# > 0 )); do
|
||||
[[ $1 == "--" ]] && { shift; break; }
|
||||
shift
|
||||
done
|
||||
exec "$@"
|
||||
SH
|
||||
|
||||
cat >"$restart_bin/systemctl" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
@@ -129,7 +152,7 @@ else
|
||||
fi
|
||||
SH
|
||||
|
||||
chmod +x "$restart_bin/qs" "$restart_bin/quickshell" "$restart_bin/hyprctl" "$restart_bin/systemctl"
|
||||
chmod +x "$restart_bin/qs" "$restart_bin/quickshell" "$restart_bin/hyprctl" "$restart_bin/systemd-cat" "$restart_bin/systemctl"
|
||||
|
||||
sleep 30 &
|
||||
restart_pid_one=$!
|
||||
@@ -167,12 +190,13 @@ restart_pid_two=""
|
||||
[[ $(grep -c '^-n -p ' "$restart_log") == 1 ]] || fail "restart launches one fresh shell process"
|
||||
grep -F "kill -p $restart_root/shell --any-display" "$restart_log" >/dev/null || fail "restart stops the shell from the session checkout"
|
||||
[[ $(<"$restart_env_log") == "unset" ]] || fail "restart uses the Hyprland session environment for the fresh shell"
|
||||
grep -F 'hl.dsp.exec_cmd("quickshell -n -p $OMARCHY_PATH/shell")' "$dispatch_log" >/dev/null || fail "restart launches the fresh shell through Hyprland"
|
||||
grep -F 'hl.dsp.exec_cmd("omarchy-launch-shell")' "$dispatch_log" >/dev/null || fail "restart launches the fresh shell through Hyprland"
|
||||
grep -F "ipc -n -p $restart_root/shell call -- shell ping" "$ipc_log" >/dev/null || fail "restart checks readiness in the session checkout"
|
||||
pass "restart replaces duplicate shell instances from the session checkout"
|
||||
|
||||
: >"$restart_log"
|
||||
printf '404\n' >"$restart_state"
|
||||
printf '303\n' >"$restart_state"
|
||||
touch "$restart_state.locked"
|
||||
|
||||
locked_error=$(PATH="$restart_bin:$PATH" \
|
||||
OMARCHY_PATH="$restart_root" \
|
||||
@@ -186,6 +210,38 @@ locked_error=$(PATH="$restart_bin:$PATH" \
|
||||
"$ROOT/bin/omarchy-restart-shell" 2>&1) && fail "restart refuses while the shell lock is active"
|
||||
|
||||
[[ $locked_error == "Refusing to restart Omarchy shell while the session is locked." ]] || fail "locked restart explains why it was refused" "$locked_error"
|
||||
[[ $(<"$restart_state") == 404 ]] || fail "locked restart preserves the running shell"
|
||||
[[ $(<"$restart_state") == 303 ]] || fail "locked restart preserves the running shell"
|
||||
[[ ! -s $restart_log ]] || fail "locked restart does not stop or launch Quickshell"
|
||||
pass "restart preserves the shell while its lock is active"
|
||||
|
||||
# A LOCK session without an active locker — dead shell or a crash-handler
|
||||
# relaunch holding no lock — is the failsafe: restart must proceed,
|
||||
# re-acquire the session lock, and wait for it to report secure.
|
||||
sleep 30 &
|
||||
restart_pid_one=$!
|
||||
printf '%s\n' "$restart_pid_one" >"$restart_state"
|
||||
rm -f "$restart_state.locked"
|
||||
: >"$restart_log"
|
||||
: >"$ipc_log"
|
||||
|
||||
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" \
|
||||
timeout 5 "$ROOT/bin/omarchy-restart-shell" || fail "locked restart recovers when the lock client is dead"
|
||||
|
||||
if kill -0 "$restart_pid_one" 2>/dev/null; then
|
||||
fail "dead-lock recovery stops the stale shell instance"
|
||||
fi
|
||||
wait "$restart_pid_one" 2>/dev/null || true
|
||||
restart_pid_one=""
|
||||
[[ $(<"$restart_state") == 303 ]] || fail "dead-lock recovery leaves one fresh shell instance"
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user