windows/svc: allow querying service start reason

The QueryServiceDynamicInformation API makes it possible to determine
why a service started. This is declared as an optional function because
the API is Win8+.

Reference:
https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryservicedynamicinformation

Change-Id: I18e9a95b35f8c37d94c9900626c1785f425701dc
Reviewed-on: https://go-review.googlesource.com/c/sys/+/358394
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Jason A. Donenfeld
2021-10-29 16:52:21 +00:00
parent c1bf0bb051
commit 6e7872819d
3 changed files with 48 additions and 3 deletions
+25 -1
View File
@@ -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
}