windows: add service notification support

This lays the groundwork for service notification and tracking by adding
the required API functions. Users can make notifiers directly using it,
or later if we're feeling ambitious, we can see if we can come up with a
generalized solution in x/windows/svc.

Change-Id: I80503cc27970fbb23bf17cd8bc50eaa7787aa6bd
Reviewed-on: https://go-review.googlesource.com/c/sys/+/176624
Run-TryBot: Jason Donenfeld <Jason@zx2c4.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
diff --git a/windows/service.go b/windows/service.go
index 994b129..237e06f 100644
--- a/windows/service.go
+++ b/windows/service.go
@@ -100,6 +100,18 @@
 	SERVICE_CONFIG_FAILURE_ACTIONS = 2
 
 	SC_ENUM_PROCESS_INFO = 0
+
+	SERVICE_NOTIFY_STATUS_CHANGE    = 2
+	SERVICE_NOTIFY_STOPPED          = 0x00000001
+	SERVICE_NOTIFY_START_PENDING    = 0x00000002
+	SERVICE_NOTIFY_STOP_PENDING     = 0x00000004
+	SERVICE_NOTIFY_RUNNING          = 0x00000008
+	SERVICE_NOTIFY_CONTINUE_PENDING = 0x00000010
+	SERVICE_NOTIFY_PAUSE_PENDING    = 0x00000020
+	SERVICE_NOTIFY_PAUSED           = 0x00000040
+	SERVICE_NOTIFY_CREATED          = 0x00000080
+	SERVICE_NOTIFY_DELETED          = 0x00000100
+	SERVICE_NOTIFY_DELETE_PENDING   = 0x00000200
 )
 
 type SERVICE_STATUS struct {
@@ -151,6 +163,16 @@
 	ServiceStatusProcess SERVICE_STATUS_PROCESS
 }
 
+type SERVICE_NOTIFY struct {
+	Version               uint32
+	NotifyCallback        uintptr
+	Context               uintptr
+	NotificationStatus    uint32
+	ServiceStatus         SERVICE_STATUS_PROCESS
+	NotificationTriggered uint32
+	ServiceNames          *uint16
+}
+
 type SERVICE_FAILURE_ACTIONS struct {
 	ResetPeriod  uint32
 	RebootMsg    *uint16
@@ -178,4 +200,5 @@
 //sys	ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err error) = advapi32.ChangeServiceConfig2W
 //sys	QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfig2W
 //sys	EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) = advapi32.EnumServicesStatusExW
-//sys   QueryServiceStatusEx(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceStatusEx
+//sys	QueryServiceStatusEx(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceStatusEx
+//sys	NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) = advapi32.NotifyServiceStatusChangeW
diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go
index 43a8057..3ec06c9 100644
--- a/windows/syscall_windows.go
+++ b/windows/syscall_windows.go
@@ -55,6 +55,10 @@
 	FILE_UNICODE_ON_DISK              = 0x00000004
 	FILE_VOLUME_IS_COMPRESSED         = 0x00008000
 	FILE_VOLUME_QUOTAS                = 0x00000020
+
+	// Return values of SleepEx and other APC functions
+	STATUS_USER_APC    = 0x000000C0
+	WAIT_IO_COMPLETION = STATUS_USER_APC
 )
 
 // StringToUTF16 is deprecated. Use UTF16FromString instead.
@@ -244,6 +248,7 @@
 //sys	SetEvent(event Handle) (err error) = kernel32.SetEvent
 //sys	ResetEvent(event Handle) (err error) = kernel32.ResetEvent
 //sys	PulseEvent(event Handle) (err error) = kernel32.PulseEvent
+//sys	SleepEx(milliseconds uint32, alertable bool) (ret uint32) = kernel32.SleepEx
 
 // Volume Management Functions
 //sys	DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW
diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go
index 3344885..02f2799 100644
--- a/windows/zsyscall_windows.go
+++ b/windows/zsyscall_windows.go
@@ -66,6 +66,7 @@
 	procQueryServiceConfig2W               = modadvapi32.NewProc("QueryServiceConfig2W")
 	procEnumServicesStatusExW              = modadvapi32.NewProc("EnumServicesStatusExW")
 	procQueryServiceStatusEx               = modadvapi32.NewProc("QueryServiceStatusEx")
+	procNotifyServiceStatusChangeW         = modadvapi32.NewProc("NotifyServiceStatusChangeW")
 	procGetLastError                       = modkernel32.NewProc("GetLastError")
 	procLoadLibraryW                       = modkernel32.NewProc("LoadLibraryW")
 	procLoadLibraryExW                     = modkernel32.NewProc("LoadLibraryExW")
@@ -183,6 +184,7 @@
 	procSetEvent                           = modkernel32.NewProc("SetEvent")
 	procResetEvent                         = modkernel32.NewProc("ResetEvent")
 	procPulseEvent                         = modkernel32.NewProc("PulseEvent")
+	procSleepEx                            = modkernel32.NewProc("SleepEx")
 	procDefineDosDeviceW                   = modkernel32.NewProc("DefineDosDeviceW")
 	procDeleteVolumeMountPointW            = modkernel32.NewProc("DeleteVolumeMountPointW")
 	procFindFirstVolumeW                   = modkernel32.NewProc("FindFirstVolumeW")
@@ -501,6 +503,14 @@
 	return
 }
 
+func NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) {
+	r0, _, _ := syscall.Syscall(procNotifyServiceStatusChangeW.Addr(), 3, uintptr(service), uintptr(notifyMask), uintptr(unsafe.Pointer(notifier)))
+	if r0 != 0 {
+		ret = syscall.Errno(r0)
+	}
+	return
+}
+
 func GetLastError() (lasterr error) {
 	r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0)
 	if r0 != 0 {
@@ -1954,6 +1964,18 @@
 	return
 }
 
+func SleepEx(milliseconds uint32, alertable bool) (ret uint32) {
+	var _p0 uint32
+	if alertable {
+		_p0 = 1
+	} else {
+		_p0 = 0
+	}
+	r0, _, _ := syscall.Syscall(procSleepEx.Addr(), 2, uintptr(milliseconds), uintptr(_p0), 0)
+	ret = uint32(r0)
+	return
+}
+
 func DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) {
 	r1, _, e1 := syscall.Syscall(procDefineDosDeviceW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)))
 	if r1 == 0 {