golang.org/x/sys@v0.9.0/windows/svc/svc_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 svc_test 9 10 import ( 11 "fmt" 12 "io/ioutil" 13 "math/rand" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "strings" 18 "testing" 19 "time" 20 21 "golang.org/x/sys/windows/svc" 22 "golang.org/x/sys/windows/svc/mgr" 23 ) 24 25 func getState(t *testing.T, s *mgr.Service) svc.State { 26 status, err := s.Query() 27 if err != nil { 28 t.Fatalf("Query(%s) failed: %s", s.Name, err) 29 } 30 return status.State 31 } 32 33 func testState(t *testing.T, s *mgr.Service, want svc.State) { 34 have := getState(t, s) 35 if have != want { 36 t.Fatalf("%s state is=%d want=%d", s.Name, have, want) 37 } 38 } 39 40 func waitState(t *testing.T, s *mgr.Service, want svc.State) { 41 for i := 0; ; i++ { 42 have := getState(t, s) 43 if have == want { 44 return 45 } 46 if i > 10 { 47 t.Fatalf("%s state is=%d, waiting timeout", s.Name, have) 48 } 49 time.Sleep(300 * time.Millisecond) 50 } 51 } 52 53 // stopAndDeleteIfInstalled stops and deletes service name, 54 // if the service is running and / or installed. 55 func stopAndDeleteIfInstalled(t *testing.T, m *mgr.Mgr, name string) { 56 s, err := m.OpenService(name) 57 if err != nil { 58 // Service is not installed. 59 return 60 61 } 62 defer s.Close() 63 64 // Make sure the service is not running, otherwise we won't be able to delete it. 65 if getState(t, s) == svc.Running { 66 _, err = s.Control(svc.Stop) 67 if err != nil { 68 t.Fatalf("Control(%s) failed: %s", s.Name, err) 69 } 70 waitState(t, s, svc.Stopped) 71 } 72 73 err = s.Delete() 74 if err != nil { 75 t.Fatalf("Delete failed: %s", err) 76 } 77 } 78 79 func TestExample(t *testing.T) { 80 if os.Getenv("GO_BUILDER_NAME") == "" { 81 // Don't install services on arbitrary users' machines. 82 t.Skip("skipping test that modifies system services: GO_BUILDER_NAME not set") 83 } 84 if testing.Short() { 85 t.Skip("skipping test in short mode that modifies system services") 86 } 87 88 const name = "svctestservice" 89 90 m, err := mgr.Connect() 91 if err != nil { 92 t.Fatalf("SCM connection failed: %s", err) 93 } 94 defer m.Disconnect() 95 96 dir, err := ioutil.TempDir("", "svc") 97 if err != nil { 98 t.Fatalf("failed to create temp directory: %v", err) 99 } 100 defer os.RemoveAll(dir) 101 102 exepath := filepath.Join(dir, "a.exe") 103 o, err := exec.Command("go", "build", "-o", exepath, "golang.org/x/sys/windows/svc/example").CombinedOutput() 104 if err != nil { 105 t.Fatalf("failed to build service program: %v\n%v", err, string(o)) 106 } 107 108 stopAndDeleteIfInstalled(t, m, name) 109 110 s, err := m.CreateService(name, exepath, mgr.Config{DisplayName: "x-sys svc test service"}, "-name", name) 111 if err != nil { 112 t.Fatalf("CreateService(%s) failed: %v", name, err) 113 } 114 defer s.Close() 115 116 args := []string{"is", "manual-started", fmt.Sprintf("%d", rand.Int())} 117 118 testState(t, s, svc.Stopped) 119 err = s.Start(args...) 120 if err != nil { 121 t.Fatalf("Start(%s) failed: %s", s.Name, err) 122 } 123 waitState(t, s, svc.Running) 124 time.Sleep(1 * time.Second) 125 126 // testing deadlock from issues 4. 127 _, err = s.Control(svc.Interrogate) 128 if err != nil { 129 t.Fatalf("Control(%s) failed: %s", s.Name, err) 130 } 131 _, err = s.Control(svc.Interrogate) 132 if err != nil { 133 t.Fatalf("Control(%s) failed: %s", s.Name, err) 134 } 135 time.Sleep(1 * time.Second) 136 137 _, err = s.Control(svc.Stop) 138 if err != nil { 139 t.Fatalf("Control(%s) failed: %s", s.Name, err) 140 } 141 waitState(t, s, svc.Stopped) 142 143 err = s.Delete() 144 if err != nil { 145 t.Fatalf("Delete failed: %s", err) 146 } 147 148 out, err := exec.Command("wevtutil.exe", "qe", "Application", "/q:*[System[Provider[@Name='"+name+"']]]", "/rd:true", "/c:10").CombinedOutput() 149 if err != nil { 150 t.Fatalf("wevtutil failed: %v\n%v", err, string(out)) 151 } 152 want := strings.Join(append([]string{name}, args...), "-") 153 // Test context passing (see servicemain in sys_386.s and sys_amd64.s). 154 want += "-123456" 155 if !strings.Contains(string(out), want) { 156 t.Errorf("%q string does not contain %q", out, want) 157 } 158 } 159 160 func TestIsAnInteractiveSession(t *testing.T) { 161 isInteractive, err := svc.IsAnInteractiveSession() 162 if err != nil { 163 t.Fatal(err) 164 } 165 if !isInteractive { 166 t.Error("IsAnInteractiveSession retuns false when running interactively.") 167 } 168 } 169 170 func TestIsWindowsService(t *testing.T) { 171 isSvc, err := svc.IsWindowsService() 172 if err != nil { 173 t.Fatal(err) 174 } 175 if isSvc { 176 t.Error("IsWindowsService retuns true when not running in a service.") 177 } 178 } 179 180 func TestIsWindowsServiceWhenParentExits(t *testing.T) { 181 if os.Getenv("GO_WANT_HELPER_PROCESS") == "parent" { 182 // in parent process 183 184 // Start the child and exit quickly. 185 child := exec.Command(os.Args[0], "-test.run=TestIsWindowsServiceWhenParentExits") 186 child.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=child") 187 err := child.Start() 188 if err != nil { 189 fmt.Fprintf(os.Stderr, fmt.Sprintf("child start failed: %v", err)) 190 os.Exit(1) 191 } 192 os.Exit(0) 193 } 194 195 if os.Getenv("GO_WANT_HELPER_PROCESS") == "child" { 196 // in child process 197 dumpPath := os.Getenv("GO_WANT_HELPER_PROCESS_FILE") 198 if dumpPath == "" { 199 // We cannot report this error. But main test will notice 200 // that we did not create dump file. 201 os.Exit(1) 202 } 203 var msg string 204 isSvc, err := svc.IsWindowsService() 205 if err != nil { 206 msg = err.Error() 207 } 208 if isSvc { 209 msg = "IsWindowsService retuns true when not running in a service." 210 } 211 err = ioutil.WriteFile(dumpPath, []byte(msg), 0644) 212 if err != nil { 213 // We cannot report this error. But main test will notice 214 // that we did not create dump file. 215 os.Exit(2) 216 } 217 os.Exit(0) 218 } 219 220 // Run in a loop until it fails. 221 for i := 0; i < 10; i++ { 222 childDumpPath := filepath.Join(t.TempDir(), "issvc.txt") 223 224 parent := exec.Command(os.Args[0], "-test.run=TestIsWindowsServiceWhenParentExits") 225 parent.Env = append(os.Environ(), 226 "GO_WANT_HELPER_PROCESS=parent", 227 "GO_WANT_HELPER_PROCESS_FILE="+childDumpPath) 228 parentOutput, err := parent.CombinedOutput() 229 if err != nil { 230 t.Errorf("parent failed: %v: %v", err, string(parentOutput)) 231 } 232 for i := 0; ; i++ { 233 if _, err := os.Stat(childDumpPath); err == nil { 234 break 235 } 236 time.Sleep(100 * time.Millisecond) 237 if i > 10 { 238 t.Fatal("timed out waiting for child ouput file to be created.") 239 } 240 } 241 childOutput, err := ioutil.ReadFile(childDumpPath) 242 if err != nil { 243 t.Fatalf("reading child ouput failed: %v", err) 244 } 245 if got, want := string(childOutput), ""; got != want { 246 t.Fatalf("child output: want %q, got %q", want, got) 247 } 248 } 249 }