mirror of https://github.com/docker/cli.git
108 lines
2.5 KiB
Go
108 lines
2.5 KiB
Go
|
package system
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"io"
|
||
|
"os"
|
||
|
|
||
|
"github.com/docker/cli/cli"
|
||
|
"github.com/docker/cli/cli/command"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
"github.com/spf13/cobra"
|
||
|
)
|
||
|
|
||
|
// newDialStdioCommand creates a new cobra.Command for `docker system dial-stdio`
|
||
|
func newDialStdioCommand(dockerCli command.Cli) *cobra.Command {
|
||
|
cmd := &cobra.Command{
|
||
|
Use: "dial-stdio",
|
||
|
Short: "Proxy the stdio stream to the daemon connection. Should not be invoked manually.",
|
||
|
Args: cli.NoArgs,
|
||
|
Hidden: true,
|
||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||
|
return runDialStdio(dockerCli)
|
||
|
},
|
||
|
}
|
||
|
return cmd
|
||
|
}
|
||
|
|
||
|
func runDialStdio(dockerCli command.Cli) error {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
dialer := dockerCli.Client().Dialer()
|
||
|
conn, err := dialer(ctx)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "failed to open the raw stream connection")
|
||
|
}
|
||
|
connHalfCloser, ok := conn.(halfCloser)
|
||
|
if !ok {
|
||
|
return errors.New("the raw stream connection does not implement halfCloser")
|
||
|
}
|
||
|
stdin2conn := make(chan error)
|
||
|
conn2stdout := make(chan error)
|
||
|
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 without waiting for stdin to be closed.
|
||
|
// (stdin is never closed when tty)
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
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 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()
|
||
|
}
|