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

View as plain text