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