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>
This commit is contained in:
Michael Crosby 2016-01-20 13:18:36 -08:00 committed by Tibor Vass
parent ac382473d9
commit 3b5b116d43
6 changed files with 50 additions and 25 deletions

View File

@ -75,6 +75,12 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
return err
}
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 {
return err

View File

@ -44,6 +44,8 @@ type DockerCli struct {
isTerminalOut bool
// client is the http client that performs all API operations
client client.APIClient
// state holds the terminal state
state *term.State
}
// 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
}
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.
// 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.

View File

@ -87,6 +87,12 @@ func (cli *DockerCli) CmdExec(args ...string) error {
return err
}
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 {
return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp)
})

View File

@ -2,41 +2,19 @@ package client
import (
"io"
"os"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/term"
"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)
if outputStream != nil || errorStream != nil {
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
if setRawTerminal && outputStream != nil {
if tty && outputStream != nil {
_, err = io.Copy(outputStream, resp.Reader)
} else {
_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)

View File

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

View File

@ -96,6 +96,12 @@ func (cli *DockerCli) CmdStart(args ...string) error {
return err
}
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 {
return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)