daemon/attach.go
41cfaa73
 package daemon
 
 import (
af94f941
 	"fmt"
41cfaa73
 	"io"
ca5ede2d
 	"time"
41cfaa73
 
ca5ede2d
 	"github.com/Sirupsen/logrus"
06d8f504
 	"github.com/docker/docker/api/types/backend"
6bb0d181
 	"github.com/docker/docker/container"
ca5ede2d
 	"github.com/docker/docker/daemon/logger"
a793564b
 	"github.com/docker/docker/errors"
e2acca67
 	"github.com/docker/docker/pkg/stdcopy"
91e5bb95
 	"github.com/docker/docker/pkg/term"
41cfaa73
 )
 
a77b7dd2
 // ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig.
 func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
91e5bb95
 	keys := []byte{}
 	var err error
 	if c.DetachKeys != "" {
 		keys, err = term.ToBytes(c.DetachKeys)
 		if err != nil {
 			return fmt.Errorf("Invalid escape keys (%s) provided", c.DetachKeys)
 		}
 	}
 
d7d512bb
 	container, err := daemon.GetContainer(prefixOrName)
8aef1a33
 	if err != nil {
a793564b
 		return err
af94f941
 	}
 	if container.IsPaused() {
a793564b
 		err := fmt.Errorf("Container %s is paused. Unpause the container before attach", prefixOrName)
 		return errors.NewRequestConflictError(err)
af94f941
 	}
 
a77b7dd2
 	inStream, outStream, errStream, err := c.GetStreams()
af94f941
 	if err != nil {
8aef1a33
 		return err
 	}
a77b7dd2
 	defer inStream.Close()
8aef1a33
 
a77b7dd2
 	if !container.Config.Tty && c.MuxStreams {
 		errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr)
af94f941
 		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
21e44d7a
 	}
 
e2acca67
 	var stdin io.ReadCloser
 	var stdout, stderr io.Writer
e6c93434
 
e2acca67
 	if c.UseStdin {
af94f941
 		stdin = inStream
e2acca67
 	}
 	if c.UseStdout {
af94f941
 		stdout = outStream
e2acca67
 	}
 	if c.UseStderr {
 		stderr = errStream
21e44d7a
 	}
 
91e5bb95
 	if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, keys); err != nil {
af94f941
 		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
 	}
 	return nil
e2acca67
 }
21e44d7a
 
a77b7dd2
 // ContainerAttachRaw attaches the provided streams to the container's stdio
 func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
d7d512bb
 	container, err := daemon.GetContainer(prefixOrName)
8aef1a33
 	if err != nil {
 		return err
 	}
a77b7dd2
 	return daemon.containerAttach(container, stdin, stdout, stderr, false, stream, nil)
9c332b16
 }
 
83ad006d
 func (daemon *Daemon) containerAttach(c *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
ca5ede2d
 	if logs {
83ad006d
 		logDriver, err := daemon.getLogger(c)
ca5ede2d
 		if err != nil {
 			return err
 		}
 		cLog, ok := logDriver.(logger.LogReader)
 		if !ok {
 			return logger.ErrReadLogsNotSupported
 		}
 		logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1})
 
 	LogLoop:
 		for {
 			select {
 			case msg, ok := <-logs.Msg:
 				if !ok {
 					break LogLoop
 				}
 				if msg.Source == "stdout" && stdout != nil {
 					stdout.Write(msg.Line)
 				}
 				if msg.Source == "stderr" && stderr != nil {
 					stderr.Write(msg.Line)
 				}
 			case err := <-logs.Err:
 				logrus.Errorf("Error streaming logs: %v", err)
 				break LogLoop
 			}
 		}
 	}
 
83ad006d
 	daemon.LogContainerEvent(c, "attach")
ca5ede2d
 
 	//stream
 	if stream {
 		var stdinPipe io.ReadCloser
 		if stdin != nil {
 			r, w := io.Pipe()
 			go func() {
 				defer w.Close()
a72b45db
 				defer logrus.Debug("Closing buffered stdin pipe")
ca5ede2d
 				io.Copy(w, stdin)
 			}()
 			stdinPipe = r
 		}
c498d470
 
 		waitChan := make(chan struct{})
 		if c.Config.StdinOnce && !c.Config.Tty {
 			go func() {
 				c.WaitStop(-1 * time.Second)
 				close(waitChan)
 			}()
 		}
 
83ad006d
 		err := <-c.Attach(stdinPipe, stdout, stderr, keys)
 		if err != nil {
3accde6d
 			if _, ok := err.(container.DetachError); ok {
83ad006d
 				daemon.LogContainerEvent(c, "detach")
 			} else {
 				logrus.Errorf("attach failed with error: %v", err)
 			}
 		}
 
ca5ede2d
 		// If we are in stdinonce mode, wait for the process to end
 		// otherwise, simply return
83ad006d
 		if c.Config.StdinOnce && !c.Config.Tty {
c498d470
 			<-waitChan
ca5ede2d
 		}
 	}
 	return nil
c30a55f1
 }