1package cmd 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "sync" 9 10 runtimedebug "runtime/debug" 11 12 "github.com/sirupsen/logrus" 13 "golang.org/x/sys/windows/svc" 14 "golang.org/x/sys/windows/svc/debug" 15 "golang.org/x/sys/windows/svc/eventlog" 16) 17 18var ( 19 _ svc.Handler = &Service{} 20 elog debug.Log 21) 22 23func NewService(args []string) *Service { 24 return &Service{args: args} 25} 26 27type Service struct { 28 args []string 29 wg sync.WaitGroup 30 mu sync.Mutex 31} 32 33func (s *Service) start(ctx context.Context, args []string, changes chan<- svc.Status) chan error { 34 s.mu.Lock() 35 defer s.mu.Unlock() 36 s.wg.Wait() 37 s.wg.Add(1) 38 result := make(chan error, 1) 39 go func() { 40 defer func() { 41 if e := recover(); e != nil { 42 changes <- svc.Status{State: svc.Stopped} 43 stack := runtimedebug.Stack() 44 result <- errors.New(string(stack)) 45 } 46 }() 47 defer s.wg.Done() 48 changes <- svc.Status{State: svc.StartPending} 49 // Start service here 50 binPath, err := exePath() 51 if err != nil { 52 panic(err) 53 } 54 configFile := args[0] 55 logPath := args[1] 56 logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) 57 if err != nil { 58 result <- fmt.Errorf("service quit: cant't open log file: %s", err) 59 } 60 defer logFile.Close() 61 62 logger := logrus.New() 63 logger.SetFormatter(&logrus.JSONFormatter{}) 64 logger.SetOutput(logFile) 65 entry := logger.WithFields(logrus.Fields{ 66 "component": "cmd", 67 }) 68 69 args = []string{binPath, "start", "-c", configFile} 70 command := newStartCommand(ctx, args, entry) 71 accepts := svc.AcceptShutdown | svc.AcceptStop 72 changes <- svc.Status{State: svc.Running, Accepts: accepts} 73 74 if err := command.Execute(); err != nil { 75 logger.WithError(err).Error("sensu-agent exited with error") 76 result <- err 77 } 78 }() 79 return result 80} 81 82func (s *Service) Execute(_ []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) { 83 ctx, cancel := context.WithCancel(context.Background()) 84 errs := s.start(ctx, s.args, changes) 85 elog, _ := eventlog.Open(serviceName) 86 defer elog.Close() 87 for { 88 select { 89 case req := <-r: 90 switch req.Cmd { 91 case svc.Stop, svc.Shutdown: 92 elog.Info(1, "service shutting down") 93 changes <- svc.Status{State: svc.StopPending} 94 cancel() 95 s.wg.Wait() 96 changes <- svc.Status{State: svc.Stopped} 97 return false, 0 98 } 99 case err := <-errs: 100 elog.Error(1, fmt.Sprintf("restarting due to error (%v) %s", s.args, err)) 101 s.start(ctx, s.args, changes) 102 } 103 } 104 return false, 0 105} 106 107func runService(args []string) error { 108 elog, err := eventlog.Open(serviceName) 109 if err != nil { 110 return err 111 } 112 defer elog.Close() 113 elog.Info(1, fmt.Sprintf("starting %s service (%v)", serviceName, args)) 114 if err := svc.Run(serviceName, NewService(args)); err != nil { 115 return err 116 } 117 elog.Info(1, fmt.Sprintf("%s service terminated", serviceName)) 118 return nil 119} 120