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"
41cfaa73
 )
 
a77b7dd2
 // ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig.
 func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
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
 	}
 
a77b7dd2
 	if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); 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
 }
 
a77b7dd2
 func (daemon *Daemon) containerAttach(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
ca5ede2d
 	if logs {
c412300d
 		logDriver, err := daemon.getLogger(container)
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
 			}
 		}
 	}
 
 	daemon.LogContainerEvent(container, "attach")
 
 	//stream
 	if stream {
 		var stdinPipe io.ReadCloser
 		if stdin != nil {
 			r, w := io.Pipe()
 			go func() {
 				defer w.Close()
 				defer logrus.Debugf("Closing buffered stdin pipe")
 				io.Copy(w, stdin)
 			}()
 			stdinPipe = r
 		}
15aa2a66
 		<-container.Attach(stdinPipe, stdout, stderr, keys)
ca5ede2d
 		// If we are in stdinonce mode, wait for the process to end
 		// otherwise, simply return
 		if container.Config.StdinOnce && !container.Config.Tty {
 			container.WaitStop(-1 * time.Second)
 		}
 	}
 	return nil
c30a55f1
 }