diff --git a/modules/power/README.md b/modules/power/README.md index 74f3e27..a6d7741 100644 --- a/modules/power/README.md +++ b/modules/power/README.md @@ -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. diff --git a/modules/power/cmd/power/check.go b/modules/power/cmd/power/check.go index 30cde61..7f92f20 100644 --- a/modules/power/cmd/power/check.go +++ b/modules/power/cmd/power/check.go @@ -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) } diff --git a/modules/power/module.json b/modules/power/module.json index babec0b..e35522a 100644 --- a/modules/power/module.json +++ b/modules/power/module.json @@ -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": {