mirror of
https://github.com/golang/sys.git
synced 2026-02-09 04:06:04 +03:00
Done with: go get go@1.18 go mod tidy go fix ./... Using go1.21.3. Also update code generators to use only the new go:build lines, not the old +build ones. For golang/go#60268. Change-Id: I6aabc42efb6ab3329981100e1db2263aac5e92a6 Reviewed-on: https://go-review.googlesource.com/c/sys/+/534222 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
411 lines
11 KiB
Go
411 lines
11 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build windows
|
|
|
|
package mgr_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/sys/windows/svc"
|
|
"golang.org/x/sys/windows/svc/mgr"
|
|
)
|
|
|
|
func TestOpenLanManServer(t *testing.T) {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED {
|
|
t.Skip("Skipping test: we don't have rights to manage services.")
|
|
}
|
|
t.Fatalf("SCM connection failed: %s", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService("LanmanServer")
|
|
if err != nil {
|
|
t.Fatalf("OpenService(lanmanserver) failed: %s", err)
|
|
}
|
|
defer s.Close()
|
|
|
|
_, err = s.Config()
|
|
if err != nil {
|
|
t.Fatalf("Config failed: %s", err)
|
|
}
|
|
}
|
|
|
|
func install(t *testing.T, m *mgr.Mgr, name, exepath string, c mgr.Config) {
|
|
// Sometimes it takes a while for the service to get
|
|
// removed after previous test run.
|
|
for i := 0; ; i++ {
|
|
s, err := m.OpenService(name)
|
|
if err != nil {
|
|
break
|
|
}
|
|
s.Close()
|
|
|
|
if i > 10 {
|
|
t.Fatalf("service %s already exists", name)
|
|
}
|
|
time.Sleep(300 * time.Millisecond)
|
|
}
|
|
|
|
s, err := m.CreateService(name, exepath, c)
|
|
if err != nil {
|
|
t.Fatalf("CreateService(%s) failed: %v", name, err)
|
|
}
|
|
defer s.Close()
|
|
}
|
|
|
|
func depString(d []string) string {
|
|
if len(d) == 0 {
|
|
return ""
|
|
}
|
|
for i := range d {
|
|
d[i] = strings.ToLower(d[i])
|
|
}
|
|
ss := sort.StringSlice(d)
|
|
ss.Sort()
|
|
return strings.Join([]string(ss), " ")
|
|
}
|
|
|
|
func testConfig(t *testing.T, s *mgr.Service, should mgr.Config) mgr.Config {
|
|
is, err := s.Config()
|
|
if err != nil {
|
|
t.Fatalf("Config failed: %s", err)
|
|
}
|
|
if should.DelayedAutoStart != is.DelayedAutoStart {
|
|
t.Fatalf("config mismatch: DelayedAutoStart is %v, but should have %v", is.DelayedAutoStart, should.DelayedAutoStart)
|
|
}
|
|
if should.DisplayName != is.DisplayName {
|
|
t.Fatalf("config mismatch: DisplayName is %q, but should have %q", is.DisplayName, should.DisplayName)
|
|
}
|
|
if should.StartType != is.StartType {
|
|
t.Fatalf("config mismatch: StartType is %v, but should have %v", is.StartType, should.StartType)
|
|
}
|
|
if should.Description != is.Description {
|
|
t.Fatalf("config mismatch: Description is %q, but should have %q", is.Description, should.Description)
|
|
}
|
|
if depString(should.Dependencies) != depString(is.Dependencies) {
|
|
t.Fatalf("config mismatch: Dependencies is %v, but should have %v", is.Dependencies, should.Dependencies)
|
|
}
|
|
return is
|
|
}
|
|
|
|
func testRecoveryActions(t *testing.T, s *mgr.Service, should []mgr.RecoveryAction) {
|
|
is, err := s.RecoveryActions()
|
|
if err != nil {
|
|
t.Fatalf("RecoveryActions failed: %s", err)
|
|
}
|
|
if len(should) != len(is) {
|
|
t.Errorf("recovery action mismatch: contains %v actions, but should have %v", len(is), len(should))
|
|
}
|
|
for i := range is {
|
|
if should[i].Type != is[i].Type {
|
|
t.Errorf("recovery action mismatch: Type is %v, but should have %v", is[i].Type, should[i].Type)
|
|
}
|
|
if should[i].Delay != is[i].Delay {
|
|
t.Errorf("recovery action mismatch: Delay is %v, but should have %v", is[i].Delay, should[i].Delay)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testResetPeriod(t *testing.T, s *mgr.Service, should uint32) {
|
|
is, err := s.ResetPeriod()
|
|
if err != nil {
|
|
t.Fatalf("ResetPeriod failed: %s", err)
|
|
}
|
|
if should != is {
|
|
t.Errorf("reset period mismatch: reset period is %v, but should have %v", is, should)
|
|
}
|
|
}
|
|
|
|
func testSetRecoveryActions(t *testing.T, s *mgr.Service) {
|
|
r := []mgr.RecoveryAction{
|
|
{
|
|
Type: mgr.NoAction,
|
|
Delay: 60000 * time.Millisecond,
|
|
},
|
|
{
|
|
Type: mgr.ServiceRestart,
|
|
Delay: 4 * time.Minute,
|
|
},
|
|
{
|
|
Type: mgr.ServiceRestart,
|
|
Delay: time.Minute,
|
|
},
|
|
{
|
|
Type: mgr.RunCommand,
|
|
Delay: 4000 * time.Millisecond,
|
|
},
|
|
}
|
|
|
|
// 4 recovery actions with reset period
|
|
err := s.SetRecoveryActions(r, uint32(10000))
|
|
if err != nil {
|
|
t.Fatalf("SetRecoveryActions failed: %v", err)
|
|
}
|
|
testRecoveryActions(t, s, r)
|
|
testResetPeriod(t, s, uint32(10000))
|
|
|
|
// Infinite reset period
|
|
err = s.SetRecoveryActions(r, syscall.INFINITE)
|
|
if err != nil {
|
|
t.Fatalf("SetRecoveryActions failed: %v", err)
|
|
}
|
|
testRecoveryActions(t, s, r)
|
|
testResetPeriod(t, s, syscall.INFINITE)
|
|
|
|
// nil recovery actions
|
|
err = s.SetRecoveryActions(nil, 0)
|
|
if err.Error() != "recoveryActions cannot be nil" {
|
|
t.Fatalf("SetRecoveryActions failed with unexpected error message of %q", err)
|
|
}
|
|
|
|
// Delete all recovery actions and reset period
|
|
err = s.ResetRecoveryActions()
|
|
if err != nil {
|
|
t.Fatalf("ResetRecoveryActions failed: %v", err)
|
|
}
|
|
testRecoveryActions(t, s, nil)
|
|
testResetPeriod(t, s, 0)
|
|
}
|
|
|
|
func testRebootMessage(t *testing.T, s *mgr.Service, should string) {
|
|
err := s.SetRebootMessage(should)
|
|
if err != nil {
|
|
t.Fatalf("SetRebootMessage failed: %v", err)
|
|
}
|
|
is, err := s.RebootMessage()
|
|
if err != nil {
|
|
t.Fatalf("RebootMessage failed: %v", err)
|
|
}
|
|
if should != is {
|
|
t.Errorf("reboot message mismatch: message is %q, but should have %q", is, should)
|
|
}
|
|
}
|
|
|
|
func testRecoveryCommand(t *testing.T, s *mgr.Service, should string) {
|
|
err := s.SetRecoveryCommand(should)
|
|
if err != nil {
|
|
t.Fatalf("SetRecoveryCommand failed: %v", err)
|
|
}
|
|
is, err := s.RecoveryCommand()
|
|
if err != nil {
|
|
t.Fatalf("RecoveryCommand failed: %v", err)
|
|
}
|
|
if should != is {
|
|
t.Errorf("recovery command mismatch: command is %q, but should have %q", is, should)
|
|
}
|
|
}
|
|
|
|
func testRecoveryActionsOnNonCrashFailures(t *testing.T, s *mgr.Service, should bool) {
|
|
err := s.SetRecoveryActionsOnNonCrashFailures(should)
|
|
if err != nil {
|
|
t.Fatalf("SetRecoveryActionsOnNonCrashFailures failed: %v", err)
|
|
}
|
|
is, err := s.RecoveryActionsOnNonCrashFailures()
|
|
if err != nil {
|
|
t.Fatalf("RecoveryActionsOnNonCrashFailures failed: %v", err)
|
|
}
|
|
if should != is {
|
|
t.Errorf("RecoveryActionsOnNonCrashFailures mismatch: flag is %v, but should have %v", is, should)
|
|
}
|
|
}
|
|
|
|
func testMultipleRecoverySettings(t *testing.T, s *mgr.Service, rebootMsgShould, recoveryCmdShould string, actionsFlagShould bool) {
|
|
err := s.SetRebootMessage(rebootMsgShould)
|
|
if err != nil {
|
|
t.Fatalf("SetRebootMessage failed: %v", err)
|
|
}
|
|
err = s.SetRecoveryActionsOnNonCrashFailures(actionsFlagShould)
|
|
if err != nil {
|
|
t.Fatalf("SetRecoveryActionsOnNonCrashFailures failed: %v", err)
|
|
}
|
|
err = s.SetRecoveryCommand(recoveryCmdShould)
|
|
if err != nil {
|
|
t.Fatalf("SetRecoveryCommand failed: %v", err)
|
|
}
|
|
|
|
rebootMsgIs, err := s.RebootMessage()
|
|
if err != nil {
|
|
t.Fatalf("RebootMessage failed: %v", err)
|
|
}
|
|
if rebootMsgShould != rebootMsgIs {
|
|
t.Errorf("reboot message mismatch: message is %q, but should have %q", rebootMsgIs, rebootMsgShould)
|
|
}
|
|
recoveryCommandIs, err := s.RecoveryCommand()
|
|
if err != nil {
|
|
t.Fatalf("RecoveryCommand failed: %v", err)
|
|
}
|
|
if recoveryCmdShould != recoveryCommandIs {
|
|
t.Errorf("recovery command mismatch: command is %q, but should have %q", recoveryCommandIs, recoveryCmdShould)
|
|
}
|
|
actionsFlagIs, err := s.RecoveryActionsOnNonCrashFailures()
|
|
if err != nil {
|
|
t.Fatalf("RecoveryActionsOnNonCrashFailures failed: %v", err)
|
|
}
|
|
if actionsFlagShould != actionsFlagIs {
|
|
t.Errorf("RecoveryActionsOnNonCrashFailures mismatch: flag is %v, but should have %v", actionsFlagIs, actionsFlagShould)
|
|
}
|
|
}
|
|
|
|
func testControl(t *testing.T, s *mgr.Service, c svc.Cmd, expectedErr error, expectedStatus svc.Status) {
|
|
status, err := s.Control(c)
|
|
if err != expectedErr {
|
|
t.Fatalf("Unexpected return from s.Control: %v (expected %v)", err, expectedErr)
|
|
}
|
|
if expectedStatus != status {
|
|
t.Fatalf("Unexpected status from s.Control: %+v (expected %+v)", status, expectedStatus)
|
|
}
|
|
}
|
|
|
|
func remove(t *testing.T, s *mgr.Service) {
|
|
err := s.Delete()
|
|
if err != nil {
|
|
t.Fatalf("Delete failed: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestMyService(t *testing.T) {
|
|
if os.Getenv("GO_BUILDER_NAME") == "" {
|
|
// Don't install services on arbitrary users' machines.
|
|
t.Skip("skipping test that modifies system services: GO_BUILDER_NAME not set")
|
|
}
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode that modifies system services")
|
|
}
|
|
|
|
const name = "mgrtestservice"
|
|
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
t.Fatalf("SCM connection failed: %s", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
c := mgr.Config{
|
|
StartType: mgr.StartDisabled,
|
|
DisplayName: "x-sys mgr test service",
|
|
Description: "x-sys mgr test service is just a test",
|
|
Dependencies: []string{"LanmanServer", "W32Time"},
|
|
}
|
|
|
|
exename := os.Args[0]
|
|
exepath, err := filepath.Abs(exename)
|
|
if err != nil {
|
|
t.Fatalf("filepath.Abs(%s) failed: %s", exename, err)
|
|
}
|
|
|
|
install(t, m, name, exepath, c)
|
|
|
|
s, err := m.OpenService(name)
|
|
if err != nil {
|
|
t.Fatalf("service %s is not installed", name)
|
|
}
|
|
defer s.Close()
|
|
defer s.Delete()
|
|
|
|
c.BinaryPathName = exepath
|
|
c = testConfig(t, s, c)
|
|
|
|
c.StartType = mgr.StartManual
|
|
err = s.UpdateConfig(c)
|
|
if err != nil {
|
|
t.Fatalf("UpdateConfig failed: %v", err)
|
|
}
|
|
|
|
testConfig(t, s, c)
|
|
|
|
c.StartType = mgr.StartAutomatic
|
|
c.DelayedAutoStart = true
|
|
err = s.UpdateConfig(c)
|
|
if err != nil {
|
|
t.Fatalf("UpdateConfig failed: %v", err)
|
|
}
|
|
|
|
testConfig(t, s, c)
|
|
|
|
svcnames, err := m.ListServices()
|
|
if err != nil {
|
|
t.Fatalf("ListServices failed: %v", err)
|
|
}
|
|
var serviceIsInstalled bool
|
|
for _, sn := range svcnames {
|
|
if sn == name {
|
|
serviceIsInstalled = true
|
|
break
|
|
}
|
|
}
|
|
if !serviceIsInstalled {
|
|
t.Errorf("ListServices failed to find %q service", name)
|
|
}
|
|
|
|
testSetRecoveryActions(t, s)
|
|
testRebootMessage(t, s, fmt.Sprintf("%s failed", name))
|
|
testRebootMessage(t, s, "") // delete reboot message
|
|
testRecoveryCommand(t, s, fmt.Sprintf("sc query %s", name))
|
|
testRecoveryCommand(t, s, "") // delete recovery command
|
|
testRecoveryActionsOnNonCrashFailures(t, s, true)
|
|
testRecoveryActionsOnNonCrashFailures(t, s, false)
|
|
testMultipleRecoverySettings(t, s, fmt.Sprintf("%s failed", name), fmt.Sprintf("sc query %s", name), true)
|
|
|
|
expectedStatus := svc.Status{
|
|
State: svc.Stopped,
|
|
}
|
|
testControl(t, s, svc.Stop, windows.ERROR_SERVICE_NOT_ACTIVE, expectedStatus)
|
|
|
|
remove(t, s)
|
|
}
|
|
|
|
func TestListDependentServices(t *testing.T) {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED {
|
|
t.Skip("Skipping test: we don't have rights to manage services.")
|
|
}
|
|
t.Fatalf("SCM connection failed: %s", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
baseServiceName := "testservice1"
|
|
dependentServiceName := "testservice2"
|
|
install(t, m, baseServiceName, "", mgr.Config{})
|
|
baseService, err := m.OpenService(baseServiceName)
|
|
if err != nil {
|
|
t.Fatalf("OpenService failed: %v", err)
|
|
}
|
|
defer remove(t, baseService)
|
|
install(t, m, dependentServiceName, "", mgr.Config{Dependencies: []string{baseServiceName}})
|
|
dependentService, err := m.OpenService(dependentServiceName)
|
|
if err != nil {
|
|
t.Fatalf("OpenService failed: %v", err)
|
|
}
|
|
defer remove(t, dependentService)
|
|
|
|
// test that both the base service and dependent service list the correct dependencies
|
|
dependentServices, err := baseService.ListDependentServices(svc.AnyActivity)
|
|
if err != nil {
|
|
t.Fatalf("baseService.ListDependentServices failed: %v", err)
|
|
}
|
|
if len(dependentServices) != 1 || dependentServices[0] != dependentServiceName {
|
|
t.Errorf("Found %v, instead of expected contents %s", dependentServices, dependentServiceName)
|
|
}
|
|
dependentServices, err = dependentService.ListDependentServices(svc.AnyActivity)
|
|
if err != nil {
|
|
t.Fatalf("dependentService.ListDependentServices failed: %v", err)
|
|
}
|
|
if len(dependentServices) != 0 {
|
|
t.Errorf("Found %v, where no service should be listed", dependentService)
|
|
}
|
|
}
|