diff --git a/windows/service.go b/windows/service.go index 9a59b42f..03383f1d 100644 --- a/windows/service.go +++ b/windows/service.go @@ -200,12 +200,19 @@ type SC_ACTION struct { Delay uint32 } +type QUERY_SERVICE_LOCK_STATUS struct { + IsLocked uint32 + LockOwner *uint16 + LockDuration uint32 +} + //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 //sys DeleteService(service Handle) (err error) = advapi32.DeleteService //sys StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW //sys QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus +//sys QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceLockStatusW //sys ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService //sys StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW //sys SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus diff --git a/windows/svc/mgr/mgr.go b/windows/svc/mgr/mgr.go index 24638d72..ad4cd6b6 100644 --- a/windows/svc/mgr/mgr.go +++ b/windows/svc/mgr/mgr.go @@ -13,6 +13,7 @@ package mgr import ( "syscall" + "time" "unicode/utf16" "unsafe" @@ -48,6 +49,36 @@ func (m *Mgr) Disconnect() error { return windows.CloseServiceHandle(m.Handle) } +type LockStatus struct { + IsLocked bool // Whether the SCM has been locked. + Age time.Duration // For how long the SCM has been locked. + Owner string // The name of the user who has locked the SCM. +} + +// LockStatus returns whether the service control manager is locked by +// the system, for how long, and by whom. A locked SCM indicates that +// most service actions will block until the system unlocks the SCM. +func (m *Mgr) LockStatus() (*LockStatus, error) { + bytesNeeded := uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{}) + 1024) + for { + bytes := make([]byte, bytesNeeded) + lockStatus := (*windows.QUERY_SERVICE_LOCK_STATUS)(unsafe.Pointer(&bytes[0])) + err := windows.QueryServiceLockStatus(m.Handle, lockStatus, uint32(len(bytes)), &bytesNeeded) + if err == windows.ERROR_INSUFFICIENT_BUFFER && bytesNeeded >= uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{})) { + continue + } + if err != nil { + return nil, err + } + status := &LockStatus{ + IsLocked: lockStatus.IsLocked != 0, + Age: time.Duration(lockStatus.LockDuration) * time.Second, + Owner: windows.UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(lockStatus.LockOwner))[:]), + } + return status, nil + } +} + func toPtr(s string) *uint16 { if len(s) == 0 { return nil diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index 6c35b90c..376bd048 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -60,6 +60,7 @@ var ( procDeleteService = modadvapi32.NewProc("DeleteService") procStartServiceW = modadvapi32.NewProc("StartServiceW") procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") + procQueryServiceLockStatusW = modadvapi32.NewProc("QueryServiceLockStatusW") procControlService = modadvapi32.NewProc("ControlService") procStartServiceCtrlDispatcherW = modadvapi32.NewProc("StartServiceCtrlDispatcherW") procSetServiceStatus = modadvapi32.NewProc("SetServiceStatus") @@ -425,6 +426,18 @@ func QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) { 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 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) { r1, _, e1 := syscall.Syscall(procControlService.Addr(), 3, uintptr(service), uintptr(control), uintptr(unsafe.Pointer(status))) if r1 == 0 {