From d742c57f534352b6cad596d0c9fe8cf84044e92a Mon Sep 17 00:00:00 2001 From: "Vojtech Vitek (V-Teq)" Date: Wed, 10 Sep 2014 15:35:48 +0200 Subject: [PATCH] DockerCli: Check IsTerminal() for both STDIN and STDOUT `docker events > /tmp/out` should not print control characters to non-terminal STDOUT. This addresses commit 26b4a4920adca614f6be17a96f254f331271faf0 without creating regression described in issue #6509. Signed-off-by: Vojtech Vitek (V-Teq) --- api/client/cli.go | 50 ++++++++++++++++++++++++++++-------------- api/client/commands.go | 14 ++++++------ api/client/hijack.go | 12 +++++----- api/client/utils.go | 6 ++--- 4 files changed, 50 insertions(+), 32 deletions(-) diff --git a/api/client/cli.go b/api/client/cli.go index a158fc7d3c..5e7f45b119 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -22,10 +22,16 @@ type DockerCli struct { in io.ReadCloser out io.Writer err io.Writer - isTerminal bool - terminalFd uintptr tlsConfig *tls.Config scheme string + // inFd holds file descriptor of the client's STDIN, if it's a valid file + inFd uintptr + // outFd holds file descriptor of the client's STDOUT, if it's a valid file + outFd uintptr + // isTerminalIn describes if client's STDIN is a TTY + isTerminalIn bool + // isTerminalOut describes if client's STDOUT is a TTY + isTerminalOut bool } var funcMap = template.FuncMap{ @@ -94,9 +100,11 @@ func (cli *DockerCli) LoadConfigFile() (err error) { func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli { var ( - isTerminal = false - terminalFd uintptr - scheme = "http" + inFd uintptr + outFd uintptr + isTerminalIn = false + isTerminalOut = false + scheme = "http" ) if tlsConfig != nil { @@ -105,23 +113,33 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC if in != nil { if file, ok := in.(*os.File); ok { - terminalFd = file.Fd() - isTerminal = term.IsTerminal(terminalFd) + inFd = file.Fd() + isTerminalIn = term.IsTerminal(inFd) + } + } + + if out != nil { + if file, ok := out.(*os.File); ok { + outFd = file.Fd() + isTerminalOut = term.IsTerminal(outFd) } } if err == nil { err = out } + return &DockerCli{ - proto: proto, - addr: addr, - in: in, - out: out, - err: err, - isTerminal: isTerminal, - terminalFd: terminalFd, - tlsConfig: tlsConfig, - scheme: scheme, + proto: proto, + addr: addr, + in: in, + out: out, + err: err, + inFd: inFd, + outFd: outFd, + isTerminalIn: isTerminalIn, + isTerminalOut: isTerminalOut, + tlsConfig: tlsConfig, + scheme: scheme, } } diff --git a/api/client/commands.go b/api/client/commands.go index fa0f69f3d6..6c26d152d6 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -277,14 +277,14 @@ func (cli *DockerCli) CmdLogin(args ...string) error { // the password or email from the config file, so prompt them if username != authconfig.Username { if password == "" { - oldState, _ := term.SaveState(cli.terminalFd) + oldState, _ := term.SaveState(cli.inFd) fmt.Fprintf(cli.out, "Password: ") - term.DisableEcho(cli.terminalFd, oldState) + term.DisableEcho(cli.inFd, oldState) password = readInput(cli.in, cli.out) fmt.Fprint(cli.out, "\n") - term.RestoreTerminal(cli.terminalFd, oldState) + term.RestoreTerminal(cli.inFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } @@ -670,7 +670,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { } if *openStdin || *attach { - if tty && cli.isTerminal { + if tty && cli.isTerminalOut { if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { log.Errorf("Error monitoring TTY size: %s", err) } @@ -1822,7 +1822,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { tty = config.GetBool("Tty") ) - if tty && cli.isTerminal { + if tty && cli.isTerminalOut { if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { log.Debugf("Error monitoring TTY size: %s", err) } @@ -2247,7 +2247,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { return err } - if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal { + if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil { log.Errorf("Error monitoring TTY size: %s", err) } @@ -2496,7 +2496,7 @@ func (cli *DockerCli) CmdExec(args ...string) error { } } - if execConfig.Tty && cli.isTerminal { + if execConfig.Tty && cli.isTerminalIn { if err := cli.monitorTtySize(execID, true); err != nil { log.Errorf("Error monitoring TTY size: %s", err) } diff --git a/api/client/hijack.go b/api/client/hijack.go index 4aba842372..7a934fde15 100644 --- a/api/client/hijack.go +++ b/api/client/hijack.go @@ -69,20 +69,20 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea var oldState *term.State - if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" { - oldState, err = term.SetRawTerminal(cli.terminalFd) + if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" { + oldState, err = term.SetRawTerminal(cli.inFd) if err != nil { return err } - defer term.RestoreTerminal(cli.terminalFd, oldState) + defer term.RestoreTerminal(cli.inFd, oldState) } if stdout != nil || stderr != nil { receiveStdout = utils.Go(func() (err error) { defer func() { if in != nil { - if setRawTerminal && cli.isTerminal { - term.RestoreTerminal(cli.terminalFd, oldState) + 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 @@ -129,7 +129,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea } } - if !cli.isTerminal { + if !cli.isTerminalIn { if err := <-sendStdin; err != nil { log.Debugf("Error sendStdin: %s", err) return err diff --git a/api/client/utils.go b/api/client/utils.go index 19e280614e..0d8310cbba 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -168,7 +168,7 @@ func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in } if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") || api.MatchesContentType(resp.Header.Get("Content-Type"), "application/x-json-stream") { - return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.terminalFd, cli.isTerminal) + return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut) } if stdout != nil || stderr != nil { // When TTY is ON, use regular copy @@ -252,10 +252,10 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error { } func (cli *DockerCli) getTtySize() (int, int) { - if !cli.isTerminal { + if !cli.isTerminalOut { return 0, 0 } - ws, err := term.GetWinsize(cli.terminalFd) + ws, err := term.GetWinsize(cli.outFd) if err != nil { log.Debugf("Error getting size: %s", err) if ws == nil {