You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

163 lines
4.4 KiB

  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. // +build windows
  5. // Package mgr can be used to manage Windows service programs.
  6. // It can be used to install and remove them. It can also start,
  7. // stop and pause them. The package can query / change current
  8. // service state and config parameters.
  9. //
  10. package mgr
  11. import (
  12. "syscall"
  13. "unicode/utf16"
  14. "unsafe"
  15. "golang.org/x/sys/windows"
  16. )
  17. // Mgr is used to manage Windows service.
  18. type Mgr struct {
  19. Handle windows.Handle
  20. }
  21. // Connect establishes a connection to the service control manager.
  22. func Connect() (*Mgr, error) {
  23. return ConnectRemote("")
  24. }
  25. // ConnectRemote establishes a connection to the
  26. // service control manager on computer named host.
  27. func ConnectRemote(host string) (*Mgr, error) {
  28. var s *uint16
  29. if host != "" {
  30. s = syscall.StringToUTF16Ptr(host)
  31. }
  32. h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return &Mgr{Handle: h}, nil
  37. }
  38. // Disconnect closes connection to the service control manager m.
  39. func (m *Mgr) Disconnect() error {
  40. return windows.CloseServiceHandle(m.Handle)
  41. }
  42. func toPtr(s string) *uint16 {
  43. if len(s) == 0 {
  44. return nil
  45. }
  46. return syscall.StringToUTF16Ptr(s)
  47. }
  48. // toStringBlock terminates strings in ss with 0, and then
  49. // concatenates them together. It also adds extra 0 at the end.
  50. func toStringBlock(ss []string) *uint16 {
  51. if len(ss) == 0 {
  52. return nil
  53. }
  54. t := ""
  55. for _, s := range ss {
  56. if s != "" {
  57. t += s + "\x00"
  58. }
  59. }
  60. if t == "" {
  61. return nil
  62. }
  63. t += "\x00"
  64. return &utf16.Encode([]rune(t))[0]
  65. }
  66. // CreateService installs new service name on the system.
  67. // The service will be executed by running exepath binary.
  68. // Use config c to specify service parameters.
  69. // Any args will be passed as command-line arguments when
  70. // the service is started; these arguments are distinct from
  71. // the arguments passed to Service.Start or via the "Start
  72. // parameters" field in the service's Properties dialog box.
  73. func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) {
  74. if c.StartType == 0 {
  75. c.StartType = StartManual
  76. }
  77. if c.ErrorControl == 0 {
  78. c.ErrorControl = ErrorNormal
  79. }
  80. if c.ServiceType == 0 {
  81. c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
  82. }
  83. s := syscall.EscapeArg(exepath)
  84. for _, v := range args {
  85. s += " " + syscall.EscapeArg(v)
  86. }
  87. h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName),
  88. windows.SERVICE_ALL_ACCESS, c.ServiceType,
  89. c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup),
  90. nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password))
  91. if err != nil {
  92. return nil, err
  93. }
  94. if c.Description != "" {
  95. err = updateDescription(h, c.Description)
  96. if err != nil {
  97. return nil, err
  98. }
  99. }
  100. return &Service{Name: name, Handle: h}, nil
  101. }
  102. // OpenService retrieves access to service name, so it can
  103. // be interrogated and controlled.
  104. func (m *Mgr) OpenService(name string) (*Service, error) {
  105. h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS)
  106. if err != nil {
  107. return nil, err
  108. }
  109. return &Service{Name: name, Handle: h}, nil
  110. }
  111. // ListServices enumerates services in the specified
  112. // service control manager database m.
  113. // If the caller does not have the SERVICE_QUERY_STATUS
  114. // access right to a service, the service is silently
  115. // omitted from the list of services returned.
  116. func (m *Mgr) ListServices() ([]string, error) {
  117. var err error
  118. var bytesNeeded, servicesReturned uint32
  119. var buf []byte
  120. for {
  121. var p *byte
  122. if len(buf) > 0 {
  123. p = &buf[0]
  124. }
  125. err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
  126. windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
  127. p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
  128. if err == nil {
  129. break
  130. }
  131. if err != syscall.ERROR_MORE_DATA {
  132. return nil, err
  133. }
  134. if bytesNeeded <= uint32(len(buf)) {
  135. return nil, err
  136. }
  137. buf = make([]byte, bytesNeeded)
  138. }
  139. if servicesReturned == 0 {
  140. return nil, nil
  141. }
  142. services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned]
  143. var names []string
  144. for _, s := range services {
  145. name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:])
  146. names = append(names, name)
  147. }
  148. return names, nil
  149. }