Fixes #19506
This fixes the issue of errors on create and the tty not being able to
be restored to its previous state because of a race where it was
in the hijack goroutine.
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
| ... | ... |
@@ -75,6 +75,12 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 75 | 75 |
return err |
| 76 | 76 |
} |
| 77 | 77 |
defer resp.Close() |
| 78 |
+ if in != nil && c.Config.Tty {
|
|
| 79 |
+ if err := cli.setRawTerminal(); err != nil {
|
|
| 80 |
+ return err |
|
| 81 |
+ } |
|
| 82 |
+ defer cli.restoreTerminal(in) |
|
| 83 |
+ } |
|
| 78 | 84 |
|
| 79 | 85 |
if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
| 80 | 86 |
return err |
| ... | ... |
@@ -44,6 +44,8 @@ type DockerCli struct {
|
| 44 | 44 |
isTerminalOut bool |
| 45 | 45 |
// client is the http client that performs all API operations |
| 46 | 46 |
client client.APIClient |
| 47 |
+ // state holds the terminal state |
|
| 48 |
+ state *term.State |
|
| 47 | 49 |
} |
| 48 | 50 |
|
| 49 | 51 |
// Initialize calls the init function that will setup the configuration for the client |
| ... | ... |
@@ -79,6 +81,27 @@ func (cli *DockerCli) ImagesFormat() string {
|
| 79 | 79 |
return cli.configFile.ImagesFormat |
| 80 | 80 |
} |
| 81 | 81 |
|
| 82 |
+func (cli *DockerCli) setRawTerminal() error {
|
|
| 83 |
+ if cli.isTerminalIn && os.Getenv("NORAW") == "" {
|
|
| 84 |
+ state, err := term.SetRawTerminal(cli.inFd) |
|
| 85 |
+ if err != nil {
|
|
| 86 |
+ return err |
|
| 87 |
+ } |
|
| 88 |
+ cli.state = state |
|
| 89 |
+ } |
|
| 90 |
+ return nil |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+func (cli *DockerCli) restoreTerminal(in io.Closer) error {
|
|
| 94 |
+ if cli.state != nil {
|
|
| 95 |
+ term.RestoreTerminal(cli.inFd, cli.state) |
|
| 96 |
+ } |
|
| 97 |
+ if in != nil {
|
|
| 98 |
+ return in.Close() |
|
| 99 |
+ } |
|
| 100 |
+ return nil |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 82 | 103 |
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. |
| 83 | 104 |
// The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config |
| 84 | 105 |
// is set the client scheme will be set to https. |
| ... | ... |
@@ -87,6 +87,12 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
| 87 | 87 |
return err |
| 88 | 88 |
} |
| 89 | 89 |
defer resp.Close() |
| 90 |
+ if in != nil && execConfig.Tty {
|
|
| 91 |
+ if err := cli.setRawTerminal(); err != nil {
|
|
| 92 |
+ return err |
|
| 93 |
+ } |
|
| 94 |
+ defer cli.restoreTerminal(in) |
|
| 95 |
+ } |
|
| 90 | 96 |
errCh = promise.Go(func() error {
|
| 91 | 97 |
return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp) |
| 92 | 98 |
}) |
| ... | ... |
@@ -2,41 +2,19 @@ package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"io" |
| 5 |
- "os" |
|
| 6 | 5 |
|
| 7 | 6 |
"github.com/Sirupsen/logrus" |
| 8 | 7 |
"github.com/docker/docker/pkg/stdcopy" |
| 9 |
- "github.com/docker/docker/pkg/term" |
|
| 10 | 8 |
"github.com/docker/engine-api/types" |
| 11 | 9 |
) |
| 12 | 10 |
|
| 13 |
-func (cli *DockerCli) holdHijackedConnection(setRawTerminal bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
|
| 14 |
- var ( |
|
| 15 |
- err error |
|
| 16 |
- oldState *term.State |
|
| 17 |
- ) |
|
| 18 |
- if inputStream != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
|
|
| 19 |
- oldState, err = term.SetRawTerminal(cli.inFd) |
|
| 20 |
- if err != nil {
|
|
| 21 |
- return err |
|
| 22 |
- } |
|
| 23 |
- defer term.RestoreTerminal(cli.inFd, oldState) |
|
| 24 |
- } |
|
| 25 |
- |
|
| 11 |
+func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
|
| 12 |
+ var err error |
|
| 26 | 13 |
receiveStdout := make(chan error, 1) |
| 27 | 14 |
if outputStream != nil || errorStream != nil {
|
| 28 | 15 |
go func() {
|
| 29 |
- defer func() {
|
|
| 30 |
- if inputStream != nil {
|
|
| 31 |
- if setRawTerminal && cli.isTerminalIn {
|
|
| 32 |
- term.RestoreTerminal(cli.inFd, oldState) |
|
| 33 |
- } |
|
| 34 |
- inputStream.Close() |
|
| 35 |
- } |
|
| 36 |
- }() |
|
| 37 |
- |
|
| 38 | 16 |
// When TTY is ON, use regular copy |
| 39 |
- if setRawTerminal && outputStream != nil {
|
|
| 17 |
+ if tty && outputStream != nil {
|
|
| 40 | 18 |
_, err = io.Copy(outputStream, resp.Reader) |
| 41 | 19 |
} else {
|
| 42 | 20 |
_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader) |
| ... | ... |
@@ -207,6 +207,12 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 207 | 207 |
if err != nil {
|
| 208 | 208 |
return err |
| 209 | 209 |
} |
| 210 |
+ if in != nil && config.Tty {
|
|
| 211 |
+ if err := cli.setRawTerminal(); err != nil {
|
|
| 212 |
+ return err |
|
| 213 |
+ } |
|
| 214 |
+ defer cli.restoreTerminal(in) |
|
| 215 |
+ } |
|
| 210 | 216 |
errCh = promise.Go(func() error {
|
| 211 | 217 |
return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp) |
| 212 | 218 |
}) |
| ... | ... |
@@ -96,6 +96,12 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 96 | 96 |
return err |
| 97 | 97 |
} |
| 98 | 98 |
defer resp.Close() |
| 99 |
+ if in != nil && c.Config.Tty {
|
|
| 100 |
+ if err := cli.setRawTerminal(); err != nil {
|
|
| 101 |
+ return err |
|
| 102 |
+ } |
|
| 103 |
+ defer cli.restoreTerminal(in) |
|
| 104 |
+ } |
|
| 99 | 105 |
|
| 100 | 106 |
cErr := promise.Go(func() error {
|
| 101 | 107 |
return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp) |