github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/syscall/syscall_linux_test.go (about)

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