Black Lives Matter. Support the Equal Justice Initiative.

Source file src/syscall/exec_unix_test.go

Documentation: 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  //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
     6  // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
     7  
     8  package syscall_test
     9  
    10  import (
    11  	"internal/testenv"
    12  	"io"
    13  	"math/rand"
    14  	"os"
    15  	"os/exec"
    16  	"os/signal"
    17  	"runtime"
    18  	"syscall"
    19  	"testing"
    20  	"time"
    21  	"unsafe"
    22  )
    23  
    24  type command struct {
    25  	pipe io.WriteCloser
    26  	proc *exec.Cmd
    27  	test *testing.T
    28  }
    29  
    30  func (c *command) Info() (pid, pgrp int) {
    31  	pid = c.proc.Process.Pid
    32  
    33  	pgrp, err := syscall.Getpgid(pid)
    34  	if err != nil {
    35  		c.test.Fatal(err)
    36  	}
    37  
    38  	return
    39  }
    40  
    41  func (c *command) Start() {
    42  	if err := c.proc.Start(); err != nil {
    43  		c.test.Fatal(err)
    44  	}
    45  }
    46  
    47  func (c *command) Stop() {
    48  	c.pipe.Close()
    49  	if err := c.proc.Wait(); err != nil {
    50  		c.test.Fatal(err)
    51  	}
    52  }
    53  
    54  func create(t *testing.T) *command {
    55  	testenv.MustHaveExec(t)
    56  
    57  	proc := exec.Command("cat")
    58  	stdin, err := proc.StdinPipe()
    59  	if err != nil {
    60  		t.Fatal(err)
    61  	}
    62  
    63  	return &command{stdin, proc, t}
    64  }
    65  
    66  func parent() (pid, pgrp int) {
    67  	return syscall.Getpid(), syscall.Getpgrp()
    68  }
    69  
    70  func TestZeroSysProcAttr(t *testing.T) {
    71  	ppid, ppgrp := parent()
    72  
    73  	cmd := create(t)
    74  
    75  	cmd.Start()
    76  	defer cmd.Stop()
    77  
    78  	cpid, cpgrp := cmd.Info()
    79  
    80  	if cpid == ppid {
    81  		t.Fatalf("Parent and child have the same process ID")
    82  	}
    83  
    84  	if cpgrp != ppgrp {
    85  		t.Fatalf("Child is not in parent's process group")
    86  	}
    87  }
    88  
    89  func TestSetpgid(t *testing.T) {
    90  	ppid, ppgrp := parent()
    91  
    92  	cmd := create(t)
    93  
    94  	cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
    95  	cmd.Start()
    96  	defer cmd.Stop()
    97  
    98  	cpid, cpgrp := cmd.Info()
    99  
   100  	if cpid == ppid {
   101  		t.Fatalf("Parent and child have the same process ID")
   102  	}
   103  
   104  	if cpgrp == ppgrp {
   105  		t.Fatalf("Parent and child are in the same process group")
   106  	}
   107  
   108  	if cpid != cpgrp {
   109  		t.Fatalf("Child's process group is not the child's process ID")
   110  	}
   111  }
   112  
   113  func TestPgid(t *testing.T) {
   114  	ppid, ppgrp := parent()
   115  
   116  	cmd1 := create(t)
   117  
   118  	cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
   119  	cmd1.Start()
   120  	defer cmd1.Stop()
   121  
   122  	cpid1, cpgrp1 := cmd1.Info()
   123  
   124  	if cpid1 == ppid {
   125  		t.Fatalf("Parent and child 1 have the same process ID")
   126  	}
   127  
   128  	if cpgrp1 == ppgrp {
   129  		t.Fatalf("Parent and child 1 are in the same process group")
   130  	}
   131  
   132  	if cpid1 != cpgrp1 {
   133  		t.Fatalf("Child 1's process group is not its process ID")
   134  	}
   135  
   136  	cmd2 := create(t)
   137  
   138  	cmd2.proc.SysProcAttr = &syscall.SysProcAttr{
   139  		Setpgid: true,
   140  		Pgid:    cpgrp1,
   141  	}
   142  	cmd2.Start()
   143  	defer cmd2.Stop()
   144  
   145  	cpid2, cpgrp2 := cmd2.Info()
   146  
   147  	if cpid2 == ppid {
   148  		t.Fatalf("Parent and child 2 have the same process ID")
   149  	}
   150  
   151  	if cpgrp2 == ppgrp {
   152  		t.Fatalf("Parent and child 2 are in the same process group")
   153  	}
   154  
   155  	if cpid2 == cpgrp2 {
   156  		t.Fatalf("Child 2's process group is its process ID")
   157  	}
   158  
   159  	if cpid1 == cpid2 {
   160  		t.Fatalf("Child 1 and 2 have the same process ID")
   161  	}
   162  
   163  	if cpgrp1 != cpgrp2 {
   164  		t.Fatalf("Child 1 and 2 are not in the same process group")
   165  	}
   166  }
   167  
   168  func TestForeground(t *testing.T) {
   169  	signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
   170  	defer signal.Reset()
   171  
   172  	tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
   173  	if err != nil {
   174  		t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s", err)
   175  	}
   176  	defer tty.Close()
   177  
   178  	// This should really be pid_t, however _C_int (aka int32) is generally
   179  	// equivalent.
   180  	fpgrp := int32(0)
   181  
   182  	errno := syscall.Ioctl(tty.Fd(), syscall.TIOCGPGRP, uintptr(unsafe.Pointer(&fpgrp)))
   183  	if errno != 0 {
   184  		t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
   185  	}
   186  
   187  	if fpgrp == 0 {
   188  		t.Fatalf("Foreground process group is zero")
   189  	}
   190  
   191  	ppid, ppgrp := parent()
   192  
   193  	cmd := create(t)
   194  
   195  	cmd.proc.SysProcAttr = &syscall.SysProcAttr{
   196  		Ctty:       int(tty.Fd()),
   197  		Foreground: true,
   198  	}
   199  	cmd.Start()
   200  
   201  	cpid, cpgrp := cmd.Info()
   202  
   203  	if cpid == ppid {
   204  		t.Fatalf("Parent and child have the same process ID")
   205  	}
   206  
   207  	if cpgrp == ppgrp {
   208  		t.Fatalf("Parent and child are in the same process group")
   209  	}
   210  
   211  	if cpid != cpgrp {
   212  		t.Fatalf("Child's process group is not the child's process ID")
   213  	}
   214  
   215  	cmd.Stop()
   216  
   217  	// This call fails on darwin/arm64. The failure doesn't matter, though.
   218  	// This is just best effort.
   219  	syscall.Ioctl(tty.Fd(), syscall.TIOCSPGRP, uintptr(unsafe.Pointer(&fpgrp)))
   220  }
   221  
   222  func TestForegroundSignal(t *testing.T) {
   223  	tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
   224  	if err != nil {
   225  		t.Skipf("couldn't open /dev/tty: %s", err)
   226  	}
   227  	defer tty.Close()
   228  
   229  	// This should really be pid_t, however _C_int (aka int32) is generally
   230  	// equivalent.
   231  	fpgrp := int32(0)
   232  
   233  	errno := syscall.Ioctl(tty.Fd(), syscall.TIOCGPGRP, uintptr(unsafe.Pointer(&fpgrp)))
   234  	if errno != 0 {
   235  		t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
   236  	}
   237  
   238  	if fpgrp == 0 {
   239  		t.Fatalf("Foreground process group is zero")
   240  	}
   241  
   242  	defer func() {
   243  		signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
   244  		syscall.Ioctl(tty.Fd(), syscall.TIOCSPGRP, uintptr(unsafe.Pointer(&fpgrp)))
   245  		signal.Reset()
   246  	}()
   247  
   248  	ch1 := make(chan os.Signal, 1)
   249  	ch2 := make(chan bool)
   250  
   251  	signal.Notify(ch1, syscall.SIGTTIN, syscall.SIGTTOU)
   252  	defer signal.Stop(ch1)
   253  
   254  	cmd := create(t)
   255  
   256  	go func() {
   257  		cmd.proc.SysProcAttr = &syscall.SysProcAttr{
   258  			Ctty:       int(tty.Fd()),
   259  			Foreground: true,
   260  		}
   261  		cmd.Start()
   262  		cmd.Stop()
   263  		close(ch2)
   264  	}()
   265  
   266  	timer := time.NewTimer(30 * time.Second)
   267  	defer timer.Stop()
   268  	for {
   269  		select {
   270  		case sig := <-ch1:
   271  			t.Errorf("unexpected signal %v", sig)
   272  		case <-ch2:
   273  			// Success.
   274  			return
   275  		case <-timer.C:
   276  			t.Fatal("timed out waiting for child process")
   277  		}
   278  	}
   279  }
   280  
   281  // Test a couple of cases that SysProcAttr can't handle. Issue 29458.
   282  func TestInvalidExec(t *testing.T) {
   283  	t.Parallel()
   284  	t.Run("SetCtty-Foreground", func(t *testing.T) {
   285  		t.Parallel()
   286  		cmd := create(t)
   287  		cmd.proc.SysProcAttr = &syscall.SysProcAttr{
   288  			Setctty:    true,
   289  			Foreground: true,
   290  			Ctty:       0,
   291  		}
   292  		if err := cmd.proc.Start(); err == nil {
   293  			t.Error("expected error setting both SetCtty and Foreground")
   294  		}
   295  	})
   296  	t.Run("invalid-Ctty", func(t *testing.T) {
   297  		t.Parallel()
   298  		cmd := create(t)
   299  		cmd.proc.SysProcAttr = &syscall.SysProcAttr{
   300  			Setctty: true,
   301  			Ctty:    3,
   302  		}
   303  		if err := cmd.proc.Start(); err == nil {
   304  			t.Error("expected error with invalid Ctty value")
   305  		}
   306  	})
   307  }
   308  
   309  // TestExec is for issue #41702.
   310  func TestExec(t *testing.T) {
   311  	testenv.MustHaveExec(t)
   312  	cmd := exec.Command(os.Args[0], "-test.run=TestExecHelper")
   313  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
   314  	o, err := cmd.CombinedOutput()
   315  	if err != nil {
   316  		t.Errorf("%s\n%v", o, err)
   317  	}
   318  }
   319  
   320  // TestExecHelper is used by TestExec. It does nothing by itself.
   321  // In testing on macOS 10.14, this used to fail with
   322  // "signal: illegal instruction" more than half the time.
   323  func TestExecHelper(t *testing.T) {
   324  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
   325  		return
   326  	}
   327  
   328  	// We don't have to worry about restoring these values.
   329  	// We are in a child process that only runs this test,
   330  	// and we are going to call syscall.Exec anyhow.
   331  	runtime.GOMAXPROCS(50)
   332  	os.Setenv("GO_WANT_HELPER_PROCESS", "3")
   333  
   334  	stop := time.Now().Add(time.Second)
   335  	for i := 0; i < 100; i++ {
   336  		go func(i int) {
   337  			r := rand.New(rand.NewSource(int64(i)))
   338  			for time.Now().Before(stop) {
   339  				r.Uint64()
   340  			}
   341  		}(i)
   342  	}
   343  
   344  	time.Sleep(10 * time.Millisecond)
   345  
   346  	argv := []string{os.Args[0], "-test.run=TestExecHelper"}
   347  	syscall.Exec(os.Args[0], argv, os.Environ())
   348  
   349  	t.Error("syscall.Exec returned")
   350  }
   351  

View as plain text