github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/system/dial_stdio.go (about) 1 package system 2 3 import ( 4 "context" 5 "io" 6 "os" 7 8 "github.com/docker/cli/cli" 9 "github.com/docker/cli/cli/command" 10 "github.com/pkg/errors" 11 "github.com/sirupsen/logrus" 12 "github.com/spf13/cobra" 13 ) 14 15 // newDialStdioCommand creates a new cobra.Command for `docker system dial-stdio` 16 func newDialStdioCommand(dockerCli command.Cli) *cobra.Command { 17 cmd := &cobra.Command{ 18 Use: "dial-stdio", 19 Short: "Proxy the stdio stream to the daemon connection. Should not be invoked manually.", 20 Args: cli.NoArgs, 21 Hidden: true, 22 RunE: func(cmd *cobra.Command, args []string) error { 23 return runDialStdio(dockerCli) 24 }, 25 } 26 return cmd 27 } 28 29 func runDialStdio(dockerCli command.Cli) error { 30 ctx, cancel := context.WithCancel(context.Background()) 31 defer cancel() 32 dialer := dockerCli.Client().Dialer() 33 conn, err := dialer(ctx) 34 if err != nil { 35 return errors.Wrap(err, "failed to open the raw stream connection") 36 } 37 defer conn.Close() 38 39 var connHalfCloser halfCloser 40 switch t := conn.(type) { 41 case halfCloser: 42 connHalfCloser = t 43 case halfReadWriteCloser: 44 connHalfCloser = &nopCloseReader{t} 45 default: 46 return errors.New("the raw stream connection does not implement halfCloser") 47 } 48 49 stdin2conn := make(chan error, 1) 50 conn2stdout := make(chan error, 1) 51 go func() { 52 stdin2conn <- copier(connHalfCloser, &halfReadCloserWrapper{os.Stdin}, "stdin to stream") 53 }() 54 go func() { 55 conn2stdout <- copier(&halfWriteCloserWrapper{os.Stdout}, connHalfCloser, "stream to stdout") 56 }() 57 select { 58 case err = <-stdin2conn: 59 if err != nil { 60 return err 61 } 62 // wait for stdout 63 err = <-conn2stdout 64 case err = <-conn2stdout: 65 // return immediately without waiting for stdin to be closed. 66 // (stdin is never closed when tty) 67 } 68 return err 69 } 70 71 func copier(to halfWriteCloser, from halfReadCloser, debugDescription string) error { 72 defer func() { 73 if err := from.CloseRead(); err != nil { 74 logrus.Errorf("error while CloseRead (%s): %v", debugDescription, err) 75 } 76 if err := to.CloseWrite(); err != nil { 77 logrus.Errorf("error while CloseWrite (%s): %v", debugDescription, err) 78 } 79 }() 80 if _, err := io.Copy(to, from); err != nil { 81 return errors.Wrapf(err, "error while Copy (%s)", debugDescription) 82 } 83 return nil 84 } 85 86 type halfReadCloser interface { 87 io.Reader 88 CloseRead() error 89 } 90 91 type halfWriteCloser interface { 92 io.Writer 93 CloseWrite() error 94 } 95 96 type halfCloser interface { 97 halfReadCloser 98 halfWriteCloser 99 } 100 101 type halfReadWriteCloser interface { 102 io.Reader 103 halfWriteCloser 104 } 105 106 type nopCloseReader struct { 107 halfReadWriteCloser 108 } 109 110 func (x *nopCloseReader) CloseRead() error { 111 return nil 112 } 113 114 type halfReadCloserWrapper struct { 115 io.ReadCloser 116 } 117 118 func (x *halfReadCloserWrapper) CloseRead() error { 119 return x.Close() 120 } 121 122 type halfWriteCloserWrapper struct { 123 io.WriteCloser 124 } 125 126 func (x *halfWriteCloserWrapper) CloseWrite() error { 127 return x.Close() 128 }