github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/syscall/exec_unix_test.go (about) 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 }