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.
91 lines
3.3 KiB
Go
91 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Check is one thing the module expects of the machine.
|
|
type Check struct {
|
|
Name string `json:"name"`
|
|
OK bool `json:"ok"`
|
|
Detail string `json:"detail"`
|
|
}
|
|
|
|
// Check says what this module expects and whether the machine meets it: its units in place and
|
|
// enabled, the runner and the moment files present, logind's file the mesh's, the inhibitor held,
|
|
// and the predecessor's leftovers.
|
|
func (m *Machine) Check(ctx context.Context, w *Watcher) map[string]any {
|
|
var checks []Check
|
|
add := func(name string, ok bool, format string, args ...any) {
|
|
checks = append(checks, Check{Name: name, OK: ok, Detail: fmt.Sprintf(format, args...)})
|
|
}
|
|
units := make([]string, 0, len(Units))
|
|
for u := range Units {
|
|
units = append(units, u)
|
|
}
|
|
sort.Strings(units)
|
|
for _, u := range units {
|
|
present := m.exists("/etc/systemd/system/" + u)
|
|
enabled, _ := m.Run(ctx, "systemctl", "is-enabled", u)
|
|
enabled = strings.TrimSpace(enabled)
|
|
want := "enabled"
|
|
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)
|
|
}
|
|
add("runner", m.exists(RunnerPath), "%s present: %v", RunnerPath, m.exists(RunnerPath))
|
|
for _, moment := range Moments {
|
|
p := MomentsDir + "/" + moment
|
|
add("moment "+moment, m.exists(p), "%s placed: %v, %d piece(s)", p, m.exists(p), len(Pieces(m.read(p)+"\n")))
|
|
}
|
|
logind := m.read(LogindFile)
|
|
add("logind's power file", strings.Contains(logind, "module power"), "%s is the mesh's (module power): %v",
|
|
LogindFile, strings.Contains(logind, "module power"))
|
|
var others []string
|
|
for _, f := range m.glob("/etc/systemd/logind.conf.d/*.conf") {
|
|
if f != LogindFile {
|
|
others = append(others, f)
|
|
}
|
|
}
|
|
add("one writer of logind's power handling", len(others) == 0,
|
|
"other logind drop-ins: %s (novox/hq ADR 0210: logind's power settings are this module's alone)", orWord(strings.Join(others, ", "), "none"))
|
|
hooks := m.glob("/etc/systemd/system-sleep/*")
|
|
add("no hand-placed sleep hooks", len(hooks) == 0,
|
|
"%s (code around sleep is a contribution to node-power now, novox/hq ADR 0211)", orWord(strings.Join(hooks, ", "), "none"))
|
|
add("udev rule", m.exists(UdevRule) || Source(m.Supplies()) == "none", "%s present: %v (only acts where there is a Mains supply)", UdevRule, m.exists(UdevRule))
|
|
if w != nil {
|
|
s := w.Snapshot()
|
|
add("sleep inhibitor", s.Inhibiting, "delay lock on sleep and shutdown held by the watcher: %v%s", s.Inhibiting, orNote(s.Problem))
|
|
add("events reach the bus", s.Pending == 0, "%d event(s) waiting for the bus", s.Pending)
|
|
}
|
|
failing := 0
|
|
for _, c := range checks {
|
|
if !c.OK {
|
|
failing++
|
|
}
|
|
}
|
|
return map[string]any{"checks": checks, "failing": failing,
|
|
"not_checked": []string{"that a contribution's own code does what it means (power_run runs one moment now)"}}
|
|
}
|
|
|
|
func orWord(s, word string) string {
|
|
if strings.TrimSpace(s) == "" {
|
|
return word
|
|
}
|
|
return s
|
|
}
|
|
|
|
func orNote(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
return " (" + s + ")"
|
|
}
|