wake_alarm + midnight_shutdown: hibernate on alarm nights instead of poweroff

- install.sh: install sleep-hook.sh to systemd-sleep hooks (step 3) and
  shutdown-wrapper.sh to /usr/local/bin/shutdown (step 5)
- shutdown-wrapper.sh: new script that intercepts shutdown calls and
  redirects to rtcwake -m disk on alarm nights (Mon/Fri/Sat/Sun), pass-
  through for reboots and cancel commands
- sleep-hook.sh: new systemd-sleep hook that restarts wake-alarm.service
  for each logged-in user after hibernate resume
- setup_midnight_shutdown.sh: check wake-alarm day; if yes set RTC alarm
  and hibernate, otherwise fall back to systemctl poweroff
This commit is contained in:
Krzysztof kuhy Rudnicki 2026-05-22 16:01:04 +02:00
parent 3a51a10ca9
commit db21d3015f
4 changed files with 98 additions and 5 deletions

View File

@ -6,32 +6,44 @@
# What it does: # What it does:
# 1. Copies wake-alarm.service to ~/.config/systemd/user/ # 1. Copies wake-alarm.service to ~/.config/systemd/user/
# 2. Enables and starts the service # 2. Enables and starts the service
# 3. Adds a sudoers entry for passwordless rtcwake # 3. Installs the systemd-sleep hook (restarts alarm after hibernate resume)
# 4. Adds a sudoers entry for passwordless rtcwake
# 5. Installs shutdown wrapper so "shutdown now" also hibernates on alarm nights
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
SERVICE_FILE="$SCRIPT_DIR/wake-alarm.service" SERVICE_FILE="$SCRIPT_DIR/wake-alarm.service"
SLEEP_HOOK_SRC="$SCRIPT_DIR/sleep-hook.sh"
SHUTDOWN_WRAPPER_SRC="$SCRIPT_DIR/shutdown-wrapper.sh"
SYSTEMD_USER_DIR="$HOME/.config/systemd/user" SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
SLEEP_HOOK_DST="/usr/lib/systemd/system-sleep/wake-alarm.sh"
SHUTDOWN_WRAPPER_DST="/usr/local/bin/shutdown"
SUDOERS_FILE="/etc/sudoers.d/wake-alarm" SUDOERS_FILE="/etc/sudoers.d/wake-alarm"
RTCWAKE_BIN="/usr/sbin/rtcwake" RTCWAKE_BIN="/usr/sbin/rtcwake"
echo "=== Weekend Wake Alarm Installer ===" echo "=== Weekend Wake Alarm Installer ==="
# 1. Install systemd user service # 1. Install systemd user service
echo "[1/3] Installing systemd user service..." echo "[1/4] Installing systemd user service..."
mkdir -p "$SYSTEMD_USER_DIR" mkdir -p "$SYSTEMD_USER_DIR"
cp "$SERVICE_FILE" "$SYSTEMD_USER_DIR/wake-alarm.service" cp "$SERVICE_FILE" "$SYSTEMD_USER_DIR/wake-alarm.service"
systemctl --user daemon-reload systemctl --user daemon-reload
echo " Installed to $SYSTEMD_USER_DIR/wake-alarm.service" echo " Installed to $SYSTEMD_USER_DIR/wake-alarm.service"
# 2. Enable service # 2. Enable service
echo "[2/3] Enabling wake-alarm.service..." echo "[2/4] Enabling wake-alarm.service..."
systemctl --user enable wake-alarm.service systemctl --user enable wake-alarm.service
echo " Service enabled (will start on next boot)" echo " Service enabled (will start on next boot)"
# 3. Add sudoers entry for rtcwake (requires root) # 3. Install systemd-sleep hook (restarts alarm after hibernate resume)
echo "[3/3] Setting up sudoers for rtcwake..." echo "[3/4] Installing systemd-sleep hook..."
sudo cp "$SLEEP_HOOK_SRC" "$SLEEP_HOOK_DST"
sudo chmod 0755 "$SLEEP_HOOK_DST"
echo " Installed to $SLEEP_HOOK_DST"
# 4. Add sudoers entry for rtcwake (requires root)
echo "[4/5] Setting up sudoers for rtcwake..."
SUDOERS_LINE="$USER ALL=(root) NOPASSWD: $RTCWAKE_BIN" SUDOERS_LINE="$USER ALL=(root) NOPASSWD: $RTCWAKE_BIN"
if [[ -f "$SUDOERS_FILE" ]] && grep -qF "$SUDOERS_LINE" "$SUDOERS_FILE"; then if [[ -f "$SUDOERS_FILE" ]] && grep -qF "$SUDOERS_LINE" "$SUDOERS_FILE"; then
echo " Sudoers entry already exists" echo " Sudoers entry already exists"
@ -42,7 +54,15 @@ else
echo " Added: $SUDOERS_LINE" echo " Added: $SUDOERS_LINE"
fi fi
# 5. Install shutdown wrapper (/usr/local/bin/shutdown shadows /usr/bin/shutdown)
echo "[5/5] Installing shutdown wrapper..."
sudo cp "$SHUTDOWN_WRAPPER_SRC" "$SHUTDOWN_WRAPPER_DST"
sudo chmod 0755 "$SHUTDOWN_WRAPPER_DST"
echo " Installed to $SHUTDOWN_WRAPPER_DST"
echo " 'shutdown now' will now hibernate (not poweroff) on alarm nights."
echo "" echo ""
echo "=== Installation complete ===" echo "=== Installation complete ==="
echo "The wake alarm will activate on boot for alarm days (Mon, Fri, Sat, Sun)." echo "The wake alarm will activate on boot for alarm days (Mon, Fri, Sat, Sun)."
echo "After hibernate resume the sleep hook will restart the alarm service."
echo "To test now: python -m python_pkg.wake_alarm._alarm --demo" echo "To test now: python -m python_pkg.wake_alarm._alarm --demo"

