You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

service.go 11KB

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