123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- // 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. It returns the most
- // recent status the service reported to the service control manager, and an
- // error if the state change request was not accepted.
- // Note that the returned service status is only set if the status change
- // request succeeded, or if it failed with error ERROR_INVALID_SERVICE_CONTROL,
- // ERROR_SERVICE_CANNOT_ACCEPT_CTRL, or ERROR_SERVICE_NOT_ACTIVE.
- 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 &&
- err != windows.ERROR_INVALID_SERVICE_CONTROL &&
- err != windows.ERROR_SERVICE_CANNOT_ACCEPT_CTRL &&
- err != windows.ERROR_SERVICE_NOT_ACTIVE {
- return svc.Status{}, err
- }
- return svc.Status{
- State: svc.State(t.CurrentState),
- Accepts: svc.Accepted(t.ControlsAccepted),
- }, err
- }
- // 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
- }
|