diff --git a/daemon/container.go b/daemon/container.go index f6884cdec0..e9c14c964e 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -420,70 +420,6 @@ func (container *Container) getExecIDs() []string { return container.execCommands.List() } -func (container *Container) exec(ec *ExecConfig) error { - container.Lock() - defer container.Unlock() - - callback := func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error { - if processConfig.Tty { - // The callback is called after the process Start() - // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave - // which we close here. - if c, ok := processConfig.Stdout.(io.Closer); ok { - c.Close() - } - } - close(ec.waitStart) - return nil - } - - // We use a callback here instead of a goroutine and an chan for - // synchronization purposes - cErr := promise.Go(func() error { return container.monitorExec(ec, callback) }) - - // Exec should not return until the process is actually running - select { - case <-ec.waitStart: - case err := <-cErr: - return err - } - - return nil -} - -func (container *Container) monitorExec(ExecConfig *ExecConfig, callback execdriver.DriverCallback) error { - var ( - err error - exitCode int - ) - pipes := execdriver.NewPipes(ExecConfig.streamConfig.stdin, ExecConfig.streamConfig.stdout, ExecConfig.streamConfig.stderr, ExecConfig.OpenStdin) - exitCode, err = container.daemon.Exec(container, ExecConfig, pipes, callback) - if err != nil { - logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) - } - logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) - if ExecConfig.OpenStdin { - if err := ExecConfig.streamConfig.stdin.Close(); err != nil { - logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) - } - } - if err := ExecConfig.streamConfig.stdout.Clean(); err != nil { - logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) - } - if err := ExecConfig.streamConfig.stderr.Clean(); err != nil { - logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) - } - if ExecConfig.ProcessConfig.Terminal != nil { - if err := ExecConfig.ProcessConfig.Terminal.Close(); err != nil { - logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) - } - } - // remove the exec command from the container's store only and not the - // daemon's store so that the exec command can be inspected. - container.execCommands.Delete(ExecConfig.ID) - return err -} - // Attach connects to the container's TTY, delegating to standard // streams or websockets depending on the configuration. func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { diff --git a/daemon/exec.go b/daemon/exec.go index 2e343bf8f0..c1ef8f58d9 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/pkg/broadcaster" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/pools" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringutils" "github.com/docker/docker/runconfig" @@ -251,7 +252,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. // the exitStatus) even after the cmd is done running. go func() { - execErr <- container.exec(ec) + execErr <- d.containerExec(container, ec) }() select { @@ -329,3 +330,67 @@ func (d *Daemon) containerExecIds() map[string]struct{} { } return ids } + +func (daemon *Daemon) containerExec(container *Container, ec *ExecConfig) error { + container.Lock() + defer container.Unlock() + + callback := func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error { + if processConfig.Tty { + // The callback is called after the process Start() + // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave + // which we close here. + if c, ok := processConfig.Stdout.(io.Closer); ok { + c.Close() + } + } + close(ec.waitStart) + return nil + } + + // We use a callback here instead of a goroutine and an chan for + // synchronization purposes + cErr := promise.Go(func() error { return daemon.monitorExec(container, ec, callback) }) + + // Exec should not return until the process is actually running + select { + case <-ec.waitStart: + case err := <-cErr: + return err + } + + return nil +} + +func (daemon *Daemon) monitorExec(container *Container, ExecConfig *ExecConfig, callback execdriver.DriverCallback) error { + var ( + err error + exitCode int + ) + pipes := execdriver.NewPipes(ExecConfig.streamConfig.stdin, ExecConfig.streamConfig.stdout, ExecConfig.streamConfig.stderr, ExecConfig.OpenStdin) + exitCode, err = daemon.Exec(container, ExecConfig, pipes, callback) + if err != nil { + logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) + } + logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) + if ExecConfig.OpenStdin { + if err := ExecConfig.streamConfig.stdin.Close(); err != nil { + logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) + } + } + if err := ExecConfig.streamConfig.stdout.Clean(); err != nil { + logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) + } + if err := ExecConfig.streamConfig.stderr.Clean(); err != nil { + logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) + } + if ExecConfig.ProcessConfig.Terminal != nil { + if err := ExecConfig.ProcessConfig.Terminal.Close(); err != nil { + logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) + } + } + // remove the exec command from the container's store only and not the + // daemon's store so that the exec command can be inspected. + container.execCommands.Delete(ExecConfig.ID) + return err +}