mgr.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. "time"
  14. "unicode/utf16"
  15. "unsafe"
  16. "golang.org/x/sys/windows"
  17. )
  18. // Mgr is used to manage Windows service.
  19. type Mgr struct {
  20. Handle windows.Handle
  21. }
  22. // Connect establishes a connection to the service control manager.
  23. func Connect() (*Mgr, error) {
  24. return ConnectRemote("")
  25. }
  26. // ConnectRemote establishes a connection to the
  27. // service control manager on computer named host.
  28. func ConnectRemote(host string) (*Mgr, error) {
  29. var s *uint16
  30. if host != "" {
  31. s = syscall.StringToUTF16Ptr(host)
  32. }
  33. h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS)
  34. if err != nil {
  35. return nil, err
  36. }
  37. return &Mgr{Handle: h}, nil
  38. }
  39. // Disconnect closes connection to the service control manager m.
  40. func (m *Mgr) Disconnect() error {
  41. return windows.CloseServiceHandle(m.Handle)
  42. }
  43. type LockStatus struct {
  44. IsLocked bool // Whether the SCM has been locked.
  45. Age time.Duration // For how long the SCM has been locked.
  46. Owner string // The name of the user who has locked the SCM.
  47. }
  48. // LockStatus returns whether the service control manager is locked by
  49. // the system, for how long, and by whom. A locked SCM indicates that
  50. // most service actions will block until the system unlocks the SCM.
  51. func (m *Mgr) LockStatus() (*LockStatus, error) {
  52. bytesNeeded := uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{}) + 1024)
  53. for {
  54. bytes := make([]byte, bytesNeeded)
  55. lockStatus := (*windows.QUERY_SERVICE_LOCK_STATUS)(unsafe.Pointer(&bytes[0]))
  56. err := windows.QueryServiceLockStatus(m.Handle, lockStatus, uint32(len(bytes)), &bytesNeeded)
  57. if err == windows.ERROR_INSUFFICIENT_BUFFER && bytesNeeded >= uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{})) {
  58. continue
  59. }
  60. if err != nil {
  61. return nil, err
  62. }
  63. status := &LockStatus{
  64. IsLocked: lockStatus.IsLocked != 0,
  65. Age: time.Duration(lockStatus.LockDuration) * time.Second,
  66. Owner: windows.UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(lockStatus.LockOwner))[:]),
  67. }
  68. return status, nil
  69. }
  70. }
  71. func toPtr(s string) *uint16 {
  72. if len(s) == 0 {
  73. return nil
  74. }
  75. return syscall.StringToUTF16Ptr(s)
  76. }
  77. // toStringBlock terminates strings in ss with 0, and then
  78. // concatenates them together. It also adds extra 0 at the end.
  79. func toStringBlock(ss []string) *uint16 {
  80. if len(ss) == 0 {
  81. return nil
  82. }
  83. t := ""
  84. for _, s := range ss {
  85. if s != "" {
  86. t += s + "\x00"
  87. }
  88. }
  89. if t == "" {
  90. return nil
  91. }
  92. t += "\x00"
  93. return &utf16.Encode([]rune(t))[0]
  94. }
  95. // CreateService installs new service name on the system.
  96. // The service will be executed by running exepath binary.
  97. // Use config c to specify service parameters.
  98. // Any args will be passed as command-line arguments when
  99. // the service is started; these arguments are distinct from
  100. // the arguments passed to Service.Start or via the "Start
  101. // parameters" field in the service's Properties dialog box.
  102. func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) {
  103. if c.StartType == 0 {
  104. c.StartType = StartManual
  105. }
  106. if c.ErrorControl == 0 {
  107. c.ErrorControl = ErrorNormal
  108. }
  109. if c.ServiceType == 0 {
  110. c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
  111. }
  112. s := syscall.EscapeArg(exepath)
  113. for _, v := range args {
  114. s += " " + syscall.EscapeArg(v)
  115. }
  116. h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName),
  117. windows.SERVICE_ALL_ACCESS, c.ServiceType,
  118. c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup),
  119. nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password))
  120. if err != nil {
  121. return nil, err
  122. }
  123. if c.SidType != windows.SERVICE_SID_TYPE_NONE {
  124. err = updateSidType(h, c.SidType)
  125. if err != nil {
  126. windows.DeleteService(h)
  127. windows.CloseServiceHandle(h)
  128. return nil, err
  129. }
  130. }
  131. if c.Description != "" {
  132. err = updateDescription(h, c.Description)
  133. if err != nil {
  134. windows.DeleteService(h)
  135. windows.CloseServiceHandle(h)
  136. return nil, err
  137. }
  138. }
  139. if c.DelayedAutoStart {
  140. err = updateStartUp(h, c.DelayedAutoStart)
  141. if err != nil {
  142. windows.DeleteService(h)
  143. windows.CloseServiceHandle(h)
  144. return nil, err
  145. }
  146. }
  147. return &Service{Name: name, Handle: h}, nil
  148. }
  149. // OpenService retrieves access to service name, so it can
  150. // be interrogated and controlled.
  151. func (m *Mgr) OpenService(name string) (*Service, error) {
  152. h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS)
  153. if err != nil {
  154. return nil, err
  155. }
  156. return &Service{Name: name, Handle: h}, nil
  157. }
  158. // ListServices enumerates services in the specified
  159. // service control manager database m.
  160. // If the caller does not have the SERVICE_QUERY_STATUS
  161. // access right to a service, the service is silently
  162. // omitted from the list of services returned.
  163. func (m *Mgr) ListServices() ([]string, error) {
  164. var err error
  165. var bytesNeeded, servicesReturned uint32
  166. var buf []byte
  167. for {
  168. var p *byte
  169. if len(buf) > 0 {
  170. p = &buf[0]
  171. }
  172. err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
  173. windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
  174. p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
  175. if err == nil {
  176. break
  177. }
  178. if err != syscall.ERROR_MORE_DATA {
  179. return nil, err
  180. }
  181. if bytesNeeded <= uint32(len(buf)) {
  182. return nil, err
  183. }
  184. buf = make([]byte, bytesNeeded)
  185. }
  186. if servicesReturned == 0 {
  187. return nil, nil
  188. }
  189. services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned]
  190. var names []string
  191. for _, s := range services {
  192. name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:])
  193. names = append(names, name)
  194. }
  195. return names, nil
  196. }