mirror of
https://github.com/rwinkhart/sys.git
synced 2026-08-30 22:06:37 -04:00
This method allows a user to list all Windows services which are dependent upon a given service. This commit makes use of the EnumDependentServices Windows API call. Without this, a user would have to iterate through each service on the system, and check if the given service is listed in each service's dependencies list. The implementation of ListDependentServices is mostly the same as Mgr.ListServices, as the API calls behave in the same way. Fixes golang/go#56766 Change-Id: I9ec18c97afd02f48deef691ccdd5c26d6501add1 Reviewed-on: https://go-review.googlesource.com/c/sys/+/451363 Reviewed-by: Than McIntosh <thanm@google.com> Run-TryBot: Alex Brainman <alex.brainman@gmail.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Benny Siegert <bsiegert@gmail.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
118 lines
3.5 KiB
Go
118 lines
3.5 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build windows
|
|
// +build windows
|
|
|
|
package mgr
|
|
|
|
import (
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/sys/windows/svc"
|
|
)
|
|
|
|
// Service is used to access Windows service.
|
|
type Service struct {
|
|
Name string
|
|
Handle windows.Handle
|
|
}
|
|
|
|
// Delete marks service s for deletion from the service control manager database.
|
|
func (s *Service) Delete() error {
|
|
return windows.DeleteService(s.Handle)
|
|
}
|
|
|
|
// Close relinquish access to the service s.
|
|
func (s *Service) Close() error {
|
|
return windows.CloseServiceHandle(s.Handle)
|
|
}
|
|
|
|
// Start starts service s.
|
|
// args will be passed to svc.Handler.Execute.
|
|
func (s *Service) Start(args ...string) error {
|
|
var p **uint16
|
|
if len(args) > 0 {
|
|
vs := make([]*uint16, len(args))
|
|
for i := range vs {
|
|
vs[i] = syscall.StringToUTF16Ptr(args[i])
|
|
}
|
|
p = &vs[0]
|
|
}
|
|
return windows.StartService(s.Handle, uint32(len(args)), p)
|
|
}
|
|
|
|
// Control sends state change request c to the service s.
|
|
func (s *Service) Control(c svc.Cmd) (svc.Status, error) {
|
|
var t windows.SERVICE_STATUS
|
|
err := windows.ControlService(s.Handle, uint32(c), &t)
|
|
if err != nil {
|
|
return svc.Status{}, err
|
|
}
|
|
return svc.Status{
|
|
State: svc.State(t.CurrentState),
|
|
Accepts: svc.Accepted(t.ControlsAccepted),
|
|
}, nil
|
|
}
|
|
|
|
// Query returns current status of service s.
|
|
func (s *Service) Query() (svc.Status, error) {
|
|
var t windows.SERVICE_STATUS_PROCESS
|
|
var needed uint32
|
|
err := windows.QueryServiceStatusEx(s.Handle, windows.SC_STATUS_PROCESS_INFO, (*byte)(unsafe.Pointer(&t)), uint32(unsafe.Sizeof(t)), &needed)
|
|
if err != nil {
|
|
return svc.Status{}, err
|
|
}
|
|
return svc.Status{
|
|
State: svc.State(t.CurrentState),
|
|
Accepts: svc.Accepted(t.ControlsAccepted),
|
|
ProcessId: t.ProcessId,
|
|
Win32ExitCode: t.Win32ExitCode,
|
|
ServiceSpecificExitCode: t.ServiceSpecificExitCode,
|
|
}, nil
|
|
}
|
|
|
|
// ListDependentServices returns the names of the services dependent on service s, which match the given status.
|
|
func (s *Service) ListDependentServices(status svc.ActivityStatus) ([]string, error) {
|
|
var bytesNeeded, returnedServiceCount uint32
|
|
var services []windows.ENUM_SERVICE_STATUS
|
|
for {
|
|
var servicesPtr *windows.ENUM_SERVICE_STATUS
|
|
if len(services) > 0 {
|
|
servicesPtr = &services[0]
|
|
}
|
|
allocatedBytes := uint32(len(services)) * uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{}))
|
|
err := windows.EnumDependentServices(s.Handle, uint32(status), servicesPtr, allocatedBytes, &bytesNeeded,
|
|
&returnedServiceCount)
|
|
if err == nil {
|
|
break
|
|
}
|
|
if err != syscall.ERROR_MORE_DATA {
|
|
return nil, err
|
|
}
|
|
if bytesNeeded <= allocatedBytes {
|
|
return nil, err
|
|
}
|
|
// ERROR_MORE_DATA indicates the provided buffer was too small, run the call again after resizing the buffer
|
|
requiredSliceLen := bytesNeeded / uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{}))
|
|
if bytesNeeded%uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{})) != 0 {
|
|
requiredSliceLen += 1
|
|
}
|
|
services = make([]windows.ENUM_SERVICE_STATUS, requiredSliceLen)
|
|
}
|
|
if returnedServiceCount == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// The slice mutated by EnumDependentServices may have a length greater than returnedServiceCount, any elements
|
|
// past that should be ignored.
|
|
var dependents []string
|
|
for i := 0; i < int(returnedServiceCount); i++ {
|
|
dependents = append(dependents, windows.UTF16PtrToString(services[i].ServiceName))
|
|
}
|
|
return dependents, nil
|
|
}
|