.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / sys@v0.0.0-20210124154548-22da62e12c0c / 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/internal/unsafeheader"
18         "golang.org/x/sys/windows"
19 )
20
21 // State describes service execution state (Stopped, Running and so on).
22 type State uint32
23
24 const (
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)
32 )
33
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.
36 type Cmd uint32
37
38 const (
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)
54 )
55
56 // Accepted is used to describe commands accepted by the service.
57 // Note that Interrogate is always accepted.
58 type Accepted uint32
59
60 const (
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)
70 )
71
72 // Status combines State and Accepted commands to fully describe running service.
73 type Status struct {
74         State                   State
75         Accepts                 Accepted
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
81 }
82
83 // ChangeRequest is sent to the service Handler to request service status change.
84 type ChangeRequest struct {
85         Cmd           Cmd
86         EventType     uint32
87         EventData     uintptr
88         CurrentStatus Status
89         Context       uintptr
90 }
91
92 // Handler is the interface that must be implemented to build Windows service.
93 type Handler interface {
94
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
101         // to the service.
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
105         // parameter.
106         Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
107 }
108
109 var (
110         // These are used by asm code.
111         goWaitsH                       uintptr
112         cWaitsH                        uintptr
113         ssHandle                       uintptr
114         sName                          *uint16
115         sArgc                          uintptr
116         sArgv                          **uint16
117         ctlHandlerExProc               uintptr
118         cSetEvent                      uintptr
119         cWaitForSingleObject           uintptr
120         cRegisterServiceCtrlHandlerExW uintptr
121 )
122
123 func init() {
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()
129 }
130
131 type ctlEvent struct {
132         cmd       Cmd
133         eventType uint32
134         eventData uintptr
135         context   uintptr
136         errno     uint32
137 }
138
139 // service provides access to windows service api.
140 type service struct {
141         name    string
142         h       windows.Handle
143         cWaits  *event
144         goWaits *event
145         c       chan ctlEvent
146         handler Handler
147 }
148
149 func newService(name string, handler Handler) (*service, error) {
150         var s service
151         var err error
152         s.name = name
153         s.c = make(chan ctlEvent)
154         s.handler = handler
155         s.cWaits, err = newEvent()
156         if err != nil {
157                 return nil, err
158         }
159         s.goWaits, err = newEvent()
160         if err != nil {
161                 s.cWaits.Close()
162                 return nil, err
163         }
164         return &s, nil
165 }
166
167 func (s *service) close() error {
168         s.cWaits.Close()
169         s.goWaits.Close()
170         return nil
171 }
172
173 type exitCode struct {
174         isSvcSpecific bool
175         errno         uint32
176 }
177
178 func (s *service) updateStatus(status *Status, ec *exitCode) error {
179         if s.h == 0 {
180                 return errors.New("updateStatus with no service status handle")
181         }
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
187         }
188         if status.Accepts&AcceptShutdown != 0 {
189                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
190         }
191         if status.Accepts&AcceptPauseAndContinue != 0 {
192                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
193         }
194         if status.Accepts&AcceptParamChange != 0 {
195                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
196         }
197         if status.Accepts&AcceptNetBindChange != 0 {
198                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
199         }
200         if status.Accepts&AcceptHardwareProfileChange != 0 {
201                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
202         }
203         if status.Accepts&AcceptPowerEvent != 0 {
204                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
205         }
206         if status.Accepts&AcceptSessionChange != 0 {
207                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
208         }
209         if status.Accepts&AcceptPreShutdown != 0 {
210                 t.ControlsAccepted |= windows.SERVICE_ACCEPT_PRESHUTDOWN
211         }
212         if ec.errno == 0 {
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
218         } else {
219                 t.Win32ExitCode = ec.errno
220                 t.ServiceSpecificExitCode = windows.NO_ERROR
221         }
222         t.CheckPoint = status.CheckPoint
223         t.WaitHint = status.WaitHint
224         return windows.SetServiceStatus(s.h, &t)
225 }
226
227 const (
228         sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
229         sysErrNewThreadInCallback
230 )
231
232 func (s *service) run() {
233         s.goWaits.Wait()
234         s.h = windows.Handle(ssHandle)
235
236         var argv []*uint16
237         hdr := (*unsafeheader.Slice)(unsafe.Pointer(&argv))
238         hdr.Data = unsafe.Pointer(sArgv)
239         hdr.Len = int(sArgc)
240         hdr.Cap = int(sArgc)
241
242         args := make([]string, len(argv))
243         for i, a := range argv {
244                 args[i] = windows.UTF16PtrToString(a)
245         }
246
247         cmdsToHandler := make(chan ChangeRequest)
248         changesFromHandler := make(chan Status)
249         exitFromHandler := make(chan exitCode)
250
251         go func() {
252                 ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
253                 exitFromHandler <- exitCode{ss, errno}
254         }()
255
256         ec := exitCode{isSvcSpecific: true, errno: 0}
257         outcr := ChangeRequest{
258                 CurrentStatus: Status{State: Stopped},
259         }
260         var outch chan ChangeRequest
261         inch := s.c
262 loop:
263         for {
264                 select {
265                 case r := <-inch:
266                         if r.errno != 0 {
267                                 ec.errno = r.errno
268                                 break loop
269                         }
270                         inch = nil
271                         outch = cmdsToHandler
272                         outcr.Cmd = r.cmd
273                         outcr.EventType = r.eventType
274                         outcr.EventData = r.eventData
275                         outcr.Context = r.context
276                 case outch <- outcr:
277                         inch = s.c
278                         outch = nil
279                 case c := <-changesFromHandler:
280                         err := s.updateStatus(&c, &ec)
281                         if err != nil {
282                                 // best suitable error number
283                                 ec.errno = sysErrSetServiceStatusFailed
284                                 if err2, ok := err.(syscall.Errno); ok {
285                                         ec.errno = uint32(err2)
286                                 }
287                                 break loop
288                         }
289                         outcr.CurrentStatus = c
290                 case ec = <-exitFromHandler:
291                         break loop
292                 }
293         }
294
295         s.updateStatus(&Status{State: Stopped}, &ec)
296         s.cWaits.Set()
297 }
298
299 func newCallback(fn interface{}) (cb uintptr, err error) {
300         defer func() {
301                 r := recover()
302                 if r == nil {
303                         return
304                 }
305                 cb = 0
306                 switch v := r.(type) {
307                 case string:
308                         err = errors.New(v)
309                 case error:
310                         err = v
311                 default:
312                         err = errors.New("unexpected panic in syscall.NewCallback")
313                 }
314         }()
315         return syscall.NewCallback(fn), nil
316 }
317
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.
321
322 // Run executes service name by calling appropriate handler function.
323 func Run(name string, handler Handler) error {
324         runtime.LockOSThread()
325
326         tid := windows.GetCurrentThreadId()
327
328         s, err := newService(name, handler)
329         if err != nil {
330                 return err
331         }
332
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()
341                 if i != tid {
342                         e.errno = sysErrNewThreadInCallback
343                 }
344                 s.c <- e
345                 // Always return NO_ERROR (0) for now.
346                 return windows.NO_ERROR
347         }
348
349         var svcmain uintptr
350         getServiceMain(&svcmain)
351         t := []windows.SERVICE_TABLE_ENTRY{
352                 {ServiceName: syscall.StringToUTF16Ptr(s.name), ServiceProc: svcmain},
353                 {ServiceName: nil, ServiceProc: 0},
354         }
355
356         goWaitsH = uintptr(s.goWaits.h)
357         cWaitsH = uintptr(s.cWaits.h)
358         sName = t[0].ServiceName
359         ctlHandlerExProc, err = newCallback(ctlHandler)
360         if err != nil {
361                 return err
362         }
363
364         go s.run()
365
366         err = windows.StartServiceCtrlDispatcher(&t[0])
367         if err != nil {
368                 return err
369         }
370         return nil
371 }
372
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)
378 }