github.com/rentongzhang/docker@v1.8.2-rc1/daemon/execdriver/windows/exec.go (about)

     1  // +build windows
     2  
     3  package windows
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/Sirupsen/logrus"
    10  	"github.com/docker/docker/daemon/execdriver"
    11  	"github.com/docker/docker/pkg/stringid"
    12  	"github.com/microsoft/hcsshim"
    13  	"github.com/natefinch/npipe"
    14  )
    15  
    16  func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
    17  
    18  	var (
    19  		inListen, outListen, errListen     *npipe.PipeListener
    20  		term                               execdriver.Terminal
    21  		err                                error
    22  		randomID                           string = stringid.GenerateRandomID()
    23  		serverPipeFormat, clientPipeFormat string
    24  		pid                                uint32
    25  		exitCode                           int32
    26  	)
    27  
    28  	active := d.activeContainers[c.ID]
    29  	if active == nil {
    30  		return -1, fmt.Errorf("Exec - No active container exists with ID %s", c.ID)
    31  	}
    32  
    33  	createProcessParms := hcsshim.CreateProcessParams{
    34  		EmulateConsole:   processConfig.Tty, // Note NOT c.ProcessConfig.Tty
    35  		WorkingDirectory: c.WorkingDir,
    36  	}
    37  
    38  	// Configure the environment for the process // Note NOT c.ProcessConfig.Tty
    39  	createProcessParms.Environment = setupEnvironmentVariables(processConfig.Env)
    40  
    41  	// We use another unique ID here for each exec instance otherwise it
    42  	// may conflict with the pipe name being used by RUN.
    43  
    44  	// We use a different pipe name between real and dummy mode in the HCS
    45  	if dummyMode {
    46  		clientPipeFormat = `\\.\pipe\docker-exec-%[1]s-%[2]s-%[3]s`
    47  		serverPipeFormat = clientPipeFormat
    48  	} else {
    49  		clientPipeFormat = `\\.\pipe\docker-exec-%[2]s-%[3]s`
    50  		serverPipeFormat = `\\.\Containers\%[1]s\Device\NamedPipe\docker-exec-%[2]s-%[3]s`
    51  	}
    52  
    53  	// Connect stdin
    54  	if pipes.Stdin != nil {
    55  		stdInPipe := fmt.Sprintf(serverPipeFormat, c.ID, randomID, "stdin")
    56  		createProcessParms.StdInPipe = fmt.Sprintf(clientPipeFormat, c.ID, randomID, "stdin")
    57  
    58  		// Listen on the named pipe
    59  		inListen, err = npipe.Listen(stdInPipe)
    60  		if err != nil {
    61  			logrus.Errorf("stdin failed to listen on %s %s ", stdInPipe, err)
    62  			return -1, err
    63  		}
    64  		defer inListen.Close()
    65  
    66  		// Launch a goroutine to do the accept. We do this so that we can
    67  		// cause an otherwise blocking goroutine to gracefully close when
    68  		// the caller (us) closes the listener
    69  		go stdinAccept(inListen, stdInPipe, pipes.Stdin)
    70  	}
    71  
    72  	// Connect stdout
    73  	stdOutPipe := fmt.Sprintf(serverPipeFormat, c.ID, randomID, "stdout")
    74  	createProcessParms.StdOutPipe = fmt.Sprintf(clientPipeFormat, c.ID, randomID, "stdout")
    75  
    76  	outListen, err = npipe.Listen(stdOutPipe)
    77  	if err != nil {
    78  		logrus.Errorf("stdout failed to listen on %s %s", stdOutPipe, err)
    79  		return -1, err
    80  	}
    81  	defer outListen.Close()
    82  	go stdouterrAccept(outListen, stdOutPipe, pipes.Stdout)
    83  
    84  	// No stderr on TTY. Note NOT c.ProcessConfig.Tty
    85  	if !processConfig.Tty {
    86  		// Connect stderr
    87  		stdErrPipe := fmt.Sprintf(serverPipeFormat, c.ID, randomID, "stderr")
    88  		createProcessParms.StdErrPipe = fmt.Sprintf(clientPipeFormat, c.ID, randomID, "stderr")
    89  
    90  		errListen, err = npipe.Listen(stdErrPipe)
    91  		if err != nil {
    92  			logrus.Errorf("Stderr failed to listen on %s %s", stdErrPipe, err)
    93  			return -1, err
    94  		}
    95  		defer errListen.Close()
    96  		go stdouterrAccept(errListen, stdErrPipe, pipes.Stderr)
    97  	}
    98  
    99  	// While this should get caught earlier, just in case, validate that we
   100  	// have something to run.
   101  	if processConfig.Entrypoint == "" {
   102  		err = errors.New("No entrypoint specified")
   103  		logrus.Error(err)
   104  		return -1, err
   105  	}
   106  
   107  	// Build the command line of the process
   108  	createProcessParms.CommandLine = processConfig.Entrypoint
   109  	for _, arg := range processConfig.Arguments {
   110  		logrus.Debugln("appending ", arg)
   111  		createProcessParms.CommandLine += " " + arg
   112  	}
   113  	logrus.Debugln("commandLine: ", createProcessParms.CommandLine)
   114  
   115  	// Start the command running in the container.
   116  	pid, err = hcsshim.CreateProcessInComputeSystem(c.ID, createProcessParms)
   117  
   118  	if err != nil {
   119  		logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
   120  		return -1, err
   121  	}
   122  
   123  	// Note NOT c.ProcessConfig.Tty
   124  	if processConfig.Tty {
   125  		term = NewTtyConsole(c.ID, pid)
   126  	} else {
   127  		term = NewStdConsole()
   128  	}
   129  	processConfig.Terminal = term
   130  
   131  	// Invoke the start callback
   132  	if startCallback != nil {
   133  		startCallback(&c.ProcessConfig, int(pid))
   134  	}
   135  
   136  	if exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid); err != nil {
   137  		logrus.Errorf("Failed to WaitForProcessInComputeSystem %s", err)
   138  		return -1, err
   139  	}
   140  
   141  	// TODO Windows - Do something with this exit code
   142  	logrus.Debugln("Exiting Run() with ExitCode 0", c.ID)
   143  	return int(exitCode), nil
   144  }