gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/system/service/service_windows.go (about) 1 package svc 2 3 import ( 4 "gitee.com/sy_183/go-common/lifecycle" 5 "golang.org/x/sys/windows/svc" 6 "os" 7 "os/signal" 8 ) 9 10 var isWindowsService = false 11 12 func init() { 13 var err error 14 isWindowsService, err = svc.IsWindowsService() 15 if err != nil { 16 panic(err) 17 } 18 } 19 20 type windowsService struct { 21 name string 22 app lifecycle.Lifecycle 23 exitCode int 24 25 notifySignals []os.Signal 26 signalCallback func(sig os.Signal) (exit bool) 27 exitCodeGetter func(err *Error) int 28 } 29 30 func New(name string, app lifecycle.Lifecycle, options ...Option) Service { 31 ws := &windowsService{ 32 name: name, 33 app: app, 34 notifySignals: DefaultNotifySignals[:], 35 signalCallback: DefaultSignalCallback, 36 exitCodeGetter: DefaultExitCodeGetter, 37 } 38 for _, option := range options { 39 option.apply(ws) 40 } 41 return ws 42 } 43 44 func (ws *windowsService) setSignalNotify(callback func(sig os.Signal) (exit bool), sig ...os.Signal) { 45 ws.signalCallback = callback 46 ws.notifySignals = sig 47 } 48 49 func (ws *windowsService) setExitCodeGetter(exitCodeGetter func(err *Error) int) { 50 ws.exitCodeGetter = exitCodeGetter 51 } 52 53 func (ws *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { 54 const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown 55 56 go ws.app.Run() 57 s <- svc.Status{State: svc.StartPending} 58 59 startedWaiter := ws.app.StartedWaiter() 60 closedWaiter := make(lifecycle.ChanFuture[error], 1) 61 62 for { 63 select { 64 case err := <-startedWaiter: 65 if err != nil { 66 return true, uint32(ws.exitCodeGetter(&Error{Type: StartError, Err: err})) 67 } 68 ws.app.AddClosedFuture(closedWaiter) 69 s <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} 70 case err := <-closedWaiter: 71 if err != nil { 72 return true, uint32(ws.exitCodeGetter(&Error{Type: ExitError, Err: err})) 73 } 74 return false, 0 75 case c := <-r: 76 switch c.Cmd { 77 case svc.Interrogate: 78 s <- c.CurrentStatus 79 case svc.Stop, svc.Shutdown: 80 s <- svc.Status{State: svc.StopPending} 81 ws.app.Close(nil) 82 } 83 } 84 } 85 } 86 87 func (ws *windowsService) Run() int { 88 if isWindowsService { 89 if err := svc.Run(ws.name, ws); err != nil { 90 return ws.exitCodeGetter(&Error{Type: ServiceError, Err: err}) 91 } 92 return ws.exitCode 93 } 94 go ws.app.Run() 95 96 sigChan := make(chan os.Signal) 97 signal.Notify(sigChan, ws.notifySignals...) 98 99 startedWaiter := ws.app.StartedWaiter() 100 closedWaiter := make(lifecycle.ChanFuture[error], 1) 101 for { 102 select { 103 case sig := <-sigChan: 104 if ws.signalCallback(sig) { 105 ws.app.Close(nil) 106 } 107 case err := <-startedWaiter: 108 if err != nil { 109 return ws.exitCodeGetter(&Error{Type: StartError, Err: err}) 110 } 111 ws.app.AddClosedFuture(closedWaiter) 112 case err := <-closedWaiter: 113 if err != nil { 114 return ws.exitCodeGetter(&Error{Type: ExitError, Err: err}) 115 } 116 return 0 117 } 118 } 119 }