github.com/fcwu/docker@v1.4.2-0.20150115145920-2a69ca89f0df/daemon/attach.go (about) 1 package daemon 2 3 import ( 4 "encoding/json" 5 "io" 6 "os" 7 "sync" 8 "time" 9 10 log "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/engine" 12 "github.com/docker/docker/pkg/jsonlog" 13 "github.com/docker/docker/pkg/promise" 14 "github.com/docker/docker/utils" 15 ) 16 17 func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status { 18 if len(job.Args) != 1 { 19 return job.Errorf("Usage: %s CONTAINER\n", job.Name) 20 } 21 22 var ( 23 name = job.Args[0] 24 logs = job.GetenvBool("logs") 25 stream = job.GetenvBool("stream") 26 stdin = job.GetenvBool("stdin") 27 stdout = job.GetenvBool("stdout") 28 stderr = job.GetenvBool("stderr") 29 ) 30 31 container := daemon.Get(name) 32 if container == nil { 33 return job.Errorf("No such container: %s", name) 34 } 35 36 //logs 37 if logs { 38 cLog, err := container.ReadLog("json") 39 if err != nil && os.IsNotExist(err) { 40 // Legacy logs 41 log.Debugf("Old logs format") 42 if stdout { 43 cLog, err := container.ReadLog("stdout") 44 if err != nil { 45 log.Errorf("Error reading logs (stdout): %s", err) 46 } else if _, err := io.Copy(job.Stdout, cLog); err != nil { 47 log.Errorf("Error streaming logs (stdout): %s", err) 48 } 49 } 50 if stderr { 51 cLog, err := container.ReadLog("stderr") 52 if err != nil { 53 log.Errorf("Error reading logs (stderr): %s", err) 54 } else if _, err := io.Copy(job.Stderr, cLog); err != nil { 55 log.Errorf("Error streaming logs (stderr): %s", err) 56 } 57 } 58 } else if err != nil { 59 log.Errorf("Error reading logs (json): %s", err) 60 } else { 61 dec := json.NewDecoder(cLog) 62 for { 63 l := &jsonlog.JSONLog{} 64 65 if err := dec.Decode(l); err == io.EOF { 66 break 67 } else if err != nil { 68 log.Errorf("Error streaming logs: %s", err) 69 break 70 } 71 if l.Stream == "stdout" && stdout { 72 io.WriteString(job.Stdout, l.Log) 73 } 74 if l.Stream == "stderr" && stderr { 75 io.WriteString(job.Stderr, l.Log) 76 } 77 } 78 } 79 } 80 81 //stream 82 if stream { 83 var ( 84 cStdin io.ReadCloser 85 cStdout, cStderr io.Writer 86 ) 87 88 if stdin { 89 r, w := io.Pipe() 90 go func() { 91 defer w.Close() 92 defer log.Debugf("Closing buffered stdin pipe") 93 io.Copy(w, job.Stdin) 94 }() 95 cStdin = r 96 } 97 if stdout { 98 cStdout = job.Stdout 99 } 100 if stderr { 101 cStderr = job.Stderr 102 } 103 104 <-daemon.attach(&container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, cStdin, cStdout, cStderr) 105 // If we are in stdinonce mode, wait for the process to end 106 // otherwise, simply return 107 if container.Config.StdinOnce && !container.Config.Tty { 108 container.WaitStop(-1 * time.Second) 109 } 110 } 111 return engine.StatusOK 112 } 113 114 func (daemon *Daemon) attach(streamConfig *StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { 115 var ( 116 cStdout, cStderr io.ReadCloser 117 cStdin io.WriteCloser 118 wg sync.WaitGroup 119 errors = make(chan error, 3) 120 ) 121 122 if stdin != nil && openStdin { 123 cStdin = streamConfig.StdinPipe() 124 wg.Add(1) 125 } 126 127 if stdout != nil { 128 cStdout = streamConfig.StdoutPipe() 129 wg.Add(1) 130 } 131 132 if stderr != nil { 133 cStderr = streamConfig.StderrPipe() 134 wg.Add(1) 135 } 136 137 // Connect stdin of container to the http conn. 138 go func() { 139 if stdin == nil || !openStdin { 140 return 141 } 142 log.Debugf("attach: stdin: begin") 143 defer func() { 144 if stdinOnce && !tty { 145 cStdin.Close() 146 } else { 147 // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr 148 if cStdout != nil { 149 cStdout.Close() 150 } 151 if cStderr != nil { 152 cStderr.Close() 153 } 154 } 155 wg.Done() 156 log.Debugf("attach: stdin: end") 157 }() 158 159 var err error 160 if tty { 161 _, err = utils.CopyEscapable(cStdin, stdin) 162 } else { 163 _, err = io.Copy(cStdin, stdin) 164 165 } 166 if err == io.ErrClosedPipe { 167 err = nil 168 } 169 if err != nil { 170 log.Errorf("attach: stdin: %s", err) 171 errors <- err 172 return 173 } 174 }() 175 176 attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) { 177 if stream == nil { 178 return 179 } 180 defer func() { 181 // Make sure stdin gets closed 182 if stdinOnce && cStdin != nil { 183 stdin.Close() 184 cStdin.Close() 185 } 186 streamPipe.Close() 187 wg.Done() 188 log.Debugf("attach: %s: end", name) 189 }() 190 191 log.Debugf("attach: %s: begin", name) 192 _, err := io.Copy(stream, streamPipe) 193 if err == io.ErrClosedPipe { 194 err = nil 195 } 196 if err != nil { 197 log.Errorf("attach: %s: %v", name, err) 198 errors <- err 199 } 200 } 201 202 go attachStream("stdout", stdout, cStdout) 203 go attachStream("stderr", stderr, cStderr) 204 205 return promise.Go(func() error { 206 wg.Wait() 207 close(errors) 208 for err := range errors { 209 if err != nil { 210 return err 211 } 212 } 213 return nil 214 }) 215 }