github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/api/client/exec.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/Sirupsen/logrus" 8 Cli "github.com/docker/docker/cli" 9 flag "github.com/docker/docker/pkg/mflag" 10 "github.com/docker/docker/pkg/promise" 11 "github.com/docker/engine-api/types" 12 ) 13 14 // CmdExec runs a command in a running container. 15 // 16 // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] 17 func (cli *DockerCli) CmdExec(args ...string) error { 18 cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, Cli.DockerCommands["exec"].Description, true) 19 detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container") 20 21 execConfig, err := ParseExec(cmd, args) 22 // just in case the ParseExec does not exit 23 if execConfig.Container == "" || err != nil { 24 return Cli.StatusError{StatusCode: 1} 25 } 26 27 if *detachKeys != "" { 28 cli.configFile.DetachKeys = *detachKeys 29 } 30 31 // Send client escape keys 32 execConfig.DetachKeys = cli.configFile.DetachKeys 33 34 response, err := cli.client.ContainerExecCreate(*execConfig) 35 if err != nil { 36 return err 37 } 38 39 execID := response.ID 40 if execID == "" { 41 fmt.Fprintf(cli.out, "exec ID empty") 42 return nil 43 } 44 45 //Temp struct for execStart so that we don't need to transfer all the execConfig 46 if !execConfig.Detach { 47 if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { 48 return err 49 } 50 } else { 51 execStartCheck := types.ExecStartCheck{ 52 Detach: execConfig.Detach, 53 Tty: execConfig.Tty, 54 } 55 56 if err := cli.client.ContainerExecStart(execID, execStartCheck); err != nil { 57 return err 58 } 59 // For now don't print this - wait for when we support exec wait() 60 // fmt.Fprintf(cli.out, "%s\n", execID) 61 return nil 62 } 63 64 // Interactive exec requested. 65 var ( 66 out, stderr io.Writer 67 in io.ReadCloser 68 errCh chan error 69 ) 70 71 if execConfig.AttachStdin { 72 in = cli.in 73 } 74 if execConfig.AttachStdout { 75 out = cli.out 76 } 77 if execConfig.AttachStderr { 78 if execConfig.Tty { 79 stderr = cli.out 80 } else { 81 stderr = cli.err 82 } 83 } 84 85 resp, err := cli.client.ContainerExecAttach(execID, *execConfig) 86 if err != nil { 87 return err 88 } 89 defer resp.Close() 90 errCh = promise.Go(func() error { 91 return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp) 92 }) 93 94 if execConfig.Tty && cli.isTerminalIn { 95 if err := cli.monitorTtySize(execID, true); err != nil { 96 fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) 97 } 98 } 99 100 if err := <-errCh; err != nil { 101 logrus.Debugf("Error hijack: %s", err) 102 return err 103 } 104 105 var status int 106 if _, status, err = getExecExitCode(cli, execID); err != nil { 107 return err 108 } 109 110 if status != 0 { 111 return Cli.StatusError{StatusCode: status} 112 } 113 114 return nil 115 } 116 117 // ParseExec parses the specified args for the specified command and generates 118 // an ExecConfig from it. 119 // If the minimal number of specified args is not right or if specified args are 120 // not valid, it will return an error. 121 func ParseExec(cmd *flag.FlagSet, args []string) (*types.ExecConfig, error) { 122 var ( 123 flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached") 124 flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY") 125 flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background") 126 flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])") 127 flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to the command") 128 execCmd []string 129 container string 130 ) 131 cmd.Require(flag.Min, 2) 132 if err := cmd.ParseFlags(args, true); err != nil { 133 return nil, err 134 } 135 container = cmd.Arg(0) 136 parsedArgs := cmd.Args() 137 execCmd = parsedArgs[1:] 138 139 execConfig := &types.ExecConfig{ 140 User: *flUser, 141 Privileged: *flPrivileged, 142 Tty: *flTty, 143 Cmd: execCmd, 144 Container: container, 145 Detach: *flDetach, 146 } 147 148 // If -d is not set, attach to everything by default 149 if !*flDetach { 150 execConfig.AttachStdout = true 151 execConfig.AttachStderr = true 152 if *flStdin { 153 execConfig.AttachStdin = true 154 } 155 } 156 157 return execConfig, nil 158 }