package systemcontrol import ( "bufio" "bytes" "context" "encoding/json" "errors" "io" "log" "net" "sync" "time" ) const DefaultSocketPath = "/run/tera-hwd/system-control.sock" type request struct { Action string `json:"action"` } type response struct { State string `json:"state,omitempty"` Pressed *bool `json:"pressed,omitempty"` Code string `json:"code,omitempty"` Error string `json:"error,omitempty"` } type Server struct { Controller Controller ResetButton ResetButtonReader RebootDelay time.Duration rebootOnce sync.Once } func (s *Server) Serve(listener net.Listener) error { if s == nil || s.Controller == nil { return errors.New("system controller is required") } for { connection, err := listener.Accept() if err != nil { if errors.Is(err, net.ErrClosed) { return nil } continue } go s.handle(connection) } } func (s *Server) handle(connection net.Conn) { defer connection.Close() _ = connection.SetDeadline(time.Now().Add(5 * time.Second)) payload, err := bufio.NewReader(io.LimitReader(connection, 4097)).ReadBytes('\n') if err != nil && !errors.Is(err, io.EOF) || len(payload) == 0 || len(payload) > 4096 || payload[len(payload)-1] != '\n' { s.write(connection, response{Code: "validation_failed", Error: "invalid system control request"}) return } decoder := json.NewDecoder(bytes.NewReader(payload)) decoder.DisallowUnknownFields() var command request if err := decoder.Decode(&command); err != nil { s.write(connection, response{Code: "validation_failed", Error: "invalid system control request"}) return } var extra any if err := decoder.Decode(&extra); !errors.Is(err, io.EOF) { s.write(connection, response{Code: "validation_failed", Error: "system control request must contain one JSON value"}) return } if command.Action == "reset_button_state" { if s.ResetButton == nil { s.write(connection, response{Code: "unavailable", Error: "reset button is not configured"}) return } pressed, err := s.ResetButton.ResetButtonPressed(context.Background()) if err != nil { s.write(connection, response{Code: "unavailable", Error: err.Error()}) return } s.write(connection, response{State: "ready", Pressed: &pressed}) return } if command.Action != "reboot" { s.write(connection, response{Code: "validation_failed", Error: "system control action is not supported"}) return } delay := s.RebootDelay if delay <= 0 { delay = 2 * time.Second } s.write(connection, response{State: "scheduled"}) s.rebootOnce.Do(func() { time.AfterFunc(delay, func() { if err := s.Controller.Reboot(context.Background()); err != nil { log.Printf("tera-hwd scheduled reboot failed: %v", err) } }) }) } func (*Server) write(connection net.Conn, value response) { _ = json.NewEncoder(connection).Encode(value) } type Client struct { path string } func NewClient(path string) *Client { if path == "" { path = DefaultSocketPath } return &Client{path: path} } func (c *Client) Reboot(ctx context.Context) error { result, err := c.request(ctx, "reboot") if err != nil { return err } if result.State != "scheduled" { return errors.New("system reboot was not scheduled") } return nil } func (c *Client) ResetButtonPressed(ctx context.Context) (bool, error) { result, err := c.request(ctx, "reset_button_state") if err != nil { return false, err } if result.State != "ready" || result.Pressed == nil { return false, errors.New("reset button state is unavailable") } return *result.Pressed, nil } func (c *Client) request(ctx context.Context, action string) (response, error) { dialer := net.Dialer{Timeout: 3 * time.Second} connection, err := dialer.DialContext(ctx, "unix", c.path) if err != nil { return response{}, errors.New("privileged system control is not reachable") } defer connection.Close() deadline := time.Now().Add(5 * time.Second) if value, ok := ctx.Deadline(); ok && value.Before(deadline) { deadline = value } _ = connection.SetDeadline(deadline) if err := json.NewEncoder(connection).Encode(request{Action: action}); err != nil { return response{}, errors.New("system control request could not be sent") } var result response if err := json.NewDecoder(io.LimitReader(connection, 4096)).Decode(&result); err != nil { return response{}, errors.New("privileged system control returned no response") } if result.Code != "" { if result.Error != "" { return response{}, errors.New(result.Error) } return response{}, errors.New("system control request failed") } return result, nil }