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  }