Home | History | Annotate | Download | only in syscall
      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 }
    141 
    142 func TestParseNetlinkMessage(t *testing.T) {
    143 	for i, b := range [][]byte{
    144 		{103, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 5, 8, 0, 3,
    145 			0, 8, 0, 6, 0, 0, 0, 0, 1, 63, 0, 10, 0, 69, 16, 0, 59, 39, 82, 64, 0, 64, 6, 21, 89, 127, 0, 0,
    146 			1, 127, 0, 0, 1, 230, 228, 31, 144, 32, 186, 155, 211, 185, 151, 209, 179, 128, 24, 1, 86,
    147 			53, 119, 0, 0, 1, 1, 8, 10, 0, 17, 234, 12, 0, 17, 189, 126, 107, 106, 108, 107, 106, 13, 10,
    148 		},
    149 		{106, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 3, 8, 0, 3,
    150 			0, 8, 0, 6, 0, 0, 0, 0, 1, 66, 0, 10, 0, 69, 0, 0, 62, 230, 255, 64, 0, 64, 6, 85, 184, 127, 0, 0,
    151 			1, 127, 0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 65, 250, 60, 192, 97, 128, 24, 1, 86, 253, 21, 0,
    152 			0, 1, 1, 8, 10, 0, 51, 106, 89, 0, 51, 102, 198, 108, 104, 106, 108, 107, 104, 108, 107, 104, 10,
    153 		},
    154 		{102, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 1, 8, 0, 3, 0,
    155 			8, 0, 6, 0, 0, 0, 0, 1, 62, 0, 10, 0, 69, 0, 0, 58, 231, 2, 64, 0, 64, 6, 85, 185, 127, 0, 0, 1, 127,
    156 			0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 86, 250, 60, 192, 97, 128, 24, 1, 86, 104, 64, 0, 0, 1, 1, 8,
    157 			10, 0, 52, 198, 200, 0, 51, 135, 232, 101, 115, 97, 103, 103, 10,
    158 		},
    159 	} {
    160 		m, err := syscall.ParseNetlinkMessage(b)
    161 		if err != syscall.EINVAL {
    162 			t.Errorf("#%d: got %v; want EINVAL", i, err)
    163 		}
    164 		if m != nil {
    165 			t.Errorf("#%d: got %v; want nil", i, m)
    166 		}
    167 	}
    168 }
    169