github.com/kunnos/engine@v1.13.1/cli/command/container/hijack.go (about) 1 package container 2 3 import ( 4 "io" 5 "runtime" 6 "sync" 7 8 "github.com/sirupsen/logrus" 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/cli/command" 11 "github.com/docker/docker/pkg/stdcopy" 12 "golang.org/x/net/context" 13 ) 14 15 // holdHijackedConnection handles copying input to and output from streams to the 16 // connection 17 func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error { 18 var ( 19 err error 20 restoreOnce sync.Once 21 ) 22 if inputStream != nil && tty { 23 if err := setRawTerminal(streams); err != nil { 24 return err 25 } 26 defer func() { 27 restoreOnce.Do(func() { 28 restoreTerminal(streams, inputStream) 29 }) 30 }() 31 } 32 33 receiveStdout := make(chan error, 1) 34 if outputStream != nil || errorStream != nil { 35 go func() { 36 // When TTY is ON, use regular copy 37 if tty && outputStream != nil { 38 _, err = io.Copy(outputStream, resp.Reader) 39 // we should restore the terminal as soon as possible once connection end 40 // so any following print messages will be in normal type. 41 if inputStream != nil { 42 restoreOnce.Do(func() { 43 restoreTerminal(streams, inputStream) 44 }) 45 } 46 } else { 47 _, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader) 48 } 49 50 logrus.Debug("[hijack] End of stdout") 51 receiveStdout <- err 52 }() 53 } 54 55 stdinDone := make(chan struct{}) 56 go func() { 57 if inputStream != nil { 58 io.Copy(resp.Conn, inputStream) 59 // we should restore the terminal as soon as possible once connection end 60 // so any following print messages will be in normal type. 61 if tty { 62 restoreOnce.Do(func() { 63 restoreTerminal(streams, inputStream) 64 }) 65 } 66 logrus.Debug("[hijack] End of stdin") 67 } 68 69 if err := resp.CloseWrite(); err != nil { 70 logrus.Debugf("Couldn't send EOF: %s", err) 71 } 72 close(stdinDone) 73 }() 74 75 select { 76 case err := <-receiveStdout: 77 if err != nil { 78 logrus.Debugf("Error receiveStdout: %s", err) 79 return err 80 } 81 case <-stdinDone: 82 if outputStream != nil || errorStream != nil { 83 select { 84 case err := <-receiveStdout: 85 if err != nil { 86 logrus.Debugf("Error receiveStdout: %s", err) 87 return err 88 } 89 case <-ctx.Done(): 90 } 91 } 92 case <-ctx.Done(): 93 } 94 95 return nil 96 } 97 98 func setRawTerminal(streams command.Streams) error { 99 if err := streams.In().SetRawTerminal(); err != nil { 100 return err 101 } 102 return streams.Out().SetRawTerminal() 103 } 104 105 func restoreTerminal(streams command.Streams, in io.Closer) error { 106 streams.In().RestoreTerminal() 107 streams.Out().RestoreTerminal() 108 // WARNING: DO NOT REMOVE THE OS CHECK !!! 109 // For some reason this Close call blocks on darwin.. 110 // As the client exists right after, simply discard the close 111 // until we find a better solution. 112 if in != nil && runtime.GOOS != "darwin" { 113 return in.Close() 114 } 115 return nil 116 }