mirror of
https://github.com/rwinkhart/sys.git
synced 2026-09-02 15:17:31 -04:00
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 <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
@@ -95,6 +95,8 @@ const (
|
|||||||
SERVICE_CONFIG_FAILURE_ACTIONS = 2
|
SERVICE_CONFIG_FAILURE_ACTIONS = 2
|
||||||
|
|
||||||
NO_ERROR = 0
|
NO_ERROR = 0
|
||||||
|
|
||||||
|
SC_ENUM_PROCESS_INFO = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
type SERVICE_STATUS struct {
|
type SERVICE_STATUS struct {
|
||||||
@@ -128,6 +130,24 @@ type SERVICE_DESCRIPTION struct {
|
|||||||
Description *uint16
|
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 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 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 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 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 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 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
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ package mgr
|
|||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
@@ -119,3 +120,43 @@ func (m *Mgr) OpenService(name string) (*Service, error) {
|
|||||||
}
|
}
|
||||||
return &Service{Name: name, Handle: h}, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -150,5 +150,20 @@ func TestMyService(t *testing.T) {
|
|||||||
|
|
||||||
testConfig(t, s, c)
|
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)
|
remove(t, s)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import (
|
|||||||
|
|
||||||
// TODO(brainman): Use EnumDependentServices to enumerate dependent services.
|
// 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.
|
// Service is used to access Windows service.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Name string
|
Name string
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ var (
|
|||||||
procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW")
|
procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW")
|
||||||
procChangeServiceConfig2W = modadvapi32.NewProc("ChangeServiceConfig2W")
|
procChangeServiceConfig2W = modadvapi32.NewProc("ChangeServiceConfig2W")
|
||||||
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
|
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
|
||||||
|
procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW")
|
||||||
procGetLastError = modkernel32.NewProc("GetLastError")
|
procGetLastError = modkernel32.NewProc("GetLastError")
|
||||||
procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
|
procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
|
||||||
procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW")
|
procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW")
|
||||||
@@ -430,6 +431,18 @@ func QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize
|
|||||||
return
|
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) {
|
func GetLastError() (lasterr error) {
|
||||||
r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0)
|
r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0)
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user