123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- package container
- import (
- "io"
- "runtime"
- "sync"
- "github.com/Sirupsen/logrus"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/cli/command"
- "github.com/docker/docker/pkg/stdcopy"
- "golang.org/x/net/context"
- )
- // holdHijackedConnection handles copying input to and output from streams to the
- // connection
- func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
- var (
- err error
- restoreOnce sync.Once
- )
- if inputStream != nil && tty {
- if err := setRawTerminal(streams); err != nil {
- return err
- }
- defer func() {
- restoreOnce.Do(func() {
- restoreTerminal(streams, inputStream)
- })
- }()
- }
- receiveStdout := make(chan error, 1)
- if outputStream != nil || errorStream != nil {
- go func() {
- // When TTY is ON, use regular copy
- if tty && outputStream != nil {
- _, err = io.Copy(outputStream, resp.Reader)
- // we should restore the terminal as soon as possible once connection end
- // so any following print messages will be in normal type.
- if inputStream != nil {
- restoreOnce.Do(func() {
- restoreTerminal(streams, inputStream)
- })
- }
- } else {
- _, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)
- }
- logrus.Debug("[hijack] End of stdout")
- receiveStdout <- err
- }()
- }
- stdinDone := make(chan struct{})
- go func() {
- if inputStream != nil {
- io.Copy(resp.Conn, inputStream)
- // we should restore the terminal as soon as possible once connection end
- // so any following print messages will be in normal type.
- if tty {
- restoreOnce.Do(func() {
- restoreTerminal(streams, inputStream)
- })
- }
- logrus.Debug("[hijack] End of stdin")
- }
- if err := resp.CloseWrite(); err != nil {
- logrus.Debugf("Couldn't send EOF: %s", err)
- }
- close(stdinDone)
- }()
- select {
- case err := <-receiveStdout:
- if err != nil {
- logrus.Debugf("Error receiveStdout: %s", err)
- return err
- }
- case <-stdinDone:
- if outputStream != nil || errorStream != nil {
- select {
- case err := <-receiveStdout:
- if err != nil {
- logrus.Debugf("Error receiveStdout: %s", err)
- return err
- }
- case <-ctx.Done():
- }
- }
- case <-ctx.Done():
- }
- return nil
- }
- func setRawTerminal(streams command.Streams) error {
- if err := streams.In().SetRawTerminal(); err != nil {
- return err
- }
- return streams.Out().SetRawTerminal()
- }
- func restoreTerminal(streams command.Streams, in io.Closer) error {
- streams.In().RestoreTerminal()
- streams.Out().RestoreTerminal()
- // WARNING: DO NOT REMOVE THE OS CHECK !!!
- // For some reason this Close call blocks on darwin..
- // As the client exists right after, simply discard the close
- // until we find a better solution.
- if in != nil && runtime.GOOS != "darwin" {
- return in.Close()
- }
- return nil
- }
|