retry poweroff on actuation failure
This commit is contained in:
@@ -168,7 +168,14 @@ func tick(ctx context.Context, src sensor.Sensor, mon *monitor.Monitor, opts *op
|
||||
}
|
||||
|
||||
if res.Shutdown {
|
||||
powerOff(opts.shutdownCmd, log)
|
||||
if err := powerOff(opts.shutdownCmd, log); err != nil {
|
||||
// Re-arm. A failed poweroff must not count as a completed one, or
|
||||
// the battery reaches cell cutoff and the HAT hard-cuts the
|
||||
// machine — the exact outcome this feature prevents.
|
||||
mon.ActuationFailed()
|
||||
log.Error("poweroff failed; will retry on the next qualifying tick",
|
||||
"attempt", mon.Retries(), "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,24 +254,25 @@ func startWatchdog(ctx context.Context, lastTick *atomic.Int64, interval time.Du
|
||||
return func() { close(done) }
|
||||
}
|
||||
|
||||
func powerOff(command string, log *slog.Logger) {
|
||||
// powerOff runs the configured poweroff command and reports whether it
|
||||
// succeeded. The caller re-arms on failure.
|
||||
func powerOff(command string, log *slog.Logger) error {
|
||||
fields := strings.Fields(command)
|
||||
if len(fields) == 0 {
|
||||
log.Error("shutdown requested but -shutdown-cmd is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := daemon.SdNotify(false, daemon.SdNotifyStopping); err != nil {
|
||||
log.Warn("could not notify systemd that we are stopping", "err", err)
|
||||
return fmt.Errorf("-shutdown-cmd is empty")
|
||||
}
|
||||
|
||||
// Deliberately NOT sending SdNotifyStopping before the attempt. Announcing
|
||||
// a stop that then fails leaves systemd believing the unit is going away
|
||||
// while it keeps running. logind ends the session on a successful poweroff
|
||||
// regardless.
|
||||
out, err := exec.Command(fields[0], fields[1:]...).CombinedOutput()
|
||||
if err != nil {
|
||||
// Do not retry. A poweroff that fails needs a human, and a retry loop
|
||||
// would fork repeatedly while the battery drains.
|
||||
log.Error("poweroff command failed",
|
||||
"cmd", command, "err", err, "output", strings.TrimSpace(string(out)))
|
||||
return
|
||||
if trimmed := strings.TrimSpace(string(out)); trimmed != "" {
|
||||
return fmt.Errorf("%s: %w: %s", command, err, trimmed)
|
||||
}
|
||||
return fmt.Errorf("%s: %w", command, err)
|
||||
}
|
||||
log.Warn("poweroff requested", "cmd", command)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user