github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/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 if in != nil && execConfig.Tty { 91 if err := cli.setRawTerminal(); err != nil { 92 return err 93 } 94 defer cli.restoreTerminal(in) 95 } 96 errCh = promise.Go(func() error { 97 return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp) 98 }) 99 100 if execConfig.Tty && cli.isTerminalIn { 101 if err := cli.monitorTtySize(execID, true); err != nil { 102 fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) 103 } 104 } 105 106 if err := <-errCh; err != nil { 107 logrus.Debugf("Error hijack: %s", err) 108 return err 109 } 110 111 var status int 112 if _, status, err = getExecExitCode(cli, execID); err != nil { 113 return err 114 } 115 116 if status != 0 { 117 return Cli.StatusError{StatusCode: status} 118 } 119 120 return nil 121 } 122 123 // ParseExec parses the specified args for the specified command and generates 124 // an ExecConfig from it. 125 // If the minimal number of specified args is not right or if specified args are 126 // not valid, it will return an error. 127 func ParseExec(cmd *flag.FlagSet, args []string) (*types.ExecConfig, error) { 128 var ( 129 flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached") 130 flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY") 131 flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background") 132 flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])") 133 flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to the command") 134 execCmd []string 135 container string 136 ) 137 cmd.Require(flag.Min, 2) 138 if err := cmd.ParseFlags(args, true); err != nil { 139 return nil, err 140 } 141 container = cmd.Arg(0) 142 parsedArgs := cmd.Args() 143 execCmd = parsedArgs[1:] 144 145 execConfig := &types.ExecConfig{ 146 User: *flUser, 147 Privileged: *flPrivileged, 148 Tty: *flTty, 149 Cmd: execCmd, 150 Container: container, 151 Detach: *flDetach, 152 } 153 154 // If -d is not set, attach to everything by default 155 if !*flDetach { 156 execConfig.AttachStdout = true 157 execConfig.AttachStderr = true 158 if *flStdin { 159 execConfig.AttachStdin = true 160 } 161 } 162 163 return execConfig, nil 164 }