1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build darwin dragonfly freebsd linux,!android netbsd openbsd
8 // Note that this test does not work on Solaris: issue #22849.
9 // Don't run the test on Android because at least some versions of the
10 // C library do not define the posix_openpt function.
22 "os/signal/internal/pty"
30 func TestTerminalSignal(t
*testing
.T
) {
31 const enteringRead
= "test program entering read"
32 if os
.Getenv("GO_TEST_TERMINAL_SIGNALS") != "" {
34 fmt
.Println(enteringRead
)
35 n
, err
:= os
.Stdin
.Read(b
[:])
38 // This is what we expect
39 fmt
.Println("read newline")
41 fmt
.Printf("read 1 byte: %q\n", b
)
44 fmt
.Printf("read %d bytes\n", n
)
55 // The test requires a shell that uses job control.
56 bash
, err
:= exec
.LookPath("bash")
58 t
.Skipf("could not find bash: %v", err
)
62 if s
:= os
.Getenv("GO_TEST_TIMEOUT_SCALE"); s
!= "" {
63 if sc
, err
:= strconv
.Atoi(s
); err
== nil {
67 pause
:= time
.Duration(scale
) * 10 * time
.Millisecond
68 wait
:= time
.Duration(scale
) * 5 * time
.Second
70 // The test only fails when using a "slow device," in this
71 // case a pseudo-terminal.
73 master
, sname
, err
:= pty
.Open()
75 ptyErr
:= err
.(*pty
.PtyError
)
76 if ptyErr
.FuncName
== "posix_openpt" && ptyErr
.Errno
== syscall
.EACCES
{
77 t
.Skip("posix_openpt failed with EACCES, assuming chroot and skipping")
82 slave
, err
:= os
.OpenFile(sname
, os
.O_RDWR
, 0)
88 // Start an interactive shell.
89 ctx
, cancel
:= context
.WithTimeout(context
.Background(), 10*time
.Second
)
91 cmd
:= exec
.CommandContext(ctx
, bash
, "--norc", "--noprofile", "-i")
95 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
98 Ctty
: int(slave
.Fd()),
101 if err
:= cmd
.Start(); err
!= nil {
105 if err
:= slave
.Close(); err
!= nil {
106 t
.Errorf("closing slave: %v", err
)
109 progReady
:= make(chan bool)
110 sawPrompt
:= make(chan bool, 10)
111 const prompt
= "prompt> "
113 // Read data from master in the background.
115 input
:= bufio
.NewReader(master
)
116 var line
, handled
[]byte
118 b
, err
:= input
.ReadByte()
120 if len(line
) > 0 ||
len(handled
) > 0 {
121 t
.Logf("%q", append(handled
, line
...))
123 if perr
, ok
:= err
.(*os
.PathError
); ok
{
126 // EOF means master is closed.
127 // EIO means child process is done.
128 // "file already closed" means deferred close of master has happened.
129 if err
!= io
.EOF
&& err
!= syscall
.EIO
&& !strings
.Contains(err
.Error(), "file already closed") {
130 t
.Logf("error reading from master: %v", err
)
135 line
= append(line
, b
)
138 t
.Logf("%q", append(handled
, line
...))
144 if bytes
.Contains(line
, []byte(enteringRead
)) {
146 handled
= append(handled
, line
...)
148 } else if bytes
.Contains(line
, []byte(prompt
)) && !bytes
.Contains(line
, []byte("PS1=")) {
150 handled
= append(handled
, line
...)
156 // Set the bash prompt so that we can see it.
157 if _
, err
:= master
.Write([]byte("PS1='" + prompt
+ "'\n")); err
!= nil {
158 t
.Fatalf("setting prompt: %v", err
)
162 case <-time
.After(wait
):
163 t
.Fatal("timed out waiting for shell prompt")
166 // Start a small program that reads from stdin
167 // (namely the code at the top of this function).
168 if _
, err
:= master
.Write([]byte("GO_TEST_TERMINAL_SIGNALS=1 " + os
.Args
[0] + " -test.run=TestTerminalSignal\n")); err
!= nil {
172 // Wait for the program to print that it is starting.
175 case <-time
.After(wait
):
176 t
.Fatal("timed out waiting for program to start")
179 // Give the program time to enter the read call.
180 // It doesn't matter much if we occasionally don't wait long enough;
181 // we won't be testing what we want to test, but the overall test
185 // Send a ^Z to stop the program.
186 if _
, err
:= master
.Write([]byte{26}); err
!= nil {
187 t
.Fatalf("writing ^Z to pty: %v", err
)
190 // Wait for the program to stop and return to the shell.
193 case <-time
.After(wait
):
194 t
.Fatal("timed out waiting for shell prompt")
197 // Restart the stopped program.
198 if _
, err
:= master
.Write([]byte("fg\n")); err
!= nil {
199 t
.Fatalf("writing %q to pty: %v", "fg", err
)
202 // Give the process time to restart.
203 // This is potentially racy: if the process does not restart
204 // quickly enough then the byte we send will go to bash rather
205 // than the program. Unfortunately there isn't anything we can
206 // look for to know that the program is running again.
207 // bash will print the program name, but that happens before it
208 // restarts the program.
209 time
.Sleep(10 * pause
)
211 // Write some data for the program to read,
212 // which should cause it to exit.
213 if _
, err
:= master
.Write([]byte{'\n'}); err
!= nil {
214 t
.Fatalf("writing %q to pty: %v", "\n", err
)
217 // Wait for the program to exit.
220 case <-time
.After(wait
):
221 t
.Fatal("timed out waiting for shell prompt")
224 // Exit the shell with the program's exit status.
225 if _
, err
:= master
.Write([]byte("exit $?\n")); err
!= nil {
226 t
.Fatalf("writing %q to pty: %v", "exit", err
)
229 if err
= cmd
.Wait(); err
!= nil {
230 t
.Errorf("subprogram failed: %v", err
)