github.com/scorpionis/docker@v1.6.0-rc7/daemon/exec.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"strings"
     8  	"sync"
     9  
    10  	log "github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/daemon/execdriver"
    12  	"github.com/docker/docker/daemon/execdriver/lxc"
    13  	"github.com/docker/docker/engine"
    14  	"github.com/docker/docker/pkg/broadcastwriter"
    15  	"github.com/docker/docker/pkg/common"
    16  	"github.com/docker/docker/pkg/ioutils"
    17  	"github.com/docker/docker/pkg/promise"
    18  	"github.com/docker/docker/runconfig"
    19  )
    20  
    21  type execConfig struct {
    22  	sync.Mutex
    23  	ID            string
    24  	Running       bool
    25  	ExitCode      int
    26  	ProcessConfig execdriver.ProcessConfig
    27  	StreamConfig
    28  	OpenStdin  bool
    29  	OpenStderr bool
    30  	OpenStdout bool
    31  	Container  *Container
    32  }
    33  
    34  type execStore struct {
    35  	s map[string]*execConfig
    36  	sync.RWMutex
    37  }
    38  
    39  func newExecStore() *execStore {
    40  	return &execStore{s: make(map[string]*execConfig, 0)}
    41  }
    42  
    43  func (e *execStore) Add(id string, execConfig *execConfig) {
    44  	e.Lock()
    45  	e.s[id] = execConfig
    46  	e.Unlock()
    47  }
    48  
    49  func (e *execStore) Get(id string) *execConfig {
    50  	e.RLock()
    51  	res := e.s[id]
    52  	e.RUnlock()
    53  	return res
    54  }
    55  
    56  func (e *execStore) Delete(id string) {
    57  	e.Lock()
    58  	delete(e.s, id)
    59  	e.Unlock()
    60  }
    61  
    62  func (e *execStore) List() []string {
    63  	var IDs []string
    64  	e.RLock()
    65  	for id := range e.s {
    66  		IDs = append(IDs, id)
    67  	}
    68  	e.RUnlock()
    69  	return IDs
    70  }
    71  
    72  func (execConfig *execConfig) Resize(h, w int) error {
    73  	return execConfig.ProcessConfig.Terminal.Resize(h, w)
    74  }
    75  
    76  func (d *Daemon) registerExecCommand(execConfig *execConfig) {
    77  	// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
    78  	execConfig.Container.execCommands.Add(execConfig.ID, execConfig)
    79  	// Storing execs in daemon for easy access via remote API.
    80  	d.execCommands.Add(execConfig.ID, execConfig)
    81  }
    82  
    83  func (d *Daemon) getExecConfig(name string) (*execConfig, error) {
    84  	if execConfig := d.execCommands.Get(name); execConfig != nil {
    85  		if !execConfig.Container.IsRunning() {
    86  			return nil, fmt.Errorf("Container %s is not running", execConfig.Container.ID)
    87  		}
    88  		return execConfig, nil
    89  	}
    90  
    91  	return nil, fmt.Errorf("No such exec instance '%s' found in daemon", name)
    92  }
    93  
    94  func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
    95  	execConfig.Container.execCommands.Delete(execConfig.ID)
    96  	d.execCommands.Delete(execConfig.ID)
    97  }
    98  
    99  func (d *Daemon) getActiveContainer(name string) (*Container, error) {
   100  	container, err := d.Get(name)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	if !container.IsRunning() {
   106  		return nil, fmt.Errorf("Container %s is not running", name)
   107  	}
   108  	if container.IsPaused() {
   109  		return nil, fmt.Errorf("Container %s is paused, unpause the container before exec", name)
   110  	}
   111  	return container, nil
   112  }
   113  
   114  func (d *Daemon) ContainerExecCreate(job *engine.Job) engine.Status {
   115  	if len(job.Args) != 1 {
   116  		return job.Errorf("Usage: %s [options] container command [args]", job.Name)
   117  	}
   118  
   119  	if strings.HasPrefix(d.execDriver.Name(), lxc.DriverName) {
   120  		return job.Error(lxc.ErrExec)
   121  	}
   122  
   123  	var name = job.Args[0]
   124  
   125  	container, err := d.getActiveContainer(name)
   126  	if err != nil {
   127  		return job.Error(err)
   128  	}
   129  
   130  	config, err := runconfig.ExecConfigFromJob(job)
   131  	if err != nil {
   132  		return job.Error(err)
   133  	}
   134  
   135  	entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd)
   136  
   137  	processConfig := execdriver.ProcessConfig{
   138  		Tty:        config.Tty,
   139  		Entrypoint: entrypoint,
   140  		Arguments:  args,
   141  	}
   142  
   143  	execConfig := &execConfig{
   144  		ID:            common.GenerateRandomID(),
   145  		OpenStdin:     config.AttachStdin,
   146  		OpenStdout:    config.AttachStdout,
   147  		OpenStderr:    config.AttachStderr,
   148  		StreamConfig:  StreamConfig{},
   149  		ProcessConfig: processConfig,
   150  		Container:     container,
   151  		Running:       false,
   152  	}
   153  
   154  	container.LogEvent("exec_create: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " "))
   155  
   156  	d.registerExecCommand(execConfig)
   157  
   158  	job.Printf("%s\n", execConfig.ID)
   159  
   160  	return engine.StatusOK
   161  }
   162  
   163  func (d *Daemon) ContainerExecStart(job *engine.Job) engine.Status {
   164  	if len(job.Args) != 1 {
   165  		return job.Errorf("Usage: %s [options] exec", job.Name)
   166  	}
   167  
   168  	var (
   169  		cStdin           io.ReadCloser
   170  		cStdout, cStderr io.Writer
   171  		execName         = job.Args[0]
   172  	)
   173  
   174  	execConfig, err := d.getExecConfig(execName)
   175  	if err != nil {
   176  		return job.Error(err)
   177  	}
   178  
   179  	func() {
   180  		execConfig.Lock()
   181  		defer execConfig.Unlock()
   182  		if execConfig.Running {
   183  			err = fmt.Errorf("Error: Exec command %s is already running", execName)
   184  		}
   185  		execConfig.Running = true
   186  	}()
   187  	if err != nil {
   188  		return job.Error(err)
   189  	}
   190  
   191  	log.Debugf("starting exec command %s in container %s", execConfig.ID, execConfig.Container.ID)
   192  	container := execConfig.Container
   193  
   194  	container.LogEvent("exec_start: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " "))
   195  
   196  	if execConfig.OpenStdin {
   197  		r, w := io.Pipe()
   198  		go func() {
   199  			defer w.Close()
   200  			defer log.Debugf("Closing buffered stdin pipe")
   201  			io.Copy(w, job.Stdin)
   202  		}()
   203  		cStdin = r
   204  	}
   205  	if execConfig.OpenStdout {
   206  		cStdout = job.Stdout
   207  	}
   208  	if execConfig.OpenStderr {
   209  		cStderr = job.Stderr
   210  	}
   211  
   212  	execConfig.StreamConfig.stderr = broadcastwriter.New()
   213  	execConfig.StreamConfig.stdout = broadcastwriter.New()
   214  	// Attach to stdin
   215  	if execConfig.OpenStdin {
   216  		execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdinPipe = io.Pipe()
   217  	} else {
   218  		execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
   219  	}
   220  
   221  	attachErr := d.Attach(&execConfig.StreamConfig, execConfig.OpenStdin, true, execConfig.ProcessConfig.Tty, cStdin, cStdout, cStderr)
   222  
   223  	execErr := make(chan error)
   224  
   225  	// Note, the execConfig data will be removed when the container
   226  	// itself is deleted.  This allows us to query it (for things like
   227  	// the exitStatus) even after the cmd is done running.
   228  
   229  	go func() {
   230  		err := container.Exec(execConfig)
   231  		if err != nil {
   232  			execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err)
   233  		}
   234  	}()
   235  
   236  	select {
   237  	case err := <-attachErr:
   238  		if err != nil {
   239  			return job.Errorf("attach failed with error: %s", err)
   240  		}
   241  		break
   242  	case err := <-execErr:
   243  		return job.Error(err)
   244  	}
   245  
   246  	return engine.StatusOK
   247  }
   248  
   249  func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
   250  	exitStatus, err := d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback)
   251  
   252  	// On err, make sure we don't leave ExitCode at zero
   253  	if err != nil && exitStatus == 0 {
   254  		exitStatus = 128
   255  	}
   256  
   257  	execConfig.ExitCode = exitStatus
   258  	execConfig.Running = false
   259  
   260  	return exitStatus, err
   261  }
   262  
   263  func (container *Container) GetExecIDs() []string {
   264  	return container.execCommands.List()
   265  }
   266  
   267  func (container *Container) Exec(execConfig *execConfig) error {
   268  	container.Lock()
   269  	defer container.Unlock()
   270  
   271  	waitStart := make(chan struct{})
   272  
   273  	callback := func(processConfig *execdriver.ProcessConfig, pid int) {
   274  		if processConfig.Tty {
   275  			// The callback is called after the process Start()
   276  			// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave
   277  			// which we close here.
   278  			if c, ok := processConfig.Stdout.(io.Closer); ok {
   279  				c.Close()
   280  			}
   281  		}
   282  		close(waitStart)
   283  	}
   284  
   285  	// We use a callback here instead of a goroutine and an chan for
   286  	// syncronization purposes
   287  	cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) })
   288  
   289  	// Exec should not return until the process is actually running
   290  	select {
   291  	case <-waitStart:
   292  	case err := <-cErr:
   293  		return err
   294  	}
   295  
   296  	return nil
   297  }
   298  
   299  func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error {
   300  	var (
   301  		err      error
   302  		exitCode int
   303  	)
   304  
   305  	pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin)
   306  	exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback)
   307  	if err != nil {
   308  		log.Errorf("Error running command in existing container %s: %s", container.ID, err)
   309  	}
   310  
   311  	log.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode)
   312  	if execConfig.OpenStdin {
   313  		if err := execConfig.StreamConfig.stdin.Close(); err != nil {
   314  			log.Errorf("Error closing stdin while running in %s: %s", container.ID, err)
   315  		}
   316  	}
   317  	if err := execConfig.StreamConfig.stdout.Clean(); err != nil {
   318  		log.Errorf("Error closing stdout while running in %s: %s", container.ID, err)
   319  	}
   320  	if err := execConfig.StreamConfig.stderr.Clean(); err != nil {
   321  		log.Errorf("Error closing stderr while running in %s: %s", container.ID, err)
   322  	}
   323  	if execConfig.ProcessConfig.Terminal != nil {
   324  		if err := execConfig.ProcessConfig.Terminal.Close(); err != nil {
   325  			log.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
   326  		}
   327  	}
   328  
   329  	return err
   330  }