github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/api/client/exec.go (about)

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