power: the sleep hooks are wanted by the sleep targets, not declared as services

The host reads a one-shot that is not running as having run, so a before-sleep or after-wake unit
declared stopped failed on its first apply; drop-ins on the sleep targets pull them in instead.
This commit is contained in:
jochen
2026-10-04 17:22:35 +02:00
parent fee36954e5
commit ac1a6fca38
3 changed files with 78 additions and 17 deletions
+7
View File
@@ -73,3 +73,10 @@ again. A machine that said `sleeping` is asleep, not out of touch.
The lesson from the day this module was written belongs to the dbus module: a full upgrade restarted
the system bus live on a workstation, and logins hung until a reboot.
**Why the sleep hooks are wanted, not enabled.** The host reads a one-shot unit that is not running
and has not failed as having run and worked, so a hook unit waiting for a sleep could never be
declared stopped. The first version tried that and was refused on its first apply. The two sleep
units are therefore not services the mesh manages. They have no install section, and the sleep
targets want them through this module's drop-ins (`sleep.target.d`, and `suspend.target.d` with
its three siblings) — the same way the laptop module asks for the NVIDIA driver's sleep actions.
+4 -1
View File
@@ -32,8 +32,11 @@ func (m *Machine) Check(ctx context.Context, w *Watcher) map[string]any {
enabled, _ := m.Run(ctx, "systemctl", "is-enabled", u)
enabled = strings.TrimSpace(enabled)
want := "enabled"
if u == "mesh-power-supply.service" {
switch u {
case "mesh-power-supply.service":
want = "static" // started by the udev rule, never by a target
case "mesh-power-before-sleep.service", "mesh-power-after-wake.service":
want = "static" // wanted by the sleep targets through the module's drop-ins, never enabled
}
add(u, present && enabled == want, "%s: file present %v, %s (want %s)", Units[u], present, orWord(enabled, "unknown"), want)
}
+67 -16
View File
@@ -127,14 +127,14 @@
"type": "file",
"path": "/etc/systemd/system/mesh-power-before-sleep.service",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nDescription=Every module's code before sleep (mesh power)\nBefore=sleep.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/lib/mesh-power/bin/power-moment before-sleep\n\n[Install]\nWantedBy=sleep.target\n"
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n#\n# Pulled in by sleep.target through this module's drop-in on it, never enabled: a one-shot waiting\n# for a sleep is not a service whose state the mesh can declare.\n[Unit]\nDescription=Every module's code before sleep (mesh power)\nBefore=sleep.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/lib/mesh-power/bin/power-moment before-sleep\n"
},
{
"id": "after-wake-unit",
"type": "file",
"path": "/etc/systemd/system/mesh-power-after-wake.service",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nDescription=Every module's code after waking (mesh power)\nAfter=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/lib/mesh-power/bin/power-moment after-wake\n\n[Install]\nWantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target\n"
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n#\n# Pulled in by the four sleep targets through this module's drop-ins on them, and ordered after\n# them, so it runs once the machine is awake again. Never enabled.\n[Unit]\nDescription=Every module's code after waking (mesh power)\nAfter=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/lib/mesh-power/bin/power-moment after-wake\n"
},
{
"id": "supply-unit",
@@ -143,6 +143,71 @@
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n#\n# Started by the module's udev rule when a power supply changes; runs on-mains or on-battery once per\n# change of source.\n[Unit]\nDescription=Every module's code for the power source (mesh power)\n\n[Service]\nType=oneshot\nExecStart=/usr/local/lib/mesh-power/bin/power-moment supply\n"
},
{
"id": "sleep-drop-ins",
"type": "directory",
"path": "/etc/systemd/system/sleep.target.d",
"mode": "0755"
},
{
"id": "sleep-wants",
"type": "file",
"path": "/etc/systemd/system/sleep.target.d/mesh-power.conf",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nWants=mesh-power-before-sleep.service\n"
},
{
"id": "suspend-drop-ins",
"type": "directory",
"path": "/etc/systemd/system/suspend.target.d",
"mode": "0755"
},
{
"id": "suspend-wants",
"type": "file",
"path": "/etc/systemd/system/suspend.target.d/mesh-power.conf",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nWants=mesh-power-after-wake.service\n"
},
{
"id": "hibernate-drop-ins",
"type": "directory",
"path": "/etc/systemd/system/hibernate.target.d",
"mode": "0755"
},
{
"id": "hibernate-wants",
"type": "file",
"path": "/etc/systemd/system/hibernate.target.d/mesh-power.conf",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nWants=mesh-power-after-wake.service\n"
},
{
"id": "hybrid-sleep-drop-ins",
"type": "directory",
"path": "/etc/systemd/system/hybrid-sleep.target.d",
"mode": "0755"
},
{
"id": "hybrid-sleep-wants",
"type": "file",
"path": "/etc/systemd/system/hybrid-sleep.target.d/mesh-power.conf",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nWants=mesh-power-after-wake.service\n"
},
{
"id": "suspend-then-hibernate-drop-ins",
"type": "directory",
"path": "/etc/systemd/system/suspend-then-hibernate.target.d",
"mode": "0755"
},
{
"id": "suspend-then-hibernate-wants",
"type": "file",
"path": "/etc/systemd/system/suspend-then-hibernate.target.d/mesh-power.conf",
"mode": "0644",
"content": "# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.\n[Unit]\nWants=mesh-power-after-wake.service\n"
},
{
"id": "udev-rule",
"type": "file",
@@ -163,20 +228,6 @@
"unit": "mesh-power-before-shutdown.service",
"state": "running",
"boot": "enabled"
},
{
"id": "before-sleep",
"type": "service",
"unit": "mesh-power-before-sleep.service",
"state": "stopped",
"boot": "enabled"
},
{
"id": "after-wake",
"type": "service",
"unit": "mesh-power-after-wake.service",
"state": "stopped",
"boot": "enabled"
}
],
"build": {