diff --git a/daemon/container.go b/daemon/container.go index ef42295344..0aeb5c1e60 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -1557,3 +1557,72 @@ func (c *Container) LogDriverType() string { } return c.hostConfig.LogConfig.Type } + +func (container *Container) GetExecIDs() []string { + return container.execCommands.List() +} + +func (container *Container) Exec(execConfig *execConfig) error { + container.Lock() + defer container.Unlock() + + waitStart := make(chan struct{}) + + callback := func(processConfig *execdriver.ProcessConfig, pid int) { + 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(waitStart) + } + + // We use a callback here instead of a goroutine and an chan for + // syncronization purposes + cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) }) + + // Exec should not return until the process is actually running + select { + case <-waitStart: + case err := <-cErr: + return err + } + + return nil +} + +func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) 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) + } + } + + return err +} diff --git a/daemon/exec.go b/daemon/exec.go index 9aa102690f..5febf083a6 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -12,7 +12,6 @@ import ( "github.com/docker/docker/daemon/execdriver/lxc" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" ) @@ -245,72 +244,3 @@ func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pi return exitStatus, err } - -func (container *Container) GetExecIDs() []string { - return container.execCommands.List() -} - -func (container *Container) Exec(execConfig *execConfig) error { - container.Lock() - defer container.Unlock() - - waitStart := make(chan struct{}) - - callback := func(processConfig *execdriver.ProcessConfig, pid int) { - 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(waitStart) - } - - // We use a callback here instead of a goroutine and an chan for - // syncronization purposes - cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) }) - - // Exec should not return until the process is actually running - select { - case <-waitStart: - case err := <-cErr: - return err - } - - return nil -} - -func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) 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) - } - } - - return err -}