diff options
Diffstat (limited to 'src/syscall/exec_pdeathsig_test.go')
-rw-r--r-- | src/syscall/exec_pdeathsig_test.go | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/syscall/exec_pdeathsig_test.go b/src/syscall/exec_pdeathsig_test.go new file mode 100644 index 0000000..96ae27b --- /dev/null +++ b/src/syscall/exec_pdeathsig_test.go @@ -0,0 +1,134 @@ +// Copyright 2015 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 freebsd || linux + +package syscall_test + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "testing" + "time" +) + +func TestDeathSignal(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + + // Copy the test binary to a location that a non-root user can read/execute + // after we drop privileges + tempDir, err := os.MkdirTemp("", "TestDeathSignal") + if err != nil { + t.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(tempDir) + os.Chmod(tempDir, 0755) + + tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) + + src, err := os.Open(os.Args[0]) + if err != nil { + t.Fatalf("cannot open binary %q, %v", os.Args[0], err) + } + defer src.Close() + + dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) + } + if _, err := io.Copy(dst, src); err != nil { + t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) + } + err = dst.Close() + if err != nil { + t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) + } + + cmd := exec.Command(tmpBinary) + cmd.Env = append(os.Environ(), "GO_DEATHSIG_PARENT=1") + chldStdin, err := cmd.StdinPipe() + if err != nil { + t.Fatalf("failed to create new stdin pipe: %v", err) + } + chldStdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatalf("failed to create new stdout pipe: %v", err) + } + cmd.Stderr = os.Stderr + + err = cmd.Start() + defer cmd.Wait() + if err != nil { + t.Fatalf("failed to start first child process: %v", err) + } + + chldPipe := bufio.NewReader(chldStdout) + + if got, err := chldPipe.ReadString('\n'); got == "start\n" { + syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) + + go func() { + time.Sleep(5 * time.Second) + chldStdin.Close() + }() + + want := "ok\n" + if got, err = chldPipe.ReadString('\n'); got != want { + t.Fatalf("expected %q, received %q, %v", want, got, err) + } + } else { + t.Fatalf("did not receive start from child, received %q, %v", got, err) + } +} + +func deathSignalParent() { + cmd := exec.Command(os.Args[0]) + cmd.Env = append(os.Environ(), + "GO_DEATHSIG_PARENT=", + "GO_DEATHSIG_CHILD=1", + ) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + attrs := syscall.SysProcAttr{ + Pdeathsig: syscall.SIGUSR1, + // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is + // unused on Ubuntu + Credential: &syscall.Credential{Uid: 99, Gid: 99}, + } + cmd.SysProcAttr = &attrs + + err := cmd.Start() + if err != nil { + fmt.Fprintf(os.Stderr, "death signal parent error: %v\n", err) + os.Exit(1) + } + cmd.Wait() + os.Exit(0) +} + +func deathSignalChild() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGUSR1) + go func() { + <-c + fmt.Println("ok") + os.Exit(0) + }() + fmt.Println("start") + + buf := make([]byte, 32) + os.Stdin.Read(buf) + + // We expected to be signaled before stdin closed + fmt.Println("not ok") + os.Exit(1) +} |