Ensure stdin does not block after container stop
| ... | ... |
@@ -16,7 +16,6 @@ import ( |
| 16 | 16 |
"github.com/Sirupsen/logrus" |
| 17 | 17 |
"github.com/docker/docker/api" |
| 18 | 18 |
"github.com/docker/docker/autogen/dockerversion" |
| 19 |
- "github.com/docker/docker/pkg/promise" |
|
| 20 | 19 |
"github.com/docker/docker/pkg/stdcopy" |
| 21 | 20 |
"github.com/docker/docker/pkg/term" |
| 22 | 21 |
) |
| ... | ... |
@@ -186,8 +185,6 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea |
| 186 | 186 |
started <- rwc |
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 |
- var receiveStdout chan error |
|
| 190 |
- |
|
| 191 | 189 |
var oldState *term.State |
| 192 | 190 |
|
| 193 | 191 |
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 |
| 198 | 198 |
defer term.RestoreTerminal(cli.inFd, oldState) |
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 |
+ receiveStdout := make(chan error, 1) |
|
| 201 | 202 |
if stdout != nil || stderr != nil {
|
| 202 |
- receiveStdout = promise.Go(func() (err error) {
|
|
| 203 |
+ go func() {
|
|
| 203 | 204 |
defer func() {
|
| 204 | 205 |
if in != nil {
|
| 205 | 206 |
if setRawTerminal && cli.isTerminalIn {
|
| 206 | 207 |
term.RestoreTerminal(cli.inFd, oldState) |
| 207 | 208 |
} |
| 208 |
- // For some reason this Close call blocks on darwin.. |
|
| 209 |
- // As the client exists right after, simply discard the close |
|
| 210 |
- // until we find a better solution. |
|
| 211 |
- if runtime.GOOS != "darwin" {
|
|
| 212 |
- in.Close() |
|
| 213 |
- } |
|
| 209 |
+ in.Close() |
|
| 214 | 210 |
} |
| 215 | 211 |
}() |
| 216 | 212 |
|
| ... | ... |
@@ -221,11 +214,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea |
| 221 | 221 |
_, err = stdcopy.StdCopy(stdout, stderr, br) |
| 222 | 222 |
} |
| 223 | 223 |
logrus.Debugf("[hijack] End of stdout")
|
| 224 |
- return err |
|
| 225 |
- }) |
|
| 224 |
+ receiveStdout <- err |
|
| 225 |
+ }() |
|
| 226 | 226 |
} |
| 227 | 227 |
|
| 228 |
- sendStdin := promise.Go(func() error {
|
|
| 228 |
+ stdinDone := make(chan struct{})
|
|
| 229 |
+ go func() {
|
|
| 229 | 230 |
if in != nil {
|
| 230 | 231 |
io.Copy(rwc, in) |
| 231 | 232 |
logrus.Debugf("[hijack] End of stdin")
|
| ... | ... |
@@ -238,22 +232,24 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea |
| 238 | 238 |
logrus.Debugf("Couldn't send EOF: %s", err)
|
| 239 | 239 |
} |
| 240 | 240 |
} |
| 241 |
- // Discard errors due to pipe interruption |
|
| 242 |
- return nil |
|
| 243 |
- }) |
|
| 241 |
+ close(stdinDone) |
|
| 242 |
+ }() |
|
| 244 | 243 |
|
| 245 |
- if stdout != nil || stderr != nil {
|
|
| 246 |
- if err := <-receiveStdout; err != nil {
|
|
| 244 |
+ select {
|
|
| 245 |
+ case err := <-receiveStdout: |
|
| 246 |
+ if err != nil {
|
|
| 247 | 247 |
logrus.Debugf("Error receiveStdout: %s", err)
|
| 248 |
- return err |
|
| 249 | 248 |
} |
| 250 |
- } |
|
| 251 |
- |
|
| 252 |
- if !cli.isTerminalIn {
|
|
| 253 |
- if err := <-sendStdin; err != nil {
|
|
| 254 |
- logrus.Debugf("Error sendStdin: %s", err)
|
|
| 255 |
- return err |
|
| 249 |
+ if cli.isTerminalIn {
|
|
| 250 |
+ return nil |
|
| 251 |
+ } |
|
| 252 |
+ case <-stdinDone: |
|
| 253 |
+ if stdout != nil || stderr != nil {
|
|
| 254 |
+ if err := <-receiveStdout; err != nil {
|
|
| 255 |
+ logrus.Debugf("Error receiveStdout: %s", err)
|
|
| 256 |
+ } |
|
| 256 | 257 |
} |
| 257 | 258 |
} |
| 259 |
+ |
|
| 258 | 260 |
return nil |
| 259 | 261 |
} |
| ... | ... |
@@ -3384,3 +3384,24 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
|
| 3384 | 3384 |
dockerCmd(c, "stop", "first") |
| 3385 | 3385 |
dockerCmd(c, "stop", "second") |
| 3386 | 3386 |
} |
| 3387 |
+ |
|
| 3388 |
+// #11957 - stdin with no tty does not exit if stdin is not closed even though container exited |
|
| 3389 |
+func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
|
|
| 3390 |
+ cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true") |
|
| 3391 |
+ in, err := cmd.StdinPipe() |
|
| 3392 |
+ c.Assert(err, check.IsNil) |
|
| 3393 |
+ defer in.Close() |
|
| 3394 |
+ c.Assert(cmd.Start(), check.IsNil) |
|
| 3395 |
+ |
|
| 3396 |
+ waitChan := make(chan error) |
|
| 3397 |
+ go func() {
|
|
| 3398 |
+ waitChan <- cmd.Wait() |
|
| 3399 |
+ }() |
|
| 3400 |
+ |
|
| 3401 |
+ select {
|
|
| 3402 |
+ case err := <-waitChan: |
|
| 3403 |
+ c.Assert(err, check.IsNil) |
|
| 3404 |
+ case <-time.After(3 * time.Second): |
|
| 3405 |
+ c.Fatal("timeout waiting for command to exit")
|
|
| 3406 |
+ } |
|
| 3407 |
+} |