diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index 7bc28f10f5..ad13e1c1eb 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -87,8 +87,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba return execdriver.ExitStatus{ExitCode: -1}, err } - var term execdriver.Terminal - p := &libcontainer.Process{ Args: append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...), Env: c.ProcessConfig.Env, @@ -96,36 +94,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba User: c.ProcessConfig.User, } - if c.ProcessConfig.Tty { - rootuid, err := container.HostUID() - if err != nil { - return execdriver.ExitStatus{ExitCode: -1}, err - } - cons, err := p.NewConsole(rootuid) - if err != nil { - return execdriver.ExitStatus{ExitCode: -1}, err - } - term, err = NewTtyConsole(cons, pipes, rootuid) - } else { - p.Stdout = pipes.Stdout - p.Stderr = pipes.Stderr - r, w, err := os.Pipe() - if err != nil { - return execdriver.ExitStatus{ExitCode: -1}, err - } - if pipes.Stdin != nil { - go func() { - io.Copy(w, pipes.Stdin) - w.Close() - }() - p.Stdin = r - } - term = &execdriver.StdConsole{} - } - if err != nil { + if err := setupPipes(container, &c.ProcessConfig, p, pipes); err != nil { return execdriver.ExitStatus{ExitCode: -1}, err } - c.ProcessConfig.Terminal = term cont, err := d.factory.Create(c.ID, container) if err != nil { @@ -398,3 +369,40 @@ func (t *TtyConsole) AttachPipes(pipes *execdriver.Pipes) error { func (t *TtyConsole) Close() error { return t.console.Close() } + +func setupPipes(container *configs.Config, processConfig *execdriver.ProcessConfig, p *libcontainer.Process, pipes *execdriver.Pipes) error { + var term execdriver.Terminal + var err error + + if processConfig.Tty { + rootuid, err := container.HostUID() + if err != nil { + return err + } + cons, err := p.NewConsole(rootuid) + if err != nil { + return err + } + term, err = NewTtyConsole(cons, pipes, rootuid) + } else { + p.Stdout = pipes.Stdout + p.Stderr = pipes.Stderr + r, w, err := os.Pipe() + if err != nil { + return err + } + if pipes.Stdin != nil { + go func() { + io.Copy(w, pipes.Stdin) + w.Close() + }() + p.Stdin = r + } + term = &execdriver.StdConsole{} + } + if err != nil { + return err + } + processConfig.Terminal = term + return nil +} diff --git a/daemon/execdriver/native/exec.go b/daemon/execdriver/native/exec.go index 04239fdac7..dd41c0ad1d 100644 --- a/daemon/execdriver/native/exec.go +++ b/daemon/execdriver/native/exec.go @@ -20,9 +20,6 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo return -1, fmt.Errorf("No active container exists with ID %s", c.ID) } - var term execdriver.Terminal - var err error - p := &libcontainer.Process{ Args: append([]string{processConfig.Entrypoint}, processConfig.Arguments...), Env: c.ProcessConfig.Env, @@ -34,29 +31,11 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo p.Capabilities = execdriver.GetAllCapabilities() } - if processConfig.Tty { - config := active.Config() - rootuid, err := config.HostUID() - if err != nil { - return -1, err - } - cons, err := p.NewConsole(rootuid) - if err != nil { - return -1, err - } - term, err = NewTtyConsole(cons, pipes, rootuid) - } else { - p.Stdout = pipes.Stdout - p.Stderr = pipes.Stderr - p.Stdin = pipes.Stdin - term = &execdriver.StdConsole{} - } - if err != nil { + config := active.Config() + if err := setupPipes(&config, processConfig, p, pipes); err != nil { return -1, err } - processConfig.Terminal = term - if err := active.Start(p); err != nil { return -1, err } diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go index 0716c486b5..c6910d4321 100644 --- a/integration-cli/docker_cli_exec_test.go +++ b/integration-cli/docker_cli_exec_test.go @@ -38,44 +38,6 @@ func (s *DockerSuite) TestExec(c *check.C) { } -func (s *DockerSuite) TestExecInteractiveStdinClose(c *check.C) { - out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat")) - if err != nil { - c.Fatal(err) - } - - contId := strings.TrimSpace(out) - - returnchan := make(chan struct{}) - - go func() { - var err error - cmd := exec.Command(dockerBinary, "exec", "-i", contId, "/bin/ls", "/") - cmd.Stdin = os.Stdin - if err != nil { - c.Fatal(err) - } - - out, err := cmd.CombinedOutput() - if err != nil { - c.Fatal(err, string(out)) - } - - if string(out) == "" { - c.Fatalf("Output was empty, likely blocked by standard input") - } - - returnchan <- struct{}{} - }() - - select { - case <-returnchan: - case <-time.After(10 * time.Second): - c.Fatal("timed out running docker exec") - } - -} - func (s *DockerSuite) TestExecInteractive(c *check.C) { runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") diff --git a/integration-cli/docker_cli_exec_unix_test.go b/integration-cli/docker_cli_exec_unix_test.go new file mode 100644 index 0000000000..bee44b9902 --- /dev/null +++ b/integration-cli/docker_cli_exec_unix_test.go @@ -0,0 +1,47 @@ +// +build !windows,!test_no_exec + +package main + +import ( + "bytes" + "io" + "os/exec" + "strings" + "time" + + "github.com/go-check/check" + "github.com/kr/pty" +) + +// regression test for #12546 +func (s *DockerSuite) TestExecInteractiveStdinClose(c *check.C) { + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat")) + if err != nil { + c.Fatal(err) + } + contId := strings.TrimSpace(out) + + cmd := exec.Command(dockerBinary, "exec", "-i", contId, "echo", "-n", "hello") + p, err := pty.Start(cmd) + if err != nil { + c.Fatal(err) + } + + b := bytes.NewBuffer(nil) + go io.Copy(b, p) + + ch := make(chan error) + go func() { ch <- cmd.Wait() }() + + select { + case err := <-ch: + if err != nil { + c.Errorf("cmd finished with error %v", err) + } + if output := b.String(); strings.TrimSpace(output) != "hello" { + c.Fatalf("Unexpected output %s", output) + } + case <-time.After(1 * time.Second): + c.Fatal("timed out running docker exec") + } +}