1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
package system
import (
"context"
"fmt"
"io"
"os"
"errors"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/bindings"
"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 fmt.Errorf("failed to open connection to podman: %w", err)
}
conn, err := bindings.GetClient(bindCtx)
if err != nil {
return fmt.Errorf("failed to get connection after initialization: %w", err)
}
netConn, err := conn.GetDialer(bindCtx)
if err != nil {
return fmt.Errorf("failed to open the raw stream connection: %w", err)
}
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("while CloseRead (%s): %v", debugDescription, err)
}
if err := to.CloseWrite(); err != nil {
logrus.Errorf("while CloseWrite (%s): %v", debugDescription, err)
}
}()
if _, err := io.Copy(to, from); err != nil {
return fmt.Errorf("while Copy (%s): %w", debugDescription, err)
}
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()
}
|