A lightweight mechanism to provide an *instant kickstart* to a Go web server instance upon changing any Go source files under the project directory (and its subdirectories).

service.go 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. // Copyright 2012 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build windows
  5. // Package svc provides everything required to build Windows service.
  6. //
  7. package svc
  8. import (
  9. "errors"
  10. "runtime"
  11. "syscall"
  12. "unsafe"
  13. "golang.org/x/sys/windows"
  14. )
  15. // State describes service execution state (Stopped, Running and so on).
  16. type State uint32
  17. const (
  18. Stopped = State(windows.SERVICE_STOPPED)
  19. StartPending = State(windows.SERVICE_START_PENDING)
  20. StopPending = State(windows.SERVICE_STOP_PENDING)
  21. Running = State(windows.SERVICE_RUNNING)
  22. ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
  23. PausePending = State(windows.SERVICE_PAUSE_PENDING)
  24. Paused = State(windows.SERVICE_PAUSED)
  25. )
  26. // Cmd represents service state change request. It is sent to a service
  27. // by the service manager, and should be actioned upon by the service.
  28. type Cmd uint32
  29. const (
  30. Stop = Cmd(windows.SERVICE_CONTROL_STOP)
  31. Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
  32. Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
  33. Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
  34. Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
  35. ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
  36. NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
  37. NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
  38. NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
  39. NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
  40. DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
  41. HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
  42. PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
  43. SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
  44. )
  45. // Accepted is used to describe commands accepted by the service.
  46. // Note that Interrogate is always accepted.
  47. type Accepted uint32
  48. const (
  49. AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP)
  50. AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
  51. AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
  52. )
  53. // Status combines State and Accepted commands to fully describe running service.
  54. type Status struct {
  55. State State
  56. Accepts Accepted
  57. CheckPoint uint32 // used to report progress during a lengthy operation
  58. WaitHint uint32 // estimated time required for a pending operation, in milliseconds
  59. }
  60. // ChangeRequest is sent to the service Handler to request service status change.
  61. type ChangeRequest struct {
  62. Cmd Cmd
  63. EventType uint32
  64. EventData uintptr
  65. CurrentStatus Status
  66. }
  67. // Handler is the interface that must be implemented to build Windows service.
  68. type Handler interface {
  69. // Execute will be called by the package code at the start of
  70. // the service, and the service will exit once Execute completes.
  71. // Inside Execute you must read service change requests from r and
  72. // act accordingly. You must keep service control manager up to date
  73. // about state of your service by writing into s as required.
  74. // args contains service name followed by argument strings passed
  75. // to the service.
  76. // You can provide service exit code in exitCode return parameter,
  77. // with 0 being "no error". You can also indicate if exit code,
  78. // if any, is service specific or not by using svcSpecificEC
  79. // parameter.
  80. Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
  81. }
  82. var (
  83. // These are used by asm code.
  84. goWaitsH uintptr
  85. cWaitsH uintptr
  86. ssHandle uintptr
  87. sName *uint16
  88. sArgc uintptr
  89. sArgv **uint16
  90. ctlHandlerExProc uintptr
  91. cSetEvent uintptr
  92. cWaitForSingleObject uintptr
  93. cRegisterServiceCtrlHandlerExW uintptr
  94. )
  95. func init() {
  96. k := syscall.MustLoadDLL("kernel32.dll")
  97. cSetEvent = k.MustFindProc("SetEvent").Addr()
  98. cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
  99. a := syscall.MustLoadDLL("advapi32.dll")
  100. cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
  101. }
  102. // The HandlerEx prototype also has a context pointer but since we don't use
  103. // it at start-up time we don't have to pass it over either.
  104. type ctlEvent struct {
  105. cmd Cmd
  106. eventType uint32
  107. eventData uintptr
  108. errno uint32
  109. }
  110. // service provides access to windows service api.
  111. type service struct {
  112. name string
  113. h windows.Handle
  114. cWaits *event
  115. goWaits *event
  116. c chan ctlEvent
  117. handler Handler
  118. }
  119. func newService(name string, handler Handler) (*service, error) {
  120. var s service
  121. var err error
  122. s.name = name
  123. s.c = make(chan ctlEvent)
  124. s.handler = handler
  125. s.cWaits, err = newEvent()
  126. if err != nil {
  127. return nil, err
  128. }
  129. s.goWaits, err = newEvent()
  130. if err != nil {
  131. s.cWaits.Close()
  132. return nil, err
  133. }
  134. return &s, nil
  135. }
  136. func (s *service) close() error {
  137. s.cWaits.Close()
  138. s.goWaits.Close()
  139. return nil
  140. }
  141. type exitCode struct {
  142. isSvcSpecific bool
  143. errno uint32
  144. }
  145. func (s *service) updateStatus(status *Status, ec *exitCode) error {
  146. if s.h == 0 {
  147. return errors.New("updateStatus with no service status handle")
  148. }
  149. var t windows.SERVICE_STATUS
  150. t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
  151. t.CurrentState = uint32(status.State)
  152. if status.Accepts&AcceptStop != 0 {
  153. t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
  154. }
  155. if status.Accepts&AcceptShutdown != 0 {
  156. t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
  157. }
  158. if status.Accepts&AcceptPauseAndContinue != 0 {
  159. t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
  160. }
  161. if ec.errno == 0 {
  162. t.Win32ExitCode = windows.NO_ERROR
  163. t.ServiceSpecificExitCode = windows.NO_ERROR
  164. } else if ec.isSvcSpecific {
  165. t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
  166. t.ServiceSpecificExitCode = ec.errno
  167. } else {
  168. t.Win32ExitCode = ec.errno
  169. t.ServiceSpecificExitCode = windows.NO_ERROR
  170. }
  171. t.CheckPoint = status.CheckPoint
  172. t.WaitHint = status.WaitHint
  173. return windows.SetServiceStatus(s.h, &t)
  174. }
  175. const (
  176. sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
  177. sysErrNewThreadInCallback
  178. )
  179. func (s *service) run() {
  180. s.goWaits.Wait()
  181. s.h = windows.Handle(ssHandle)
  182. argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
  183. args := make([]string, len(argv))
  184. for i, a := range argv {
  185. args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:])
  186. }
  187. cmdsToHandler := make(chan ChangeRequest)
  188. changesFromHandler := make(chan Status)
  189. exitFromHandler := make(chan exitCode)
  190. go func() {
  191. ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
  192. exitFromHandler <- exitCode{ss, errno}
  193. }()
  194. status := Status{State: Stopped}
  195. ec := exitCode{isSvcSpecific: true, errno: 0}
  196. var outch chan ChangeRequest
  197. inch := s.c
  198. var cmd Cmd
  199. var evtype uint32
  200. var evdata uintptr
  201. loop:
  202. for {
  203. select {
  204. case r := <-inch:
  205. if r.errno != 0 {
  206. ec.errno = r.errno
  207. break loop
  208. }
  209. inch = nil
  210. outch = cmdsToHandler
  211. cmd = r.cmd
  212. evtype = r.eventType
  213. evdata = r.eventData
  214. case outch <- ChangeRequest{cmd, evtype, evdata, status}:
  215. inch = s.c
  216. outch = nil
  217. case c := <-changesFromHandler:
  218. err := s.updateStatus(&c, &ec)
  219. if err != nil {
  220. // best suitable error number
  221. ec.errno = sysErrSetServiceStatusFailed
  222. if err2, ok := err.(syscall.Errno); ok {
  223. ec.errno = uint32(err2)
  224. }
  225. break loop
  226. }
  227. status = c
  228. case ec = <-exitFromHandler:
  229. break loop
  230. }
  231. }
  232. s.updateStatus(&Status{State: Stopped}, &ec)
  233. s.cWaits.Set()
  234. }
  235. func newCallback(fn interface{}) (cb uintptr, err error) {
  236. defer func() {
  237. r := recover()
  238. if r == nil {
  239. return
  240. }
  241. cb = 0
  242. switch v := r.(type) {
  243. case string:
  244. err = errors.New(v)
  245. case error:
  246. err = v
  247. default:
  248. err = errors.New("unexpected panic in syscall.NewCallback")
  249. }
  250. }()
  251. return syscall.NewCallback(fn), nil
  252. }
  253. // BUG(brainman): There is no mechanism to run multiple services
  254. // inside one single executable. Perhaps, it can be overcome by
  255. // using RegisterServiceCtrlHandlerEx Windows api.
  256. // Run executes service name by calling appropriate handler function.
  257. func Run(name string, handler Handler) error {
  258. runtime.LockOSThread()
  259. tid := windows.GetCurrentThreadId()
  260. s, err := newService(name, handler)
  261. if err != nil {
  262. return err
  263. }
  264. ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
  265. e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
  266. // We assume that this callback function is running on
  267. // the same thread as Run. Nowhere in MS documentation
  268. // I could find statement to guarantee that. So putting
  269. // check here to verify, otherwise things will go bad
  270. // quickly, if ignored.
  271. i := windows.GetCurrentThreadId()
  272. if i != tid {
  273. e.errno = sysErrNewThreadInCallback
  274. }
  275. s.c <- e
  276. // Always return NO_ERROR (0) for now.
  277. return 0
  278. }
  279. var svcmain uintptr
  280. getServiceMain(&svcmain)
  281. t := []windows.SERVICE_TABLE_ENTRY{
  282. {syscall.StringToUTF16Ptr(s.name), svcmain},
  283. {nil, 0},
  284. }
  285. goWaitsH = uintptr(s.goWaits.h)
  286. cWaitsH = uintptr(s.cWaits.h)
  287. sName = t[0].ServiceName
  288. ctlHandlerExProc, err = newCallback(ctlHandler)
  289. if err != nil {
  290. return err
  291. }
  292. go s.run()
  293. err = windows.StartServiceCtrlDispatcher(&t[0])
  294. if err != nil {
  295. return err
  296. }
  297. return nil
  298. }
  299. // StatusHandle returns service status handle. It is safe to call this function
  300. // from inside the Handler.Execute because then it is guaranteed to be set.
  301. // This code will have to change once multiple services are possible per process.
  302. func StatusHandle() windows.Handle {
  303. return windows.Handle(ssHandle)
  304. }