github.com/sams1990/dockerrepo@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  }