123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- // Copyright 2015 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
- package syscall_test
- import (
- "internal/testenv"
- "io"
- "math/rand"
- "os"
- "os/exec"
- "os/signal"
- "runtime"
- "syscall"
- "testing"
- "time"
- "unsafe"
- )
- type command struct {
- pipe io.WriteCloser
- proc *exec.Cmd
- test *testing.T
- }
- func (c *command) Info() (pid, pgrp int) {
- pid = c.proc.Process.Pid
- pgrp, err := syscall.Getpgid(pid)
- if err != nil {
- c.test.Fatal(err)
- }
- return
- }
- func (c *command) Start() {
- if err := c.proc.Start(); err != nil {
- c.test.Fatal(err)
- }
- }
- func (c *command) Stop() {
- c.pipe.Close()
- if err := c.proc.Wait(); err != nil {
- c.test.Fatal(err)
- }
- }
- func create(t *testing.T) *command {
- testenv.MustHaveExec(t)
- proc := exec.Command("cat")
- stdin, err := proc.StdinPipe()
- if err != nil {
- t.Fatal(err)
- }
- return &command{stdin, proc, t}
- }
- func parent() (pid, pgrp int) {
- return syscall.Getpid(), syscall.Getpgrp()
- }
- func TestZeroSysProcAttr(t *testing.T) {
- ppid, ppgrp := parent()
- cmd := create(t)
- cmd.Start()
- defer cmd.Stop()
- cpid, cpgrp := cmd.Info()
- if cpid == ppid {
- t.Fatalf("Parent and child have the same process ID")
- }
- if cpgrp != ppgrp {
- t.Fatalf("Child is not in parent's process group")
- }
- }
- func TestSetpgid(t *testing.T) {
- ppid, ppgrp := parent()
- cmd := create(t)
- cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
- cmd.Start()
- defer cmd.Stop()
- cpid, cpgrp := cmd.Info()
- if cpid == ppid {
- t.Fatalf("Parent and child have the same process ID")
- }
- if cpgrp == ppgrp {
- t.Fatalf("Parent and child are in the same process group")
- }
- if cpid != cpgrp {
- t.Fatalf("Child's process group is not the child's process ID")
- }
- }
- func TestPgid(t *testing.T) {
- ppid, ppgrp := parent()
- cmd1 := create(t)
- cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
- cmd1.Start()
- defer cmd1.Stop()
- cpid1, cpgrp1 := cmd1.Info()
- if cpid1 == ppid {
- t.Fatalf("Parent and child 1 have the same process ID")
- }
- if cpgrp1 == ppgrp {
- t.Fatalf("Parent and child 1 are in the same process group")
- }
- if cpid1 != cpgrp1 {
- t.Fatalf("Child 1's process group is not its process ID")
- }
- cmd2 := create(t)
- cmd2.proc.SysProcAttr = &syscall.SysProcAttr{
- Setpgid: true,
- Pgid: cpgrp1,
- }
- cmd2.Start()
- defer cmd2.Stop()
- cpid2, cpgrp2 := cmd2.Info()
- if cpid2 == ppid {
- t.Fatalf("Parent and child 2 have the same process ID")
- }
- if cpgrp2 == ppgrp {
- t.Fatalf("Parent and child 2 are in the same process group")
- }
- if cpid2 == cpgrp2 {
- t.Fatalf("Child 2's process group is its process ID")
- }
- if cpid1 == cpid2 {
- t.Fatalf("Child 1 and 2 have the same process ID")
- }
- if cpgrp1 != cpgrp2 {
- t.Fatalf("Child 1 and 2 are not in the same process group")
- }
- }
- func TestForeground(t *testing.T) {
- if runtime.GOOS == "hurd" {
- t.Skip("skipping; TestForeground: fails on GNU/Hurd")
- }
- signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
- defer signal.Reset()
- tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
- if err != nil {
- t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s", err)
- }
- defer tty.Close()
- fpgrp := syscall.Pid_t(0)
- errno := syscall.Ioctl(tty.Fd(), syscall.TIOCGPGRP, unsafe.Pointer(&fpgrp))
- if errno != 0 {
- t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
- }
- if fpgrp == 0 {
- t.Fatalf("Foreground process group is zero")
- }
- ppid, ppgrp := parent()
- cmd := create(t)
- cmd.proc.SysProcAttr = &syscall.SysProcAttr{
- Ctty: int(tty.Fd()),
- Foreground: true,
- }
- cmd.Start()
- cpid, cpgrp := cmd.Info()
- if cpid == ppid {
- t.Fatalf("Parent and child have the same process ID")
- }
- if cpgrp == ppgrp {
- t.Fatalf("Parent and child are in the same process group")
- }
- if cpid != cpgrp {
- t.Fatalf("Child's process group is not the child's process ID")
- }
- cmd.Stop()
- // This call fails on darwin/arm64. The failure doesn't matter, though.
- // This is just best effort.
- syscall.Ioctl(tty.Fd(), syscall.TIOCSPGRP, unsafe.Pointer(&fpgrp))
- }
- func TestForegroundSignal(t *testing.T) {
- tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
- if err != nil {
- t.Skipf("couldn't open /dev/tty: %s", err)
- }
- defer tty.Close()
- // This should really be pid_t, however _C_int (aka int32) is generally
- // equivalent.
- fpgrp := int32(0)
- errno := syscall.Ioctl(tty.Fd(), syscall.TIOCGPGRP, unsafe.Pointer(&fpgrp))
- if errno != 0 {
- t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
- }
- if fpgrp == 0 {
- t.Fatalf("Foreground process group is zero")
- }
- defer func() {
- signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
- syscall.Ioctl(tty.Fd(), syscall.TIOCSPGRP, unsafe.Pointer(&fpgrp))
- signal.Reset()
- }()
- ch1 := make(chan os.Signal, 1)
- ch2 := make(chan bool)
- signal.Notify(ch1, syscall.SIGTTIN, syscall.SIGTTOU)
- defer signal.Stop(ch1)
- cmd := create(t)
- go func() {
- cmd.proc.SysProcAttr = &syscall.SysProcAttr{
- Ctty: int(tty.Fd()),
- Foreground: true,
- }
- cmd.Start()
- cmd.Stop()
- close(ch2)
- }()
- timer := time.NewTimer(30 * time.Second)
- defer timer.Stop()
- for {
- select {
- case sig := <-ch1:
- t.Errorf("unexpected signal %v", sig)
- case <-ch2:
- // Success.
- return
- case <-timer.C:
- t.Fatal("timed out waiting for child process")
- }
- }
- }
- // Test a couple of cases that SysProcAttr can't handle. Issue 29458.
- func TestInvalidExec(t *testing.T) {
- t.Parallel()
- t.Run("SetCtty-Foreground", func(t *testing.T) {
- t.Parallel()
- cmd := create(t)
- cmd.proc.SysProcAttr = &syscall.SysProcAttr{
- Setctty: true,
- Foreground: true,
- Ctty: 0,
- }
- if err := cmd.proc.Start(); err == nil {
- t.Error("expected error setting both SetCtty and Foreground")
- }
- })
- t.Run("invalid-Ctty", func(t *testing.T) {
- t.Parallel()
- cmd := create(t)
- cmd.proc.SysProcAttr = &syscall.SysProcAttr{
- Setctty: true,
- Ctty: 3,
- }
- if err := cmd.proc.Start(); err == nil {
- t.Error("expected error with invalid Ctty value")
- }
- })
- }
- // TestExec is for issue #41702.
- func TestExec(t *testing.T) {
- testenv.MustHaveExec(t)
- cmd := exec.Command(os.Args[0], "-test.run=TestExecHelper")
- cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
- o, err := cmd.CombinedOutput()
- if err != nil {
- t.Errorf("%s\n%v", o, err)
- }
- }
- // TestExecHelper is used by TestExec. It does nothing by itself.
- // In testing on macOS 10.14, this used to fail with
- // "signal: illegal instruction" more than half the time.
- func TestExecHelper(t *testing.T) {
- if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
- return
- }
- // We don't have to worry about restoring these values.
- // We are in a child process that only runs this test,
- // and we are going to call syscall.Exec anyhow.
- os.Setenv("GO_WANT_HELPER_PROCESS", "3")
- stop := time.Now().Add(time.Second)
- for i := 0; i < 100; i++ {
- go func(i int) {
- r := rand.New(rand.NewSource(int64(i)))
- for time.Now().Before(stop) {
- r.Uint64()
- }
- }(i)
- }
- time.Sleep(10 * time.Millisecond)
- argv := []string{os.Args[0], "-test.run=TestExecHelper"}
- syscall.Exec(os.Args[0], argv, os.Environ())
- t.Error("syscall.Exec returned")
- }
|