Source file
src/os/signal/signal_cgo_test.go
1
2
3
4
5
6
7
8
9
10
11 package signal_test
12
13 import (
14 "bufio"
15 "bytes"
16 "context"
17 "fmt"
18 "io"
19 "io/fs"
20 "os"
21 "os/exec"
22 ptypkg "os/signal/internal/pty"
23 "strconv"
24 "strings"
25 "sync"
26 "syscall"
27 "testing"
28 "time"
29 )
30
31 func TestTerminalSignal(t *testing.T) {
32 const enteringRead = "test program entering read"
33 if os.Getenv("GO_TEST_TERMINAL_SIGNALS") != "" {
34 var b [1]byte
35 fmt.Println(enteringRead)
36 n, err := os.Stdin.Read(b[:])
37 if n == 1 {
38 if b[0] == '\n' {
39
40 fmt.Println("read newline")
41 } else {
42 fmt.Printf("read 1 byte: %q\n", b)
43 }
44 } else {
45 fmt.Printf("read %d bytes\n", n)
46 }
47 if err != nil {
48 fmt.Println(err)
49 os.Exit(1)
50 }
51 os.Exit(0)
52 }
53
54 t.Parallel()
55
56
57 bash, err := exec.LookPath("bash")
58 if err != nil {
59 t.Skipf("could not find bash: %v", err)
60 }
61
62 scale := 1
63 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
64 if sc, err := strconv.Atoi(s); err == nil {
65 scale = sc
66 }
67 }
68 pause := time.Duration(scale) * 10 * time.Millisecond
69 wait := time.Duration(scale) * 5 * time.Second
70
71
72
73
74 pty, procTTYName, err := ptypkg.Open()
75 if err != nil {
76 ptyErr := err.(*ptypkg.PtyError)
77 if ptyErr.FuncName == "posix_openpt" && ptyErr.Errno == syscall.EACCES {
78 t.Skip("posix_openpt failed with EACCES, assuming chroot and skipping")
79 }
80 t.Fatal(err)
81 }
82 defer pty.Close()
83 procTTY, err := os.OpenFile(procTTYName, os.O_RDWR, 0)
84 if err != nil {
85 t.Fatal(err)
86 }
87 defer procTTY.Close()
88
89
90 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
91 defer cancel()
92 cmd := exec.CommandContext(ctx, bash, "--norc", "--noprofile", "-i")
93
94 cmd.Env = append(os.Environ(), "HISTFILE=")
95 cmd.Stdin = procTTY
96 cmd.Stdout = procTTY
97 cmd.Stderr = procTTY
98 cmd.SysProcAttr = &syscall.SysProcAttr{
99 Setsid: true,
100 Setctty: true,
101 Ctty: 0,
102 }
103
104 if err := cmd.Start(); err != nil {
105 t.Fatal(err)
106 }
107
108 if err := procTTY.Close(); err != nil {
109 t.Errorf("closing procTTY: %v", err)
110 }
111
112 progReady := make(chan bool)
113 sawPrompt := make(chan bool, 10)
114 const prompt = "prompt> "
115
116
117 var wg sync.WaitGroup
118 wg.Add(1)
119 defer wg.Wait()
120 go func() {
121 defer wg.Done()
122 input := bufio.NewReader(pty)
123 var line, handled []byte
124 for {
125 b, err := input.ReadByte()
126 if err != nil {
127 if len(line) > 0 || len(handled) > 0 {
128 t.Logf("%q", append(handled, line...))
129 }
130 if perr, ok := err.(*fs.PathError); ok {
131 err = perr.Err
132 }
133
134
135
136 if err != io.EOF && err != syscall.EIO && !strings.Contains(err.Error(), "file already closed") {
137 t.Logf("error reading from pty: %v", err)
138 }
139 return
140 }
141
142 line = append(line, b)
143
144 if b == '\n' {
145 t.Logf("%q", append(handled, line...))
146 line = nil
147 handled = nil
148 continue
149 }
150
151 if bytes.Contains(line, []byte(enteringRead)) {
152 close(progReady)
153 handled = append(handled, line...)
154 line = nil
155 } else if bytes.Contains(line, []byte(prompt)) && !bytes.Contains(line, []byte("PS1=")) {
156 sawPrompt <- true
157 handled = append(handled, line...)
158 line = nil
159 }
160 }
161 }()
162
163
164 if _, err := pty.Write([]byte("PS1='" + prompt + "'\n")); err != nil {
165 t.Fatalf("setting prompt: %v", err)
166 }
167 select {
168 case <-sawPrompt:
169 case <-time.After(wait):
170 t.Fatal("timed out waiting for shell prompt")
171 }
172
173
174
175 if _, err := pty.Write([]byte("GO_TEST_TERMINAL_SIGNALS=1 " + os.Args[0] + " -test.run=TestTerminalSignal\n")); err != nil {
176 t.Fatal(err)
177 }
178
179
180 select {
181 case <-progReady:
182 case <-time.After(wait):
183 t.Fatal("timed out waiting for program to start")
184 }
185
186
187
188
189
190 time.Sleep(pause)
191
192
193 if _, err := pty.Write([]byte{26}); err != nil {
194 t.Fatalf("writing ^Z to pty: %v", err)
195 }
196
197
198 select {
199 case <-sawPrompt:
200 case <-time.After(wait):
201 t.Fatal("timed out waiting for shell prompt")
202 }
203
204
205 if _, err := pty.Write([]byte("fg\n")); err != nil {
206 t.Fatalf("writing %q to pty: %v", "fg", err)
207 }
208
209
210
211
212
213
214
215
216 time.Sleep(10 * pause)
217
218
219
220 if _, err := pty.Write([]byte{'\n'}); err != nil {
221 t.Fatalf("writing %q to pty: %v", "\n", err)
222 }
223
224
225 select {
226 case <-sawPrompt:
227 case <-time.After(wait):
228 t.Fatal("timed out waiting for shell prompt")
229 }
230
231
232 if _, err := pty.Write([]byte("exit $?\n")); err != nil {
233 t.Fatalf("writing %q to pty: %v", "exit", err)
234 }
235
236 if err = cmd.Wait(); err != nil {
237 t.Errorf("subprogram failed: %v", err)
238 }
239 }
240
View as plain text