github.com/psychoss/docker@v1.9.0/daemon/exec.go (about)

     1  package daemon
     2  
     3  import (
     4  	"io"
     5  	"io/ioutil"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/daemon/execdriver"
    12  	derr "github.com/docker/docker/errors"
    13  	"github.com/docker/docker/pkg/broadcaster"
    14  	"github.com/docker/docker/pkg/ioutils"
    15  	"github.com/docker/docker/pkg/pools"
    16  	"github.com/docker/docker/pkg/stringid"
    17  	"github.com/docker/docker/pkg/stringutils"
    18  	"github.com/docker/docker/runconfig"
    19  )
    20  
    21  // ExecConfig holds the configurations for execs. The Daemon keeps
    22  // track of both running and finished execs so that they can be
    23  // examined both during and after completion.
    24  type ExecConfig struct {
    25  	sync.Mutex
    26  	ID            string
    27  	Running       bool
    28  	ExitCode      int
    29  	ProcessConfig *execdriver.ProcessConfig
    30  	streamConfig
    31  	OpenStdin  bool
    32  	OpenStderr bool
    33  	OpenStdout bool
    34  	Container  *Container
    35  	canRemove  bool
    36  
    37  	// waitStart will be closed immediately after the exec is really started.
    38  	waitStart chan struct{}
    39  }
    40  
    41  type execStore struct {
    42  	s map[string]*ExecConfig
    43  	sync.RWMutex
    44  }
    45  
    46  func newExecStore() *execStore {
    47  	return &execStore{s: make(map[string]*ExecConfig, 0)}
    48  }
    49  
    50  func (e *execStore) Add(id string, ExecConfig *ExecConfig) {
    51  	e.Lock()
    52  	e.s[id] = ExecConfig
    53  	e.Unlock()
    54  }
    55  
    56  func (e *execStore) Get(id string) *ExecConfig {
    57  	e.RLock()
    58  	res := e.s[id]
    59  	e.RUnlock()
    60  	return res
    61  }
    62  
    63  func (e *execStore) Delete(id string) {
    64  	e.Lock()
    65  	delete(e.s, id)
    66  	e.Unlock()
    67  }
    68  
    69  func (e *execStore) List() []string {
    70  	var IDs []string
    71  	e.RLock()
    72  	for id := range e.s {
    73  		IDs = append(IDs, id)
    74  	}
    75  	e.RUnlock()
    76  	return IDs
    77  }
    78  
    79  func (ExecConfig *ExecConfig) resize(h, w int) error {
    80  	select {
    81  	case <-ExecConfig.waitStart:
    82  	case <-time.After(time.Second):
    83  		return derr.ErrorCodeExecResize.WithArgs(ExecConfig.ID)
    84  	}
    85  	return ExecConfig.ProcessConfig.Terminal.Resize(h, w)
    86  }
    87  
    88  func (d *Daemon) registerExecCommand(ExecConfig *ExecConfig) {
    89  	// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
    90  	ExecConfig.Container.execCommands.Add(ExecConfig.ID, ExecConfig)
    91  	// Storing execs in daemon for easy access via remote API.
    92  	d.execCommands.Add(ExecConfig.ID, ExecConfig)
    93  }
    94  
    95  // ExecExists looks up the exec instance and returns a bool if it exists or not.
    96  // It will also return the error produced by `getExecConfig`
    97  func (d *Daemon) ExecExists(name string) (bool, error) {
    98  	if _, err := d.getExecConfig(name); err != nil {
    99  		return false, err
   100  	}
   101  	return true, nil
   102  }
   103  
   104  // getExecConfig looks up the exec instance by name. If the container associated
   105  // with the exec instance is stopped or paused, it will return an error.
   106  func (d *Daemon) getExecConfig(name string) (*ExecConfig, error) {
   107  	ec := d.execCommands.Get(name)
   108  
   109  	// If the exec is found but its container is not in the daemon's list of
   110  	// containers then it must have been delete, in which case instead of
   111  	// saying the container isn't running, we should return a 404 so that
   112  	// the user sees the same error now that they will after the
   113  	// 5 minute clean-up loop is run which erases old/dead execs.
   114  
   115  	if ec != nil && d.containers.Get(ec.Container.ID) != nil {
   116  		if !ec.Container.IsRunning() {
   117  			return nil, derr.ErrorCodeContainerNotRunning.WithArgs(ec.Container.ID, ec.Container.State.String())
   118  		}
   119  		if ec.Container.isPaused() {
   120  			return nil, derr.ErrorCodeExecPaused.WithArgs(ec.Container.ID)
   121  		}
   122  		return ec, nil
   123  	}
   124  
   125  	return nil, derr.ErrorCodeNoExecID.WithArgs(name)
   126  }
   127  
   128  func (d *Daemon) unregisterExecCommand(ExecConfig *ExecConfig) {
   129  	ExecConfig.Container.execCommands.Delete(ExecConfig.ID)
   130  	d.execCommands.Delete(ExecConfig.ID)
   131  }
   132  
   133  func (d *Daemon) getActiveContainer(name string) (*Container, error) {
   134  	container, err := d.Get(name)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	if !container.IsRunning() {
   140  		return nil, derr.ErrorCodeNotRunning.WithArgs(name)
   141  	}
   142  	if container.isPaused() {
   143  		return nil, derr.ErrorCodeExecPaused.WithArgs(name)
   144  	}
   145  	return container, nil
   146  }
   147  
   148  // ContainerExecCreate sets up an exec in a running container.
   149  func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, error) {
   150  	// Not all drivers support Exec (LXC for example)
   151  	if err := checkExecSupport(d.execDriver.Name()); err != nil {
   152  		return "", err
   153  	}
   154  
   155  	container, err := d.getActiveContainer(config.Container)
   156  	if err != nil {
   157  		return "", err
   158  	}
   159  
   160  	cmd := stringutils.NewStrSlice(config.Cmd...)
   161  	entrypoint, args := d.getEntrypointAndArgs(stringutils.NewStrSlice(), cmd)
   162  
   163  	user := config.User
   164  	if len(user) == 0 {
   165  		user = container.Config.User
   166  	}
   167  
   168  	processConfig := &execdriver.ProcessConfig{
   169  		Tty:        config.Tty,
   170  		Entrypoint: entrypoint,
   171  		Arguments:  args,
   172  		User:       user,
   173  		Privileged: config.Privileged,
   174  	}
   175  
   176  	ExecConfig := &ExecConfig{
   177  		ID:            stringid.GenerateNonCryptoID(),
   178  		OpenStdin:     config.AttachStdin,
   179  		OpenStdout:    config.AttachStdout,
   180  		OpenStderr:    config.AttachStderr,
   181  		streamConfig:  streamConfig{},
   182  		ProcessConfig: processConfig,
   183  		Container:     container,
   184  		Running:       false,
   185  		waitStart:     make(chan struct{}),
   186  	}
   187  
   188  	d.registerExecCommand(ExecConfig)
   189  
   190  	container.logEvent("exec_create: " + ExecConfig.ProcessConfig.Entrypoint + " " + strings.Join(ExecConfig.ProcessConfig.Arguments, " "))
   191  
   192  	return ExecConfig.ID, nil
   193  }
   194  
   195  // ContainerExecStart starts a previously set up exec instance. The
   196  // std streams are set up.
   197  func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error {
   198  	var (
   199  		cStdin           io.ReadCloser
   200  		cStdout, cStderr io.Writer
   201  	)
   202  
   203  	ec, err := d.getExecConfig(name)
   204  	if err != nil {
   205  		return derr.ErrorCodeNoExecID.WithArgs(name)
   206  	}
   207  
   208  	ec.Lock()
   209  	if ec.Running {
   210  		ec.Unlock()
   211  		return derr.ErrorCodeExecRunning.WithArgs(ec.ID)
   212  	}
   213  	ec.Running = true
   214  	ec.Unlock()
   215  
   216  	logrus.Debugf("starting exec command %s in container %s", ec.ID, ec.Container.ID)
   217  	container := ec.Container
   218  	container.logEvent("exec_start: " + ec.ProcessConfig.Entrypoint + " " + strings.Join(ec.ProcessConfig.Arguments, " "))
   219  
   220  	if ec.OpenStdin {
   221  		r, w := io.Pipe()
   222  		go func() {
   223  			defer w.Close()
   224  			defer logrus.Debugf("Closing buffered stdin pipe")
   225  			pools.Copy(w, stdin)
   226  		}()
   227  		cStdin = r
   228  	}
   229  	if ec.OpenStdout {
   230  		cStdout = stdout
   231  	}
   232  	if ec.OpenStderr {
   233  		cStderr = stderr
   234  	}
   235  
   236  	ec.streamConfig.stderr = new(broadcaster.Unbuffered)
   237  	ec.streamConfig.stdout = new(broadcaster.Unbuffered)
   238  	// Attach to stdin
   239  	if ec.OpenStdin {
   240  		ec.streamConfig.stdin, ec.streamConfig.stdinPipe = io.Pipe()
   241  	} else {
   242  		ec.streamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
   243  	}
   244  
   245  	attachErr := attach(&ec.streamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr)
   246  
   247  	execErr := make(chan error)
   248  
   249  	// Note, the ExecConfig data will be removed when the container
   250  	// itself is deleted.  This allows us to query it (for things like
   251  	// the exitStatus) even after the cmd is done running.
   252  
   253  	go func() {
   254  		execErr <- container.exec(ec)
   255  	}()
   256  
   257  	select {
   258  	case err := <-attachErr:
   259  		if err != nil {
   260  			return derr.ErrorCodeExecAttach.WithArgs(err)
   261  		}
   262  		return nil
   263  	case err := <-execErr:
   264  		if aErr := <-attachErr; aErr != nil && err == nil {
   265  			return derr.ErrorCodeExecAttach.WithArgs(aErr)
   266  		}
   267  		if err == nil {
   268  			return nil
   269  		}
   270  
   271  		// Maybe the container stopped while we were trying to exec
   272  		if !container.IsRunning() {
   273  			return derr.ErrorCodeExecContainerStopped
   274  		}
   275  		return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, container.ID, err)
   276  	}
   277  }
   278  
   279  // Exec calls the underlying exec driver to run
   280  func (d *Daemon) Exec(c *Container, ExecConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
   281  	hooks := execdriver.Hooks{
   282  		Start: startCallback,
   283  	}
   284  	exitStatus, err := d.execDriver.Exec(c.command, ExecConfig.ProcessConfig, pipes, hooks)
   285  
   286  	// On err, make sure we don't leave ExitCode at zero
   287  	if err != nil && exitStatus == 0 {
   288  		exitStatus = 128
   289  	}
   290  
   291  	ExecConfig.ExitCode = exitStatus
   292  	ExecConfig.Running = false
   293  
   294  	return exitStatus, err
   295  }
   296  
   297  // execCommandGC runs a ticker to clean up the daemon references
   298  // of exec configs that are no longer part of the container.
   299  func (d *Daemon) execCommandGC() {
   300  	for range time.Tick(5 * time.Minute) {
   301  		var (
   302  			cleaned          int
   303  			liveExecCommands = d.containerExecIds()
   304  		)
   305  		for id, config := range d.execCommands.s {
   306  			if config.canRemove {
   307  				cleaned++
   308  				d.execCommands.Delete(id)
   309  			} else {
   310  				if _, exists := liveExecCommands[id]; !exists {
   311  					config.canRemove = true
   312  				}
   313  			}
   314  		}
   315  		if cleaned > 0 {
   316  			logrus.Debugf("clean %d unused exec commands", cleaned)
   317  		}
   318  	}
   319  }
   320  
   321  // containerExecIds returns a list of all the current exec ids that are in use
   322  // and running inside a container.
   323  func (d *Daemon) containerExecIds() map[string]struct{} {
   324  	ids := map[string]struct{}{}
   325  	for _, c := range d.containers.List() {
   326  		for _, id := range c.execCommands.List() {
   327  			ids[id] = struct{}{}
   328  		}
   329  	}
   330  	return ids
   331  }