Browse Source

Move tty set and restore to caller

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>
Michael Crosby 9 years ago
parent
commit
a1eb6029cc
6 changed files with 50 additions and 25 deletions
  1. 6 0
      api/client/attach.go
  2. 23 0
      api/client/cli.go
  3. 6 0
      api/client/exec.go
  4. 3 25
      api/client/hijack.go
  5. 6 0
      api/client/run.go
  6. 6 0
      api/client/start.go

+ 6 - 0
api/client/attach.go

@@ -75,6 +75,12 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		return err
 		return err
 	}
 	}
 	defer resp.Close()
 	defer resp.Close()
+	if in != nil && c.Config.Tty {
+		if err := cli.setRawTerminal(); err != nil {
+			return err
+		}
+		defer cli.restoreTerminal(in)
+	}
 
 
 	if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
 	if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
 		return err
 		return err

+ 23 - 0
api/client/cli.go

@@ -44,6 +44,8 @@ type DockerCli struct {
 	isTerminalOut bool
 	isTerminalOut bool
 	// client is the http client that performs all API operations
 	// client is the http client that performs all API operations
 	client client.APIClient
 	client client.APIClient
+	// state holds the terminal state
+	state *term.State
 }
 }
 
 
 // Initialize calls the init function that will setup the configuration for the client
 // Initialize calls the init function that will setup the configuration for the client
@@ -79,6 +81,27 @@ func (cli *DockerCli) ImagesFormat() string {
 	return cli.configFile.ImagesFormat
 	return cli.configFile.ImagesFormat
 }
 }
 
 
+func (cli *DockerCli) setRawTerminal() error {
+	if cli.isTerminalIn && os.Getenv("NORAW") == "" {
+		state, err := term.SetRawTerminal(cli.inFd)
+		if err != nil {
+			return err
+		}
+		cli.state = state
+	}
+	return nil
+}
+
+func (cli *DockerCli) restoreTerminal(in io.Closer) error {
+	if cli.state != nil {
+		term.RestoreTerminal(cli.inFd, cli.state)
+	}
+	if in != nil {
+		return in.Close()
+	}
+	return nil
+}
+
 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
 // is set the client scheme will be set to https.
 // is set the client scheme will be set to https.

+ 6 - 0
api/client/exec.go

@@ -87,6 +87,12 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 		return err
 		return err
 	}
 	}
 	defer resp.Close()
 	defer resp.Close()
+	if in != nil && execConfig.Tty {
+		if err := cli.setRawTerminal(); err != nil {
+			return err
+		}
+		defer cli.restoreTerminal(in)
+	}
 	errCh = promise.Go(func() error {
 	errCh = promise.Go(func() error {
 		return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp)
 		return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp)
 	})
 	})

+ 3 - 25
api/client/hijack.go

@@ -2,41 +2,19 @@ package client
 
 
 import (
 import (
 	"io"
 	"io"
-	"os"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/docker/docker/pkg/stdcopy"
-	"github.com/docker/docker/pkg/term"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
 )
 )
 
 
-func (cli *DockerCli) holdHijackedConnection(setRawTerminal bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
-	var (
-		err      error
-		oldState *term.State
-	)
-	if inputStream != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
-		oldState, err = term.SetRawTerminal(cli.inFd)
-		if err != nil {
-			return err
-		}
-		defer term.RestoreTerminal(cli.inFd, oldState)
-	}
-
+func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
+	var err error
 	receiveStdout := make(chan error, 1)
 	receiveStdout := make(chan error, 1)
 	if outputStream != nil || errorStream != nil {
 	if outputStream != nil || errorStream != nil {
 		go func() {
 		go func() {
-			defer func() {
-				if inputStream != nil {
-					if setRawTerminal && cli.isTerminalIn {
-						term.RestoreTerminal(cli.inFd, oldState)
-					}
-					inputStream.Close()
-				}
-			}()
-
 			// When TTY is ON, use regular copy
 			// When TTY is ON, use regular copy
-			if setRawTerminal && outputStream != nil {
+			if tty && outputStream != nil {
 				_, err = io.Copy(outputStream, resp.Reader)
 				_, err = io.Copy(outputStream, resp.Reader)
 			} else {
 			} else {
 				_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)
 				_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)

+ 6 - 0
api/client/run.go

@@ -207,6 +207,12 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
+		if in != nil && config.Tty {
+			if err := cli.setRawTerminal(); err != nil {
+				return err
+			}
+			defer cli.restoreTerminal(in)
+		}
 		errCh = promise.Go(func() error {
 		errCh = promise.Go(func() error {
 			return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp)
 			return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp)
 		})
 		})

+ 6 - 0
api/client/start.go

@@ -96,6 +96,12 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 			return err
 			return err
 		}
 		}
 		defer resp.Close()
 		defer resp.Close()
+		if in != nil && c.Config.Tty {
+			if err := cli.setRawTerminal(); err != nil {
+				return err
+			}
+			defer cli.restoreTerminal(in)
+		}
 
 
 		cErr := promise.Go(func() error {
 		cErr := promise.Go(func() error {
 			return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)
 			return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)