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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package syscall_test 8 9 import ( 10 "internal/testenv" 11 "io" 12 "os" 13 "os/exec" 14 "os/signal" 15 "syscall" 16 "testing" 17 "unsafe" 18 ) 19 20 type command struct { 21 pipe io.WriteCloser 22 proc *exec.Cmd 23 test *testing.T 24 } 25 26 func (c *command) Info() (pid, pgrp int) { 27 pid = c.proc.Process.Pid 28 29 pgrp, err := syscall.Getpgid(pid) 30 if err != nil { 31 c.test.Fatal(err) 32 } 33 34 return 35 } 36 37 func (c *command) Start() { 38 if err := c.proc.Start(); err != nil { 39 c.test.Fatal(err) 40 } 41 } 42 43 func (c *command) Stop() { 44 c.pipe.Close() 45 if err := c.proc.Wait(); err != nil { 46 c.test.Fatal(err) 47 } 48 } 49 50 func create(t *testing.T) *command { 51 testenv.MustHaveExec(t) 52 53 proc := exec.Command("cat") 54 stdin, err := proc.StdinPipe() 55 if err != nil { 56 t.Fatal(err) 57 } 58 59 return &command{stdin, proc, t} 60 } 61 62 func parent() (pid, pgrp int) { 63 return syscall.Getpid(), syscall.Getpgrp() 64 } 65 66 func TestZeroSysProcAttr(t *testing.T) { 67 ppid, ppgrp := parent() 68 69 cmd := create(t) 70 71 cmd.Start() 72 defer cmd.Stop() 73 74 cpid, cpgrp := cmd.Info() 75 76 if cpid == ppid { 77 t.Fatalf("Parent and child have the same process ID") 78 } 79 80 if cpgrp != ppgrp { 81 t.Fatalf("Child is not in parent's process group") 82 } 83 } 84 85 func TestSetpgid(t *testing.T) { 86 ppid, ppgrp := parent() 87 88 cmd := create(t) 89 90 cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 91 cmd.Start() 92 defer cmd.Stop() 93 94 cpid, cpgrp := cmd.Info() 95 96 if cpid == ppid { 97 t.Fatalf("Parent and child have the same process ID") 98 } 99 100 if cpgrp == ppgrp { 101 t.Fatalf("Parent and child are in the same process group") 102 } 103 104 if cpid != cpgrp { 105 t.Fatalf("Child's process group is not the child's process ID") 106 } 107 } 108 109 func TestPgid(t *testing.T) { 110 ppid, ppgrp := parent() 111 112 cmd1 := create(t) 113 114 cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 115 cmd1.Start() 116 defer cmd1.Stop() 117 118 cpid1, cpgrp1 := cmd1.Info() 119 120 if cpid1 == ppid { 121 t.Fatalf("Parent and child 1 have the same process ID") 122 } 123 124 if cpgrp1 == ppgrp { 125 t.Fatalf("Parent and child 1 are in the same process group") 126 } 127 128 if cpid1 != cpgrp1 { 129 t.Fatalf("Child 1's process group is not its process ID") 130 } 131 132 cmd2 := create(t) 133 134 cmd2.proc.SysProcAttr = &syscall.SysProcAttr{ 135 Setpgid: true, 136 Pgid: cpgrp1, 137 } 138 cmd2.Start() 139 defer cmd2.Stop() 140 141 cpid2, cpgrp2 := cmd2.Info() 142 143 if cpid2 == ppid { 144 t.Fatalf("Parent and child 2 have the same process ID") 145 } 146 147 if cpgrp2 == ppgrp { 148 t.Fatalf("Parent and child 2 are in the same process group") 149 } 150 151 if cpid2 == cpgrp2 { 152 t.Fatalf("Child 2's process group is its process ID") 153 } 154 155 if cpid1 == cpid2 { 156 t.Fatalf("Child 1 and 2 have the same process ID") 157 } 158 159 if cpgrp1 != cpgrp2 { 160 t.Fatalf("Child 1 and 2 are not in the same process group") 161 } 162 } 163 164 func TestForeground(t *testing.T) { 165 signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU) 166 167 tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) 168 if err != nil { 169 t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s", err) 170 } 171 172 fpgrp := 0 173 174 errno := syscall.Ioctl(tty.Fd(), syscall.TIOCGPGRP, uintptr(unsafe.Pointer(&fpgrp))) 175 if errno != 0 { 176 t.Fatalf("TIOCGPGRP failed with error code: %s", errno) 177 } 178 179 if fpgrp == 0 { 180 t.Fatalf("Foreground process group is zero") 181 } 182 183 ppid, ppgrp := parent() 184 185 cmd := create(t) 186 187 cmd.proc.SysProcAttr = &syscall.SysProcAttr{ 188 Ctty: int(tty.Fd()), 189 Foreground: true, 190 } 191 cmd.Start() 192 193 cpid, cpgrp := cmd.Info() 194 195 if cpid == ppid { 196 t.Fatalf("Parent and child have the same process ID") 197 } 198 199 if cpgrp == ppgrp { 200 t.Fatalf("Parent and child are in the same process group") 201 } 202 203 if cpid != cpgrp { 204 t.Fatalf("Child's process group is not the child's process ID") 205 } 206 207 cmd.Stop() 208 209 errno = syscall.Ioctl(tty.Fd(), syscall.TIOCSPGRP, uintptr(unsafe.Pointer(&fpgrp))) 210 if errno != 0 { 211 t.Fatalf("TIOCSPGRP failed with error code: %s", errno) 212 } 213 214 signal.Reset() 215 } 216