diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index e612bb731..2c70387dc 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -101,6 +101,7 @@ type Server struct { // connection (such as on Windows by default). Even if this // is true, the ForceDaemon pref can override this. resetOnZero bool + opts Options bsMu sync.Mutex // lock order: bsMu, then mu bs *ipn.BackendServer @@ -725,11 +726,24 @@ func Run(ctx context.Context, logf logger.Logf, logid string, getEngine func() ( } } + server, err := New(logf, logid, store, eng, serverModeUser, opts) + if err != nil { + return err + } + serverMu.Lock() + serverOrNil = server + serverMu.Unlock() + return server.Serve(ctx, listen) +} + +// New returns a new Server. +// +// The opts.StatePath option is ignored; it's only used by Run. +func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engine, serverModeUser *user.User, opts Options) (*Server, error) { b, err := ipnlocal.NewLocalBackend(logf, logid, store, eng) if err != nil { - return fmt.Errorf("NewLocalBackend: %v", err) + return nil, fmt.Errorf("NewLocalBackend: %v", err) } - defer b.Shutdown() b.SetDecompressor(func() (controlclient.Decompressor, error) { return smallzstd.NewDecoder(nil) }) @@ -746,35 +760,45 @@ func Run(ctx context.Context, logf logger.Logf, logid string, getEngine func() ( logf: logf, resetOnZero: !opts.SurviveDisconnects, serverModeUser: serverModeUser, + opts: opts, } server.bs = ipn.NewBackendServer(logf, b, server.writeToClients) + return server, nil +} - serverMu.Lock() - serverOrNil = server - serverMu.Unlock() - - if opts.AutostartStateKey != "" { - server.bs.GotCommand(context.TODO(), &ipn.Command{ +// Serve accepts connections from ln forever. +// +// The context is only used to suppress errors +func (s *Server) Serve(ctx context.Context, ln net.Listener) error { + defer s.b.Shutdown() + if s.opts.AutostartStateKey != "" { + s.bs.GotCommand(ctx, &ipn.Command{ Version: version.Long, Start: &ipn.StartArgs{ - Opts: ipn.Options{StateKey: opts.AutostartStateKey}, + Opts: ipn.Options{StateKey: s.opts.AutostartStateKey}, }, }) } systemd.Ready() - for i := 1; ctx.Err() == nil; i++ { - c, err := listen.Accept() + bo := backoff.NewBackoff("ipnserver", s.logf, 30*time.Second) + var connNum int + for { + if ctx.Err() != nil { + return ctx.Err() + } + c, err := ln.Accept() if err != nil { - if ctx.Err() == nil { - logf("ipnserver: Accept: %v", err) - bo.BackOff(ctx, err) + if ctx.Err() != nil { + return ctx.Err() } + s.logf("ipnserver: Accept: %v", err) + bo.BackOff(ctx, err) continue } - go server.serveConn(ctx, c, logger.WithPrefix(logf, fmt.Sprintf("ipnserver: conn%d: ", i))) + connNum++ + go s.serveConn(ctx, c, logger.WithPrefix(s.logf, fmt.Sprintf("ipnserver: conn%d: ", connNum))) } - return ctx.Err() } // BabysitProc runs the current executable as a child process with the