term.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package execdriver
  2. import (
  3. "github.com/dotcloud/docker/pkg/term"
  4. "github.com/kr/pty"
  5. "io"
  6. "os"
  7. )
  8. type Term interface {
  9. io.Closer
  10. Resize(height, width int) error
  11. }
  12. type Pipes struct {
  13. Stdin io.ReadCloser
  14. Stdout, Stderr io.Writer
  15. }
  16. func NewPipes(stdin io.ReadCloser, stdout, stderr io.Writer, useStdin bool) *Pipes {
  17. p := &Pipes{
  18. Stdout: stdout,
  19. Stderr: stderr,
  20. }
  21. if useStdin {
  22. p.Stdin = stdin
  23. }
  24. return p
  25. }
  26. func SetTerminal(command *Command, pipes *Pipes) error {
  27. var (
  28. term Term
  29. err error
  30. )
  31. if command.Tty {
  32. term, err = NewTtyConsole(command, pipes)
  33. } else {
  34. term, err = NewStdConsole(command, pipes)
  35. }
  36. if err != nil {
  37. return err
  38. }
  39. command.Terminal = term
  40. return nil
  41. }
  42. type TtyConsole struct {
  43. Master *os.File
  44. Slave *os.File
  45. }
  46. func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
  47. ptyMaster, ptySlave, err := pty.Open()
  48. if err != nil {
  49. return nil, err
  50. }
  51. tty := &TtyConsole{
  52. Master: ptyMaster,
  53. Slave: ptySlave,
  54. }
  55. if err := tty.attach(command, pipes); err != nil {
  56. tty.Close()
  57. return nil, err
  58. }
  59. return tty, nil
  60. }
  61. func (t *TtyConsole) Resize(h, w int) error {
  62. return term.SetWinsize(t.Master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
  63. }
  64. func (t *TtyConsole) attach(command *Command, pipes *Pipes) error {
  65. command.Stdout = t.Slave
  66. command.Stderr = t.Slave
  67. command.Console = t.Slave.Name()
  68. go func() {
  69. if wb, ok := pipes.Stdout.(interface {
  70. CloseWriters() error
  71. }); ok {
  72. defer wb.CloseWriters()
  73. }
  74. io.Copy(pipes.Stdout, t.Master)
  75. }()
  76. if pipes.Stdin != nil {
  77. command.Stdin = t.Slave
  78. command.SysProcAttr.Setctty = true
  79. go func() {
  80. defer pipes.Stdin.Close()
  81. io.Copy(t.Master, pipes.Stdin)
  82. }()
  83. }
  84. return nil
  85. }
  86. func (t *TtyConsole) Close() error {
  87. t.Slave.Close()
  88. return t.Master.Close()
  89. }
  90. type StdConsole struct {
  91. }
  92. func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) {
  93. std := &StdConsole{}
  94. if err := std.attach(command, pipes); err != nil {
  95. return nil, err
  96. }
  97. return std, nil
  98. }
  99. func (s *StdConsole) attach(command *Command, pipes *Pipes) error {
  100. command.Stdout = pipes.Stdout
  101. command.Stderr = pipes.Stderr
  102. if pipes.Stdin != nil {
  103. stdin, err := command.StdinPipe()
  104. if err != nil {
  105. return err
  106. }
  107. go func() {
  108. defer stdin.Close()
  109. io.Copy(stdin, pipes.Stdin)
  110. }()
  111. }
  112. return nil
  113. }
  114. func (s *StdConsole) Resize(h, w int) error {
  115. // we do not need to reside a non tty
  116. return nil
  117. }
  118. func (s *StdConsole) Close() error {
  119. // nothing to close here
  120. return nil
  121. }