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