diff --git a/windows/service.go b/windows/service.go index 5b28ae16..f8deca83 100644 --- a/windows/service.go +++ b/windows/service.go @@ -17,8 +17,6 @@ const ( SC_MANAGER_ALL_ACCESS = 0xf003f ) -//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW - const ( SERVICE_KERNEL_DRIVER = 1 SERVICE_FILE_SYSTEM_DRIVER = 2 @@ -133,6 +131,14 @@ const ( SC_EVENT_DATABASE_CHANGE = 0 SC_EVENT_PROPERTY_CHANGE = 1 SC_EVENT_STATUS_CHANGE = 2 + + SERVICE_START_REASON_DEMAND = 0x00000001 + SERVICE_START_REASON_AUTO = 0x00000002 + SERVICE_START_REASON_TRIGGER = 0x00000004 + SERVICE_START_REASON_RESTART_ON_FAILURE = 0x00000008 + SERVICE_START_REASON_DELAYEDAUTO = 0x00000010 + + SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON = 1 ) type SERVICE_STATUS struct { @@ -217,6 +223,7 @@ type QUERY_SERVICE_LOCK_STATUS struct { LockDuration uint32 } +//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW //sys CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle //sys CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW //sys OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW @@ -237,3 +244,4 @@ type QUERY_SERVICE_LOCK_STATUS struct { //sys SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications? //sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications? //sys RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW +//sys QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInfo unsafe.Pointer) (err error) = advapi32.QueryServiceDynamicInformation? diff --git a/windows/svc/service.go b/windows/svc/service.go index 9ad6eb4d..5b05c3e3 100644 --- a/windows/svc/service.go +++ b/windows/svc/service.go @@ -80,6 +80,17 @@ type Status struct { ServiceSpecificExitCode uint32 // set if the service has exited with a service-specific exit code } +// StartReason is the reason that the service was started. +type StartReason uint32 + +const ( + StartReasonDemand = StartReason(windows.SERVICE_START_REASON_DEMAND) + StartReasonAuto = StartReason(windows.SERVICE_START_REASON_AUTO) + StartReasonTrigger = StartReason(windows.SERVICE_START_REASON_TRIGGER) + StartReasonRestartOnFailure = StartReason(windows.SERVICE_START_REASON_RESTART_ON_FAILURE) + StartReasonDelayedAuto = StartReason(windows.SERVICE_START_REASON_DELAYEDAUTO) +) + // ChangeRequest is sent to the service Handler to request service status change. type ChangeRequest struct { Cmd Cmd @@ -284,7 +295,20 @@ func Run(name string, handler Handler) error { // 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 theService.h } + +// DynamicStartReason returns the reason why the service was started. It is safe +// to call this function from inside the Handler.Execute because then it is +// guaranteed to be set. +func DynamicStartReason() (StartReason, error) { + var allocReason *uint32 + err := windows.QueryServiceDynamicInformation(theService.h, windows.SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON, unsafe.Pointer(&allocReason)) + if err != nil { + return 0, err + } + reason := StartReason(*allocReason) + windows.LocalFree(windows.Handle(unsafe.Pointer(allocReason))) + return reason, nil +} diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index 044ecb1b..91817d6d 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -115,6 +115,7 @@ var ( procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W") procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW") + procQueryServiceDynamicInformation = modadvapi32.NewProc("QueryServiceDynamicInformation") procQueryServiceLockStatusW = modadvapi32.NewProc("QueryServiceLockStatusW") procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx") @@ -976,6 +977,18 @@ func QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, buf return } +func QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInfo unsafe.Pointer) (err error) { + err = procQueryServiceDynamicInformation.Find() + if err != nil { + return + } + r1, _, e1 := syscall.Syscall(procQueryServiceDynamicInformation.Addr(), 3, uintptr(service), uintptr(infoLevel), uintptr(dynamicInfo)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procQueryServiceLockStatusW.Addr(), 4, uintptr(mgr), uintptr(unsafe.Pointer(lockStatus)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0) if r1 == 0 {