...

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