windows/svc: make IsWindowsService handle parent exit

IsWindowsService fails when parent process has exited.

This change makes IsWindowsService return false instead.

Added a test to verify the change.

Change-Id: Ibb2aa9478e8afd9e35011bbdc0985bdf8f0af9cc
Reviewed-on: https://go-review.googlesource.com/c/sys/+/294729
TryBot-Result: Go Bot <gobot@golang.org>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Trust: Alex Brainman <alex.brainman@gmail.com>
This commit is contained in:
Alex Brainman
2021-02-21 11:27:02 +11:00
parent f36f78243c
commit b0d1d43c01
2 changed files with 78 additions and 0 deletions

View File

@@ -96,6 +96,13 @@ func IsWindowsService() (bool, error) {
var psid uint32
err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid)
if err != nil {
if err == windows.ERROR_INVALID_PARAMETER {
// This error happens when Windows cannot find process parent.
// Perhaps process parent exited.
// Assume we are not running in a service, because service
// parent process (services.exe) cannot exit.
return false, nil
}
return false, err
}
if psid != 0 {

View File

@@ -171,3 +171,74 @@ func TestIsWindowsService(t *testing.T) {
t.Error("IsWindowsService retuns true when not running in a service.")
}
}
func TestIsWindowsServiceWhenParentExits(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") == "parent" {
// in parent process
// Start the child and exit quickly.
child := exec.Command(os.Args[0], "-test.run=TestIsWindowsServiceWhenParentExits")
child.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=child")
err := child.Start()
if err != nil {
fmt.Fprintf(os.Stderr, fmt.Sprintf("child start failed: %v", err))
os.Exit(1)
}
os.Exit(0)
}
if os.Getenv("GO_WANT_HELPER_PROCESS") == "child" {
// in child process
dumpPath := os.Getenv("GO_WANT_HELPER_PROCESS_FILE")
if dumpPath == "" {
// We cannot report this error. But main test will notice
// that we did not create dump file.
os.Exit(1)
}
var msg string
isSvc, err := svc.IsWindowsService()
if err != nil {
msg = err.Error()
}
if isSvc {
msg = "IsWindowsService retuns true when not running in a service."
}
err = os.WriteFile(dumpPath, []byte(msg), 0644)
if err != nil {
// We cannot report this error. But main test will notice
// that we did not create dump file.
os.Exit(2)
}
os.Exit(0)
}
// Run in a loop until it fails.
for i := 0; i < 10; i++ {
childDumpPath := filepath.Join(t.TempDir(), "issvc.txt")
parent := exec.Command(os.Args[0], "-test.run=TestIsWindowsServiceWhenParentExits")
parent.Env = append(os.Environ(),
"GO_WANT_HELPER_PROCESS=parent",
"GO_WANT_HELPER_PROCESS_FILE="+childDumpPath)
parentOutput, err := parent.CombinedOutput()
if err != nil {
t.Errorf("parent failed: %v: %v", err, string(parentOutput))
}
for i := 0; ; i++ {
if _, err := os.Stat(childDumpPath); err == nil {
break
}
time.Sleep(100 * time.Millisecond)
if i > 10 {
t.Fatal("timed out waiting for child ouput file to be created.")
}
}
childOutput, err := ioutil.ReadFile(childDumpPath)
if err != nil {
t.Fatalf("reading child ouput failed: %v", err)
}
if got, want := string(childOutput), ""; got != want {
t.Fatalf("child output: want %q, got %q", want, got)
}
}
}