github.com/gravitational/moby@v1.13.1/daemon/attach.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"time"
     7  
     8  	"github.com/Sirupsen/logrus"
     9  	"github.com/docker/docker/api/errors"
    10  	"github.com/docker/docker/api/types/backend"
    11  	"github.com/docker/docker/container"
    12  	"github.com/docker/docker/daemon/logger"
    13  	"github.com/docker/docker/pkg/stdcopy"
    14  	"github.com/docker/docker/pkg/term"
    15  )
    16  
    17  // ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig.
    18  func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
    19  	keys := []byte{}
    20  	var err error
    21  	if c.DetachKeys != "" {
    22  		keys, err = term.ToBytes(c.DetachKeys)
    23  		if err != nil {
    24  			return fmt.Errorf("Invalid escape keys (%s) provided", c.DetachKeys)
    25  		}
    26  	}
    27  
    28  	container, err := daemon.GetContainer(prefixOrName)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	if container.IsPaused() {
    33  		err := fmt.Errorf("Container %s is paused. Unpause the container before attach", prefixOrName)
    34  		return errors.NewRequestConflictError(err)
    35  	}
    36  
    37  	inStream, outStream, errStream, err := c.GetStreams()
    38  	if err != nil {
    39  		return err
    40  	}
    41  	defer inStream.Close()
    42  
    43  	if !container.Config.Tty && c.MuxStreams {
    44  		errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr)
    45  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
    46  	}
    47  
    48  	var stdin io.ReadCloser
    49  	var stdout, stderr io.Writer
    50  
    51  	if c.UseStdin {
    52  		stdin = inStream
    53  	}
    54  	if c.UseStdout {
    55  		stdout = outStream
    56  	}
    57  	if c.UseStderr {
    58  		stderr = errStream
    59  	}
    60  
    61  	if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, keys); err != nil {
    62  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
    63  	}
    64  	return nil
    65  }
    66  
    67  // ContainerAttachRaw attaches the provided streams to the container's stdio
    68  func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
    69  	container, err := daemon.GetContainer(prefixOrName)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	return daemon.containerAttach(container, stdin, stdout, stderr, false, stream, nil)
    74  }
    75  
    76  func (daemon *Daemon) containerAttach(c *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
    77  	if logs {
    78  		logDriver, err := daemon.getLogger(c)
    79  		if err != nil {
    80  			return err
    81  		}
    82  		cLog, ok := logDriver.(logger.LogReader)
    83  		if !ok {
    84  			return logger.ErrReadLogsNotSupported
    85  		}
    86  		logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1})
    87  
    88  	LogLoop:
    89  		for {
    90  			select {
    91  			case msg, ok := <-logs.Msg:
    92  				if !ok {
    93  					break LogLoop
    94  				}
    95  				if msg.Source == "stdout" && stdout != nil {
    96  					stdout.Write(msg.Line)
    97  				}
    98  				if msg.Source == "stderr" && stderr != nil {
    99  					stderr.Write(msg.Line)
   100  				}
   101  			case err := <-logs.Err:
   102  				logrus.Errorf("Error streaming logs: %v", err)
   103  				break LogLoop
   104  			}
   105  		}
   106  	}
   107  
   108  	daemon.LogContainerEvent(c, "attach")
   109  
   110  	//stream
   111  	if stream {
   112  		var stdinPipe io.ReadCloser
   113  		if stdin != nil {
   114  			r, w := io.Pipe()
   115  			go func() {
   116  				defer w.Close()
   117  				defer logrus.Debug("Closing buffered stdin pipe")
   118  				io.Copy(w, stdin)
   119  			}()
   120  			stdinPipe = r
   121  		}
   122  
   123  		waitChan := make(chan struct{})
   124  		if c.Config.StdinOnce && !c.Config.Tty {
   125  			go func() {
   126  				c.WaitStop(-1 * time.Second)
   127  				close(waitChan)
   128  			}()
   129  		}
   130  
   131  		err := <-c.Attach(stdinPipe, stdout, stderr, keys)
   132  		if err != nil {
   133  			if _, ok := err.(container.DetachError); ok {
   134  				daemon.LogContainerEvent(c, "detach")
   135  			} else {
   136  				logrus.Errorf("attach failed with error: %v", err)
   137  			}
   138  		}
   139  
   140  		// If we are in stdinonce mode, wait for the process to end
   141  		// otherwise, simply return
   142  		if c.Config.StdinOnce && !c.Config.Tty {
   143  			<-waitChan
   144  		}
   145  	}
   146  	return nil
   147  }