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