cli: move setRawTerminal and restoreTerminal to holdHijackedConnection
| ... | ... |
@@ -71,12 +71,6 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 71 | 71 |
return err |
| 72 | 72 |
} |
| 73 | 73 |
defer resp.Close() |
| 74 |
- if in != nil && c.Config.Tty {
|
|
| 75 |
- if err := cli.setRawTerminal(); err != nil {
|
|
| 76 |
- return err |
|
| 77 |
- } |
|
| 78 |
- defer cli.restoreTerminal(in) |
|
| 79 |
- } |
|
| 80 | 74 |
|
| 81 | 75 |
if c.Config.Tty && cli.isTerminalOut {
|
| 82 | 76 |
height, width := cli.getTtySize() |
| ... | ... |
@@ -92,8 +86,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 92 | 92 |
logrus.Debugf("Error monitoring TTY size: %s", err)
|
| 93 | 93 |
} |
| 94 | 94 |
} |
| 95 |
- |
|
| 96 |
- if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
|
| 95 |
+ if err := cli.holdHijackedConnection(context.Background(), c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
|
| 97 | 96 |
return err |
| 98 | 97 |
} |
| 99 | 98 |
|
| ... | ... |
@@ -89,14 +89,8 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
| 89 | 89 |
return err |
| 90 | 90 |
} |
| 91 | 91 |
defer resp.Close() |
| 92 |
- if in != nil && execConfig.Tty {
|
|
| 93 |
- if err := cli.setRawTerminal(); err != nil {
|
|
| 94 |
- return err |
|
| 95 |
- } |
|
| 96 |
- defer cli.restoreTerminal(in) |
|
| 97 |
- } |
|
| 98 | 92 |
errCh = promise.Go(func() error {
|
| 99 |
- return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp) |
|
| 93 |
+ return cli.holdHijackedConnection(context.Background(), execConfig.Tty, in, out, stderr, resp) |
|
| 100 | 94 |
}) |
| 101 | 95 |
|
| 102 | 96 |
if execConfig.Tty && cli.isTerminalIn {
|
| ... | ... |
@@ -2,23 +2,48 @@ package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"io" |
| 5 |
+ "sync" |
|
| 6 |
+ |
|
| 7 |
+ "golang.org/x/net/context" |
|
| 5 | 8 |
|
| 6 | 9 |
"github.com/Sirupsen/logrus" |
| 7 | 10 |
"github.com/docker/docker/pkg/stdcopy" |
| 8 | 11 |
"github.com/docker/engine-api/types" |
| 9 | 12 |
) |
| 10 | 13 |
|
| 11 |
-func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
|
| 12 |
- var err error |
|
| 14 |
+func (cli *DockerCli) holdHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
|
| 15 |
+ var ( |
|
| 16 |
+ err error |
|
| 17 |
+ restoreOnce sync.Once |
|
| 18 |
+ ) |
|
| 19 |
+ if inputStream != nil && tty {
|
|
| 20 |
+ if err := cli.setRawTerminal(); err != nil {
|
|
| 21 |
+ return err |
|
| 22 |
+ } |
|
| 23 |
+ defer func() {
|
|
| 24 |
+ restoreOnce.Do(func() {
|
|
| 25 |
+ cli.restoreTerminal(inputStream) |
|
| 26 |
+ }) |
|
| 27 |
+ }() |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 13 | 30 |
receiveStdout := make(chan error, 1) |
| 14 | 31 |
if outputStream != nil || errorStream != nil {
|
| 15 | 32 |
go func() {
|
| 16 | 33 |
// When TTY is ON, use regular copy |
| 17 | 34 |
if tty && outputStream != nil {
|
| 18 | 35 |
_, err = io.Copy(outputStream, resp.Reader) |
| 36 |
+ // we should restore the terminal as soon as possible once connection end |
|
| 37 |
+ // so any following print messages will be in normal type. |
|
| 38 |
+ if inputStream != nil {
|
|
| 39 |
+ restoreOnce.Do(func() {
|
|
| 40 |
+ cli.restoreTerminal(inputStream) |
|
| 41 |
+ }) |
|
| 42 |
+ } |
|
| 19 | 43 |
} else {
|
| 20 | 44 |
_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader) |
| 21 | 45 |
} |
| 46 |
+ |
|
| 22 | 47 |
logrus.Debugf("[hijack] End of stdout")
|
| 23 | 48 |
receiveStdout <- err |
| 24 | 49 |
}() |
| ... | ... |
@@ -28,6 +53,13 @@ func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser |
| 28 | 28 |
go func() {
|
| 29 | 29 |
if inputStream != nil {
|
| 30 | 30 |
io.Copy(resp.Conn, inputStream) |
| 31 |
+ // we should restore the terminal as soon as possible once connection end |
|
| 32 |
+ // so any following print messages will be in normal type. |
|
| 33 |
+ if tty {
|
|
| 34 |
+ restoreOnce.Do(func() {
|
|
| 35 |
+ cli.restoreTerminal(inputStream) |
|
| 36 |
+ }) |
|
| 37 |
+ } |
|
| 31 | 38 |
logrus.Debugf("[hijack] End of stdin")
|
| 32 | 39 |
} |
| 33 | 40 |
|
| ... | ... |
@@ -45,11 +77,16 @@ func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser |
| 45 | 45 |
} |
| 46 | 46 |
case <-stdinDone: |
| 47 | 47 |
if outputStream != nil || errorStream != nil {
|
| 48 |
- if err := <-receiveStdout; err != nil {
|
|
| 49 |
- logrus.Debugf("Error receiveStdout: %s", err)
|
|
| 50 |
- return err |
|
| 48 |
+ select {
|
|
| 49 |
+ case err := <-receiveStdout: |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ logrus.Debugf("Error receiveStdout: %s", err)
|
|
| 52 |
+ return err |
|
| 53 |
+ } |
|
| 54 |
+ case <-ctx.Done(): |
|
| 51 | 55 |
} |
| 52 | 56 |
} |
| 57 |
+ case <-ctx.Done(): |
|
| 53 | 58 |
} |
| 54 | 59 |
|
| 55 | 60 |
return nil |
| ... | ... |
@@ -158,6 +158,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 158 | 158 |
var ( |
| 159 | 159 |
waitDisplayID chan struct{}
|
| 160 | 160 |
errCh chan error |
| 161 |
+ cancelFun context.CancelFunc |
|
| 162 |
+ ctx context.Context |
|
| 161 | 163 |
) |
| 162 | 164 |
if !config.AttachStdout && !config.AttachStderr {
|
| 163 | 165 |
// Make this asynchronous to allow the client to write to stdin before having to read the ID |
| ... | ... |
@@ -170,8 +172,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 170 | 170 |
if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
|
| 171 | 171 |
return ErrConflictRestartPolicyAndAutoRemove |
| 172 | 172 |
} |
| 173 |
- |
|
| 174 |
- if config.AttachStdin || config.AttachStdout || config.AttachStderr {
|
|
| 173 |
+ attach := config.AttachStdin || config.AttachStdout || config.AttachStderr |
|
| 174 |
+ if attach {
|
|
| 175 | 175 |
var ( |
| 176 | 176 |
out, stderr io.Writer |
| 177 | 177 |
in io.ReadCloser |
| ... | ... |
@@ -207,14 +209,9 @@ 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 |
+ ctx, cancelFun = context.WithCancel(context.Background()) |
|
| 216 | 211 |
errCh = promise.Go(func() error {
|
| 217 |
- return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp) |
|
| 212 |
+ return cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp) |
|
| 218 | 213 |
}) |
| 219 | 214 |
} |
| 220 | 215 |
|
| ... | ... |
@@ -228,6 +225,14 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 228 | 228 |
|
| 229 | 229 |
//start the container |
| 230 | 230 |
if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil {
|
| 231 |
+ // If we have holdHijackedConnection, we should notify |
|
| 232 |
+ // holdHijackedConnection we are going to exit and wait |
|
| 233 |
+ // to avoid the terminal are not restored. |
|
| 234 |
+ if attach {
|
|
| 235 |
+ cancelFun() |
|
| 236 |
+ <-errCh |
|
| 237 |
+ } |
|
| 238 |
+ |
|
| 231 | 239 |
cmd.ReportError(err.Error(), false) |
| 232 | 240 |
return runStartContainerErr(err) |
| 233 | 241 |
} |
| ... | ... |
@@ -89,6 +89,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 89 | 89 |
} |
| 90 | 90 |
|
| 91 | 91 |
var in io.ReadCloser |
| 92 |
+ |
|
| 92 | 93 |
if options.Stdin {
|
| 93 | 94 |
in = cli.in |
| 94 | 95 |
} |
| ... | ... |
@@ -98,19 +99,15 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 98 | 98 |
return err |
| 99 | 99 |
} |
| 100 | 100 |
defer resp.Close() |
| 101 |
- if in != nil && c.Config.Tty {
|
|
| 102 |
- if err := cli.setRawTerminal(); err != nil {
|
|
| 103 |
- return err |
|
| 104 |
- } |
|
| 105 |
- defer cli.restoreTerminal(in) |
|
| 106 |
- } |
|
| 107 |
- |
|
| 101 |
+ ctx, cancelFun := context.WithCancel(context.Background()) |
|
| 108 | 102 |
cErr := promise.Go(func() error {
|
| 109 |
- return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp) |
|
| 103 |
+ return cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp) |
|
| 110 | 104 |
}) |
| 111 | 105 |
|
| 112 | 106 |
// 3. Start the container. |
| 113 | 107 |
if err := cli.client.ContainerStart(context.Background(), containerID); err != nil {
|
| 108 |
+ cancelFun() |
|
| 109 |
+ <-cErr |
|
| 114 | 110 |
return err |
| 115 | 111 |
} |
| 116 | 112 |
|