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