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