github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+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  	inStream, outStream, errStream, err := c.GetStreams()
    54  	if err != nil {
    55  		return err
    56  	}
    57  	defer inStream.Close()
    58  
    59  	if !ctr.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(ctr, &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  	ctr, 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:        ctr.Config.Tty,
    91  		CloseStdin: ctr.Config.StdinOnce,
    92  	}
    93  	ctr.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(ctr, &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  		var ierr term.EscapeError
   180  		if errors.Is(err, context.Canceled) || errors.As(err, &ierr) {
   181  			daemon.LogContainerEvent(c, "detach")
   182  		} else {
   183  			logrus.Errorf("attach failed with error: %v", err)
   184  		}
   185  	}
   186  
   187  	return nil
   188  }