package daemon import ( "fmt" "os" "path/filepath" "runtime" "github.com/kardianos/service" ) // program implements the kardianos/service.Interface for greywall daemon // lifecycle management. It delegates actual work to the Server type. type program struct { server *Server socketPath string tun2socksPath string debug bool } // NewProgram creates a new program instance for use with kardianos/service. func NewProgram(socketPath, tun2socksPath string, debug bool) *program { return &program{ socketPath: socketPath, tun2socksPath: tun2socksPath, debug: debug, } } // Start is called by kardianos/service when the service starts. It verifies // the tun2socks binary exists, creates and starts the Server. The accept loop // already runs in a goroutine, so this returns immediately. func (p *program) Start(_ service.Service) error { if _, err := os.Stat(p.tun2socksPath); err != nil { return fmt.Errorf("tun2socks binary not found at %s (run 'sudo greywall daemon install' first)", p.tun2socksPath) } Logf("Starting daemon (tun2socks=%s, socket=%s)", p.tun2socksPath, p.socketPath) p.server = NewServer(p.socketPath, p.tun2socksPath, p.debug) if err := p.server.Start(); err != nil { return fmt.Errorf("failed to start daemon server: %w", err) } Logf("Daemon started, listening on %s", p.socketPath) return nil } // Stop is called by kardianos/service when the service stops. func (p *program) Stop(_ service.Service) error { if p.server == nil { return nil } Logf("Stopping daemon") if err := p.server.Stop(); err != nil { Logf("Shutdown error: %v", err) return err } Logf("Daemon stopped") return nil } // NewServiceConfig returns a kardianos/service config matching the existing // LaunchDaemon setup. The Name matches LaunchDaemonLabel so service.Control() // can find and manage the already-installed service. func NewServiceConfig() *service.Config { return &service.Config{ Name: LaunchDaemonLabel, DisplayName: "Greywall Daemon", Description: "Greywall transparent network sandboxing daemon", Arguments: []string{"daemon", "run"}, } } // DefaultTun2socksPath returns the expected tun2socks binary path based on // the install directory and current architecture. func DefaultTun2socksPath() string { return filepath.Join(InstallLibDir, "tun2socks-darwin-"+runtime.GOARCH) }