github.com/squaremo/docker@v1.3.2-0.20150516120342-42cfc9554972/api/client/exec.go (about) 1 package client 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 8 "github.com/Sirupsen/logrus" 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/pkg/promise" 11 "github.com/docker/docker/runconfig" 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", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true) 19 20 execConfig, err := runconfig.ParseExec(cmd, args) 21 // just in case the ParseExec does not exit 22 if execConfig.Container == "" || err != nil { 23 return StatusError{StatusCode: 1} 24 } 25 26 stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil) 27 if err != nil { 28 return err 29 } 30 31 var response types.ContainerExecCreateResponse 32 if err := json.NewDecoder(stream).Decode(&response); err != nil { 33 return err 34 } 35 36 execID := response.ID 37 38 if execID == "" { 39 fmt.Fprintf(cli.out, "exec ID empty") 40 return nil 41 } 42 43 //Temp struct for execStart so that we don't need to transfer all the execConfig 44 execStartCheck := &types.ExecStartCheck{ 45 Detach: execConfig.Detach, 46 Tty: execConfig.Tty, 47 } 48 49 if !execConfig.Detach { 50 if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { 51 return err 52 } 53 } else { 54 if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execStartCheck, nil)); err != nil { 55 return err 56 } 57 // For now don't print this - wait for when we support exec wait() 58 // fmt.Fprintf(cli.out, "%s\n", execID) 59 return nil 60 } 61 62 // Interactive exec requested. 63 var ( 64 out, stderr io.Writer 65 in io.ReadCloser 66 hijacked = make(chan io.Closer) 67 errCh chan error 68 ) 69 70 // Block the return until the chan gets closed 71 defer func() { 72 logrus.Debugf("End of CmdExec(), Waiting for hijack to finish.") 73 if _, ok := <-hijacked; ok { 74 fmt.Fprintln(cli.err, "Hijack did not finish (chan still open)") 75 } 76 }() 77 78 if execConfig.AttachStdin { 79 in = cli.in 80 } 81 if execConfig.AttachStdout { 82 out = cli.out 83 } 84 if execConfig.AttachStderr { 85 if execConfig.Tty { 86 stderr = cli.out 87 } else { 88 stderr = cli.err 89 } 90 } 91 errCh = promise.Go(func() error { 92 return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig) 93 }) 94 95 // Acknowledge the hijack before starting 96 select { 97 case closer := <-hijacked: 98 // Make sure that hijack gets closed when returning. (result 99 // in closing hijack chan and freeing server's goroutines. 100 if closer != nil { 101 defer closer.Close() 102 } 103 case err := <-errCh: 104 if err != nil { 105 logrus.Debugf("Error hijack: %s", err) 106 return err 107 } 108 } 109 110 if execConfig.Tty && cli.isTerminalIn { 111 if err := cli.monitorTtySize(execID, true); err != nil { 112 fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) 113 } 114 } 115 116 if err := <-errCh; err != nil { 117 logrus.Debugf("Error hijack: %s", err) 118 return err 119 } 120 121 var status int 122 if _, status, err = getExecExitCode(cli, execID); err != nil { 123 return err 124 } 125 126 if status != 0 { 127 return StatusError{StatusCode: status} 128 } 129 130 return nil 131 }