Start begins listening for control connections.
()
| 96 | |
| 97 | // Start begins listening for control connections. |
| 98 | func (s *Server) Start() error { |
| 99 | if s.SocketPath == "" { |
| 100 | return fmt.Errorf("socket path required") |
| 101 | } |
| 102 | |
| 103 | // Check if socket file exists and is actually a socket before removing |
| 104 | if info, err := os.Lstat(s.SocketPath); err == nil { |
| 105 | if info.Mode()&os.ModeSocket != 0 { |
| 106 | if err := os.Remove(s.SocketPath); err != nil { |
| 107 | return fmt.Errorf("remove existing socket: %w", err) |
| 108 | } |
| 109 | } else { |
| 110 | return fmt.Errorf("socket path exists but is not a socket: %s", s.SocketPath) |
| 111 | } |
| 112 | } else if !os.IsNotExist(err) { |
| 113 | return fmt.Errorf("check socket path: %w", err) |
| 114 | } |
| 115 | |
| 116 | listener, err := net.Listen("unix", s.SocketPath) |
| 117 | if err != nil { |
| 118 | return fmt.Errorf("listen on unix socket: %w", err) |
| 119 | } |
| 120 | s.socketListener = listener |
| 121 | |
| 122 | if err := os.Chmod(s.SocketPath, os.FileMode(s.SocketPerms)); err != nil { |
| 123 | listener.Close() |
| 124 | return fmt.Errorf("chmod socket: %w", err) |
| 125 | } |
| 126 | |
| 127 | // Set startedAt after successful socket setup to ensure uptime reflects |
| 128 | // the actual time the server became available. |
| 129 | s.startedAt = time.Now() |
| 130 | |
| 131 | s.logger.Info("control socket listening", "path", s.SocketPath) |
| 132 | |
| 133 | s.wg.Add(1) |
| 134 | go func() { |
| 135 | defer s.wg.Done() |
| 136 | if err := s.httpServer.Serve(listener); err != nil && err != http.ErrServerClosed { |
| 137 | s.logger.Error("http server error", "error", err) |
| 138 | } |
| 139 | }() |
| 140 | |
| 141 | return nil |
| 142 | } |
| 143 | |
| 144 | // Close gracefully shuts down the control server. |
| 145 | func (s *Server) Close() error { |