github.com/ld86/docker@v1.7.1-rc3/daemon/exec.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/daemon/execdriver"
    12  	"github.com/docker/docker/pkg/broadcastwriter"
    13  	"github.com/docker/docker/pkg/ioutils"
    14  	"github.com/docker/docker/pkg/stringid"
    15  	"github.com/docker/docker/runconfig"
    16  )
    17  
    18  type execConfig struct {
    19  	sync.Mutex
    20  	ID            string
    21  	Running       bool
    22  	ExitCode      int
    23  	ProcessConfig execdriver.ProcessConfig
    24  	StreamConfig
    25  	OpenStdin  bool
    26  	OpenStderr bool
    27  	OpenStdout bool
    28  	Container  *Container
    29  }
    30  
    31  type execStore struct {
    32  	s map[string]*execConfig
    33  	sync.RWMutex
    34  }
    35  
    36  func newExecStore() *execStore {
    37  	return &execStore{s: make(map[string]*execConfig, 0)}
    38  }
    39  
    40  func (e *execStore) Add(id string, execConfig *execConfig) {
    41  	e.Lock()
    42  	e.s[id] = execConfig
    43  	e.Unlock()
    44  }
    45  
    46  func (e *execStore) Get(id string) *execConfig {
    47  	e.RLock()
    48  	res := e.s[id]
    49  	e.RUnlock()
    50  	return res
    51  }
    52  
    53  func (e *execStore) Delete(id string) {
    54  	e.Lock()
    55  	delete(e.s, id)
    56  	e.Unlock()
    57  }
    58  
    59  func (e *execStore) List() []string {
    60  	var IDs []string
    61  	e.RLock()
    62  	for id := range e.s {
    63  		IDs = append(IDs, id)
    64  	}
    65  	e.RUnlock()
    66  	return IDs
    67  }
    68  
    69  func (execConfig *execConfig) Resize(h, w int) error {
    70  	return execConfig.ProcessConfig.Terminal.Resize(h, w)
    71  }
    72  
    73  func (d *Daemon) registerExecCommand(execConfig *execConfig) {
    74  	// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
    75  	execConfig.Container.execCommands.Add(execConfig.ID, execConfig)
    76  	// Storing execs in daemon for easy access via remote API.
    77  	d.execCommands.Add(execConfig.ID, execConfig)
    78  }
    79  
    80  func (d *Daemon) getExecConfig(name string) (*execConfig, error) {
    81  	if execConfig := d.execCommands.Get(name); execConfig != nil {
    82  		if !execConfig.Container.IsRunning() {
    83  			return nil, fmt.Errorf("Container %s is not running", execConfig.Container.ID)
    84  		}
    85  		return execConfig, nil
    86  	}
    87  
    88  	return nil, fmt.Errorf("No such exec instance '%s' found in daemon", name)
    89  }
    90  
    91  func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
    92  	execConfig.Container.execCommands.Delete(execConfig.ID)
    93  	d.execCommands.Delete(execConfig.ID)
    94  }
    95  
    96  func (d *Daemon) getActiveContainer(name string) (*Container, error) {
    97  	container, err := d.Get(name)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	if !container.IsRunning() {
   103  		return nil, fmt.Errorf("Container %s is not running", name)
   104  	}
   105  	if container.IsPaused() {
   106  		return nil, fmt.Errorf("Container %s is paused, unpause the container before exec", name)
   107  	}
   108  	return container, nil
   109  }
   110  
   111  func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, error) {
   112  	// Not all drivers support Exec (LXC for example)
   113  	if err := checkExecSupport(d.execDriver.Name()); err != nil {
   114  		return "", err
   115  	}
   116  
   117  	container, err := d.getActiveContainer(config.Container)
   118  	if err != nil {
   119  		return "", err
   120  	}
   121  
   122  	cmd := runconfig.NewCommand(config.Cmd...)
   123  	entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd)
   124  
   125  	user := config.User
   126  	if len(user) == 0 {
   127  		user = container.Config.User
   128  	}
   129  
   130  	processConfig := execdriver.ProcessConfig{
   131  		Tty:        config.Tty,
   132  		Entrypoint: entrypoint,
   133  		Arguments:  args,
   134  		User:       user,
   135  	}
   136  
   137  	execConfig := &execConfig{
   138  		ID:            stringid.GenerateRandomID(),
   139  		OpenStdin:     config.AttachStdin,
   140  		OpenStdout:    config.AttachStdout,
   141  		OpenStderr:    config.AttachStderr,
   142  		StreamConfig:  StreamConfig{},
   143  		ProcessConfig: processConfig,
   144  		Container:     container,
   145  		Running:       false,
   146  	}
   147  
   148  	container.LogEvent("exec_create: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " "))
   149  
   150  	d.registerExecCommand(execConfig)
   151  
   152  	return execConfig.ID, nil
   153  
   154  }
   155  
   156  func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error {
   157  
   158  	var (
   159  		cStdin           io.ReadCloser
   160  		cStdout, cStderr io.Writer
   161  	)
   162  
   163  	execConfig, err := d.getExecConfig(execName)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	func() {
   169  		execConfig.Lock()
   170  		defer execConfig.Unlock()
   171  		if execConfig.Running {
   172  			err = fmt.Errorf("Error: Exec command %s is already running", execName)
   173  		}
   174  		execConfig.Running = true
   175  	}()
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	logrus.Debugf("starting exec command %s in container %s", execConfig.ID, execConfig.Container.ID)
   181  	container := execConfig.Container
   182  
   183  	container.LogEvent("exec_start: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " "))
   184  
   185  	if execConfig.OpenStdin {
   186  		r, w := io.Pipe()
   187  		go func() {
   188  			defer w.Close()
   189  			defer logrus.Debugf("Closing buffered stdin pipe")
   190  			io.Copy(w, stdin)
   191  		}()
   192  		cStdin = r
   193  	}
   194  	if execConfig.OpenStdout {
   195  		cStdout = stdout
   196  	}
   197  	if execConfig.OpenStderr {
   198  		cStderr = stderr
   199  	}
   200  
   201  	execConfig.StreamConfig.stderr = broadcastwriter.New()
   202  	execConfig.StreamConfig.stdout = broadcastwriter.New()
   203  	// Attach to stdin
   204  	if execConfig.OpenStdin {
   205  		execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdinPipe = io.Pipe()
   206  	} else {
   207  		execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
   208  	}
   209  
   210  	attachErr := attach(&execConfig.StreamConfig, execConfig.OpenStdin, true, execConfig.ProcessConfig.Tty, cStdin, cStdout, cStderr)
   211  
   212  	execErr := make(chan error)
   213  
   214  	// Note, the execConfig data will be removed when the container
   215  	// itself is deleted.  This allows us to query it (for things like
   216  	// the exitStatus) even after the cmd is done running.
   217  
   218  	go func() {
   219  		if err := container.Exec(execConfig); err != nil {
   220  			execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err)
   221  		}
   222  	}()
   223  
   224  	select {
   225  	case err := <-attachErr:
   226  		if err != nil {
   227  			return fmt.Errorf("attach failed with error: %s", err)
   228  		}
   229  		break
   230  	case err := <-execErr:
   231  		return err
   232  	}
   233  
   234  	return nil
   235  }
   236  
   237  func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
   238  	exitStatus, err := d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback)
   239  
   240  	// On err, make sure we don't leave ExitCode at zero
   241  	if err != nil && exitStatus == 0 {
   242  		exitStatus = 128
   243  	}
   244  
   245  	execConfig.ExitCode = exitStatus
   246  	execConfig.Running = false
   247  
   248  	return exitStatus, err
   249  }