From 992780349731479d99da01925452f2f6d0d8ef37 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 27 Mar 2023 23:13:24 +0700 Subject: [PATCH] cmd/ctrld: response to OS service manager earlier When startup, ctrld waits for network up before calling s.Run to starts its logic. However, if network is down on startup, ctrld will hang on waiting for network up. That causes OS service manager unhappy, as ctrld do not response to it, marking ctrld as failure service and never start ctrld again. To fix this, we should call s.Run as soon as possible, and use a channel for waiting a signal that we can actual do our logic after network up. Update #34 --- cmd/ctrld/cli.go | 47 +++++++++++++++++++++++++++++++---------------- cmd/ctrld/prog.go | 7 ++++++- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/cmd/ctrld/cli.go b/cmd/ctrld/cli.go index d722358..f25cc63 100644 --- a/cmd/ctrld/cli.go +++ b/cmd/ctrld/cli.go @@ -90,6 +90,35 @@ func initCLI() { if daemon && runtime.GOOS == "windows" { log.Fatal("Cannot run in daemon mode. Please install a Windows service.") } + + waitCh := make(chan struct{}) + stopCh := make(chan struct{}) + if !daemon { + // We need to call s.Run() as soon as possible to response to the OS manager, so it + // can see ctrld is running and don't mark ctrld as failed service. + go func() { + p := &prog{ + waitCh: waitCh, + stopCh: stopCh, + } + s, err := service.New(p, svcConfig) + if err != nil { + mainLog.Fatal().Err(err).Msg("failed create new service") + } + serviceLogger, err := s.Logger(nil) + if err != nil { + mainLog.Error().Err(err).Msg("failed to get service logger") + return + } + + if err := s.Run(); err != nil { + if sErr := serviceLogger.Error(err); sErr != nil { + mainLog.Error().Err(sErr).Msg("failed to write service log") + } + mainLog.Error().Err(err).Msg("failed to start service") + } + }() + } noConfigStart := isNoConfigStart(cmd) writeDefaultConfig := !noConfigStart && configBase64 == "" configs := []struct { @@ -150,22 +179,8 @@ func initCLI() { os.Exit(0) } - s, err := service.New(&prog{}, svcConfig) - if err != nil { - mainLog.Fatal().Err(err).Msg("failed create new service") - } - serviceLogger, err := s.Logger(nil) - if err != nil { - mainLog.Error().Err(err).Msg("failed to get service logger") - return - } - - if err := s.Run(); err != nil { - if sErr := serviceLogger.Error(err); sErr != nil { - mainLog.Error().Err(sErr).Msg("failed to write service log") - } - mainLog.Error().Err(err).Msg("failed to start service") - } + close(waitCh) + <-stopCh }, } runCmd.Flags().BoolVarP(&daemon, "daemon", "d", false, "Run as daemon") diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index 6f4abad..0206402 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -28,7 +28,9 @@ var svcConfig = &service.Config{ } type prog struct { - mu sync.Mutex + mu sync.Mutex + waitCh chan struct{} + stopCh chan struct{} cfg *ctrld.Config cache dnscache.Cacher @@ -41,6 +43,8 @@ func (p *prog) Start(s service.Service) error { } func (p *prog) run() { + // Wait the caller to signal that we can do our logic. + <-p.waitCh p.preRun() if p.cfg.Service.CacheEnable { cacher, err := dnscache.NewLRUCache(p.cfg.Service.CacheSize) @@ -132,6 +136,7 @@ func (p *prog) Stop(s service.Service) error { return err } mainLog.Info().Msg("Service stopped") + close(p.stopCh) return nil }