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