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  }