daemon/attach.go
41cfaa73
 package daemon
 
 import (
af94f941
 	"fmt"
41cfaa73
 	"io"
af94f941
 	"net/http"
ca5ede2d
 	"time"
41cfaa73
 
ca5ede2d
 	"github.com/Sirupsen/logrus"
6bb0d181
 	"github.com/docker/docker/container"
ca5ede2d
 	"github.com/docker/docker/daemon/logger"
af94f941
 	derr "github.com/docker/docker/errors"
e2acca67
 	"github.com/docker/docker/pkg/stdcopy"
41cfaa73
 )
 
abd72d40
 // ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs.
e2acca67
 type ContainerAttachWithLogsConfig struct {
15aa2a66
 	Hijacker   http.Hijacker
 	Upgrade    bool
 	UseStdin   bool
 	UseStdout  bool
 	UseStderr  bool
 	Logs       bool
 	Stream     bool
 	DetachKeys []byte
c2496d97
 }
 
abd72d40
 // ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
b08f071e
 func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerAttachWithLogsConfig) error {
af94f941
 	if c.Hijacker == nil {
 		return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName)
 	}
d7d512bb
 	container, err := daemon.GetContainer(prefixOrName)
8aef1a33
 	if err != nil {
af94f941
 		return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName)
 	}
 	if container.IsPaused() {
 		return derr.ErrorCodePausedContainer.WithArgs(prefixOrName)
 	}
 
 	conn, _, err := c.Hijacker.Hijack()
 	if err != nil {
8aef1a33
 		return err
 	}
af94f941
 	defer conn.Close()
 	// Flush the options to make sure the client sets the raw mode
 	conn.Write([]byte{})
 	inStream := conn.(io.ReadCloser)
 	outStream := conn.(io.Writer)
 
 	if c.Upgrade {
 		fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
 	} else {
 		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
 	}
8aef1a33
 
e2acca67
 	var errStream io.Writer
21e44d7a
 
b6a6c569
 	if !container.Config.Tty {
af94f941
 		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
 		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
e2acca67
 	} else {
af94f941
 		errStream = outStream
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
 	}
 
15aa2a66
 	if err := daemon.attachWithLogs(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
 
abd72d40
 // ContainerWsAttachWithLogsConfig attach with websockets, since all
4e62bd97
 // stream data is delegated to the websocket to handle there.
e2acca67
 type ContainerWsAttachWithLogsConfig struct {
 	InStream             io.ReadCloser
 	OutStream, ErrStream io.Writer
 	Logs, Stream         bool
15aa2a66
 	DetachKeys           []byte
41cfaa73
 }
c30a55f1
 
abd72d40
 // ContainerWsAttachWithLogs websocket connection
b08f071e
 func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *ContainerWsAttachWithLogsConfig) error {
d7d512bb
 	container, err := daemon.GetContainer(prefixOrName)
8aef1a33
 	if err != nil {
 		return err
 	}
15aa2a66
 	return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream, c.DetachKeys)
ca5ede2d
 }
 
15aa2a66
 func (daemon *Daemon) attachWithLogs(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
 }