@@ -653,7 +653,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
653653
654654 if * openStdin || * attach {
655655 if tty && cli .isTerminal {
656- if err := cli .monitorTtySize (cmd .Arg (0 )); err != nil {
656+ if err := cli .monitorTtySize (cmd .Arg (0 ), false ); err != nil {
657657 log .Errorf ("Error monitoring TTY size: %s" , err )
658658 }
659659 }
@@ -1805,7 +1805,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
18051805 )
18061806
18071807 if tty && cli .isTerminal {
1808- if err := cli .monitorTtySize (cmd .Arg (0 )); err != nil {
1808+ if err := cli .monitorTtySize (cmd .Arg (0 ), false ); err != nil {
18091809 log .Debugf ("Error monitoring TTY size: %s" , err )
18101810 }
18111811 }
@@ -2136,7 +2136,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
21362136 }
21372137
21382138 if (config .AttachStdin || config .AttachStdout || config .AttachStderr ) && config .Tty && cli .isTerminal {
2139- if err := cli .monitorTtySize (runResult .Get ("Id" )); err != nil {
2139+ if err := cli .monitorTtySize (runResult .Get ("Id" ), false ); err != nil {
21402140 log .Errorf ("Error monitoring TTY size: %s" , err )
21412141 }
21422142 }
@@ -2299,3 +2299,101 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
22992299 }
23002300 return nil
23012301}
2302+
2303+ func (cli * DockerCli ) CmdExec (args ... string ) error {
2304+ cmd := cli .Subcmd ("exec" , "CONTAINER COMMAND [ARG...]" , "Run a command in an existing container" )
2305+
2306+ execConfig , err := runconfig .ParseExec (cmd , args )
2307+ if err != nil {
2308+ return err
2309+ }
2310+ if execConfig .Container == "" {
2311+ cmd .Usage ()
2312+ return nil
2313+ }
2314+
2315+ stream , _ , err := cli .call ("POST" , "/containers/" + execConfig .Container + "/exec" , execConfig , false )
2316+ if err != nil {
2317+ return err
2318+ }
2319+
2320+ var execResult engine.Env
2321+ if err := execResult .Decode (stream ); err != nil {
2322+ return err
2323+ }
2324+
2325+ execID := execResult .Get ("Id" )
2326+
2327+ if execID == "" {
2328+ fmt .Fprintf (cli .out , "exec ID empty" )
2329+ return nil
2330+ }
2331+
2332+ if execConfig .Detach {
2333+ if _ , _ , err := readBody (cli .call ("POST" , "/exec/" + execID + "/start" , execConfig , false )); err != nil {
2334+ return err
2335+ }
2336+ return nil
2337+ }
2338+
2339+ // Interactive exec requested.
2340+ var (
2341+ out , stderr io.Writer
2342+ in io.ReadCloser
2343+ hijacked = make (chan io.Closer )
2344+ errCh chan error
2345+ )
2346+
2347+ // Block the return until the chan gets closed
2348+ defer func () {
2349+ log .Debugf ("End of CmdExec(), Waiting for hijack to finish." )
2350+ if _ , ok := <- hijacked ; ok {
2351+ log .Errorf ("Hijack did not finish (chan still open)" )
2352+ }
2353+ }()
2354+
2355+ if execConfig .AttachStdin {
2356+ in = cli .in
2357+ }
2358+ if execConfig .AttachStdout {
2359+ out = cli .out
2360+ }
2361+ if execConfig .AttachStderr {
2362+ if execConfig .Tty {
2363+ stderr = cli .out
2364+ } else {
2365+ stderr = cli .err
2366+ }
2367+ }
2368+ errCh = utils .Go (func () error {
2369+ return cli .hijack ("POST" , "/exec/" + execID + "/start" , execConfig .Tty , in , out , stderr , hijacked , execConfig )
2370+ })
2371+
2372+ // Acknowledge the hijack before starting
2373+ select {
2374+ case closer := <- hijacked :
2375+ // Make sure that hijack gets closed when returning. (result
2376+ // in closing hijack chan and freeing server's goroutines.
2377+ if closer != nil {
2378+ defer closer .Close ()
2379+ }
2380+ case err := <- errCh :
2381+ if err != nil {
2382+ log .Debugf ("Error hijack: %s" , err )
2383+ return err
2384+ }
2385+ }
2386+
2387+ if execConfig .Tty && cli .isTerminal {
2388+ if err := cli .monitorTtySize (execID , true ); err != nil {
2389+ log .Errorf ("Error monitoring TTY size: %s" , err )
2390+ }
2391+ }
2392+
2393+ if err := <- errCh ; err != nil {
2394+ log .Debugf ("Error hijack: %s" , err )
2395+ return err
2396+ }
2397+
2398+ return nil
2399+ }
0 commit comments