initsystem_windows.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // +build windows
  2. /*
  3. Copyright 2017 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package initsystem
  15. import (
  16. "fmt"
  17. "time"
  18. "golang.org/x/sys/windows/svc"
  19. "golang.org/x/sys/windows/svc/mgr"
  20. )
  21. // WindowsInitSystem is the windows implementation of InitSystem
  22. type WindowsInitSystem struct{}
  23. // EnableCommand return a string describing how to enable a service
  24. func (sysd WindowsInitSystem) EnableCommand(service string) string {
  25. return fmt.Sprintf("Set-Service '%s' -StartupType Automatic", service)
  26. }
  27. // ServiceStart tries to start a specific service
  28. // Following Windows documentation: https://docs.microsoft.com/en-us/windows/desktop/Services/starting-a-service
  29. func (sysd WindowsInitSystem) ServiceStart(service string) error {
  30. m, err := mgr.Connect()
  31. if err != nil {
  32. return err
  33. }
  34. defer m.Disconnect()
  35. s, err := m.OpenService(service)
  36. if err != nil {
  37. return fmt.Errorf("could not access service %s: %v", service, err)
  38. }
  39. defer s.Close()
  40. // Check if service is already started
  41. status, err := s.Query()
  42. if err != nil {
  43. return fmt.Errorf("could not query service %s: %v", service, err)
  44. }
  45. if status.State != svc.Stopped && status.State != svc.StopPending {
  46. return nil
  47. }
  48. timeout := time.Now().Add(10 * time.Second)
  49. for status.State != svc.Stopped {
  50. if timeout.Before(time.Now()) {
  51. return fmt.Errorf("timeout waiting for %s service to stop", service)
  52. }
  53. time.Sleep(300 * time.Millisecond)
  54. status, err = s.Query()
  55. if err != nil {
  56. return fmt.Errorf("could not retrieve %s service status: %v", service, err)
  57. }
  58. }
  59. // Start the service
  60. err = s.Start("is", "manual-started")
  61. if err != nil {
  62. return fmt.Errorf("could not start service %s: %v", service, err)
  63. }
  64. // Check that the start was successful
  65. status, err = s.Query()
  66. if err != nil {
  67. return fmt.Errorf("could not query service %s: %v", service, err)
  68. }
  69. timeout = time.Now().Add(10 * time.Second)
  70. for status.State != svc.Running {
  71. if timeout.Before(time.Now()) {
  72. return fmt.Errorf("timeout waiting for %s service to start", service)
  73. }
  74. time.Sleep(300 * time.Millisecond)
  75. status, err = s.Query()
  76. if err != nil {
  77. return fmt.Errorf("could not retrieve %s service status: %v", service, err)
  78. }
  79. }
  80. return nil
  81. }
  82. // ServiceRestart tries to reload the environment and restart the specific service
  83. func (sysd WindowsInitSystem) ServiceRestart(service string) error {
  84. if err := sysd.ServiceStop(service); err != nil {
  85. return fmt.Errorf("couldn't stop service %s: %v", service, err)
  86. }
  87. if err := sysd.ServiceStart(service); err != nil {
  88. return fmt.Errorf("couldn't start service %s: %v", service, err)
  89. }
  90. return nil
  91. }
  92. // ServiceStop tries to stop a specific service
  93. // Following Windows documentation: https://docs.microsoft.com/en-us/windows/desktop/Services/stopping-a-service
  94. func (sysd WindowsInitSystem) ServiceStop(service string) error {
  95. m, err := mgr.Connect()
  96. if err != nil {
  97. return err
  98. }
  99. defer m.Disconnect()
  100. s, err := m.OpenService(service)
  101. if err != nil {
  102. return fmt.Errorf("could not access service %s: %v", service, err)
  103. }
  104. defer s.Close()
  105. // Check if service is already stopped
  106. status, err := s.Query()
  107. if err != nil {
  108. return fmt.Errorf("could not query service %s: %v", service, err)
  109. }
  110. if status.State == svc.Stopped {
  111. return nil
  112. }
  113. // If StopPending, check that service eventually stops
  114. if status.State == svc.StopPending {
  115. timeout := time.Now().Add(10 * time.Second)
  116. for status.State != svc.Stopped {
  117. if timeout.Before(time.Now()) {
  118. return fmt.Errorf("timeout waiting for %s service to stop", service)
  119. }
  120. time.Sleep(300 * time.Millisecond)
  121. status, err = s.Query()
  122. if err != nil {
  123. return fmt.Errorf("could not retrieve %s service status: %v", service, err)
  124. }
  125. }
  126. return nil
  127. }
  128. // Stop the service
  129. status, err = s.Control(svc.Stop)
  130. if err != nil {
  131. return fmt.Errorf("could not stop service %s: %v", service, err)
  132. }
  133. // Check that the stop was successful
  134. status, err = s.Query()
  135. if err != nil {
  136. return fmt.Errorf("could not query service %s: %v", service, err)
  137. }
  138. timeout := time.Now().Add(10 * time.Second)
  139. for status.State != svc.Stopped {
  140. if timeout.Before(time.Now()) {
  141. return fmt.Errorf("timeout waiting for %s service to stop", service)
  142. }
  143. time.Sleep(300 * time.Millisecond)
  144. status, err = s.Query()
  145. if err != nil {
  146. return fmt.Errorf("could not retrieve %s service status: %v", service, err)
  147. }
  148. }
  149. return nil
  150. }
  151. // ServiceExists ensures the service is defined for this init system.
  152. func (sysd WindowsInitSystem) ServiceExists(service string) bool {
  153. m, err := mgr.Connect()
  154. if err != nil {
  155. return false
  156. }
  157. defer m.Disconnect()
  158. s, err := m.OpenService(service)
  159. if err != nil {
  160. return false
  161. }
  162. defer s.Close()
  163. return true
  164. }
  165. // ServiceIsEnabled ensures the service is enabled to start on each boot.
  166. func (sysd WindowsInitSystem) ServiceIsEnabled(service string) bool {
  167. m, err := mgr.Connect()
  168. if err != nil {
  169. return false
  170. }
  171. defer m.Disconnect()
  172. s, err := m.OpenService(service)
  173. if err != nil {
  174. return false
  175. }
  176. defer s.Close()
  177. c, err := s.Config()
  178. if err != nil {
  179. return false
  180. }
  181. return c.StartType != mgr.StartDisabled
  182. }
  183. // ServiceIsActive ensures the service is running, or attempting to run. (crash looping in the case of kubelet)
  184. func (sysd WindowsInitSystem) ServiceIsActive(service string) bool {
  185. m, err := mgr.Connect()
  186. if err != nil {
  187. return false
  188. }
  189. defer m.Disconnect()
  190. s, err := m.OpenService(service)
  191. if err != nil {
  192. return false
  193. }
  194. defer s.Close()
  195. status, err := s.Query()
  196. if err != nil {
  197. return false
  198. }
  199. return status.State == svc.Running
  200. }
  201. // GetInitSystem returns an InitSystem for the current system, or nil
  202. // if we cannot detect a supported init system.
  203. // This indicates we will skip init system checks, not an error.
  204. func GetInitSystem() (InitSystem, error) {
  205. m, err := mgr.Connect()
  206. if err != nil {
  207. return nil, fmt.Errorf("no supported init system detected: %v", err)
  208. }
  209. defer m.Disconnect()
  210. return &WindowsInitSystem{}, nil
  211. }