gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/system/service.v2/service_windows.go (about)

     1  package svc
     2  
     3  import (
     4  	"gitee.com/sy_183/go-common/lifecycle.v2"
     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  	ws.app.Background()
    57  	s <- svc.Status{State: svc.StartPending}
    58  
    59  	startedWaiter := ws.app.Waiter(lifecycle.FutureTypeStarted)
    60  	closedWaiter := ws.app.Waiter(lifecycle.FutureTypeClosed)
    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  			s <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
    69  		case err := <-closedWaiter:
    70  			if err != nil {
    71  				return true, uint32(ws.exitCodeGetter(&Error{Type: ExitError, Err: err}))
    72  			}
    73  			return false, 0
    74  		case c := <-r:
    75  			switch c.Cmd {
    76  			case svc.Interrogate:
    77  				s <- c.CurrentStatus
    78  			case svc.Stop, svc.Shutdown:
    79  				s <- svc.Status{State: svc.StopPending}
    80  				ws.app.Close()
    81  			}
    82  		}
    83  	}
    84  }
    85  
    86  func (ws *windowsService) Run() int {
    87  	if isWindowsService {
    88  		if err := svc.Run(ws.name, ws); err != nil {
    89  			return ws.exitCodeGetter(&Error{Type: ServiceError, Err: err})
    90  		}
    91  		return ws.exitCode
    92  	}
    93  	ws.app.Background()
    94  
    95  	sigChan := make(chan os.Signal)
    96  	signal.Notify(sigChan, ws.notifySignals...)
    97  
    98  	startedWaiter := ws.app.Waiter(lifecycle.FutureTypeStarted)
    99  	closedWaiter := ws.app.Waiter(lifecycle.FutureTypeClosed)
   100  	for {
   101  		select {
   102  		case sig := <-sigChan:
   103  			if ws.signalCallback(sig) {
   104  				ws.app.Close()
   105  			}
   106  		case err := <-startedWaiter:
   107  			if err != nil {
   108  				return ws.exitCodeGetter(&Error{Type: StartError, Err: err})
   109  			}
   110  		case err := <-closedWaiter:
   111  			if err != nil {
   112  				return ws.exitCodeGetter(&Error{Type: ExitError, Err: err})
   113  			}
   114  			return 0
   115  		}
   116  	}
   117  }