Merge pull request #16289 from cpuguy83/11957_fix_stdin_block_after_container_exit

Ensure stdin does not block after container stop
This commit is contained in:
Jess Frazelle 2015-09-23 15:29:48 -07:00
commit c0c941627a
2 changed files with 42 additions and 25 deletions

View File

@ -16,7 +16,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/term"
)
@ -186,8 +185,6 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
started <- rwc
}
var receiveStdout chan error
var oldState *term.State
if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
@ -198,19 +195,15 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
defer term.RestoreTerminal(cli.inFd, oldState)
}
receiveStdout := make(chan error, 1)
if stdout != nil || stderr != nil {
receiveStdout = promise.Go(func() (err error) {
go func() {
defer func() {
if in != nil {
if setRawTerminal && cli.isTerminalIn {
term.RestoreTerminal(cli.inFd, oldState)
}
// For some reason this Close call blocks on darwin..
// As the client exists right after, simply discard the close
// until we find a better solution.
if runtime.GOOS != "darwin" {
in.Close()
}
in.Close()
}
}()
@ -221,11 +214,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
_, err = stdcopy.StdCopy(stdout, stderr, br)
}
logrus.Debugf("[hijack] End of stdout")
return err
})
receiveStdout <- err
}()
}
sendStdin := promise.Go(func() error {
stdinDone := make(chan struct{})
go func() {
if in != nil {
io.Copy(rwc, in)
logrus.Debugf("[hijack] End of stdin")
@ -238,22 +232,24 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
logrus.Debugf("Couldn't send EOF: %s", err)
}
}
// Discard errors due to pipe interruption
return nil
})
close(stdinDone)
}()
if stdout != nil || stderr != nil {
if err := <-receiveStdout; err != nil {
select {
case err := <-receiveStdout:
if err != nil {
logrus.Debugf("Error receiveStdout: %s", err)
return err
}
if cli.isTerminalIn {
return nil
}
case <-stdinDone:
if stdout != nil || stderr != nil {
if err := <-receiveStdout; err != nil {
logrus.Debugf("Error receiveStdout: %s", err)
}
}
}
if !cli.isTerminalIn {
if err := <-sendStdin; err != nil {
logrus.Debugf("Error sendStdin: %s", err)
return err
}
}
return nil
}

View File

@ -3384,3 +3384,24 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
dockerCmd(c, "stop", "first")
dockerCmd(c, "stop", "second")
}
// #11957 - stdin with no tty does not exit if stdin is not closed even though container exited
func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true")
in, err := cmd.StdinPipe()
c.Assert(err, check.IsNil)
defer in.Close()
c.Assert(cmd.Start(), check.IsNil)
waitChan := make(chan error)
go func() {
waitChan <- cmd.Wait()
}()
select {
case err := <-waitChan:
c.Assert(err, check.IsNil)
case <-time.After(3 * time.Second):
c.Fatal("timeout waiting for command to exit")
}
}