github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/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 // +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 }