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