github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/api/client/container/exec.go (about) 1 package container 2 3 import ( 4 "fmt" 5 "io" 6 7 "golang.org/x/net/context" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/api/client" 11 "github.com/docker/docker/cli" 12 "github.com/docker/docker/pkg/promise" 13 "github.com/docker/engine-api/types" 14 "github.com/spf13/cobra" 15 ) 16 17 type execOptions struct { 18 detachKeys string 19 interactive bool 20 tty bool 21 detach bool 22 user string 23 privileged bool 24 } 25 26 // NewExecCommand creats a new cobra.Command for `docker exec` 27 func NewExecCommand(dockerCli *client.DockerCli) *cobra.Command { 28 var opts execOptions 29 30 cmd := &cobra.Command{ 31 Use: "exec CONTAINER COMMAND [ARG...]", 32 Short: "Run a command in a running container", 33 Args: cli.RequiresMinArgs(2), 34 RunE: func(cmd *cobra.Command, args []string) error { 35 container := args[0] 36 execCmd := args[1:] 37 return runExec(dockerCli, &opts, container, execCmd) 38 }, 39 } 40 41 flags := cmd.Flags() 42 flags.SetInterspersed(false) 43 44 flags.StringVarP(&opts.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container") 45 flags.BoolVarP(&opts.interactive, "interactive", "i", false, "Keep STDIN open even if not attached") 46 flags.BoolVarP(&opts.tty, "tty", "t", false, "Allocate a pseudo-TTY") 47 flags.BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background") 48 flags.StringVarP(&opts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])") 49 flags.BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the command") 50 51 return cmd 52 } 53 54 func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, execCmd []string) error { 55 execConfig, err := parseExec(opts, container, execCmd) 56 // just in case the ParseExec does not exit 57 if container == "" || err != nil { 58 return cli.StatusError{StatusCode: 1} 59 } 60 61 if opts.detachKeys != "" { 62 dockerCli.ConfigFile().DetachKeys = opts.detachKeys 63 } 64 65 // Send client escape keys 66 execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys 67 68 ctx := context.Background() 69 70 response, err := dockerCli.Client().ContainerExecCreate(ctx, container, *execConfig) 71 if err != nil { 72 return err 73 } 74 75 execID := response.ID 76 if execID == "" { 77 fmt.Fprintf(dockerCli.Out(), "exec ID empty") 78 return nil 79 } 80 81 //Temp struct for execStart so that we don't need to transfer all the execConfig 82 if !execConfig.Detach { 83 if err := dockerCli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { 84 return err 85 } 86 } else { 87 execStartCheck := types.ExecStartCheck{ 88 Detach: execConfig.Detach, 89 Tty: execConfig.Tty, 90 } 91 92 if err := dockerCli.Client().ContainerExecStart(ctx, execID, execStartCheck); err != nil { 93 return err 94 } 95 // For now don't print this - wait for when we support exec wait() 96 // fmt.Fprintf(dockerCli.Out(), "%s\n", execID) 97 return nil 98 } 99 100 // Interactive exec requested. 101 var ( 102 out, stderr io.Writer 103 in io.ReadCloser 104 errCh chan error 105 ) 106 107 if execConfig.AttachStdin { 108 in = dockerCli.In() 109 } 110 if execConfig.AttachStdout { 111 out = dockerCli.Out() 112 } 113 if execConfig.AttachStderr { 114 if execConfig.Tty { 115 stderr = dockerCli.Out() 116 } else { 117 stderr = dockerCli.Err() 118 } 119 } 120 121 resp, err := dockerCli.Client().ContainerExecAttach(ctx, execID, *execConfig) 122 if err != nil { 123 return err 124 } 125 defer resp.Close() 126 errCh = promise.Go(func() error { 127 return dockerCli.HoldHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp) 128 }) 129 130 if execConfig.Tty && dockerCli.IsTerminalIn() { 131 if err := dockerCli.MonitorTtySize(ctx, execID, true); err != nil { 132 fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) 133 } 134 } 135 136 if err := <-errCh; err != nil { 137 logrus.Debugf("Error hijack: %s", err) 138 return err 139 } 140 141 var status int 142 if _, status, err = dockerCli.GetExecExitCode(ctx, execID); err != nil { 143 return err 144 } 145 146 if status != 0 { 147 return cli.StatusError{StatusCode: status} 148 } 149 150 return nil 151 } 152 153 // parseExec parses the specified args for the specified command and generates 154 // an ExecConfig from it. 155 func parseExec(opts *execOptions, container string, execCmd []string) (*types.ExecConfig, error) { 156 execConfig := &types.ExecConfig{ 157 User: opts.user, 158 Privileged: opts.privileged, 159 Tty: opts.tty, 160 Cmd: execCmd, 161 Detach: opts.detach, 162 // container is not used here 163 } 164 165 // If -d is not set, attach to everything by default 166 if !opts.detach { 167 execConfig.AttachStdout = true 168 execConfig.AttachStderr = true 169 if opts.interactive { 170 execConfig.AttachStdin = true 171 } 172 } 173 174 return execConfig, nil 175 }