github.com/kunnos/engine@v1.13.1/cli/command/container/attach.go (about) 1 package container 2 3 import ( 4 "fmt" 5 "io" 6 "net/http/httputil" 7 8 "golang.org/x/net/context" 9 10 "github.com/sirupsen/logrus" 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/cli" 13 "github.com/docker/docker/cli/command" 14 "github.com/docker/docker/pkg/signal" 15 "github.com/spf13/cobra" 16 ) 17 18 type attachOptions struct { 19 noStdin bool 20 proxy bool 21 detachKeys string 22 23 container string 24 } 25 26 // NewAttachCommand creates a new cobra.Command for `docker attach` 27 func NewAttachCommand(dockerCli *command.DockerCli) *cobra.Command { 28 var opts attachOptions 29 30 cmd := &cobra.Command{ 31 Use: "attach [OPTIONS] CONTAINER", 32 Short: "Attach to a running container", 33 Args: cli.ExactArgs(1), 34 RunE: func(cmd *cobra.Command, args []string) error { 35 opts.container = args[0] 36 return runAttach(dockerCli, &opts) 37 }, 38 } 39 40 flags := cmd.Flags() 41 flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN") 42 flags.BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process") 43 flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container") 44 return cmd 45 } 46 47 func runAttach(dockerCli *command.DockerCli, opts *attachOptions) error { 48 ctx := context.Background() 49 client := dockerCli.Client() 50 51 c, err := client.ContainerInspect(ctx, opts.container) 52 if err != nil { 53 return err 54 } 55 56 if !c.State.Running { 57 return fmt.Errorf("You cannot attach to a stopped container, start it first") 58 } 59 60 if c.State.Paused { 61 return fmt.Errorf("You cannot attach to a paused container, unpause it first") 62 } 63 64 if err := dockerCli.In().CheckTty(!opts.noStdin, c.Config.Tty); err != nil { 65 return err 66 } 67 68 if opts.detachKeys != "" { 69 dockerCli.ConfigFile().DetachKeys = opts.detachKeys 70 } 71 72 options := types.ContainerAttachOptions{ 73 Stream: true, 74 Stdin: !opts.noStdin && c.Config.OpenStdin, 75 Stdout: true, 76 Stderr: true, 77 DetachKeys: dockerCli.ConfigFile().DetachKeys, 78 } 79 80 var in io.ReadCloser 81 if options.Stdin { 82 in = dockerCli.In() 83 } 84 85 if opts.proxy && !c.Config.Tty { 86 sigc := ForwardAllSignals(ctx, dockerCli, opts.container) 87 defer signal.StopCatch(sigc) 88 } 89 90 resp, errAttach := client.ContainerAttach(ctx, opts.container, options) 91 if errAttach != nil && errAttach != httputil.ErrPersistEOF { 92 // ContainerAttach returns an ErrPersistEOF (connection closed) 93 // means server met an error and put it in Hijacked connection 94 // keep the error and read detailed error message from hijacked connection later 95 return errAttach 96 } 97 defer resp.Close() 98 99 if c.Config.Tty && dockerCli.Out().IsTerminal() { 100 height, width := dockerCli.Out().GetTtySize() 101 // To handle the case where a user repeatedly attaches/detaches without resizing their 102 // terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially 103 // resize it, then go back to normal. Without this, every attach after the first will 104 // require the user to manually resize or hit enter. 105 resizeTtyTo(ctx, client, opts.container, height+1, width+1, false) 106 107 // After the above resizing occurs, the call to MonitorTtySize below will handle resetting back 108 // to the actual size. 109 if err := MonitorTtySize(ctx, dockerCli, opts.container, false); err != nil { 110 logrus.Debugf("Error monitoring TTY size: %s", err) 111 } 112 } 113 if err := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil { 114 return err 115 } 116 117 if errAttach != nil { 118 return errAttach 119 } 120 121 _, status, err := getExitCode(ctx, dockerCli, opts.container) 122 if err != nil { 123 return err 124 } 125 if status != 0 { 126 return cli.StatusError{StatusCode: status} 127 } 128 129 return nil 130 }