Browse Source

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) <vvitek@redhat.com>
Vojtech Vitek (V-Teq) 10 years ago
parent
commit
d742c57f53
4 changed files with 50 additions and 32 deletions
  1. 34 16
      api/client/cli.go
  2. 7 7
      api/client/commands.go
  3. 6 6
      api/client/hijack.go
  4. 3 3
      api/client/utils.go

+ 34 - 16
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,
 	}
 }

+ 7 - 7
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)
 		}

+ 6 - 6
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

+ 3 - 3
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 {