Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/command/container/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o

var in io.ReadCloser
if options.Stdin {
in = dockerCLI.In()
in = stdinForAttach(dockerCLI.In())
}

if opts.Proxy && !c.Config.Tty {
Expand Down
16 changes: 16 additions & 0 deletions cli/command/container/background.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package container

import (
"io"

"github.com/docker/cli/cli/streams"
)

// stdinForAttach returns stdin for hijacking, or nil when copying it would
// spin (a background job whose stdin is still the controlling TTY).
func stdinForAttach(in *streams.In) io.ReadCloser {
if stdinFromBackgroundJob(in) {
return nil
}
return in
}
22 changes: 22 additions & 0 deletions cli/command/container/background_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package container

import (
"os"
"testing"

"github.com/docker/cli/cli/streams"
"gotest.tools/v3/assert"
)

func TestStdinForAttachNonTTY(t *testing.T) {
r, w, err := os.Pipe()
assert.NilError(t, err)
t.Cleanup(func() {
_ = r.Close()
_ = w.Close()
})

in := streams.NewIn(r)
got := stdinForAttach(in)
assert.Equal(t, got, in)
}
25 changes: 25 additions & 0 deletions cli/command/container/background_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build !windows

package container

import (
"golang.org/x/sys/unix"

"github.com/docker/cli/cli/streams"
)

// stdinFromBackgroundJob reports whether stdin is a TTY whose foreground
// process group is not us. Reading that TTY from a background job (for
// example `docker run -i ... &`) produces SIGTTIN; the CLI catches every
// signal for forwarding, so the default stop-in-background does not fire
// and the read retries in a tight loop.
func stdinFromBackgroundJob(in *streams.In) bool {
if in == nil || !in.IsTerminal() {
return false
}
fg, err := unix.IoctlGetInt(int(in.FD()), unix.TIOCGPGRP)
if err != nil {
return false
}
return unix.Getpgrp() != fg
}
7 changes: 7 additions & 0 deletions cli/command/container/background_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package container

import "github.com/docker/cli/cli/streams"

func stdinFromBackgroundJob(_ *streams.In) bool {
return false
}
2 changes: 1 addition & 1 deletion cli/command/container/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl
)

if execOptions.AttachStdin {
in = dockerCli.In()
in = stdinForAttach(dockerCli.In())
}
if execOptions.AttachStdout {
out = dockerCli.Out()
Expand Down
2 changes: 1 addition & 1 deletion cli/command/container/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func attachContainer(ctx context.Context, dockerCli command.Cli, containerID str
in io.ReadCloser
)
if options.Stdin {
in = dockerCli.In()
in = stdinForAttach(dockerCli.In())
}
if options.Stdout {
out = dockerCli.Out()
Expand Down
2 changes: 1 addition & 1 deletion cli/command/container/signals.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func ForwardAllSignals(ctx context.Context, apiClient client.ContainerAPIClient,
break
}
}
if sig == "" {
if sig == "" || sig == "TTIN" || sig == "TTOU" {
continue
}

Expand Down
2 changes: 1 addition & 1 deletion cli/command/container/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
var in io.ReadCloser

if options.Stdin {
in = dockerCli.In()
in = stdinForAttach(dockerCli.In())
}

resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.Container.ID, options)
Expand Down