From fb4cac33e3196ff7f507ab9b2d2a44b0142f5b5a Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Wed, 14 Jun 2017 16:48:48 +1000 Subject: [PATCH] windows/svc/mgr: add Mgr.ListServices Add API to list services installed on the system. Fixes golang/go#20596 Change-Id: Ifa2f20ef15ccb962bd21d03788ce931dd45f2630 Reviewed-on: https://go-review.googlesource.com/45711 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- windows/service.go | 21 +++++++++++++++++++ windows/svc/mgr/mgr.go | 41 +++++++++++++++++++++++++++++++++++++ windows/svc/mgr/mgr_test.go | 15 ++++++++++++++ windows/svc/mgr/service.go | 2 -- windows/zsyscall_windows.go | 13 ++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/windows/service.go b/windows/service.go index 1c11d392..a500dd7d 100644 --- a/windows/service.go +++ b/windows/service.go @@ -95,6 +95,8 @@ const ( SERVICE_CONFIG_FAILURE_ACTIONS = 2 NO_ERROR = 0 + + SC_ENUM_PROCESS_INFO = 0 ) type SERVICE_STATUS struct { @@ -128,6 +130,24 @@ type SERVICE_DESCRIPTION struct { Description *uint16 } +type SERVICE_STATUS_PROCESS struct { + ServiceType uint32 + CurrentState uint32 + ControlsAccepted uint32 + Win32ExitCode uint32 + ServiceSpecificExitCode uint32 + CheckPoint uint32 + WaitHint uint32 + ProcessId uint32 + ServiceFlags uint32 +} + +type ENUM_SERVICE_STATUS_PROCESS struct { + ServiceName *uint16 + DisplayName *uint16 + ServiceStatusProcess SERVICE_STATUS_PROCESS +} + //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 @@ -141,3 +161,4 @@ type SERVICE_DESCRIPTION struct { //sys QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfigW //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 diff --git a/windows/svc/mgr/mgr.go b/windows/svc/mgr/mgr.go index e20a1faa..76965b56 100644 --- a/windows/svc/mgr/mgr.go +++ b/windows/svc/mgr/mgr.go @@ -14,6 +14,7 @@ package mgr import ( "syscall" "unicode/utf16" + "unsafe" "golang.org/x/sys/windows" ) @@ -119,3 +120,43 @@ func (m *Mgr) OpenService(name string) (*Service, error) { } return &Service{Name: name, Handle: h}, nil } + +// ListServices enumerates services in the specified +// service control manager database m. +// If the caller does not have the SERVICE_QUERY_STATUS +// access right to a service, the service is silently +// omitted from the list of services returned. +func (m *Mgr) ListServices() ([]string, error) { + var err error + var bytesNeeded, servicesReturned uint32 + var buf []byte + for { + var p *byte + if len(buf) > 0 { + p = &buf[0] + } + err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO, + windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL, + p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil) + if err == nil { + break + } + if err != syscall.ERROR_MORE_DATA { + return nil, err + } + if bytesNeeded <= uint32(len(buf)) { + return nil, err + } + buf = make([]byte, bytesNeeded) + } + if servicesReturned == 0 { + return nil, nil + } + services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned] + var names []string + for _, s := range services { + name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:]) + names = append(names, name) + } + return names, nil +} diff --git a/windows/svc/mgr/mgr_test.go b/windows/svc/mgr/mgr_test.go index 78be970c..e67407cf 100644 --- a/windows/svc/mgr/mgr_test.go +++ b/windows/svc/mgr/mgr_test.go @@ -150,5 +150,20 @@ func TestMyService(t *testing.T) { testConfig(t, s, c) + svcnames, err := m.ListServices() + if err != nil { + t.Fatalf("ListServices failed: %v", err) + } + var myserviceIsInstalled bool + for _, sn := range svcnames { + if sn == name { + myserviceIsInstalled = true + break + } + } + if !myserviceIsInstalled { + t.Errorf("ListServices failed to find %q service", name) + } + remove(t, s) } diff --git a/windows/svc/mgr/service.go b/windows/svc/mgr/service.go index ac9fba5a..fdc46af5 100644 --- a/windows/svc/mgr/service.go +++ b/windows/svc/mgr/service.go @@ -15,8 +15,6 @@ import ( // TODO(brainman): Use EnumDependentServices to enumerate dependent services. -// TODO(brainman): Use EnumServicesStatus to enumerate services in the specified service control manager database. - // Service is used to access Windows service. type Service struct { Name string diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index f7bc8d68..b9ee8278 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -64,6 +64,7 @@ var ( procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW") procChangeServiceConfig2W = modadvapi32.NewProc("ChangeServiceConfig2W") procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W") + procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") procGetLastError = modkernel32.NewProc("GetLastError") procLoadLibraryW = modkernel32.NewProc("LoadLibraryW") procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW") @@ -430,6 +431,18 @@ func QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize return } +func EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) { + r1, _, e1 := syscall.Syscall12(procEnumServicesStatusExW.Addr(), 10, uintptr(mgr), uintptr(infoLevel), uintptr(serviceType), uintptr(serviceState), uintptr(unsafe.Pointer(services)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned)), uintptr(unsafe.Pointer(resumeHandle)), uintptr(unsafe.Pointer(groupName)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func GetLastError() (lasterr error) { r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0) if r0 != 0 {