本站源代码
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.

365 lines
11KB

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