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  }