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  }