github.com/gunjan5/docker@v1.8.2/daemon/exec.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/daemon/execdriver"
    13  	"github.com/docker/docker/pkg/broadcastwriter"
    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/runconfig"
    18  )
    19  
    20  type execConfig struct {
    21  	sync.Mutex
    22  	ID            string
    23  	Running       bool
    24  	ExitCode      int
    25  	ProcessConfig *execdriver.ProcessConfig
    26  	StreamConfig
    27  	OpenStdin  bool
    28  	OpenStderr bool
    29  	OpenStdout bool
    30  	Container  *Container
    31  	canRemove  bool
    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(config *runconfig.ExecConfig) (string, error) {
   115  	// Not all drivers support Exec (LXC for example)
   116  	if err := checkExecSupport(d.execDriver.Name()); err != nil {
   117  		return "", err
   118  	}
   119  
   120  	container, err := d.getActiveContainer(config.Container)
   121  	if err != nil {
   122  		return "", err
   123  	}
   124  
   125  	cmd := runconfig.NewCommand(config.Cmd...)
   126  	entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd)
   127  
   128  	user := config.User
   129  	if len(user) == 0 {
   130  		user = container.Config.User
   131  	}
   132  
   133  	processConfig := &execdriver.ProcessConfig{
   134  		Tty:        config.Tty,
   135  		Entrypoint: entrypoint,
   136  		Arguments:  args,
   137  		User:       user,
   138  	}
   139  
   140  	execConfig := &execConfig{
   141  		ID:            stringid.GenerateRandomID(),
   142  		OpenStdin:     config.AttachStdin,
   143  		OpenStdout:    config.AttachStdout,
   144  		OpenStderr:    config.AttachStderr,
   145  		StreamConfig:  StreamConfig{},
   146  		ProcessConfig: processConfig,
   147  		Container:     container,
   148  		Running:       false,
   149  	}
   150  
   151  	d.registerExecCommand(execConfig)
   152  
   153  	container.LogEvent("exec_create: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " "))
   154  
   155  	return execConfig.ID, nil
   156  
   157  }
   158  
   159  func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error {
   160  
   161  	var (
   162  		cStdin           io.ReadCloser
   163  		cStdout, cStderr io.Writer
   164  	)
   165  
   166  	execConfig, err := d.getExecConfig(execName)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	func() {
   172  		execConfig.Lock()
   173  		defer execConfig.Unlock()
   174  		if execConfig.Running {
   175  			err = fmt.Errorf("Error: Exec command %s is already running", execName)
   176  		}
   177  		execConfig.Running = true
   178  	}()
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	logrus.Debugf("starting exec command %s in container %s", execConfig.ID, execConfig.Container.ID)
   184  	container := execConfig.Container
   185  
   186  	container.LogEvent("exec_start: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " "))
   187  
   188  	if execConfig.OpenStdin {
   189  		r, w := io.Pipe()
   190  		go func() {
   191  			defer w.Close()
   192  			defer logrus.Debugf("Closing buffered stdin pipe")
   193  			pools.Copy(w, stdin)
   194  		}()
   195  		cStdin = r
   196  	}
   197  	if execConfig.OpenStdout {
   198  		cStdout = stdout
   199  	}
   200  	if execConfig.OpenStderr {
   201  		cStderr = stderr
   202  	}
   203  
   204  	execConfig.StreamConfig.stderr = broadcastwriter.New()
   205  	execConfig.StreamConfig.stdout = broadcastwriter.New()
   206  	// Attach to stdin
   207  	if execConfig.OpenStdin {
   208  		execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdinPipe = io.Pipe()
   209  	} else {
   210  		execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
   211  	}
   212  
   213  	attachErr := attach(&execConfig.StreamConfig, execConfig.OpenStdin, true, execConfig.ProcessConfig.Tty, cStdin, cStdout, cStderr)
   214  
   215  	execErr := make(chan error)
   216  
   217  	// Note, the execConfig data will be removed when the container
   218  	// itself is deleted.  This allows us to query it (for things like
   219  	// the exitStatus) even after the cmd is done running.
   220  
   221  	go func() {
   222  		if err := container.Exec(execConfig); err != nil {
   223  			execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err)
   224  		}
   225  	}()
   226  	select {
   227  	case err := <-attachErr:
   228  		if err != nil {
   229  			return fmt.Errorf("attach failed with error: %s", err)
   230  		}
   231  		break
   232  	case err := <-execErr:
   233  		return err
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
   240  	exitStatus, err := d.execDriver.Exec(c.command, execConfig.ProcessConfig, pipes, startCallback)
   241  
   242  	// On err, make sure we don't leave ExitCode at zero
   243  	if err != nil && exitStatus == 0 {
   244  		exitStatus = 128
   245  	}
   246  
   247  	execConfig.ExitCode = exitStatus
   248  	execConfig.Running = false
   249  
   250  	return exitStatus, err
   251  }
   252  
   253  // execCommandGC runs a ticker to clean up the daemon references
   254  // of exec configs that are no longer part of the container.
   255  func (d *Daemon) execCommandGC() {
   256  	for range time.Tick(5 * time.Minute) {
   257  		var (
   258  			cleaned          int
   259  			liveExecCommands = d.containerExecIds()
   260  		)
   261  		for id, config := range d.execCommands.s {
   262  			if config.canRemove {
   263  				cleaned++
   264  				d.execCommands.Delete(id)
   265  			} else {
   266  				if _, exists := liveExecCommands[id]; !exists {
   267  					config.canRemove = true
   268  				}
   269  			}
   270  		}
   271  		if cleaned > 0 {
   272  			logrus.Debugf("clean %d unused exec commands", cleaned)
   273  		}
   274  	}
   275  }
   276  
   277  // containerExecIds returns a list of all the current exec ids that are in use
   278  // and running inside a container.
   279  func (d *Daemon) containerExecIds() map[string]struct{} {
   280  	ids := map[string]struct{}{}
   281  	for _, c := range d.containers.List() {
   282  		for _, id := range c.execCommands.List() {
   283  			ids[id] = struct{}{}
   284  		}
   285  	}
   286  	return ids
   287  }