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.
7 // Package svc provides everything required to build Windows service.
17 "golang.org/x/sys/internal/unsafeheader"
18 "golang.org/x/sys/windows"
21 // State describes service execution state (Stopped, Running and so on).
25 Stopped = State(windows.SERVICE_STOPPED)
26 StartPending = State(windows.SERVICE_START_PENDING)
27 StopPending = State(windows.SERVICE_STOP_PENDING)
28 Running = State(windows.SERVICE_RUNNING)
29 ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
30 PausePending = State(windows.SERVICE_PAUSE_PENDING)
31 Paused = State(windows.SERVICE_PAUSED)
34 // Cmd represents service state change request. It is sent to a service
35 // by the service manager, and should be actioned upon by the service.
39 Stop = Cmd(windows.SERVICE_CONTROL_STOP)
40 Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
41 Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
42 Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
43 Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
44 ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
45 NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
46 NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
47 NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
48 NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
49 DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
50 HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
51 PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
52 SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
53 PreShutdown = Cmd(windows.SERVICE_CONTROL_PRESHUTDOWN)
56 // Accepted is used to describe commands accepted by the service.
57 // Note that Interrogate is always accepted.
61 AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP)
62 AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
63 AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
64 AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)
65 AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE)
66 AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
67 AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT)
68 AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE)
69 AcceptPreShutdown = Accepted(windows.SERVICE_ACCEPT_PRESHUTDOWN)
72 // Status combines State and Accepted commands to fully describe running service.
76 CheckPoint uint32 // used to report progress during a lengthy operation
77 WaitHint uint32 // estimated time required for a pending operation, in milliseconds
78 ProcessId uint32 // if the service is running, the process identifier of it, and otherwise zero
79 Win32ExitCode uint32 // set if the service has exited with a win32 exit code
80 ServiceSpecificExitCode uint32 // set if the service has exited with a service-specific exit code
83 // ChangeRequest is sent to the service Handler to request service status change.
84 type ChangeRequest struct {
92 // Handler is the interface that must be implemented to build Windows service.
93 type Handler interface {
95 // Execute will be called by the package code at the start of
96 // the service, and the service will exit once Execute completes.
97 // Inside Execute you must read service change requests from r and
98 // act accordingly. You must keep service control manager up to date
99 // about state of your service by writing into s as required.
100 // args contains service name followed by argument strings passed
102 // You can provide service exit code in exitCode return parameter,
103 // with 0 being "no error". You can also indicate if exit code,
104 // if any, is service specific or not by using svcSpecificEC
106 Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
110 // These are used by asm code.
117 ctlHandlerExProc uintptr
119 cWaitForSingleObject uintptr
120 cRegisterServiceCtrlHandlerExW uintptr
124 k := windows.NewLazySystemDLL("kernel32.dll")
125 cSetEvent = k.NewProc("SetEvent").Addr()
126 cWaitForSingleObject = k.NewProc("WaitForSingleObject").Addr()
127 a := windows.NewLazySystemDLL("advapi32.dll")
128 cRegisterServiceCtrlHandlerExW = a.NewProc("RegisterServiceCtrlHandlerExW").Addr()
131 type ctlEvent struct {
139 // service provides access to windows service api.
140 type service struct {
149 func newService(name string, handler Handler) (*service, error) {
153 s.c = make(chan ctlEvent)
155 s.cWaits, err = newEvent()
159 s.goWaits, err = newEvent()
167 func (s *service) close() error {
173 type exitCode struct {
178 func (s *service) updateStatus(status *Status, ec *exitCode) error {
180 return errors.New("updateStatus with no service status handle")
182 var t windows.SERVICE_STATUS
183 t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
184 t.CurrentState = uint32(status.State)
185 if status.Accepts&AcceptStop != 0 {
186 t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
188 if status.Accepts&AcceptShutdown != 0 {
189 t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
191 if status.Accepts&AcceptPauseAndContinue != 0 {
192 t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
194 if status.Accepts&AcceptParamChange != 0 {
195 t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
197 if status.Accepts&AcceptNetBindChange != 0 {
198 t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
200 if status.Accepts&AcceptHardwareProfileChange != 0 {
201 t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
203 if status.Accepts&AcceptPowerEvent != 0 {
204 t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
206 if status.Accepts&AcceptSessionChange != 0 {
207 t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
209 if status.Accepts&AcceptPreShutdown != 0 {
210 t.ControlsAccepted |= windows.SERVICE_ACCEPT_PRESHUTDOWN
213 t.Win32ExitCode = windows.NO_ERROR
214 t.ServiceSpecificExitCode = windows.NO_ERROR
215 } else if ec.isSvcSpecific {
216 t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
217 t.ServiceSpecificExitCode = ec.errno
219 t.Win32ExitCode = ec.errno
220 t.ServiceSpecificExitCode = windows.NO_ERROR
222 t.CheckPoint = status.CheckPoint
223 t.WaitHint = status.WaitHint
224 return windows.SetServiceStatus(s.h, &t)
228 sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
229 sysErrNewThreadInCallback
232 func (s *service) run() {
234 s.h = windows.Handle(ssHandle)
237 hdr := (*unsafeheader.Slice)(unsafe.Pointer(&argv))
238 hdr.Data = unsafe.Pointer(sArgv)
242 args := make([]string, len(argv))
243 for i, a := range argv {
244 args[i] = windows.UTF16PtrToString(a)
247 cmdsToHandler := make(chan ChangeRequest)
248 changesFromHandler := make(chan Status)
249 exitFromHandler := make(chan exitCode)
252 ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
253 exitFromHandler <- exitCode{ss, errno}
256 ec := exitCode{isSvcSpecific: true, errno: 0}
257 outcr := ChangeRequest{
258 CurrentStatus: Status{State: Stopped},
260 var outch chan ChangeRequest
271 outch = cmdsToHandler
273 outcr.EventType = r.eventType
274 outcr.EventData = r.eventData
275 outcr.Context = r.context
279 case c := <-changesFromHandler:
280 err := s.updateStatus(&c, &ec)
282 // best suitable error number
283 ec.errno = sysErrSetServiceStatusFailed
284 if err2, ok := err.(syscall.Errno); ok {
285 ec.errno = uint32(err2)
289 outcr.CurrentStatus = c
290 case ec = <-exitFromHandler:
295 s.updateStatus(&Status{State: Stopped}, &ec)
299 func newCallback(fn interface{}) (cb uintptr, err error) {
306 switch v := r.(type) {
312 err = errors.New("unexpected panic in syscall.NewCallback")
315 return syscall.NewCallback(fn), nil
318 // BUG(brainman): There is no mechanism to run multiple services
319 // inside one single executable. Perhaps, it can be overcome by
320 // using RegisterServiceCtrlHandlerEx Windows api.
322 // Run executes service name by calling appropriate handler function.
323 func Run(name string, handler Handler) error {
324 runtime.LockOSThread()
326 tid := windows.GetCurrentThreadId()
328 s, err := newService(name, handler)
333 ctlHandler := func(ctl, evtype, evdata, context uintptr) uintptr {
334 e := ctlEvent{cmd: Cmd(ctl), eventType: uint32(evtype), eventData: evdata, context: context}
335 // We assume that this callback function is running on
336 // the same thread as Run. Nowhere in MS documentation
337 // I could find statement to guarantee that. So putting
338 // check here to verify, otherwise things will go bad
339 // quickly, if ignored.
340 i := windows.GetCurrentThreadId()
342 e.errno = sysErrNewThreadInCallback
345 // Always return NO_ERROR (0) for now.
346 return windows.NO_ERROR
350 getServiceMain(&svcmain)
351 t := []windows.SERVICE_TABLE_ENTRY{
352 {ServiceName: syscall.StringToUTF16Ptr(s.name), ServiceProc: svcmain},
353 {ServiceName: nil, ServiceProc: 0},
356 goWaitsH = uintptr(s.goWaits.h)
357 cWaitsH = uintptr(s.cWaits.h)
358 sName = t[0].ServiceName
359 ctlHandlerExProc, err = newCallback(ctlHandler)
366 err = windows.StartServiceCtrlDispatcher(&t[0])
373 // StatusHandle returns service status handle. It is safe to call this function
374 // from inside the Handler.Execute because then it is guaranteed to be set.
375 // This code will have to change once multiple services are possible per process.
376 func StatusHandle() windows.Handle {
377 return windows.Handle(ssHandle)