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