github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/command/system/dial_stdio.go (about)

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