package wincmd import ( "errors" "fmt" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/eventlog" "golang.org/x/sys/windows/svc/mgr" "time" ) type SvcStatus svc.State const ( Stopped = SvcStatus(svc.Stopped) StartPending = SvcStatus(svc.StartPending) StopPending = SvcStatus(svc.StopPending) Running = SvcStatus(svc.Running) ContinuePending = SvcStatus(svc.ContinuePending) PausePending = SvcStatus(svc.PausePending) Paused = SvcStatus(svc.Paused) StartManual = windows.SERVICE_DEMAND_START // the service must be started manually StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots StartDisabled = windows.SERVICE_DISABLED // the service cannot be started // The severity of the error, and action taken, // if this service fails to start. ErrorCritical = windows.SERVICE_ERROR_CRITICAL ErrorIgnore = windows.SERVICE_ERROR_IGNORE ErrorNormal = windows.SERVICE_ERROR_NORMAL ErrorSevere = windows.SERVICE_ERROR_SEVERE ) type WinSvcExecute struct { Run func() Stop func() Interrupt func() Pause func() Continue func() OtherMethod func(svc.Cmd) Name string Accepted []svc.Accepted } type WinSvcInput struct { Name string DisplayName string ExecPath string DelayedAutoStart bool Description string StartType uint32 Args []string } type WinSvc struct { *mgr.Service } func IsServiceExists(name string) (bool, error) { if !Isas() { return false, errors.New("permission deny") } winmgr, err := mgr.Connect() if err != nil { return false, err } defer winmgr.Disconnect() lists, err := winmgr.ListServices() if err != nil { return false, err } for _, v := range lists { if name == v { return true, nil } } return false, nil } func CreateService(mysvc WinSvcInput) (*WinSvc, error) { if !Isas() { return nil, errors.New("permission deny") } if exists, err := IsServiceExists(mysvc.Name); err != nil { return nil, err } else if exists { return nil, errors.New("service already exists") } winmgr, err := mgr.Connect() if err != nil { return nil, err } defer winmgr.Disconnect() mycfg := mgr.Config{ DisplayName: mysvc.DisplayName, StartType: mysvc.StartType, DelayedAutoStart: mysvc.DelayedAutoStart, Description: mysvc.Description, } gsvc, err := winmgr.CreateService(mysvc.Name, mysvc.ExecPath, mycfg, mysvc.Args...) if err != nil { return nil, err } err = eventlog.InstallAsEventCreate(mysvc.Name, eventlog.Error|eventlog.Warning|eventlog.Info) if err != nil { gsvc.Delete() return nil, fmt.Errorf("winsvc.InstallService: InstallAsEventCreate failed, err = %v", err) } var result WinSvc result.Service = gsvc return &result, nil } func OpenService(name string) (*WinSvc, error) { if !Isas() { return nil, errors.New("permission deny") } if exists, err := IsServiceExists(name); err != nil { return nil, err } else if !exists { return nil, errors.New("service not exists") } winmgr, err := mgr.Connect() if err != nil { return nil, err } defer winmgr.Disconnect() gsvc, err := winmgr.OpenService(name) if err != nil { return nil, err } var result WinSvc result.Service = gsvc return &result, nil } func DeleteService(name string) error { mysvc, err := OpenService(name) if err != nil { return err } err = mysvc.Service.Delete() if err != nil { mysvc.Close() return err } mysvc.Close() err = eventlog.Remove(name) if err != nil { return err } var count int for { if ok, err := IsServiceExists(name); err != nil { return err } else if !ok { return nil } time.Sleep(time.Millisecond * 300) count++ if count > 100 { return errors.New("timeout") } } } func StopService(name string) error { mysvc, err := OpenService(name) if err != nil { return err } defer mysvc.Close() _, err = mysvc.Service.Control(svc.Stop) if err != nil { return err } var count int for { status, err := mysvc.Service.Query() if err != nil { return err } if status.State == svc.Stopped { return nil } time.Sleep(time.Millisecond * 100) count++ if count > 100 { return errors.New("timeout") } } } func StartService(name string) error { mysvc, err := OpenService(name) if err != nil { return err } defer mysvc.Close() err = mysvc.Service.Start() if err != nil { return err } var count int for { status, err := mysvc.Service.Query() if err != nil { return err } if status.State == svc.Running { return nil } time.Sleep(time.Millisecond * 100) count++ if count > 100 { return errors.New("timeout") } } } func ServiceStatus(name string) (SvcStatus, error) { mysvc, err := OpenService(name) if err != nil { return Stopped, err } defer mysvc.Close() status, err := mysvc.Service.Query() return SvcStatus(status.State), err } func InService() (bool, error) { if !Isas() { return false, nil } return svc.IsWindowsService() } func (w *WinSvc) Stop() error { return StopService(w.Name) } func (w *WinSvc) Delete() error { if err := w.Close(); err != nil { return err } return DeleteService(w.Name) } func (w *WinSvc) StartService() error { err := w.Service.Start() if err != nil { return err } var count int for { sts, err := w.Query() if err != nil { return err } if SvcStatus(sts.State) == Running { return nil } time.Sleep(time.Millisecond * 100) count++ if count > 100 { return errors.New("timeout") } } } func InServiceBool() bool { ok, _ := svc.IsWindowsService() return ok } func (w *WinSvcExecute) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { var sva svc.Accepted alreadyStoped := make(chan int) for _, v := range w.Accepted { sva = sva | v } s <- svc.Status{State: svc.StartPending} go func() { s <- svc.Status{State: svc.Running, Accepts: sva} w.Run() alreadyStoped <- 1 }() for { select { case <-alreadyStoped: return case c := <-r: switch c.Cmd { case svc.Interrogate: s <- c.CurrentStatus w.Interrupt() s <- c.CurrentStatus case svc.Stop, svc.Shutdown: s <- svc.Status{State: svc.StopPending} w.Stop() s <- svc.Status{State: svc.Stopped} return case svc.Pause: s <- svc.Status{State: svc.PausePending, Accepts: sva} if w.Pause != nil { w.Pause() } s <- svc.Status{State: svc.Paused, Accepts: sva} case svc.Continue: s <- svc.Status{State: svc.ContinuePending, Accepts: sva} if w.Continue != nil { w.Continue() } s <- svc.Status{State: svc.Running, Accepts: sva} default: if w.OtherMethod != nil { w.OtherMethod(c.Cmd) } s <- svc.Status{State: svc.Running, Accepts: sva} } } } } func NewWinSvcExecute(name string, run, stop func()) *WinSvcExecute { var res WinSvcExecute res.Run = run res.Stop = stop res.Interrupt = func() { time.Sleep(time.Millisecond) } res.Accepted = []svc.Accepted{svc.AcceptStop, svc.AcceptShutdown, svc.AcceptPauseAndContinue} return &res } func (w *WinSvcExecute) StartService() error { return svc.Run(w.Name, w) } func (w *WinSvcExecute) InService() (bool, error) { if !Isas() { return false, nil } return svc.IsWindowsService() } func (w *WinSvcExecute) InServiceBool() bool { ok, _ := svc.IsWindowsService() return ok }