github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/daemon/attach.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 8 "github.com/docker/docker/api/types/backend" 9 "github.com/docker/docker/container" 10 "github.com/docker/docker/container/stream" 11 "github.com/docker/docker/daemon/logger" 12 "github.com/docker/docker/errdefs" 13 "github.com/docker/docker/pkg/stdcopy" 14 "github.com/docker/docker/pkg/term" 15 "github.com/pkg/errors" 16 "github.com/sirupsen/logrus" 17 ) 18 19 // ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig. 20 func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error { 21 keys := []byte{} 22 var err error 23 if c.DetachKeys != "" { 24 keys, err = term.ToBytes(c.DetachKeys) 25 if err != nil { 26 return errdefs.InvalidParameter(errors.Errorf("Invalid detach keys (%s) provided", c.DetachKeys)) 27 } 28 } 29 30 container, err := daemon.GetContainer(prefixOrName) 31 if err != nil { 32 return err 33 } 34 if container.IsPaused() { 35 err := fmt.Errorf("container %s is paused, unpause the container before attach", prefixOrName) 36 return errdefs.Conflict(err) 37 } 38 if container.IsRestarting() { 39 err := fmt.Errorf("container %s is restarting, wait until the container is running", prefixOrName) 40 return errdefs.Conflict(err) 41 } 42 43 cfg := stream.AttachConfig{ 44 UseStdin: c.UseStdin, 45 UseStdout: c.UseStdout, 46 UseStderr: c.UseStderr, 47 TTY: container.Config.Tty, 48 CloseStdin: container.Config.StdinOnce, 49 DetachKeys: keys, 50 } 51 container.StreamConfig.AttachStreams(&cfg) 52 53 inStream, outStream, errStream, err := c.GetStreams() 54 if err != nil { 55 return err 56 } 57 defer inStream.Close() 58 59 if !container.Config.Tty && c.MuxStreams { 60 errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr) 61 outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) 62 } 63 64 if cfg.UseStdin { 65 cfg.Stdin = inStream 66 } 67 if cfg.UseStdout { 68 cfg.Stdout = outStream 69 } 70 if cfg.UseStderr { 71 cfg.Stderr = errStream 72 } 73 74 if err := daemon.containerAttach(container, &cfg, c.Logs, c.Stream); err != nil { 75 fmt.Fprintf(outStream, "Error attaching: %s\n", err) 76 } 77 return nil 78 } 79 80 // ContainerAttachRaw attaches the provided streams to the container's stdio 81 func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, doStream bool, attached chan struct{}) error { 82 container, err := daemon.GetContainer(prefixOrName) 83 if err != nil { 84 return err 85 } 86 cfg := stream.AttachConfig{ 87 UseStdin: stdin != nil, 88 UseStdout: stdout != nil, 89 UseStderr: stderr != nil, 90 TTY: container.Config.Tty, 91 CloseStdin: container.Config.StdinOnce, 92 } 93 container.StreamConfig.AttachStreams(&cfg) 94 close(attached) 95 if cfg.UseStdin { 96 cfg.Stdin = stdin 97 } 98 if cfg.UseStdout { 99 cfg.Stdout = stdout 100 } 101 if cfg.UseStderr { 102 cfg.Stderr = stderr 103 } 104 105 return daemon.containerAttach(container, &cfg, false, doStream) 106 } 107 108 func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.AttachConfig, logs, doStream bool) error { 109 if logs { 110 logDriver, logCreated, err := daemon.getLogger(c) 111 if err != nil { 112 return err 113 } 114 if logCreated { 115 defer func() { 116 if err = logDriver.Close(); err != nil { 117 logrus.Errorf("Error closing logger: %v", err) 118 } 119 }() 120 } 121 cLog, ok := logDriver.(logger.LogReader) 122 if !ok { 123 return logger.ErrReadLogsNotSupported{} 124 } 125 logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1}) 126 defer logs.ConsumerGone() 127 128 LogLoop: 129 for { 130 select { 131 case msg, ok := <-logs.Msg: 132 if !ok { 133 break LogLoop 134 } 135 if msg.Source == "stdout" && cfg.Stdout != nil { 136 cfg.Stdout.Write(msg.Line) 137 } 138 if msg.Source == "stderr" && cfg.Stderr != nil { 139 cfg.Stderr.Write(msg.Line) 140 } 141 case err := <-logs.Err: 142 logrus.Errorf("Error streaming logs: %v", err) 143 break LogLoop 144 } 145 } 146 } 147 148 daemon.LogContainerEvent(c, "attach") 149 150 if !doStream { 151 return nil 152 } 153 154 if cfg.Stdin != nil { 155 r, w := io.Pipe() 156 go func(stdin io.ReadCloser) { 157 defer w.Close() 158 defer logrus.Debug("Closing buffered stdin pipe") 159 io.Copy(w, stdin) 160 }(cfg.Stdin) 161 cfg.Stdin = r 162 } 163 164 if !c.Config.OpenStdin { 165 cfg.Stdin = nil 166 } 167 168 if c.Config.StdinOnce && !c.Config.Tty { 169 // Wait for the container to stop before returning. 170 waitChan := c.Wait(context.Background(), container.WaitConditionNotRunning) 171 defer func() { 172 <-waitChan // Ignore returned exit code. 173 }() 174 } 175 176 ctx := c.InitAttachContext() 177 err := <-c.StreamConfig.CopyStreams(ctx, cfg) 178 if err != nil { 179 if _, ok := errors.Cause(err).(term.EscapeError); ok || err == context.Canceled { 180 daemon.LogContainerEvent(c, "detach") 181 } else { 182 logrus.Errorf("attach failed with error: %v", err) 183 } 184 } 185 186 return nil 187 }