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