func (d *Daemon) monitorExec(container *container.Container, execConfig *exec.Config, callback execdriver.DriverCallback) error { pipes := execdriver.NewPipes(execConfig.Stdin(), execConfig.Stdout(), execConfig.Stderr(), execConfig.OpenStdin) exitCode, err := d.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 err := execConfig.CloseStreams(); err != nil { logrus.Errorf("%s: %s", container.ID, err) } if execConfig.ProcessConfig.Terminal != nil { if err := execConfig.WaitResize(); err != nil { logrus.Errorf("Error waiting for resize: %v", err) } 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 }
// Exec calls the underlying exec driver to run func (d *Daemon) Exec(c *container.Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) { hooks := execdriver.Hooks{ Start: startCallback, } exitStatus, err := d.execDriver.Exec(c.Command, execConfig.ProcessConfig, pipes, hooks) // On err, make sure we don't leave ExitCode at zero if err != nil && exitStatus == 0 { exitStatus = 128 } execConfig.ExitCode = &exitStatus execConfig.Running = false return exitStatus, err }
func (d *Daemon) containerExec(container *container.Container, ec *exec.Config) 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() } } ec.Close() return nil } // We use a callback here instead of a goroutine and an chan for // synchronization purposes cErr := promise.Go(func() error { return d.monitorExec(container, ec, callback) }) return ec.Wait(cErr) }