attach.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package daemon
  2. import (
  3. "io"
  4. "github.com/dotcloud/docker/utils"
  5. )
  6. func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
  7. var (
  8. cStdout, cStderr io.ReadCloser
  9. nJobs int
  10. errors = make(chan error, 3)
  11. )
  12. if stdin != nil && container.Config.OpenStdin {
  13. nJobs += 1
  14. if cStdin, err := container.StdinPipe(); err != nil {
  15. errors <- err
  16. } else {
  17. go func() {
  18. utils.Debugf("attach: stdin: begin")
  19. defer utils.Debugf("attach: stdin: end")
  20. // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
  21. if container.Config.StdinOnce && !container.Config.Tty {
  22. defer cStdin.Close()
  23. } else {
  24. defer func() {
  25. if cStdout != nil {
  26. cStdout.Close()
  27. }
  28. if cStderr != nil {
  29. cStderr.Close()
  30. }
  31. }()
  32. }
  33. if container.Config.Tty {
  34. _, err = utils.CopyEscapable(cStdin, stdin)
  35. } else {
  36. _, err = io.Copy(cStdin, stdin)
  37. }
  38. if err == io.ErrClosedPipe {
  39. err = nil
  40. }
  41. if err != nil {
  42. utils.Errorf("attach: stdin: %s", err)
  43. }
  44. errors <- err
  45. }()
  46. }
  47. }
  48. if stdout != nil {
  49. nJobs += 1
  50. if p, err := container.StdoutPipe(); err != nil {
  51. errors <- err
  52. } else {
  53. cStdout = p
  54. go func() {
  55. utils.Debugf("attach: stdout: begin")
  56. defer utils.Debugf("attach: stdout: end")
  57. // If we are in StdinOnce mode, then close stdin
  58. if container.Config.StdinOnce && stdin != nil {
  59. defer stdin.Close()
  60. }
  61. if stdinCloser != nil {
  62. defer stdinCloser.Close()
  63. }
  64. _, err := io.Copy(stdout, cStdout)
  65. if err == io.ErrClosedPipe {
  66. err = nil
  67. }
  68. if err != nil {
  69. utils.Errorf("attach: stdout: %s", err)
  70. }
  71. errors <- err
  72. }()
  73. }
  74. } else {
  75. go func() {
  76. if stdinCloser != nil {
  77. defer stdinCloser.Close()
  78. }
  79. if cStdout, err := container.StdoutPipe(); err != nil {
  80. utils.Errorf("attach: stdout pipe: %s", err)
  81. } else {
  82. io.Copy(&utils.NopWriter{}, cStdout)
  83. }
  84. }()
  85. }
  86. if stderr != nil {
  87. nJobs += 1
  88. if p, err := container.StderrPipe(); err != nil {
  89. errors <- err
  90. } else {
  91. cStderr = p
  92. go func() {
  93. utils.Debugf("attach: stderr: begin")
  94. defer utils.Debugf("attach: stderr: end")
  95. // If we are in StdinOnce mode, then close stdin
  96. if container.Config.StdinOnce && stdin != nil {
  97. defer stdin.Close()
  98. }
  99. if stdinCloser != nil {
  100. defer stdinCloser.Close()
  101. }
  102. _, err := io.Copy(stderr, cStderr)
  103. if err == io.ErrClosedPipe {
  104. err = nil
  105. }
  106. if err != nil {
  107. utils.Errorf("attach: stderr: %s", err)
  108. }
  109. errors <- err
  110. }()
  111. }
  112. } else {
  113. go func() {
  114. if stdinCloser != nil {
  115. defer stdinCloser.Close()
  116. }
  117. if cStderr, err := container.StderrPipe(); err != nil {
  118. utils.Errorf("attach: stdout pipe: %s", err)
  119. } else {
  120. io.Copy(&utils.NopWriter{}, cStderr)
  121. }
  122. }()
  123. }
  124. return utils.Go(func() error {
  125. defer func() {
  126. if cStdout != nil {
  127. cStdout.Close()
  128. }
  129. if cStderr != nil {
  130. cStderr.Close()
  131. }
  132. }()
  133. // FIXME: how to clean up the stdin goroutine without the unwanted side effect
  134. // of closing the passed stdin? Add an intermediary io.Pipe?
  135. for i := 0; i < nJobs; i += 1 {
  136. utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
  137. if err := <-errors; err != nil {
  138. utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
  139. return err
  140. }
  141. utils.Debugf("attach: job %d completed successfully", i+1)
  142. }
  143. utils.Debugf("attach: all jobs completed successfully")
  144. return nil
  145. })
  146. }