From 6f9e9ee9ece7396a2b769c1e240eaa893cfce2ae Mon Sep 17 00:00:00 2001
From: Jake Parks <jamesparks10@gmail.com>
Date: Thu, 30 Sep 2021 19:41:25 +0000
Subject: Fixes #11668

Adding dial-stdio CLI cmd

Signed-off-by: Jake Parks <jamesparks10@gmail.com>

Made dial-stdio URI configurable

Slight refactors

Signed-off-by: Jake Parks <jamesparks10@gmail.com>

Added simple test for existence of `podman system dial-stdio` command

Fix 'system dial-stdio' integration tests

Changed link in comment to permalink
---
 cmd/podman/system/dial_stdio.go | 145 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)
 create mode 100644 cmd/podman/system/dial_stdio.go

(limited to 'cmd')

diff --git a/cmd/podman/system/dial_stdio.go b/cmd/podman/system/dial_stdio.go
new file mode 100644
index 000000000..eae89f38e
--- /dev/null
+++ b/cmd/podman/system/dial_stdio.go
@@ -0,0 +1,145 @@
+package system
+
+import (
+	"context"
+	"io"
+	"os"
+
+	"github.com/containers/podman/v3/cmd/podman/registry"
+	"github.com/containers/podman/v3/cmd/podman/validate"
+	"github.com/containers/podman/v3/pkg/bindings"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+)
+
+var (
+	dialStdioCommand = &cobra.Command{
+		Use:    "dial-stdio",
+		Short:  "Proxy the stdio stream to the daemon connection. Should not be invoked manually.",
+		Args:   validate.NoArgs,
+		Hidden: true,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runDialStdio()
+		},
+		Example: "podman system dial-stdio",
+	}
+)
+
+func init() {
+	registry.Commands = append(registry.Commands, registry.CliCommand{
+		Command: dialStdioCommand,
+		Parent:  systemCmd,
+	})
+}
+
+func runDialStdio() error {
+	ctx := registry.Context()
+	cfg := registry.PodmanConfig()
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+	bindCtx, err := bindings.NewConnection(ctx, cfg.URI)
+	if err != nil {
+		return errors.Wrap(err, "failed to open connection to podman")
+	}
+	conn, err := bindings.GetClient(bindCtx)
+	if err != nil {
+		return errors.Wrap(err, "failed to get connection after initialization")
+	}
+	netConn, err := conn.GetDialer(bindCtx)
+	if err != nil {
+		return errors.Wrap(err, "failed to open the raw stream connection")
+	}
+	defer netConn.Close()
+
+	var connHalfCloser halfCloser
+	switch t := netConn.(type) {
+	case halfCloser:
+		connHalfCloser = t
+	case halfReadWriteCloser:
+		connHalfCloser = &nopCloseReader{t}
+	default:
+		return errors.New("the raw stream connection does not implement halfCloser")
+	}
+
+	stdin2conn := make(chan error, 1)
+	conn2stdout := make(chan error, 1)
+	go func() {
+		stdin2conn <- copier(connHalfCloser, &halfReadCloserWrapper{os.Stdin}, "stdin to stream")
+	}()
+	go func() {
+		conn2stdout <- copier(&halfWriteCloserWrapper{os.Stdout}, connHalfCloser, "stream to stdout")
+	}()
+	select {
+	case err = <-stdin2conn:
+		if err != nil {
+			return err
+		}
+		// wait for stdout
+		err = <-conn2stdout
+	case err = <-conn2stdout:
+		// return immediately
+	}
+	return err
+}
+
+// Below portion taken from original docker CLI
+// https://github.com/docker/cli/blob/v20.10.9/cli/command/system/dial_stdio.go
+func copier(to halfWriteCloser, from halfReadCloser, debugDescription string) error {
+	defer func() {
+		if err := from.CloseRead(); err != nil {
+			logrus.Errorf("error while CloseRead (%s): %v", debugDescription, err)
+		}
+		if err := to.CloseWrite(); err != nil {
+			logrus.Errorf("error while CloseWrite (%s): %v", debugDescription, err)
+		}
+	}()
+	if _, err := io.Copy(to, from); err != nil {
+		return errors.Wrapf(err, "error while Copy (%s)", debugDescription)
+	}
+	return nil
+}
+
+type halfReadCloser interface {
+	io.Reader
+	CloseRead() error
+}
+
+type halfWriteCloser interface {
+	io.Writer
+	CloseWrite() error
+}
+
+type halfCloser interface {
+	halfReadCloser
+	halfWriteCloser
+}
+
+type halfReadWriteCloser interface {
+	io.Reader
+	halfWriteCloser
+}
+
+type nopCloseReader struct {
+	halfReadWriteCloser
+}
+
+func (x *nopCloseReader) CloseRead() error {
+	return nil
+}
+
+type halfReadCloserWrapper struct {
+	io.ReadCloser
+}
+
+func (x *halfReadCloserWrapper) CloseRead() error {
+	return x.Close()
+}
+
+type halfWriteCloserWrapper struct {
+	io.WriteCloser
+}
+
+func (x *halfWriteCloserWrapper) CloseWrite() error {
+	return x.Close()
+}
-- 
cgit v1.2.3-54-g00ecf