Source file src/syscall/exec_pdeathsig_test.go

     1  // Copyright 2015 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  //go:build freebsd || linux
     6  
     7  package syscall_test
     8  
     9  import (
    10  	"bufio"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"os/exec"
    15  	"os/signal"
    16  	"path/filepath"
    17  	"syscall"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  func TestDeathSignal(t *testing.T) {
    23  	if os.Getuid() != 0 {
    24  		t.Skip("skipping root only test")
    25  	}
    26  
    27  	// Copy the test binary to a location that a non-root user can read/execute
    28  	// after we drop privileges
    29  	tempDir, err := os.MkdirTemp("", "TestDeathSignal")
    30  	if err != nil {
    31  		t.Fatalf("cannot create temporary directory: %v", err)
    32  	}
    33  	defer os.RemoveAll(tempDir)
    34  	os.Chmod(tempDir, 0755)
    35  
    36  	tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
    37  
    38  	src, err := os.Open(os.Args[0])
    39  	if err != nil {
    40  		t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
    41  	}
    42  	defer src.Close()
    43  
    44  	dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
    45  	if err != nil {
    46  		t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
    47  	}
    48  	if _, err := io.Copy(dst, src); err != nil {
    49  		t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
    50  	}
    51  	err = dst.Close()
    52  	if err != nil {
    53  		t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
    54  	}
    55  
    56  	cmd := exec.Command(tmpBinary)
    57  	cmd.Env = append(os.Environ(), "GO_DEATHSIG_PARENT=1")
    58  	chldStdin, err := cmd.StdinPipe()
    59  	if err != nil {
    60  		t.Fatalf("failed to create new stdin pipe: %v", err)
    61  	}
    62  	chldStdout, err := cmd.StdoutPipe()
    63  	if err != nil {
    64  		t.Fatalf("failed to create new stdout pipe: %v", err)
    65  	}
    66  	cmd.Stderr = os.Stderr
    67  
    68  	err = cmd.Start()
    69  	defer cmd.Wait()
    70  	if err != nil {
    71  		t.Fatalf("failed to start first child process: %v", err)
    72  	}
    73  
    74  	chldPipe := bufio.NewReader(chldStdout)
    75  
    76  	if got, err := chldPipe.ReadString('\n'); got == "start\n" {
    77  		syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
    78  
    79  		go func() {
    80  			time.Sleep(5 * time.Second)
    81  			chldStdin.Close()
    82  		}()
    83  
    84  		want := "ok\n"
    85  		if got, err = chldPipe.ReadString('\n'); got != want {
    86  			t.Fatalf("expected %q, received %q, %v", want, got, err)
    87  		}
    88  	} else {
    89  		t.Fatalf("did not receive start from child, received %q, %v", got, err)
    90  	}
    91  }
    92  
    93  func deathSignalParent() {
    94  	cmd := exec.Command(os.Args[0])
    95  	cmd.Env = append(os.Environ(),
    96  		"GO_DEATHSIG_PARENT=",
    97  		"GO_DEATHSIG_CHILD=1",
    98  	)
    99  	cmd.Stdin = os.Stdin
   100  	cmd.Stdout = os.Stdout
   101  	attrs := syscall.SysProcAttr{
   102  		Pdeathsig: syscall.SIGUSR1,
   103  		// UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is
   104  		// unused on Ubuntu
   105  		Credential: &syscall.Credential{Uid: 99, Gid: 99},
   106  	}
   107  	cmd.SysProcAttr = &attrs
   108  
   109  	err := cmd.Start()
   110  	if err != nil {
   111  		fmt.Fprintf(os.Stderr, "death signal parent error: %v\n", err)
   112  		os.Exit(1)
   113  	}
   114  	cmd.Wait()
   115  	os.Exit(0)
   116  }
   117  
   118  func deathSignalChild() {
   119  	c := make(chan os.Signal, 1)
   120  	signal.Notify(c, syscall.SIGUSR1)
   121  	go func() {
   122  		<-c
   123  		fmt.Println("ok")
   124  		os.Exit(0)
   125  	}()
   126  	fmt.Println("start")
   127  
   128  	buf := make([]byte, 32)
   129  	os.Stdin.Read(buf)
   130  
   131  	// We expected to be signaled before stdin closed
   132  	fmt.Println("not ok")
   133  	os.Exit(1)
   134  }
   135  

View as plain text