Source file
src/syscall/exec_unix_test.go
Documentation: syscall
1
2
3
4
5
6
7 package syscall_test
8
9 import (
10 "bytes"
11 "fmt"
12 "internal/testenv"
13 "io"
14 "math/rand"
15 "os"
16 "os/exec"
17 "os/signal"
18 "strconv"
19 "syscall"
20 "testing"
21 "time"
22 "unsafe"
23 )
24
25 type command struct {
26 pipe io.WriteCloser
27 proc *exec.Cmd
28 test *testing.T
29 }
30
31 func (c *command) Info() (pid, pgrp int) {
32 pid = c.proc.Process.Pid
33
34 pgrp, err := syscall.Getpgid(pid)
35 if err != nil {
36 c.test.Fatal(err)
37 }
38
39 return
40 }
41
42 func (c *command) Start() {
43 if err := c.proc.Start(); err != nil {
44 c.test.Fatal(err)
45 }
46 }
47
48 func (c *command) Stop() {
49 c.pipe.Close()
50 if err := c.proc.Wait(); err != nil {
51 c.test.Fatal(err)
52 }
53 }
54
55 func create(t *testing.T) *command {
56 testenv.MustHaveExec(t)
57
58 proc := exec.Command("cat")
59 stdin, err := proc.StdinPipe()
60 if err != nil {
61 t.Fatal(err)
62 }
63
64 return &command{stdin, proc, t}
65 }
66
67 func parent() (pid, pgrp int) {
68 return syscall.Getpid(), syscall.Getpgrp()
69 }
70
71 func TestZeroSysProcAttr(t *testing.T) {
72 ppid, ppgrp := parent()
73
74 cmd := create(t)
75
76 cmd.Start()
77 defer cmd.Stop()
78
79 cpid, cpgrp := cmd.Info()
80
81 if cpid == ppid {
82 t.Fatalf("Parent and child have the same process ID")
83 }
84
85 if cpgrp != ppgrp {
86 t.Fatalf("Child is not in parent's process group")
87 }
88 }
89
90 func TestSetpgid(t *testing.T) {
91 ppid, ppgrp := parent()
92
93 cmd := create(t)
94
95 cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
96 cmd.Start()
97 defer cmd.Stop()
98
99 cpid, cpgrp := cmd.Info()
100
101 if cpid == ppid {
102 t.Fatalf("Parent and child have the same process ID")
103 }
104
105 if cpgrp == ppgrp {
106 t.Fatalf("Parent and child are in the same process group")
107 }
108
109 if cpid != cpgrp {
110 t.Fatalf("Child's process group is not the child's process ID")
111 }
112 }
113
114 func TestPgid(t *testing.T) {
115 ppid, ppgrp := parent()
116
117 cmd1 := create(t)
118
119 cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
120 cmd1.Start()
121 defer cmd1.Stop()
122
123 cpid1, cpgrp1 := cmd1.Info()
124
125 if cpid1 == ppid {
126 t.Fatalf("Parent and child 1 have the same process ID")
127 }
128
129 if cpgrp1 == ppgrp {
130 t.Fatalf("Parent and child 1 are in the same process group")
131 }
132
133 if cpid1 != cpgrp1 {
134 t.Fatalf("Child 1's process group is not its process ID")
135 }
136
137 cmd2 := create(t)
138
139 cmd2.proc.SysProcAttr = &syscall.SysProcAttr{
140 Setpgid: true,
141 Pgid: cpgrp1,
142 }
143 cmd2.Start()
144 defer cmd2.Stop()
145
146 cpid2, cpgrp2 := cmd2.Info()
147
148 if cpid2 == ppid {
149 t.Fatalf("Parent and child 2 have the same process ID")
150 }
151
152 if cpgrp2 == ppgrp {
153 t.Fatalf("Parent and child 2 are in the same process group")
154 }
155
156 if cpid2 == cpgrp2 {
157 t.Fatalf("Child 2's process group is its process ID")
158 }
159
160 if cpid1 == cpid2 {
161 t.Fatalf("Child 1 and 2 have the same process ID")
162 }
163
164 if cpgrp1 != cpgrp2 {
165 t.Fatalf("Child 1 and 2 are not in the same process group")
166 }
167 }
168
169 func TestForeground(t *testing.T) {
170 signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
171 defer signal.Reset()
172
173 tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
174 if err != nil {
175 t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s", err)
176 }
177 defer tty.Close()
178
179
180
181 fpgrp := int32(0)
182
183 errno := syscall.IoctlPtr(tty.Fd(), syscall.TIOCGPGRP, unsafe.Pointer(&fpgrp))
184 if errno != 0 {
185 t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
186 }
187
188 if fpgrp == 0 {
189 t.Fatalf("Foreground process group is zero")
190 }
191
192 ppid, ppgrp := parent()
193
194 cmd := create(t)
195
196 cmd.proc.SysProcAttr = &syscall.SysProcAttr{
197 Ctty: int(tty.Fd()),
198 Foreground: true,
199 }
200 cmd.Start()
201
202 cpid, cpgrp := cmd.Info()
203
204 if cpid == ppid {
205 t.Fatalf("Parent and child have the same process ID")
206 }
207
208 if cpgrp == ppgrp {
209 t.Fatalf("Parent and child are in the same process group")
210 }
211
212 if cpid != cpgrp {
213 t.Fatalf("Child's process group is not the child's process ID")
214 }
215
216 cmd.Stop()
217
218
219
220 syscall.IoctlPtr(tty.Fd(), syscall.TIOCSPGRP, unsafe.Pointer(&fpgrp))
221 }
222
223 func TestForegroundSignal(t *testing.T) {
224 tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
225 if err != nil {
226 t.Skipf("couldn't open /dev/tty: %s", err)
227 }
228 defer tty.Close()
229
230
231
232 fpgrp := int32(0)
233
234 errno := syscall.IoctlPtr(tty.Fd(), syscall.TIOCGPGRP, unsafe.Pointer(&fpgrp))
235 if errno != 0 {
236 t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
237 }
238
239 if fpgrp == 0 {
240 t.Fatalf("Foreground process group is zero")
241 }
242
243 defer func() {
244 signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
245 syscall.IoctlPtr(tty.Fd(), syscall.TIOCSPGRP, unsafe.Pointer(&fpgrp))
246 signal.Reset()
247 }()
248
249 ch1 := make(chan os.Signal, 1)
250 ch2 := make(chan bool)
251
252 signal.Notify(ch1, syscall.SIGTTIN, syscall.SIGTTOU)
253 defer signal.Stop(ch1)
254
255 cmd := create(t)
256
257 go func() {
258 cmd.proc.SysProcAttr = &syscall.SysProcAttr{
259 Ctty: int(tty.Fd()),
260 Foreground: true,
261 }
262 cmd.Start()
263 cmd.Stop()
264 close(ch2)
265 }()
266
267 timer := time.NewTimer(30 * time.Second)
268 defer timer.Stop()
269 for {
270 select {
271 case sig := <-ch1:
272 t.Errorf("unexpected signal %v", sig)
273 case <-ch2:
274
275 return
276 case <-timer.C:
277 t.Fatal("timed out waiting for child process")
278 }
279 }
280 }
281
282
283 func TestInvalidExec(t *testing.T) {
284 t.Parallel()
285 t.Run("SetCtty-Foreground", func(t *testing.T) {
286 t.Parallel()
287 cmd := create(t)
288 cmd.proc.SysProcAttr = &syscall.SysProcAttr{
289 Setctty: true,
290 Foreground: true,
291 Ctty: 0,
292 }
293 if err := cmd.proc.Start(); err == nil {
294 t.Error("expected error setting both SetCtty and Foreground")
295 }
296 })
297 t.Run("invalid-Ctty", func(t *testing.T) {
298 t.Parallel()
299 cmd := create(t)
300 cmd.proc.SysProcAttr = &syscall.SysProcAttr{
301 Setctty: true,
302 Ctty: 3,
303 }
304 if err := cmd.proc.Start(); err == nil {
305 t.Error("expected error with invalid Ctty value")
306 }
307 })
308 }
309
310
311 func TestExec(t *testing.T) {
312 testenv.MustHaveExec(t)
313 cmd := exec.Command(os.Args[0], "-test.run=^TestExecHelper$")
314 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
315 o, err := cmd.CombinedOutput()
316 if err != nil {
317 t.Errorf("%s\n%v", o, err)
318 }
319 }
320
321
322
323
324 func TestExecHelper(t *testing.T) {
325 if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
326 return
327 }
328
329
330
331
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
352
353 func TestRlimitRestored(t *testing.T) {
354 if os.Getenv("GO_WANT_HELPER_PROCESS") != "" {
355 fmt.Println(syscall.OrigRlimitNofile().Cur)
356 os.Exit(0)
357 }
358
359 orig := syscall.OrigRlimitNofile()
360 if orig == nil {
361 t.Skip("skipping test because rlimit not adjusted at startup")
362 }
363
364 executable, err := os.Executable()
365 if err != nil {
366 executable = os.Args[0]
367 }
368
369 cmd := testenv.Command(t, executable, "-test.run=^TestRlimitRestored$")
370 cmd = testenv.CleanCmdEnv(cmd)
371 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
372
373 out, err := cmd.CombinedOutput()
374 if len(out) > 0 {
375 t.Logf("%s", out)
376 }
377 if err != nil {
378 t.Fatalf("subprocess failed: %v", err)
379 }
380 s := string(bytes.TrimSpace(out))
381 v, err := strconv.ParseUint(s, 10, 64)
382 if err != nil {
383 t.Fatalf("could not parse %q as number: %v", s, v)
384 }
385
386 if v != uint64(orig.Cur) {
387 t.Errorf("exec rlimit = %d, want %d", v, orig)
388 }
389 }
390
391 func TestForkExecNilArgv(t *testing.T) {
392 defer func() {
393 if p := recover(); p != nil {
394 t.Fatal("forkExec panicked")
395 }
396 }()
397
398
399
400 syscall.ForkExec("/dev/null", nil, nil)
401 }
402
View as plain text