golang.org/x/sys@v0.9.0/windows/svc/mgr/mgr_test.go (about) 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 5 //go:build windows 6 // +build windows 7 8 package mgr_test 9 10 import ( 11 "fmt" 12 "os" 13 "path/filepath" 14 "sort" 15 "strings" 16 "syscall" 17 "testing" 18 "time" 19 20 "golang.org/x/sys/windows" 21 "golang.org/x/sys/windows/svc" 22 "golang.org/x/sys/windows/svc/mgr" 23 ) 24 25 func TestOpenLanManServer(t *testing.T) { 26 m, err := mgr.Connect() 27 if err != nil { 28 if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED { 29 t.Skip("Skipping test: we don't have rights to manage services.") 30 } 31 t.Fatalf("SCM connection failed: %s", err) 32 } 33 defer m.Disconnect() 34 35 s, err := m.OpenService("LanmanServer") 36 if err != nil { 37 t.Fatalf("OpenService(lanmanserver) failed: %s", err) 38 } 39 defer s.Close() 40 41 _, err = s.Config() 42 if err != nil { 43 t.Fatalf("Config failed: %s", err) 44 } 45 } 46 47 func install(t *testing.T, m *mgr.Mgr, name, exepath string, c mgr.Config) { 48 // Sometimes it takes a while for the service to get 49 // removed after previous test run. 50 for i := 0; ; i++ { 51 s, err := m.OpenService(name) 52 if err != nil { 53 break 54 } 55 s.Close() 56 57 if i > 10 { 58 t.Fatalf("service %s already exists", name) 59 } 60 time.Sleep(300 * time.Millisecond) 61 } 62 63 s, err := m.CreateService(name, exepath, c) 64 if err != nil { 65 t.Fatalf("CreateService(%s) failed: %v", name, err) 66 } 67 defer s.Close() 68 } 69 70 func depString(d []string) string { 71 if len(d) == 0 { 72 return "" 73 } 74 for i := range d { 75 d[i] = strings.ToLower(d[i]) 76 } 77 ss := sort.StringSlice(d) 78 ss.Sort() 79 return strings.Join([]string(ss), " ") 80 } 81 82 func testConfig(t *testing.T, s *mgr.Service, should mgr.Config) mgr.Config { 83 is, err := s.Config() 84 if err != nil { 85 t.Fatalf("Config failed: %s", err) 86 } 87 if should.DelayedAutoStart != is.DelayedAutoStart { 88 t.Fatalf("config mismatch: DelayedAutoStart is %v, but should have %v", is.DelayedAutoStart, should.DelayedAutoStart) 89 } 90 if should.DisplayName != is.DisplayName { 91 t.Fatalf("config mismatch: DisplayName is %q, but should have %q", is.DisplayName, should.DisplayName) 92 } 93 if should.StartType != is.StartType { 94 t.Fatalf("config mismatch: StartType is %v, but should have %v", is.StartType, should.StartType) 95 } 96 if should.Description != is.Description { 97 t.Fatalf("config mismatch: Description is %q, but should have %q", is.Description, should.Description) 98 } 99 if depString(should.Dependencies) != depString(is.Dependencies) { 100 t.Fatalf("config mismatch: Dependencies is %v, but should have %v", is.Dependencies, should.Dependencies) 101 } 102 return is 103 } 104 105 func testRecoveryActions(t *testing.T, s *mgr.Service, should []mgr.RecoveryAction) { 106 is, err := s.RecoveryActions() 107 if err != nil { 108 t.Fatalf("RecoveryActions failed: %s", err) 109 } 110 if len(should) != len(is) { 111 t.Errorf("recovery action mismatch: contains %v actions, but should have %v", len(is), len(should)) 112 } 113 for i := range is { 114 if should[i].Type != is[i].Type { 115 t.Errorf("recovery action mismatch: Type is %v, but should have %v", is[i].Type, should[i].Type) 116 } 117 if should[i].Delay != is[i].Delay { 118 t.Errorf("recovery action mismatch: Delay is %v, but should have %v", is[i].Delay, should[i].Delay) 119 } 120 } 121 } 122 123 func testResetPeriod(t *testing.T, s *mgr.Service, should uint32) { 124 is, err := s.ResetPeriod() 125 if err != nil { 126 t.Fatalf("ResetPeriod failed: %s", err) 127 } 128 if should != is { 129 t.Errorf("reset period mismatch: reset period is %v, but should have %v", is, should) 130 } 131 } 132 133 func testSetRecoveryActions(t *testing.T, s *mgr.Service) { 134 r := []mgr.RecoveryAction{ 135 { 136 Type: mgr.NoAction, 137 Delay: 60000 * time.Millisecond, 138 }, 139 { 140 Type: mgr.ServiceRestart, 141 Delay: 4 * time.Minute, 142 }, 143 { 144 Type: mgr.ServiceRestart, 145 Delay: time.Minute, 146 }, 147 { 148 Type: mgr.RunCommand, 149 Delay: 4000 * time.Millisecond, 150 }, 151 } 152 153 // 4 recovery actions with reset period 154 err := s.SetRecoveryActions(r, uint32(10000)) 155 if err != nil { 156 t.Fatalf("SetRecoveryActions failed: %v", err) 157 } 158 testRecoveryActions(t, s, r) 159 testResetPeriod(t, s, uint32(10000)) 160 161 // Infinite reset period 162 err = s.SetRecoveryActions(r, syscall.INFINITE) 163 if err != nil { 164 t.Fatalf("SetRecoveryActions failed: %v", err) 165 } 166 testRecoveryActions(t, s, r) 167 testResetPeriod(t, s, syscall.INFINITE) 168 169 // nil recovery actions 170 err = s.SetRecoveryActions(nil, 0) 171 if err.Error() != "recoveryActions cannot be nil" { 172 t.Fatalf("SetRecoveryActions failed with unexpected error message of %q", err) 173 } 174 175 // Delete all recovery actions and reset period 176 err = s.ResetRecoveryActions() 177 if err != nil { 178 t.Fatalf("ResetRecoveryActions failed: %v", err) 179 } 180 testRecoveryActions(t, s, nil) 181 testResetPeriod(t, s, 0) 182 } 183 184 func testRebootMessage(t *testing.T, s *mgr.Service, should string) { 185 err := s.SetRebootMessage(should) 186 if err != nil { 187 t.Fatalf("SetRebootMessage failed: %v", err) 188 } 189 is, err := s.RebootMessage() 190 if err != nil { 191 t.Fatalf("RebootMessage failed: %v", err) 192 } 193 if should != is { 194 t.Errorf("reboot message mismatch: message is %q, but should have %q", is, should) 195 } 196 } 197 198 func testRecoveryCommand(t *testing.T, s *mgr.Service, should string) { 199 err := s.SetRecoveryCommand(should) 200 if err != nil { 201 t.Fatalf("SetRecoveryCommand failed: %v", err) 202 } 203 is, err := s.RecoveryCommand() 204 if err != nil { 205 t.Fatalf("RecoveryCommand failed: %v", err) 206 } 207 if should != is { 208 t.Errorf("recovery command mismatch: command is %q, but should have %q", is, should) 209 } 210 } 211 212 func testControl(t *testing.T, s *mgr.Service, c svc.Cmd, expectedErr error, expectedStatus svc.Status) { 213 status, err := s.Control(c) 214 if err != expectedErr { 215 t.Fatalf("Unexpected return from s.Control: %v (expected %v)", err, expectedErr) 216 } 217 if expectedStatus != status { 218 t.Fatalf("Unexpected status from s.Control: %+v (expected %+v)", status, expectedStatus) 219 } 220 } 221 222 func remove(t *testing.T, s *mgr.Service) { 223 err := s.Delete() 224 if err != nil { 225 t.Fatalf("Delete failed: %s", err) 226 } 227 } 228 229 func TestMyService(t *testing.T) { 230 if os.Getenv("GO_BUILDER_NAME") == "" { 231 // Don't install services on arbitrary users' machines. 232 t.Skip("skipping test that modifies system services: GO_BUILDER_NAME not set") 233 } 234 if testing.Short() { 235 t.Skip("skipping test in short mode that modifies system services") 236 } 237 238 const name = "mgrtestservice" 239 240 m, err := mgr.Connect() 241 if err != nil { 242 t.Fatalf("SCM connection failed: %s", err) 243 } 244 defer m.Disconnect() 245 246 c := mgr.Config{ 247 StartType: mgr.StartDisabled, 248 DisplayName: "x-sys mgr test service", 249 Description: "x-sys mgr test service is just a test", 250 Dependencies: []string{"LanmanServer", "W32Time"}, 251 } 252 253 exename := os.Args[0] 254 exepath, err := filepath.Abs(exename) 255 if err != nil { 256 t.Fatalf("filepath.Abs(%s) failed: %s", exename, err) 257 } 258 259 install(t, m, name, exepath, c) 260 261 s, err := m.OpenService(name) 262 if err != nil { 263 t.Fatalf("service %s is not installed", name) 264 } 265 defer s.Close() 266 defer s.Delete() 267 268 c.BinaryPathName = exepath 269 c = testConfig(t, s, c) 270 271 c.StartType = mgr.StartManual 272 err = s.UpdateConfig(c) 273 if err != nil { 274 t.Fatalf("UpdateConfig failed: %v", err) 275 } 276 277 testConfig(t, s, c) 278 279 c.StartType = mgr.StartAutomatic 280 c.DelayedAutoStart = true 281 err = s.UpdateConfig(c) 282 if err != nil { 283 t.Fatalf("UpdateConfig failed: %v", err) 284 } 285 286 testConfig(t, s, c) 287 288 svcnames, err := m.ListServices() 289 if err != nil { 290 t.Fatalf("ListServices failed: %v", err) 291 } 292 var serviceIsInstalled bool 293 for _, sn := range svcnames { 294 if sn == name { 295 serviceIsInstalled = true 296 break 297 } 298 } 299 if !serviceIsInstalled { 300 t.Errorf("ListServices failed to find %q service", name) 301 } 302 303 testSetRecoveryActions(t, s) 304 testRebootMessage(t, s, fmt.Sprintf("%s failed", name)) 305 testRebootMessage(t, s, "") // delete reboot message 306 testRecoveryCommand(t, s, fmt.Sprintf("sc query %s", name)) 307 testRecoveryCommand(t, s, "") // delete recovery command 308 309 expectedStatus := svc.Status{ 310 State: svc.Stopped, 311 } 312 testControl(t, s, svc.Stop, windows.ERROR_SERVICE_NOT_ACTIVE, expectedStatus) 313 314 remove(t, s) 315 } 316 317 func TestListDependentServices(t *testing.T) { 318 m, err := mgr.Connect() 319 if err != nil { 320 if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED { 321 t.Skip("Skipping test: we don't have rights to manage services.") 322 } 323 t.Fatalf("SCM connection failed: %s", err) 324 } 325 defer m.Disconnect() 326 327 baseServiceName := "testservice1" 328 dependentServiceName := "testservice2" 329 install(t, m, baseServiceName, "", mgr.Config{}) 330 baseService, err := m.OpenService(baseServiceName) 331 if err != nil { 332 t.Fatalf("OpenService failed: %v", err) 333 } 334 defer remove(t, baseService) 335 install(t, m, dependentServiceName, "", mgr.Config{Dependencies: []string{baseServiceName}}) 336 dependentService, err := m.OpenService(dependentServiceName) 337 if err != nil { 338 t.Fatalf("OpenService failed: %v", err) 339 } 340 defer remove(t, dependentService) 341 342 // test that both the base service and dependent service list the correct dependencies 343 dependentServices, err := baseService.ListDependentServices(svc.AnyActivity) 344 if err != nil { 345 t.Fatalf("baseService.ListDependentServices failed: %v", err) 346 } 347 if len(dependentServices) != 1 || dependentServices[0] != dependentServiceName { 348 t.Errorf("Found %v, instead of expected contents %s", dependentServices, dependentServiceName) 349 } 350 dependentServices, err = dependentService.ListDependentServices(svc.AnyActivity) 351 if err != nil { 352 t.Fatalf("dependentService.ListDependentServices failed: %v", err) 353 } 354 if len(dependentServices) != 0 { 355 t.Errorf("Found %v, where no service should be listed", dependentService) 356 } 357 }