diff --git a/windows/svc/debug/service.go b/windows/svc/debug/service.go index d5ab94b2..123df989 100644 --- a/windows/svc/debug/service.go +++ b/windows/svc/debug/service.go @@ -31,7 +31,7 @@ func Run(name string, handler svc.Handler) error { for { select { case <-sig: - cmds <- svc.ChangeRequest{svc.Stop, status} + cmds <- svc.ChangeRequest{svc.Stop, 0, 0, status} case status = <-changes: } } diff --git a/windows/svc/service.go b/windows/svc/service.go index 9864f7a7..1ea4a88f 100644 --- a/windows/svc/service.go +++ b/windows/svc/service.go @@ -35,11 +35,20 @@ const ( type Cmd uint32 const ( - Stop = Cmd(windows.SERVICE_CONTROL_STOP) - Pause = Cmd(windows.SERVICE_CONTROL_PAUSE) - Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE) - Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE) - Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN) + Stop = Cmd(windows.SERVICE_CONTROL_STOP) + Pause = Cmd(windows.SERVICE_CONTROL_PAUSE) + Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE) + Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE) + Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN) + ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE) + NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD) + NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE) + NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE) + NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE) + DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT) + HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE) + PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT) + SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE) ) // Accepted is used to describe commands accepted by the service. @@ -63,6 +72,8 @@ type Status struct { // ChangeRequest is sent to the service Handler to request service status change. type ChangeRequest struct { Cmd Cmd + EventType uint32 + EventData uintptr CurrentStatus Status } @@ -85,16 +96,16 @@ type Handler interface { var ( // These are used by asm code. - goWaitsH uintptr - cWaitsH uintptr - ssHandle uintptr - sName *uint16 - sArgc uintptr - sArgv **uint16 - ctlHandlerProc uintptr - cSetEvent uintptr - cWaitForSingleObject uintptr - cRegisterServiceCtrlHandlerW uintptr + goWaitsH uintptr + cWaitsH uintptr + ssHandle uintptr + sName *uint16 + sArgc uintptr + sArgv **uint16 + ctlHandlerExProc uintptr + cSetEvent uintptr + cWaitForSingleObject uintptr + cRegisterServiceCtrlHandlerExW uintptr ) func init() { @@ -102,12 +113,16 @@ func init() { cSetEvent = k.MustFindProc("SetEvent").Addr() cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr() a := syscall.MustLoadDLL("advapi32.dll") - cRegisterServiceCtrlHandlerW = a.MustFindProc("RegisterServiceCtrlHandlerW").Addr() + cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr() } +// The HandlerEx prototype also has a context pointer but since we don't use +// it at start-up time we don't have to pass it over either. type ctlEvent struct { - cmd Cmd - errno uint32 + cmd Cmd + eventType uint32 + eventData uintptr + errno uint32 } // service provides access to windows service api. @@ -208,6 +223,8 @@ func (s *service) run() { var outch chan ChangeRequest inch := s.c var cmd Cmd + var evtype uint32 + var evdata uintptr loop: for { select { @@ -219,7 +236,9 @@ loop: inch = nil outch = cmdsToHandler cmd = r.cmd - case outch <- ChangeRequest{cmd, status}: + evtype = r.eventType + evdata = r.eventData + case outch <- ChangeRequest{cmd, evtype, evdata, status}: inch = s.c outch = nil case c := <-changesFromHandler: @@ -276,8 +295,8 @@ func Run(name string, handler Handler) error { return err } - ctlHandler := func(ctl uint32) uintptr { - e := ctlEvent{cmd: Cmd(ctl)} + ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { + e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata} // We assume that this callback function is running on // the same thread as Run. Nowhere in MS documentation // I could find statement to guarantee that. So putting @@ -288,6 +307,7 @@ func Run(name string, handler Handler) error { e.errno = sysErrNewThreadInCallback } s.c <- e + // Always return NO_ERROR (0) for now. return 0 } @@ -301,7 +321,7 @@ func Run(name string, handler Handler) error { goWaitsH = uintptr(s.goWaits.h) cWaitsH = uintptr(s.cWaits.h) sName = t[0].ServiceName - ctlHandlerProc, err = newCallback(ctlHandler) + ctlHandlerExProc, err = newCallback(ctlHandler) if err != nil { return err } @@ -314,3 +334,10 @@ func Run(name string, handler Handler) error { } return nil } + +// StatusHandle returns service status handle. It is safe to call this function +// from inside the Handler.Execute because then it is guaranteed to be set. +// This code will have to change once multiple services are possible per process. +func StatusHandle() windows.Handle { + return windows.Handle(ssHandle) +} diff --git a/windows/svc/sys_386.s b/windows/svc/sys_386.s index 5e11bfad..2c82a9d9 100644 --- a/windows/svc/sys_386.s +++ b/windows/svc/sys_386.s @@ -22,7 +22,8 @@ TEXT ·servicemain(SB),7,$0 MOVL AX, (SP) MOVL $·servicectlhandler(SB), AX MOVL AX, 4(SP) - MOVL ·cRegisterServiceCtrlHandlerW(SB), AX + MOVL $0, 8(SP) + MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX MOVL SP, BP CALL AX MOVL BP, SP @@ -61,7 +62,7 @@ exit: // I do not know why, but this seems to be the only way to call // ctlHandlerProc on Windows 7. -// func servicectlhandler(ctl uint32) uintptr +// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { TEXT ·servicectlhandler(SB),7,$0 - MOVL ·ctlHandlerProc(SB), CX + MOVL ·ctlHandlerExProc(SB), CX JMP CX diff --git a/windows/svc/sys_amd64.s b/windows/svc/sys_amd64.s index 87dbec83..06b42590 100644 --- a/windows/svc/sys_amd64.s +++ b/windows/svc/sys_amd64.s @@ -13,7 +13,8 @@ TEXT ·servicemain(SB),7,$0 MOVQ ·sName(SB), CX MOVQ $·servicectlhandler(SB), DX - MOVQ ·cRegisterServiceCtrlHandlerW(SB), AX + // BUG(pastarmovj): Figure out a way to pass in context in R8. + MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX CALL AX CMPQ AX, $0 JE exit @@ -35,7 +36,7 @@ exit: // I do not know why, but this seems to be the only way to call // ctlHandlerProc on Windows 7. -// func servicectlhandler(ctl uint32) uintptr +// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { TEXT ·servicectlhandler(SB),7,$0 - MOVQ ·ctlHandlerProc(SB), AX + MOVQ ·ctlHandlerExProc(SB), AX JMP AX