38
wake_alarm/shutdown-wrapper.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/bash
# Wrapper for /usr/bin/shutdown that redirects to rtcwake -m disk on alarm
# nights (Mon, Fri, Sat, Sun by tomorrow's day-of-week). This ensures that
# both the automated systemd timer AND manual "shutdown now" hibernate
# correctly so the PC wakes for the morning alarm.
#
# Install to /usr/local/bin/shutdown (takes priority over /usr/bin/shutdown
# because /usr/local/bin appears first in PATH).
set -euo pipefail
REAL_SHUTDOWN=/usr/bin/shutdown
RTCWAKE=/usr/sbin/rtcwake
WAKE_AFTER_HOURS=8 # Must match WAKE_AFTER_HOURS in python_pkg/wake_alarm/_constants.py
# Pass through reboots and cancel commands unchanged.
for arg in "$@"; do
case "$arg" in
-r|--reboot|-c|--cancel)
exec "$REAL_SHUTDOWN" "$@"
;;
esac
done
# Check if tomorrow is an alarm day (Mon=1, Fri=5, Sat=6, Sun=7 in date +%u).
tomorrow_dow=$(date -d "tomorrow" +%u)
case "$tomorrow_dow" in
1|5|6|7)
wake_epoch=$(( $(printf '%(%s)T' -1) + WAKE_AFTER_HOURS * 3600 ))
logger -t shutdown-wrapper \
"Tomorrow is alarm day (dow=$tomorrow_dow) — hibernating, RTC wake at epoch $wake_epoch"
sudo "$RTCWAKE" -m no -t "$wake_epoch"
exec /usr/bin/systemctl hibernate
;;
*)
exec "$REAL_SHUTDOWN" "$@"
;;
esac

29
wake_alarm/sleep-hook.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
# systemd-sleep hook: restart wake-alarm.service after resume from hibernate.
#
# Installed to /usr/lib/systemd/system-sleep/wake-alarm.sh by install.sh.
#
# When the PC hibernates (rtcwake -m disk) and resumes the next morning,
# the user session is restored but wake-alarm.service is in a stopped state
# (it ran at login the previous evening and exited with Restart=no).
# This hook restarts it so the alarm fires on the correct alarm day.
if [[ "$1" != "post" ]]; then
exit 0
fi
logger -t wake-alarm-hook "Woke from sleep (type=$2) — restarting wake-alarm.service for active sessions"
# Start wake-alarm.service for every logged-in user that has a running session
# bus. Works with systemd >= 219.
while IFS= read -r uid; do
runtime_dir="/run/user/$uid"
[[ -d "$runtime_dir" ]] || continue
username=$(id -nu "$uid" 2>/dev/null) || continue
logger -t wake-alarm-hook "Starting wake-alarm.service for user $username (uid=$uid)"
XDG_RUNTIME_DIR="$runtime_dir" \
DBUS_SESSION_BUS_ADDRESS="unix:path=${runtime_dir}/bus" \
runuser -u "$username" -- \
systemctl --user start wake-alarm.service 2>/dev/null \
|| logger -t wake-alarm-hook "Failed to start wake-alarm.service for $username (non-fatal)"
done < <(loginctl list-sessions --no-legend 2>/dev/null | awk '{print $2}' | sort -u)

View File

@ -0,0 +1,6 @@
{
"date": "2026-05-22",
"dismissed_at": null,
"skip_workout": false,
"hmac": "2261cf16bef81ce45f8f0664454c441a88bdacf1e71161a10c50472ff63fdf8c"
}