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  }