|
@@ -170,150 +170,6 @@ func (container *Container) WriteHostConfig() (err error) {
|
|
|
return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
|
|
|
}
|
|
|
|
|
|
-func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
|
|
- var cStdout, cStderr io.ReadCloser
|
|
|
-
|
|
|
- var 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
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
func populateCommand(c *Container, env []string) error {
|
|
|
var (
|
|
|
en *execdriver.Network
|