From e47d895ffe23599a122cde8ff819f1aea78c5a06 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 29 Jul 2026 20:21:45 -0700 Subject: [PATCH] Release sleep inhibitor after locking (#6436) --- bin/omarchy-system-sleep-monitor | 44 +++++++++--- test/shell.d/sleep-monitor-test.sh | 106 +++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 8 deletions(-) create mode 100755 test/shell.d/sleep-monitor-test.sh diff --git a/bin/omarchy-system-sleep-monitor b/bin/omarchy-system-sleep-monitor index 64d8d1f2..2100ceeb 100755 --- a/bin/omarchy-system-sleep-monitor +++ b/bin/omarchy-system-sleep-monitor @@ -4,8 +4,6 @@ # omarchy:group=system # omarchy:hidden=true -monitor_command='exec dbus-monitor --system "type='\''signal'\'',sender='\''org.freedesktop.login1'\'',interface='\''org.freedesktop.login1.Manager'\'',member='\''PrepareForSleep'\''"' - consume_sleep_events() { local line sleep_lock sleep_lock="$OMARCHY_PATH/bin/omarchy-system-sleep-lock" @@ -13,15 +11,45 @@ consume_sleep_events() { while IFS= read -r line; do if [[ $line == *"boolean true"* ]]; then "$sleep_lock" - exit 0 + return 0 fi done } -if [[ ${1:-} == "--consume" ]]; then - consume_sleep_events - exit 0 -fi +monitor_sleep_events() { + local monitor_fd monitor_pid status + + coproc SLEEP_EVENTS { + exec dbus-monitor --system \ + "type='signal',sender='org.freedesktop.login1',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'" + } + monitor_fd=${SLEEP_EVENTS[0]} + monitor_pid=$SLEEP_EVENTS_PID + + cleanup_monitor() { + kill "$monitor_pid" 2>/dev/null || true + wait "$monitor_pid" 2>/dev/null || true + } + trap cleanup_monitor EXIT + + consume_sleep_events <&"$monitor_fd" + status=$? + cleanup_monitor + trap - EXIT + + return "$status" +} + +case ${1:-} in + --consume) + consume_sleep_events + exit 0 + ;; + --inhibited) + monitor_sleep_events + exit 0 + ;; +esac sleep_monitor="$OMARCHY_PATH/bin/omarchy-system-sleep-monitor" @@ -30,4 +58,4 @@ exec systemd-inhibit \ --mode=delay \ --who=Omarchy \ --why="Lock screen before suspend" \ - bash -lc "$monitor_command | \"$sleep_monitor\" --consume" + "$sleep_monitor" --inhibited diff --git a/test/shell.d/sleep-monitor-test.sh b/test/shell.d/sleep-monitor-test.sh new file mode 100755 index 00000000..20850d4b --- /dev/null +++ b/test/shell.d/sleep-monitor-test.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +sleep_monitor="$ROOT/bin/omarchy-system-sleep-monitor" +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT + +mock_bin="$tmpdir/bin" +mock_omarchy="$tmpdir/omarchy" +producer_pid_file="$tmpdir/producer-pid" +lock_log="$tmpdir/lock-log" +mkdir -p "$mock_bin" "$mock_omarchy/bin" + +cat >"$mock_bin/systemd-inhibit" <<'SH' +#!/bin/bash + +while [[ $1 == --* ]]; do + shift +done + +exec "$@" +SH + +cat >"$mock_bin/dbus-monitor" <<'SH' +#!/bin/bash + +echo "$$" >"$PRODUCER_PID_FILE" +printf ' boolean true\n' +exec sleep 30 +SH + +cat >"$mock_omarchy/bin/omarchy-system-sleep-lock" <<'SH' +#!/bin/bash + +echo locked >>"$LOCK_LOG" +SH + +chmod +x \ + "$mock_bin/systemd-inhibit" \ + "$mock_bin/dbus-monitor" \ + "$mock_omarchy/bin/omarchy-system-sleep-lock" +ln -s "$sleep_monitor" "$mock_omarchy/bin/omarchy-system-sleep-monitor" + +start_us=${EPOCHREALTIME//[!0-9]/} +OMARCHY_PATH="$mock_omarchy" \ + PATH="$mock_bin:$PATH" \ + PRODUCER_PID_FILE="$producer_pid_file" \ + LOCK_LOG="$lock_log" \ + "$sleep_monitor" +elapsed_us=$((10#${EPOCHREALTIME//[!0-9]/} - 10#$start_us)) + +[[ $(<"$lock_log") == "locked" ]] || + fail "sleep monitor invokes the lock helper for a sleep event" +pass "sleep monitor invokes the lock helper for a sleep event" + +(( elapsed_us < 2000000 )) || + fail "sleep monitor releases the inhibitor after locking" "elapsed: ${elapsed_us}us" +pass "sleep monitor releases the inhibitor after locking" + +producer_pid=$(<"$producer_pid_file") +if kill -0 "$producer_pid" 2>/dev/null; then + fail "sleep monitor reaps its event producer" "producer still running: $producer_pid" +fi +pass "sleep monitor reaps its event producer" + +# Terminating the monitor must also clean up the producer instead of orphaning +# it under the user systemd instance. +cat >"$mock_bin/dbus-monitor" <<'SH' +#!/bin/bash + +sleep 0.1 +echo "$$" >"$PRODUCER_PID_FILE" +exec sleep 30 +SH +chmod +x "$mock_bin/dbus-monitor" +rm -f "$producer_pid_file" + +OMARCHY_PATH="$mock_omarchy" \ + PATH="$mock_bin:$PATH" \ + PRODUCER_PID_FILE="$producer_pid_file" \ + LOCK_LOG="$lock_log" \ + "$sleep_monitor" & +monitor_pid=$! + +for _ in {1..100}; do + [[ -s $producer_pid_file ]] && break + sleep 0.01 +done +if [[ ! -s $producer_pid_file ]]; then + kill "$monitor_pid" 2>/dev/null || true + wait "$monitor_pid" 2>/dev/null || true + fail "sleep monitor starts its event producer" +fi + +producer_pid=$(<"$producer_pid_file") +kill "$monitor_pid" +wait "$monitor_pid" 2>/dev/null || true + +if kill -0 "$producer_pid" 2>/dev/null; then + kill "$producer_pid" 2>/dev/null || true + fail "sleep monitor cleans up its producer when terminated" "producer still running: $producer_pid" +fi +pass "sleep monitor cleans up its producer when terminated"