package daemon import ( "context" "errors" "fmt" "isle/bootstrap" "isle/garage" "isle/secrets" "code.betamike.com/micropelago/pmux/pmuxlib" "dev.mediocregopher.com/mediocre-go-lib.git/mlog" ) // Children manages all child processes of a network. Child processes are // comprised of: // - nebula // - dnsmasq // - garage (0 or more, depending on configured storage allocations) type Children struct { logger *mlog.Logger opts Opts pmuxCancelFn context.CancelFunc pmuxStoppedCh chan struct{} } // NewChildren initialized and returns a Children instance. If initialization // fails an error is returned. func NewChildren( ctx context.Context, logger *mlog.Logger, binDirPath string, secretsStore secrets.Store, daemonConfig Config, hostBootstrap bootstrap.Bootstrap, opts *Opts, ) ( *Children, error, ) { opts = opts.withDefaults() logger.Info(ctx, "Loading secrets") garageRPCSecret, err := garage.GetRPCSecret(ctx, secretsStore) if err != nil && !errors.Is(err, secrets.ErrNotFound) { return nil, fmt.Errorf("loading garage RPC secret: %w", err) } pmuxCtx, pmuxCancelFn := context.WithCancel(context.Background()) c := &Children{ logger: logger, opts: *opts, pmuxCancelFn: pmuxCancelFn, pmuxStoppedCh: make(chan struct{}), } pmuxConfig, err := c.newPmuxConfig( ctx, garageRPCSecret, binDirPath, daemonConfig, hostBootstrap, ) if err != nil { return nil, fmt.Errorf("generating pmux config: %w", err) } go func() { defer close(c.pmuxStoppedCh) pmuxlib.Run(pmuxCtx, c.opts.Stdout, c.opts.Stderr, pmuxConfig) c.logger.Debug(pmuxCtx, "pmux stopped") }() initErr := c.postPmuxInit(ctx, daemonConfig, hostBootstrap) if initErr != nil { logger.Warn(ctx, "failed to initialize Children, shutting down child processes", err) if err := c.Shutdown(); err != nil { panic(fmt.Sprintf( "failed to shut down child processes after initialization"+ " error, there may be zombie children leftover."+ " Original error: %v", initErr, )) } return nil, initErr } return c, nil } // Shutdown blocks until all child processes have gracefully shut themselves // down. // // If this returns an error then it's possible that child processes are // still running and are no longer managed. func (c *Children) Shutdown() error { c.pmuxCancelFn() <-c.pmuxStoppedCh return nil }