Source file
src/syscall/exec_unix_test.go
1
2
3
4
5
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
177
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
216
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
228
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
272 return
273 case <-timer.C:
274 t.Fatal("timed out waiting for child process")
275 }
276 }
277 }
278
279
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
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
319
320
321 func TestExecHelper(t *testing.T) {
322 if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
323 return
324 }
325
326
327
328
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 }
348
View as plain text