OSDN Git Service

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