attach.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package daemon
  2. import (
  3. "fmt"
  4. "io"
  5. "time"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api/types/backend"
  8. "github.com/docker/docker/container"
  9. "github.com/docker/docker/daemon/logger"
  10. "github.com/docker/docker/errors"
  11. "github.com/docker/docker/pkg/stdcopy"
  12. "github.com/docker/docker/pkg/term"
  13. )
  14. // ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig.
  15. func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
  16. keys := []byte{}
  17. var err error
  18. if c.DetachKeys != "" {
  19. keys, err = term.ToBytes(c.DetachKeys)
  20. if err != nil {
  21. return fmt.Errorf("Invalid escape keys (%s) provided", c.DetachKeys)
  22. }
  23. }
  24. container, err := daemon.GetContainer(prefixOrName)
  25. if err != nil {
  26. return err
  27. }
  28. if container.IsPaused() {
  29. err := fmt.Errorf("Container %s is paused. Unpause the container before attach", prefixOrName)
  30. return errors.NewRequestConflictError(err)
  31. }
  32. inStream, outStream, errStream, err := c.GetStreams()
  33. if err != nil {
  34. return err
  35. }
  36. defer inStream.Close()
  37. if !container.Config.Tty && c.MuxStreams {
  38. errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr)
  39. outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  40. }
  41. var stdin io.ReadCloser
  42. var stdout, stderr io.Writer
  43. if c.UseStdin {
  44. stdin = inStream
  45. }
  46. if c.UseStdout {
  47. stdout = outStream
  48. }
  49. if c.UseStderr {
  50. stderr = errStream
  51. }
  52. if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, keys); err != nil {
  53. fmt.Fprintf(outStream, "Error attaching: %s\n", err)
  54. }
  55. return nil
  56. }
  57. // ContainerAttachRaw attaches the provided streams to the container's stdio
  58. func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
  59. container, err := daemon.GetContainer(prefixOrName)
  60. if err != nil {
  61. return err
  62. }
  63. return daemon.containerAttach(container, stdin, stdout, stderr, false, stream, nil)
  64. }
  65. func (daemon *Daemon) containerAttach(c *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
  66. if logs {
  67. logDriver, err := daemon.getLogger(c)
  68. if err != nil {
  69. return err
  70. }
  71. cLog, ok := logDriver.(logger.LogReader)
  72. if !ok {
  73. return logger.ErrReadLogsNotSupported
  74. }
  75. logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1})
  76. LogLoop:
  77. for {
  78. select {
  79. case msg, ok := <-logs.Msg:
  80. if !ok {
  81. break LogLoop
  82. }
  83. if msg.Source == "stdout" && stdout != nil {
  84. stdout.Write(msg.Line)
  85. }
  86. if msg.Source == "stderr" && stderr != nil {
  87. stderr.Write(msg.Line)
  88. }
  89. case err := <-logs.Err:
  90. logrus.Errorf("Error streaming logs: %v", err)
  91. break LogLoop
  92. }
  93. }
  94. }
  95. daemon.LogContainerEvent(c, "attach")
  96. //stream
  97. if stream {
  98. var stdinPipe io.ReadCloser
  99. if stdin != nil {
  100. r, w := io.Pipe()
  101. go func() {
  102. defer w.Close()
  103. defer logrus.Debug("Closing buffered stdin pipe")
  104. io.Copy(w, stdin)
  105. }()
  106. stdinPipe = r
  107. }
  108. waitChan := make(chan struct{})
  109. if c.Config.StdinOnce && !c.Config.Tty {
  110. go func() {
  111. c.WaitStop(-1 * time.Second)
  112. close(waitChan)
  113. }()
  114. }
  115. err := <-c.Attach(stdinPipe, stdout, stderr, keys)
  116. if err != nil {
  117. if _, ok := err.(container.DetachError); ok {
  118. daemon.LogContainerEvent(c, "detach")
  119. } else {
  120. logrus.Errorf("attach failed with error: %v", err)
  121. }
  122. }
  123. // If we are in stdinonce mode, wait for the process to end
  124. // otherwise, simply return
  125. if c.Config.StdinOnce && !c.Config.Tty {
  126. <-waitChan
  127. }
  128. }
  129. return nil
  130. }