Fix sleep lock session environment race (#6493)

Found by @tobi.
This commit is contained in:
David Heinemeier Hansson
2026-08-01 15:38:55 -05:00
committed by GitHub
parent 0af39c5260
commit 2cff67fa45
5 changed files with 260 additions and 1 deletions
+1
View File
@@ -1432,6 +1432,7 @@ retire systemd/user/bt-agent.service 0406b577a1225dc2a9f86638d3c346eb3635168576f
retire systemd/user/omarchy-recover-internal-monitor.service b9b92cedc44cf3cb6216948629be55b53d16746e31896dc6469fd49ba55e82f4 retire systemd/user/omarchy-recover-internal-monitor.service b9b92cedc44cf3cb6216948629be55b53d16746e31896dc6469fd49ba55e82f4
retire systemd/user/omarchy-recover-internal-monitor.service e1483079b9f2aefcd43b4722c75a31643e5f3bb5a2eada5f52f1d7d201e8c289 retire systemd/user/omarchy-recover-internal-monitor.service e1483079b9f2aefcd43b4722c75a31643e5f3bb5a2eada5f52f1d7d201e8c289
retire systemd/user/omarchy-sleep-lock.service 6870b232a6c0474b59187882e6d25ae771bba735098bcbedef8a2b73b97e2b6a retire systemd/user/omarchy-sleep-lock.service 6870b232a6c0474b59187882e6d25ae771bba735098bcbedef8a2b73b97e2b6a
retire systemd/user/omarchy-sleep-lock.service bcd1a76cb5c63514922bc5e11af22ae480fc6d06a99863364e02bdf3c7bdceaf
retire uwsm/default 2e89b03a3710b70cf754ea0c8db4388bb35a7519870036abff5ea8a584eafbc0 retire uwsm/default 2e89b03a3710b70cf754ea0c8db4388bb35a7519870036abff5ea8a584eafbc0
retire uwsm/default 3622ba134d29b639a155424a5226215e213c8508144e98001ffe128a8967c9b5 retire uwsm/default 3622ba134d29b639a155424a5226215e213c8508144e98001ffe128a8967c9b5
retire uwsm/default 73712c5a677532bd4afcee5800ae9b76e3d02a2130659a96a8b7fbe2fd2c3d53 retire uwsm/default 73712c5a677532bd4afcee5800ae9b76e3d02a2130659a96a8b7fbe2fd2c3d53
@@ -1,7 +1,13 @@
[Unit] [Unit]
Description=Lock Omarchy before suspend Description=Lock Omarchy before suspend
After=dbus.socket # The monitor calls into the running Omarchy shell. Wait until UWSM has imported
# OMARCHY_PATH and WAYLAND_DISPLAY, but keep the default target ordering so the
# monitor starts before graphical-session.target is reached.
After=dbus.socket wayland-session-waitenv.service
Requires=dbus.socket Requires=dbus.socket
PartOf=graphical-session.target
ConditionEnvironment=OMARCHY_PATH
ConditionEnvironment=WAYLAND_DISPLAY
[Service] [Service]
Type=simple Type=simple
+79
View File
@@ -0,0 +1,79 @@
echo "Repair the pre-suspend lock monitor's graphical session environment"
# The old unit started before UWSM finished importing OMARCHY_PATH and
# WAYLAND_DISPLAY. A full unit retained from before Omarchy 4 also shadows the
# corrected package unit, so add the lifecycle constraints as a drop-in without
# discarding any user customizations.
user_config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
sleep_lock_unit="$user_config_home/systemd/user/omarchy-sleep-lock.service"
sleep_lock_dropin_dir="$user_config_home/systemd/user/omarchy-sleep-lock.service.d"
sleep_lock_dropin="$sleep_lock_dropin_dir/90-omarchy-session-environment.conf"
if [[ -f $sleep_lock_unit ]]; then
mkdir -p "$sleep_lock_dropin_dir"
printf '%s\n' \
'[Unit]' \
'After=dbus.socket wayland-session-waitenv.service' \
'Requires=dbus.socket' \
'PartOf=graphical-session.target' \
'ConditionEnvironment=OMARCHY_PATH' \
'ConditionEnvironment=WAYLAND_DISPLAY' >"$sleep_lock_dropin"
fi
# With no live user manager there cannot be an inherited monitor to replace.
# The package unit (and compatibility drop-in, when needed) will be loaded by
# the fresh manager at the next login.
user_manager_socket="${XDG_RUNTIME_DIR:-/run/user/$UID}/systemd/private"
if ! error=$(systemctl --user show-environment 2>&1); then
if [[ -S $user_manager_socket ]]; then
echo "Could not reach the running user service manager: $error"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
fi
exit 0
fi
if ! error=$(systemctl --user daemon-reload 2>&1); then
echo "Could not reload the user service manager: $error"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
fi
if ! graphical_state=$(systemctl --user show --property=ActiveState --value graphical-session.target 2>&1); then
echo "Could not inspect graphical-session.target: $graphical_state"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
fi
if [[ $graphical_state == "active" ]]; then
if ! error=$(systemctl --user reset-failed omarchy-sleep-lock.service 2>&1); then
echo "Could not reset omarchy-sleep-lock.service: $error"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
elif ! error=$(systemctl --user restart omarchy-sleep-lock.service 2>&1); then
echo "Could not restart omarchy-sleep-lock.service: $error"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
elif ! sleep_lock_state=$(systemctl --user show --property=ActiveState --value omarchy-sleep-lock.service 2>&1); then
echo "Could not inspect omarchy-sleep-lock.service: $sleep_lock_state"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
elif [[ $sleep_lock_state != "active" ]]; then
echo "omarchy-sleep-lock.service did not stay active after restart."
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
fi
else
# A pre-fix monitor was not tied to graphical-session.target and can outlive
# logout under a lingering user manager. Stop it so the next target start
# creates a process with the next graphical session's environment.
if ! error=$(systemctl --user stop omarchy-sleep-lock.service 2>&1); then
echo "Could not stop stale omarchy-sleep-lock.service: $error"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
elif ! error=$(systemctl --user reset-failed omarchy-sleep-lock.service 2>&1); then
echo "Could not reset omarchy-sleep-lock.service: $error"
echo "The pre-suspend lock repair will be retried by omarchy-migrate."
exit 1
fi
fi
+162
View File
@@ -0,0 +1,162 @@
#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
stub_bin="$test_tmp/bin"
mkdir -p "$stub_bin"
cat >"$stub_bin/systemctl" <<'STUB'
#!/bin/bash
printf '%s\n' "$*" >>"$SYSTEMCTL_CALLS"
case "$*" in
'--user show-environment')
[[ ${MANAGER_AVAILABLE:-true} == "true" ]]
;;
'--user daemon-reload')
if [[ ${FAIL_SYSTEMCTL_ACTION:-} == "daemon-reload" ]]; then
echo "reload failed" >&2
exit 1
fi
;;
'--user show --property=ActiveState --value graphical-session.target')
if [[ ${FAIL_SYSTEMCTL_ACTION:-} == "show-graphical" ]]; then
echo "state lookup failed" >&2
exit 1
fi
printf '%s\n' "${GRAPHICAL_STATE:-inactive}"
;;
'--user show --property=ActiveState --value omarchy-sleep-lock.service')
printf '%s\n' "${SLEEP_LOCK_STATE:-inactive}"
;;
'--user restart omarchy-sleep-lock.service')
if [[ ${FAIL_SYSTEMCTL_ACTION:-} == "restart" ]]; then
echo "restart failed" >&2
exit 1
fi
;;
'--user stop omarchy-sleep-lock.service')
if [[ ${FAIL_SYSTEMCTL_ACTION:-} == "stop" ]]; then
echo "stop failed" >&2
exit 1
fi
;;
'--user reset-failed omarchy-sleep-lock.service') ;;
*) exit 1 ;;
esac
STUB
chmod +x "$stub_bin/systemctl"
migration="$ROOT/migrations/1785608166.sh"
run_migration() {
local home="$1" calls="$2"
shift 2
mkdir -p "$home/run"
HOME="$home" XDG_CONFIG_HOME="$home/.config" XDG_RUNTIME_DIR="$home/run" \
SYSTEMCTL_CALLS="$calls" PATH="$stub_bin:$PATH" \
"$@" bash -euo pipefail "$migration"
}
active_home="$test_tmp/active-home"
active_calls="$test_tmp/active-calls"
mkdir -p "$active_home/.config/systemd/user"
printf '%s\n' '[Service]' 'ExecStart=/usr/bin/omarchy-system-sleep-monitor' \
>"$active_home/.config/systemd/user/omarchy-sleep-lock.service"
run_migration "$active_home" "$active_calls" \
env GRAPHICAL_STATE=active SLEEP_LOCK_STATE=active >/dev/null
dropin="$active_home/.config/systemd/user/omarchy-sleep-lock.service.d/90-omarchy-session-environment.conf"
grep -Fx 'After=dbus.socket wayland-session-waitenv.service' "$dropin" >/dev/null ||
fail "sleep lock migration orders a retained unit after the session environment import"
grep -Fx 'PartOf=graphical-session.target' "$dropin" >/dev/null ||
fail "sleep lock migration ties a retained unit to the graphical session lifecycle"
grep -Fx 'ConditionEnvironment=OMARCHY_PATH' "$dropin" >/dev/null ||
fail "sleep lock migration gates a retained unit on the Omarchy path"
grep -Fx 'ConditionEnvironment=WAYLAND_DISPLAY' "$dropin" >/dev/null ||
fail "sleep lock migration gates a retained unit on the Wayland display"
pass "sleep lock migration repairs retained full-unit overrides with a drop-in"
grep -Fx -- '--user daemon-reload' "$active_calls" >/dev/null ||
fail "sleep lock migration reloads the live user manager"
grep -Fx -- '--user reset-failed omarchy-sleep-lock.service' "$active_calls" >/dev/null ||
fail "sleep lock migration cannot recover a start-limited monitor"
grep -Fx -- '--user restart omarchy-sleep-lock.service' "$active_calls" >/dev/null ||
fail "sleep lock migration leaves an active monitor with its inherited environment"
grep -Fx -- '--user stop omarchy-sleep-lock.service' "$active_calls" >/dev/null &&
fail "sleep lock migration stops the monitor inside an active graphical session"
pass "sleep lock migration replaces the monitor inside an active graphical session"
inactive_home="$test_tmp/inactive-home"
inactive_calls="$test_tmp/inactive-calls"
mkdir -p "$inactive_home"
run_migration "$inactive_home" "$inactive_calls" \
env GRAPHICAL_STATE=inactive >/dev/null
grep -Fx -- '--user stop omarchy-sleep-lock.service' "$inactive_calls" >/dev/null ||
fail "sleep lock migration leaves a stale monitor running after logout"
grep -Fx -- '--user restart omarchy-sleep-lock.service' "$inactive_calls" >/dev/null &&
fail "sleep lock migration starts the monitor outside a graphical session"
pass "sleep lock migration stops a monitor left behind after logout"
failed_calls="$test_tmp/failed-calls"
if run_migration "$test_tmp/failed-home" "$failed_calls" \
env GRAPHICAL_STATE=active SLEEP_LOCK_STATE=active FAIL_SYSTEMCTL_ACTION=restart \
>"$test_tmp/failed-output" 2>&1; then
fail "sleep lock migration marks a failed active-session repair complete"
fi
grep -F 'will be retried by omarchy-migrate' "$test_tmp/failed-output" >/dev/null ||
fail "sleep lock migration does not explain that a failed repair remains pending"
pass "sleep lock migration keeps an active-session repair failure retryable"
reload_failed_calls="$test_tmp/reload-failed-calls"
if run_migration "$test_tmp/reload-failed-home" "$reload_failed_calls" \
env FAIL_SYSTEMCTL_ACTION=daemon-reload >"$test_tmp/reload-failed-output" 2>&1; then
fail "sleep lock migration ignores a failed user-manager reload"
fi
grep -F 'Could not reload the user service manager' "$test_tmp/reload-failed-output" >/dev/null ||
fail "sleep lock migration does not report a failed user-manager reload"
pass "sleep lock migration keeps a failed user-manager reload retryable"
stop_failed_calls="$test_tmp/stop-failed-calls"
if run_migration "$test_tmp/stop-failed-home" "$stop_failed_calls" \
env GRAPHICAL_STATE=inactive FAIL_SYSTEMCTL_ACTION=stop \
>"$test_tmp/stop-failed-output" 2>&1; then
fail "sleep lock migration ignores a stale monitor that could not be stopped"
fi
grep -F 'Could not stop stale omarchy-sleep-lock.service' "$test_tmp/stop-failed-output" >/dev/null ||
fail "sleep lock migration does not report a stale monitor stop failure"
pass "sleep lock migration keeps a stale-monitor stop failure retryable"
state_failed_calls="$test_tmp/state-failed-calls"
if run_migration "$test_tmp/state-failed-home" "$state_failed_calls" \
env FAIL_SYSTEMCTL_ACTION=show-graphical >"$test_tmp/state-failed-output" 2>&1; then
fail "sleep lock migration ignores a failed session-state inspection"
fi
grep -F 'Could not inspect graphical-session.target' "$test_tmp/state-failed-output" >/dev/null ||
fail "sleep lock migration does not report a failed session-state inspection"
pass "sleep lock migration keeps an indeterminate session repair retryable"
deferred_home="$test_tmp/deferred-home"
deferred_calls="$test_tmp/deferred-calls"
mkdir -p "$deferred_home/.config/systemd/user"
touch "$deferred_home/.config/systemd/user/omarchy-sleep-lock.service"
run_migration "$deferred_home" "$deferred_calls" \
env MANAGER_AVAILABLE=false >/dev/null
[[ -f $deferred_home/.config/systemd/user/omarchy-sleep-lock.service.d/90-omarchy-session-environment.conf ]] ||
fail "sleep lock migration does not persist the repair without a live user manager"
deferred_call_count=$(wc -l <"$deferred_calls")
(( deferred_call_count == 1 )) ||
fail "sleep lock migration tries to mutate a user manager that is not running"
pass "sleep lock migration defers safely when no user manager is running"
+11
View File
@@ -16,6 +16,16 @@ sleep_service="$ROOT/default/systemd/user/omarchy-sleep-lock.service"
grep -Fx 'ExecStart=/usr/bin/omarchy-system-sleep-monitor' "$sleep_service" >/dev/null grep -Fx 'ExecStart=/usr/bin/omarchy-system-sleep-monitor' "$sleep_service" >/dev/null
pass "sleep lock service uses the package-backed monitor path" pass "sleep lock service uses the package-backed monitor path"
grep -Fx 'After=dbus.socket wayland-session-waitenv.service' "$sleep_service" >/dev/null ||
fail "sleep lock monitor starts before UWSM imports the graphical session environment"
grep -Fx 'PartOf=graphical-session.target' "$sleep_service" >/dev/null ||
fail "sleep lock monitor survives logout with a stale Wayland environment"
grep -Fx 'ConditionEnvironment=OMARCHY_PATH' "$sleep_service" >/dev/null ||
fail "sleep lock monitor can start without the Omarchy shell path"
grep -Fx 'ConditionEnvironment=WAYLAND_DISPLAY' "$sleep_service" >/dev/null ||
fail "sleep lock monitor can start without a Wayland display"
pass "sleep lock service follows the initialized graphical session"
first_run_units="$ROOT/install/user/first-run/enable-user-units.sh" first_run_units="$ROOT/install/user/first-run/enable-user-units.sh"
grep -Fx 'systemctl --user daemon-reload' "$first_run_units" >/dev/null grep -Fx 'systemctl --user daemon-reload' "$first_run_units" >/dev/null
grep -F 'omarchy-sleep-lock.service' "$first_run_units" >/dev/null grep -F 'omarchy-sleep-lock.service' "$first_run_units" >/dev/null
@@ -23,6 +33,7 @@ pass "first-run reloads and enables the sleep lock service"
upgrade_to_quattro="$ROOT/bin/omarchy-upgrade-to-quattro" upgrade_to_quattro="$ROOT/bin/omarchy-upgrade-to-quattro"
grep -F '6870b232a6c0474b59187882e6d25ae771bba735098bcbedef8a2b73b97e2b6a' "$upgrade_to_quattro" >/dev/null grep -F '6870b232a6c0474b59187882e6d25ae771bba735098bcbedef8a2b73b97e2b6a' "$upgrade_to_quattro" >/dev/null
grep -F 'bcd1a76cb5c63514922bc5e11af22ae480fc6d06a99863364e02bdf3c7bdceaf' "$upgrade_to_quattro" >/dev/null
grep -F 'ExecStart=%h/.local/share/omarchy/bin/omarchy-system-sleep-monitor' "$upgrade_to_quattro" >/dev/null grep -F 'ExecStart=%h/.local/share/omarchy/bin/omarchy-system-sleep-monitor' "$upgrade_to_quattro" >/dev/null
grep -F 'ExecStart=/usr/bin/omarchy-system-sleep-monitor' "$upgrade_to_quattro" >/dev/null grep -F 'ExecStart=/usr/bin/omarchy-system-sleep-monitor' "$upgrade_to_quattro" >/dev/null
grep -F 'reset-failed omarchy-sleep-lock.service' "$upgrade_to_quattro" >/dev/null grep -F 'reset-failed omarchy-sleep-lock.service' "$upgrade_to_quattro" >/dev/null