@@ -3,17 +3,28 @@ package streams
33import (
44 "errors"
55 "io"
6- "os"
7- "runtime"
86
97 "github.com/moby/term"
108)
119
1210// In is an input stream to read user input. It implements [io.ReadCloser]
1311// with additional utilities, such as putting the terminal in raw mode.
1412type In struct {
15- commonStream
1613 in io.ReadCloser
14+ cs commonStream
15+ }
16+
17+ // NewIn returns a new [In] from an [io.ReadCloser].
18+ func NewIn (in io.ReadCloser ) * In {
19+ return & In {
20+ in : in ,
21+ cs : newCommonStream (in ),
22+ }
23+ }
24+
25+ // FD returns the file descriptor number for this stream.
26+ func (i * In ) FD () uintptr {
27+ return i .cs .fd
1728}
1829
1930// Read implements the [io.Reader] interface.
@@ -26,36 +37,37 @@ func (i *In) Close() error {
2637 return i .in .Close ()
2738}
2839
40+ // IsTerminal returns whether this stream is connected to a terminal.
41+ func (i * In ) IsTerminal () bool {
42+ return i .cs .isTerminal ()
43+ }
44+
2945// SetRawTerminal sets raw mode on the input terminal. It is a no-op if In
3046// is not a TTY, or if the "NORAW" environment variable is set to a non-empty
3147// value.
32- func (i * In ) SetRawTerminal () (err error ) {
33- if ! i .isTerminal || os .Getenv ("NORAW" ) != "" {
34- return nil
35- }
36- i .state , err = term .SetRawTerminal (i .fd )
37- return err
48+ func (i * In ) SetRawTerminal () error {
49+ return i .cs .setRawTerminal (term .SetRawTerminal )
50+ }
51+
52+ // RestoreTerminal restores the terminal state if SetRawTerminal succeeded earlier.
53+ func (i * In ) RestoreTerminal () {
54+ i .cs .restoreTerminal ()
3855}
3956
40- // CheckTty checks if we are trying to attach to a container TTY
41- // from a non-TTY client input stream, and if so, returns an error.
57+ // CheckTty reports an error when stdin is requested for a TTY-enabled
58+ // container, but the client stdin is not itself a terminal (for example,
59+ // when input is piped or redirected).
4260func (i * In ) CheckTty (attachStdin , ttyMode bool ) error {
43- // In order to attach to a container tty, input stream for the client must
44- // be a tty itself: redirecting or piping the client standard input is
45- // incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
46- if ttyMode && attachStdin && ! i .isTerminal {
47- const eText = "the input device is not a TTY"
48- if runtime .GOOS == "windows" {
49- return errors .New (eText + ". If you are using mintty, try prefixing the command with 'winpty'" )
50- }
51- return errors .New (eText )
61+ // TODO(thaJeztah): consider inlining this code and deprecating the method.
62+ if ! ttyMode || ! attachStdin || i .cs .isTerminal () {
63+ return nil
5264 }
53- return nil
65+ return errors . New ( "cannot attach stdin to a TTY-enabled container because stdin is not a terminal" )
5466}
5567
56- // NewIn returns a new [In] from an [io.ReadCloser].
57- func NewIn ( in io. ReadCloser ) * In {
58- i := & In { in : in }
59- i . fd , i . isTerminal = term . GetFdInfo ( in )
60- return i
68+ // SetIsTerminal overrides whether a terminal is connected. It is used to
69+ // override this property in unit-tests, and should not be depended on for
70+ // other purposes.
71+ func ( i * In ) SetIsTerminal ( isTerminal bool ) {
72+ i . cs . setIsTerminal ( isTerminal )
6173}
0 commit comments