2014-05-05 23:48:56 +00:00
|
|
|
package daemon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
|
2014-07-24 22:19:50 +00:00
|
|
|
"github.com/docker/docker/utils"
|
2014-05-05 23:48:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
|
|
|
var (
|
|
|
|
cStdout, cStderr io.ReadCloser
|
|
|
|
nJobs int
|
|
|
|
errors = make(chan error, 3)
|
|
|
|
)
|
|
|
|
|
|
|
|
if stdin != nil && container.Config.OpenStdin {
|
|
|
|
nJobs += 1
|
|
|
|
if cStdin, err := container.StdinPipe(); err != nil {
|
|
|
|
errors <- err
|
|
|
|
} else {
|
|
|
|
go func() {
|
|
|
|
utils.Debugf("attach: stdin: begin")
|
|
|
|
defer utils.Debugf("attach: stdin: end")
|
|
|
|
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
|
|
|
|
if container.Config.StdinOnce && !container.Config.Tty {
|
|
|
|
defer cStdin.Close()
|
|
|
|
} else {
|
|
|
|
defer func() {
|
|
|
|
if cStdout != nil {
|
|
|
|
cStdout.Close()
|
|
|
|
}
|
|
|
|
if cStderr != nil {
|
|
|
|
cStderr.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
if container.Config.Tty {
|
|
|
|
_, err = utils.CopyEscapable(cStdin, stdin)
|
|
|
|
} else {
|
|
|
|
_, err = io.Copy(cStdin, stdin)
|
|
|
|
}
|
|
|
|
if err == io.ErrClosedPipe {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
utils.Errorf("attach: stdin: %s", err)
|
|
|
|
}
|
|
|
|
errors <- err
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if stdout != nil {
|
|
|
|
nJobs += 1
|
|
|
|
if p, err := container.StdoutPipe(); err != nil {
|
|
|
|
errors <- err
|
|
|
|
} else {
|
|
|
|
cStdout = p
|
|
|
|
go func() {
|
|
|
|
utils.Debugf("attach: stdout: begin")
|
|
|
|
defer utils.Debugf("attach: stdout: end")
|
|
|
|
// If we are in StdinOnce mode, then close stdin
|
|
|
|
if container.Config.StdinOnce && stdin != nil {
|
|
|
|
defer stdin.Close()
|
|
|
|
}
|
|
|
|
if stdinCloser != nil {
|
|
|
|
defer stdinCloser.Close()
|
|
|
|
}
|
|
|
|
_, err := io.Copy(stdout, cStdout)
|
|
|
|
if err == io.ErrClosedPipe {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
utils.Errorf("attach: stdout: %s", err)
|
|
|
|
}
|
|
|
|
errors <- err
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
go func() {
|
|
|
|
if stdinCloser != nil {
|
|
|
|
defer stdinCloser.Close()
|
|
|
|
}
|
|
|
|
if cStdout, err := container.StdoutPipe(); err != nil {
|
|
|
|
utils.Errorf("attach: stdout pipe: %s", err)
|
|
|
|
} else {
|
|
|
|
io.Copy(&utils.NopWriter{}, cStdout)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
if stderr != nil {
|
|
|
|
nJobs += 1
|
|
|
|
if p, err := container.StderrPipe(); err != nil {
|
|
|
|
errors <- err
|
|
|
|
} else {
|
|
|
|
cStderr = p
|
|
|
|
go func() {
|
|
|
|
utils.Debugf("attach: stderr: begin")
|
|
|
|
defer utils.Debugf("attach: stderr: end")
|
|
|
|
// If we are in StdinOnce mode, then close stdin
|
|
|
|
if container.Config.StdinOnce && stdin != nil {
|
|
|
|
defer stdin.Close()
|
|
|
|
}
|
|
|
|
if stdinCloser != nil {
|
|
|
|
defer stdinCloser.Close()
|
|
|
|
}
|
|
|
|
_, err := io.Copy(stderr, cStderr)
|
|
|
|
if err == io.ErrClosedPipe {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
utils.Errorf("attach: stderr: %s", err)
|
|
|
|
}
|
|
|
|
errors <- err
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
go func() {
|
|
|
|
if stdinCloser != nil {
|
|
|
|
defer stdinCloser.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if cStderr, err := container.StderrPipe(); err != nil {
|
|
|
|
utils.Errorf("attach: stdout pipe: %s", err)
|
|
|
|
} else {
|
|
|
|
io.Copy(&utils.NopWriter{}, cStderr)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
return utils.Go(func() error {
|
|
|
|
defer func() {
|
|
|
|
if cStdout != nil {
|
|
|
|
cStdout.Close()
|
|
|
|
}
|
|
|
|
if cStderr != nil {
|
|
|
|
cStderr.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// FIXME: how to clean up the stdin goroutine without the unwanted side effect
|
|
|
|
// of closing the passed stdin? Add an intermediary io.Pipe?
|
|
|
|
for i := 0; i < nJobs; i += 1 {
|
|
|
|
utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
|
|
|
|
if err := <-errors; err != nil {
|
|
|
|
utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
utils.Debugf("attach: job %d completed successfully", i+1)
|
|
|
|
}
|
|
|
|
utils.Debugf("attach: all jobs completed successfully")
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|