summaryrefslogtreecommitdiff
path: root/cmd/podman/system
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/system')
-rw-r--r--cmd/podman/system/dial_stdio.go145
1 files changed, 145 insertions, 0 deletions
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()
+}