port the sleep and suspend section

The original ran unconditionally, so a laptop that went through it stopped
suspending — a hot bag and a flat battery. It is a server concern: dev is skipped
with the reason printed, like the ballast.

Four things wrong with it beyond the role:

  HandleLidSwitchDocked was never set. A laptop used as a homelab server, docked
  and closed, still suspends — which is the exact machine this setting exists
  for. Added.

  RuntimeDirectorySize=10% was set alongside the sleep handlers. It is the size
  of /run, has nothing to do with sleeping, and 10% is systemd's own default, so
  the line never did anything. Dropped.

  systemd-logind was restarted on every pass whether or not anything changed,
  disturbing live sessions for nothing. The step now checks first and does not
  reach the restart when the machine is already configured. (The platform's own
  scripts/setup-old/setup.sh already had this guard; the machine script did not.)

  The settings were sed'd into logind.conf in place. They are a drop-in at
  /etc/systemd/logind.conf.d/99-machine-setup.conf now, so what this script set
  is one file that can be read or removed on its own.

Current state is printed before anything is asked — whether the targets are
masked, and what the lid, idle and power-key handlers actually do. logind_effective
reads the main file and every drop-in and takes the last match, since a drop-in
overrides logind.conf; reading only the main file reports a configured machine as
unconfigured.

The power-button consequence is stated rather than left to be discovered: after
this, pressing power physically does nothing and a clean shutdown is
`sudo poweroff`.

WSL has no logind and cannot suspend, and says so.

Verified both roles on this host, which the old script had already configured —
correctly reports the targets masked and the handlers set, and correctly reports
itself not fully configured because HandleLidSwitchDocked is missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 18:11:16 +00:00
co-authored by Claude Opus 5
parent 6ffd3534bd
commit c87127ff93
2 changed files with 134 additions and 1 deletions
+83
View File
@@ -355,6 +355,89 @@ EOF
sysctl -q -w "fs.inotify.max_user_instances=${INOTIFY_INSTANCES}"
}
# -----------------------------------------------------------------------------
# Sleep and suspend
# -----------------------------------------------------------------------------
#
# A server that suspends is a server that is off. The machine stops answering,
# and on a box with no keyboard attached there is nothing to wake it — which is
# the whole failure: it looks like a crash, and the only fix is physical.
#
# Two independent mechanisms, and both have to be dealt with:
#
# the sleep targets what suspend/hibernate hang off. Masking them means
# nothing can trigger a sleep, including a stray
# `systemctl suspend`
# logind's handlers what closing a lid, pressing the power button or going
# idle DO. These are what a desktop image sets, and they
# act before anything reaches a target
#
# Written as a drop-in rather than by editing logind.conf in place, so what this
# script set is one file that can be read or deleted on its own.
SLEEP_TARGETS=(sleep.target suspend.target hibernate.target hybrid-sleep.target)
LOGIND_DROPIN=/etc/systemd/logind.conf.d/99-machine-setup.conf
# The value actually in force for a logind setting, or empty for the default.
# Drop-ins override the main file and later ones override earlier, so the last
# match wins — reading only logind.conf would miss a setting made by a drop-in
# and report the machine as unconfigured when it is not.
logind_effective() {
local key="$1"
{
[[ -r /etc/systemd/logind.conf ]] && grep -hE "^${key}=" /etc/systemd/logind.conf
for f in /etc/systemd/logind.conf.d/*.conf; do
[[ -r "$f" ]] && grep -hE "^${key}=" "$f"
done
} 2>/dev/null | tail -1 | cut -d= -f2-
}
sleep_targets_masked() {
local t
for t in "${SLEEP_TARGETS[@]}"; do
[[ "$(systemctl is-enabled "$t" 2>/dev/null)" == "masked" ]] || return 1
done
}
# What the machine should be set to. RuntimeDirectorySize is deliberately NOT
# here: the original set it to 10% alongside these, which is both unrelated to
# sleeping — it is the size of /run — and systemd's own default, so the line
# never did anything.
logind_wanted() {
cat <<'EOF'
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
HandlePowerKey=ignore
IdleAction=none
EOF
}
# Is every wanted setting already in force?
logind_is_configured() {
local line key value
while IFS= read -r line; do
key="${line%%=*}"
value="${line#*=}"
[[ "$(logind_effective "$key")" == "$value" ]] || return 1
done < <(logind_wanted)
}
disable_sleep() {
systemctl mask "${SLEEP_TARGETS[@]}" >/dev/null 2>&1
mkdir -p "$(dirname "$LOGIND_DROPIN")"
{
echo "# Written by machine-setup: this machine is a server and must not sleep."
echo "[Login]"
logind_wanted
} >"$LOGIND_DROPIN"
# Only restart when something actually changed — a needless restart of logind
# disturbs live sessions, and this step runs on every pass.
systemctl restart systemd-logind
}
# -----------------------------------------------------------------------------
# Timezone
# -----------------------------------------------------------------------------