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