service.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2012 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build windows
  5. // +build windows
  6. package mgr
  7. import (
  8. "syscall"
  9. "unsafe"
  10. "golang.org/x/sys/windows"
  11. "golang.org/x/sys/windows/svc"
  12. )
  13. // Service is used to access Windows service.
  14. type Service struct {
  15. Name string
  16. Handle windows.Handle
  17. }
  18. // Delete marks service s for deletion from the service control manager database.
  19. func (s *Service) Delete() error {
  20. return windows.DeleteService(s.Handle)
  21. }
  22. // Close relinquish access to the service s.
  23. func (s *Service) Close() error {
  24. return windows.CloseServiceHandle(s.Handle)
  25. }
  26. // Start starts service s.
  27. // args will be passed to svc.Handler.Execute.
  28. func (s *Service) Start(args ...string) error {
  29. var p **uint16
  30. if len(args) > 0 {
  31. vs := make([]*uint16, len(args))
  32. for i := range vs {
  33. vs[i] = syscall.StringToUTF16Ptr(args[i])
  34. }
  35. p = &vs[0]
  36. }
  37. return windows.StartService(s.Handle, uint32(len(args)), p)
  38. }
  39. // Control sends state change request c to the service s. It returns the most
  40. // recent status the service reported to the service control manager, and an
  41. // error if the state change request was not accepted.
  42. // Note that the returned service status is only set if the status change
  43. // request succeeded, or if it failed with error ERROR_INVALID_SERVICE_CONTROL,
  44. // ERROR_SERVICE_CANNOT_ACCEPT_CTRL, or ERROR_SERVICE_NOT_ACTIVE.
  45. func (s *Service) Control(c svc.Cmd) (svc.Status, error) {
  46. var t windows.SERVICE_STATUS
  47. err := windows.ControlService(s.Handle, uint32(c), &t)
  48. if err != nil &&
  49. err != windows.ERROR_INVALID_SERVICE_CONTROL &&
  50. err != windows.ERROR_SERVICE_CANNOT_ACCEPT_CTRL &&
  51. err != windows.ERROR_SERVICE_NOT_ACTIVE {
  52. return svc.Status{}, err
  53. }
  54. return svc.Status{
  55. State: svc.State(t.CurrentState),
  56. Accepts: svc.Accepted(t.ControlsAccepted),
  57. }, err
  58. }
  59. // Query returns current status of service s.
  60. func (s *Service) Query() (svc.Status, error) {
  61. var t windows.SERVICE_STATUS_PROCESS
  62. var needed uint32
  63. err := windows.QueryServiceStatusEx(s.Handle, windows.SC_STATUS_PROCESS_INFO, (*byte)(unsafe.Pointer(&t)), uint32(unsafe.Sizeof(t)), &needed)
  64. if err != nil {
  65. return svc.Status{}, err
  66. }
  67. return svc.Status{
  68. State: svc.State(t.CurrentState),
  69. Accepts: svc.Accepted(t.ControlsAccepted),
  70. ProcessId: t.ProcessId,
  71. Win32ExitCode: t.Win32ExitCode,
  72. ServiceSpecificExitCode: t.ServiceSpecificExitCode,
  73. }, nil
  74. }
  75. // ListDependentServices returns the names of the services dependent on service s, which match the given status.
  76. func (s *Service) ListDependentServices(status svc.ActivityStatus) ([]string, error) {
  77. var bytesNeeded, returnedServiceCount uint32
  78. var services []windows.ENUM_SERVICE_STATUS
  79. for {
  80. var servicesPtr *windows.ENUM_SERVICE_STATUS
  81. if len(services) > 0 {
  82. servicesPtr = &services[0]
  83. }
  84. allocatedBytes := uint32(len(services)) * uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{}))
  85. err := windows.EnumDependentServices(s.Handle, uint32(status), servicesPtr, allocatedBytes, &bytesNeeded,
  86. &returnedServiceCount)
  87. if err == nil {
  88. break
  89. }
  90. if err != syscall.ERROR_MORE_DATA {
  91. return nil, err
  92. }
  93. if bytesNeeded <= allocatedBytes {
  94. return nil, err
  95. }
  96. // ERROR_MORE_DATA indicates the provided buffer was too small, run the call again after resizing the buffer
  97. requiredSliceLen := bytesNeeded / uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{}))
  98. if bytesNeeded%uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{})) != 0 {
  99. requiredSliceLen += 1
  100. }
  101. services = make([]windows.ENUM_SERVICE_STATUS, requiredSliceLen)
  102. }
  103. if returnedServiceCount == 0 {
  104. return nil, nil
  105. }
  106. // The slice mutated by EnumDependentServices may have a length greater than returnedServiceCount, any elements
  107. // past that should be ignored.
  108. var dependents []string
  109. for i := 0; i < int(returnedServiceCount); i++ {
  110. dependents = append(dependents, windows.UTF16PtrToString(services[i].ServiceName))
  111. }
  112. return dependents, nil
  113. }