github.com/vincentwoo/docker@v0.7.3-0.20160116130405-82401a4b13c0/daemon/attach.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "io" 6 "net/http" 7 "time" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/container" 11 "github.com/docker/docker/daemon/logger" 12 derr "github.com/docker/docker/errors" 13 "github.com/docker/docker/pkg/stdcopy" 14 ) 15 16 // ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs. 17 type ContainerAttachWithLogsConfig struct { 18 Hijacker http.Hijacker 19 Upgrade bool 20 UseStdin bool 21 UseStdout bool 22 UseStderr bool 23 Logs bool 24 Stream bool 25 DetachKeys []byte 26 } 27 28 // ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig. 29 func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerAttachWithLogsConfig) error { 30 if c.Hijacker == nil { 31 return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName) 32 } 33 container, err := daemon.GetContainer(prefixOrName) 34 if err != nil { 35 return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName) 36 } 37 if container.IsPaused() { 38 return derr.ErrorCodePausedContainer.WithArgs(prefixOrName) 39 } 40 41 conn, _, err := c.Hijacker.Hijack() 42 if err != nil { 43 return err 44 } 45 defer conn.Close() 46 // Flush the options to make sure the client sets the raw mode 47 conn.Write([]byte{}) 48 inStream := conn.(io.ReadCloser) 49 outStream := conn.(io.Writer) 50 51 if c.Upgrade { 52 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") 53 } else { 54 fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") 55 } 56 57 var errStream io.Writer 58 59 if !container.Config.Tty { 60 errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) 61 outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) 62 } else { 63 errStream = outStream 64 } 65 66 var stdin io.ReadCloser 67 var stdout, stderr io.Writer 68 69 if c.UseStdin { 70 stdin = inStream 71 } 72 if c.UseStdout { 73 stdout = outStream 74 } 75 if c.UseStderr { 76 stderr = errStream 77 } 78 79 if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); err != nil { 80 fmt.Fprintf(outStream, "Error attaching: %s\n", err) 81 } 82 return nil 83 } 84 85 // ContainerWsAttachWithLogsConfig attach with websockets, since all 86 // stream data is delegated to the websocket to handle there. 87 type ContainerWsAttachWithLogsConfig struct { 88 InStream io.ReadCloser 89 OutStream, ErrStream io.Writer 90 Logs, Stream bool 91 DetachKeys []byte 92 } 93 94 // ContainerWsAttachWithLogs websocket connection 95 func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *ContainerWsAttachWithLogsConfig) error { 96 container, err := daemon.GetContainer(prefixOrName) 97 if err != nil { 98 return err 99 } 100 return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream, c.DetachKeys) 101 } 102 103 func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error { 104 if logs { 105 logDriver, err := daemon.getLogger(container) 106 if err != nil { 107 return err 108 } 109 cLog, ok := logDriver.(logger.LogReader) 110 if !ok { 111 return logger.ErrReadLogsNotSupported 112 } 113 logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1}) 114 115 LogLoop: 116 for { 117 select { 118 case msg, ok := <-logs.Msg: 119 if !ok { 120 break LogLoop 121 } 122 if msg.Source == "stdout" && stdout != nil { 123 stdout.Write(msg.Line) 124 } 125 if msg.Source == "stderr" && stderr != nil { 126 stderr.Write(msg.Line) 127 } 128 case err := <-logs.Err: 129 logrus.Errorf("Error streaming logs: %v", err) 130 break LogLoop 131 } 132 } 133 } 134 135 daemon.LogContainerEvent(container, "attach") 136 137 //stream 138 if stream { 139 var stdinPipe io.ReadCloser 140 if stdin != nil { 141 r, w := io.Pipe() 142 go func() { 143 defer w.Close() 144 defer logrus.Debugf("Closing buffered stdin pipe") 145 io.Copy(w, stdin) 146 }() 147 stdinPipe = r 148 } 149 <-container.Attach(stdinPipe, stdout, stderr, keys) 150 // If we are in stdinonce mode, wait for the process to end 151 // otherwise, simply return 152 if container.Config.StdinOnce && !container.Config.Tty { 153 container.WaitStop(-1 * time.Second) 154 } 155 } 156 return nil 157 }