Source file
src/syscall/exec_unix_test.go
Documentation: syscall
1
2
3
4
5
6
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
179
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
218
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
230
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
274 return
275 case <-timer.C:
276 t.Fatal("timed out waiting for child process")
277 }
278 }
279 }
280
281
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
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
321
322
323 func TestExecHelper(t *testing.T) {
324 if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
325 return
326 }
327
328
329
330
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 }
351
View as plain text