Skip to content

Commit e62c3de

Browse files
pastarmovjalexbrainman
authored andcommitted
windows: use RegisterServiceCtrlHandlerEx to allow for advanced notifications
This allows services written with this library to register for more advanced notifications like SERVICE_CONTROL_DEVICEEVENT for example. Also the code now exposes the service status handle through a new api call, because the handle is needed to register for such notifications and can not be obtained by any other means. Change-Id: I80796e1dd9d94ec9d6c236d0413b17288c67fe1f Reviewed-on: https://go-review.googlesource.com/42812 Reviewed-by: Alex Brainman <[email protected]> Run-TryBot: Alex Brainman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 98b5b1e commit e62c3de

4 files changed

Lines changed: 58 additions & 29 deletions

File tree

windows/svc/debug/service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func Run(name string, handler svc.Handler) error {
3131
for {
3232
select {
3333
case <-sig:
34-
cmds <- svc.ChangeRequest{svc.Stop, status}
34+
cmds <- svc.ChangeRequest{svc.Stop, 0, 0, status}
3535
case status = <-changes:
3636
}
3737
}

windows/svc/service.go

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,20 @@ const (
3535
type Cmd uint32
3636

3737
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)
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)
4352
)
4453

4554
// Accepted is used to describe commands accepted by the service.
@@ -63,6 +72,8 @@ type Status struct {
6372
// ChangeRequest is sent to the service Handler to request service status change.
6473
type ChangeRequest struct {
6574
Cmd Cmd
75+
EventType uint32
76+
EventData uintptr
6677
CurrentStatus Status
6778
}
6879

@@ -85,29 +96,33 @@ type Handler interface {
8596

8697
var (
8798
// These are used by asm code.
88-
goWaitsH uintptr
89-
cWaitsH uintptr
90-
ssHandle uintptr
91-
sName *uint16
92-
sArgc uintptr
93-
sArgv **uint16
94-
ctlHandlerProc uintptr
95-
cSetEvent uintptr
96-
cWaitForSingleObject uintptr
97-
cRegisterServiceCtrlHandlerW uintptr
99+
goWaitsH uintptr
100+
cWaitsH uintptr
101+
ssHandle uintptr
102+
sName *uint16
103+
sArgc uintptr
104+
sArgv **uint16
105+
ctlHandlerExProc uintptr
106+
cSetEvent uintptr
107+
cWaitForSingleObject uintptr
108+
cRegisterServiceCtrlHandlerExW uintptr
98109
)
99110

100111
func init() {
101112
k := syscall.MustLoadDLL("kernel32.dll")
102113
cSetEvent = k.MustFindProc("SetEvent").Addr()
103114
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
104115
a := syscall.MustLoadDLL("advapi32.dll")
105-
cRegisterServiceCtrlHandlerW = a.MustFindProc("RegisterServiceCtrlHandlerW").Addr()
116+
cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
106117
}
107118

119+
// The HandlerEx prototype also has a context pointer but since we don't use
120+
// it at start-up time we don't have to pass it over either.
108121
type ctlEvent struct {
109-
cmd Cmd
110-
errno uint32
122+
cmd Cmd
123+
eventType uint32
124+
eventData uintptr
125+
errno uint32
111126
}
112127

113128
// service provides access to windows service api.
@@ -208,6 +223,8 @@ func (s *service) run() {
208223
var outch chan ChangeRequest
209224
inch := s.c
210225
var cmd Cmd
226+
var evtype uint32
227+
var evdata uintptr
211228
loop:
212229
for {
213230
select {
@@ -219,7 +236,9 @@ loop:
219236
inch = nil
220237
outch = cmdsToHandler
221238
cmd = r.cmd
222-
case outch <- ChangeRequest{cmd, status}:
239+
evtype = r.eventType
240+
evdata = r.eventData
241+
case outch <- ChangeRequest{cmd, evtype, evdata, status}:
223242
inch = s.c
224243
outch = nil
225244
case c := <-changesFromHandler:
@@ -276,8 +295,8 @@ func Run(name string, handler Handler) error {
276295
return err
277296
}
278297

279-
ctlHandler := func(ctl uint32) uintptr {
280-
e := ctlEvent{cmd: Cmd(ctl)}
298+
ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
299+
e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
281300
// We assume that this callback function is running on
282301
// the same thread as Run. Nowhere in MS documentation
283302
// I could find statement to guarantee that. So putting
@@ -288,6 +307,7 @@ func Run(name string, handler Handler) error {
288307
e.errno = sysErrNewThreadInCallback
289308
}
290309
s.c <- e
310+
// Always return NO_ERROR (0) for now.
291311
return 0
292312
}
293313

@@ -301,7 +321,7 @@ func Run(name string, handler Handler) error {
301321
goWaitsH = uintptr(s.goWaits.h)
302322
cWaitsH = uintptr(s.cWaits.h)
303323
sName = t[0].ServiceName
304-
ctlHandlerProc, err = newCallback(ctlHandler)
324+
ctlHandlerExProc, err = newCallback(ctlHandler)
305325
if err != nil {
306326
return err
307327
}
@@ -314,3 +334,10 @@ func Run(name string, handler Handler) error {
314334
}
315335
return nil
316336
}
337+
338+
// StatusHandle returns service status handle. It is safe to call this function
339+
// from inside the Handler.Execute because then it is guaranteed to be set.
340+
// This code will have to change once multiple services are possible per process.
341+
func StatusHandle() windows.Handle {
342+
return windows.Handle(ssHandle)
343+
}

windows/svc/sys_386.s

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ TEXT ·servicemain(SB),7,$0
2222
MOVL AX, (SP)
2323
MOVL $·servicectlhandler(SB), AX
2424
MOVL AX, 4(SP)
25-
MOVL ·cRegisterServiceCtrlHandlerW(SB), AX
25+
MOVL $0, 8(SP)
26+
MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX
2627
MOVL SP, BP
2728
CALL AX
2829
MOVL BP, SP
@@ -61,7 +62,7 @@ exit:
6162
// I do not know why, but this seems to be the only way to call
6263
// ctlHandlerProc on Windows 7.
6364

64-
// func servicectlhandler(ctl uint32) uintptr
65+
// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
6566
TEXT ·servicectlhandler(SB),7,$0
66-
MOVL ·ctlHandlerProc(SB), CX
67+
MOVL ·ctlHandlerExProc(SB), CX
6768
JMP CX

windows/svc/sys_amd64.s

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ TEXT ·servicemain(SB),7,$0
1313

1414
MOVQ ·sName(SB), CX
1515
MOVQ $·servicectlhandler(SB), DX
16-
MOVQ ·cRegisterServiceCtrlHandlerW(SB), AX
16+
// BUG(pastarmovj): Figure out a way to pass in context in R8.
17+
MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX
1718
CALL AX
1819
CMPQ AX, $0
1920
JE exit
@@ -35,7 +36,7 @@ exit:
3536
// I do not know why, but this seems to be the only way to call
3637
// ctlHandlerProc on Windows 7.
3738

38-
// func servicectlhandler(ctl uint32) uintptr
39+
// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
3940
TEXT ·servicectlhandler(SB),7,$0
40-
MOVQ ·ctlHandlerProc(SB), AX
41+
MOVQ ·ctlHandlerExProc(SB), AX
4142
JMP AX

0 commit comments

Comments
 (0)