// Package systemcontrol defines the privileged system-control boundary used by // the unprivileged management API. package systemcontrol import ( "context" "errors" "fmt" "os" "os/exec" "path/filepath" "strings" ) type Controller interface { Reboot(context.Context) error } // ResetButtonReader exposes the physical settings-reset button state without // granting the unprivileged API direct GPIO access. type ResetButtonReader interface { ResetButtonPressed(context.Context) (bool, error) } type SysfsResetButton struct { ValuePath string ActiveLow bool } func (b SysfsResetButton) ResetButtonPressed(ctx context.Context) (bool, error) { if err := ctx.Err(); err != nil { return false, err } path := strings.TrimSpace(b.ValuePath) if path == "" || !filepath.IsAbs(path) { return false, errors.New("reset button value path is not configured") } content, err := os.ReadFile(path) if err != nil { return false, fmt.Errorf("read reset button value: %w", err) } value := strings.TrimSpace(string(content)) if value != "0" && value != "1" { return false, errors.New("reset button value must be 0 or 1") } pressed := value == "1" if b.ActiveLow { pressed = !pressed } return pressed, nil } type CommandController struct { RebootCommand string } func (c CommandController) Reboot(ctx context.Context) error { command := strings.TrimSpace(c.RebootCommand) if command == "" { command = "/sbin/reboot" } if err := ctx.Err(); err != nil { return err } process := exec.Command(command) if err := process.Start(); err != nil { return err } return process.Process.Release() } type MockController struct { RebootError error } func (c MockController) Reboot(context.Context) error { return c.RebootError } type UnavailableController struct{ Reason string } func (c UnavailableController) Reboot(context.Context) error { reason := strings.TrimSpace(c.Reason) if reason == "" { reason = "privileged system control is not configured" } return errors.New(reason) }