in.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package command
  2. import (
  3. "errors"
  4. "io"
  5. "os"
  6. "runtime"
  7. "github.com/docker/docker/pkg/term"
  8. )
  9. // InStream is an input stream used by the DockerCli to read user input
  10. type InStream struct {
  11. in io.ReadCloser
  12. fd uintptr
  13. isTerminal bool
  14. state *term.State
  15. }
  16. func (i *InStream) Read(p []byte) (int, error) {
  17. return i.in.Read(p)
  18. }
  19. // Close implements the Closer interface
  20. func (i *InStream) Close() error {
  21. return i.in.Close()
  22. }
  23. // FD returns the file descriptor number for this stream
  24. func (i *InStream) FD() uintptr {
  25. return i.fd
  26. }
  27. // IsTerminal returns true if this stream is connected to a terminal
  28. func (i *InStream) IsTerminal() bool {
  29. return i.isTerminal
  30. }
  31. // SetRawTerminal sets raw mode on the input terminal
  32. func (i *InStream) SetRawTerminal() (err error) {
  33. if os.Getenv("NORAW") != "" || !i.isTerminal {
  34. return nil
  35. }
  36. i.state, err = term.SetRawTerminal(i.fd)
  37. return err
  38. }
  39. // RestoreTerminal restores normal mode to the terminal
  40. func (i *InStream) RestoreTerminal() {
  41. if i.state != nil {
  42. term.RestoreTerminal(i.fd, i.state)
  43. }
  44. }
  45. // CheckTty checks if we are trying to attach to a container tty
  46. // from a non-tty client input stream, and if so, returns an error.
  47. func (i *InStream) CheckTty(attachStdin, ttyMode bool) error {
  48. // In order to attach to a container tty, input stream for the client must
  49. // be a tty itself: redirecting or piping the client standard input is
  50. // incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
  51. if ttyMode && attachStdin && !i.isTerminal {
  52. eText := "the input device is not a TTY"
  53. if runtime.GOOS == "windows" {
  54. return errors.New(eText + ". If you are using mintty, try prefixing the command with 'winpty'")
  55. }
  56. return errors.New(eText)
  57. }
  58. return nil
  59. }
  60. // NewInStream returns a new InStream object from a ReadCloser
  61. func NewInStream(in io.ReadCloser) *InStream {
  62. fd, isTerminal := term.GetFdInfo(in)
  63. return &InStream{in: in, fd: fd, isTerminal: isTerminal}
  64. }