github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/daemon/execdriver/native/driver.go (about)

     1  // +build linux,cgo
     2  
     3  package native
     4  
     5  import (
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"sync"
    12  	"syscall"
    13  	"time"
    14  
    15  	"github.com/Sirupsen/logrus"
    16  	"github.com/docker/docker/daemon/execdriver"
    17  	"github.com/docker/docker/pkg/reexec"
    18  	sysinfo "github.com/docker/docker/pkg/system"
    19  	"github.com/docker/docker/pkg/term"
    20  	"github.com/docker/libcontainer"
    21  	"github.com/docker/libcontainer/apparmor"
    22  	"github.com/docker/libcontainer/cgroups/systemd"
    23  	"github.com/docker/libcontainer/configs"
    24  	"github.com/docker/libcontainer/system"
    25  	"github.com/docker/libcontainer/utils"
    26  )
    27  
    28  const (
    29  	DriverName = "native"
    30  	Version    = "0.2"
    31  )
    32  
    33  type driver struct {
    34  	root             string
    35  	initPath         string
    36  	activeContainers map[string]libcontainer.Container
    37  	machineMemory    int64
    38  	factory          libcontainer.Factory
    39  	sync.Mutex
    40  }
    41  
    42  func NewDriver(root, initPath string) (*driver, error) {
    43  	meminfo, err := sysinfo.ReadMemInfo()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	if err := os.MkdirAll(root, 0700); err != nil {
    49  		return nil, err
    50  	}
    51  	// native driver root is at docker_root/execdriver/native. Put apparmor at docker_root
    52  	if err := apparmor.InstallDefaultProfile(); err != nil {
    53  		return nil, err
    54  	}
    55  	cgm := libcontainer.Cgroupfs
    56  	if systemd.UseSystemd() {
    57  		cgm = libcontainer.SystemdCgroups
    58  	}
    59  
    60  	f, err := libcontainer.New(
    61  		root,
    62  		cgm,
    63  		libcontainer.InitPath(reexec.Self(), DriverName),
    64  	)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	return &driver{
    70  		root:             root,
    71  		initPath:         initPath,
    72  		activeContainers: make(map[string]libcontainer.Container),
    73  		machineMemory:    meminfo.MemTotal,
    74  		factory:          f,
    75  	}, nil
    76  }
    77  
    78  type execOutput struct {
    79  	exitCode int
    80  	err      error
    81  }
    82  
    83  func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
    84  	// take the Command and populate the libcontainer.Config from it
    85  	container, err := d.createContainer(c)
    86  	if err != nil {
    87  		return execdriver.ExitStatus{ExitCode: -1}, err
    88  	}
    89  
    90  	p := &libcontainer.Process{
    91  		Args: append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...),
    92  		Env:  c.ProcessConfig.Env,
    93  		Cwd:  c.WorkingDir,
    94  		User: c.ProcessConfig.User,
    95  	}
    96  
    97  	if err := setupPipes(container, &c.ProcessConfig, p, pipes); err != nil {
    98  		return execdriver.ExitStatus{ExitCode: -1}, err
    99  	}
   100  
   101  	cont, err := d.factory.Create(c.ID, container)
   102  	if err != nil {
   103  		return execdriver.ExitStatus{ExitCode: -1}, err
   104  	}
   105  	d.Lock()
   106  	d.activeContainers[c.ID] = cont
   107  	d.Unlock()
   108  	defer func() {
   109  		cont.Destroy()
   110  		d.cleanContainer(c.ID)
   111  	}()
   112  
   113  	if err := cont.Start(p); err != nil {
   114  		return execdriver.ExitStatus{ExitCode: -1}, err
   115  	}
   116  
   117  	if startCallback != nil {
   118  		pid, err := p.Pid()
   119  		if err != nil {
   120  			p.Signal(os.Kill)
   121  			p.Wait()
   122  			return execdriver.ExitStatus{ExitCode: -1}, err
   123  		}
   124  		startCallback(&c.ProcessConfig, pid)
   125  	}
   126  
   127  	oom := notifyOnOOM(cont)
   128  	waitF := p.Wait
   129  	if nss := cont.Config().Namespaces; !nss.Contains(configs.NEWPID) {
   130  		// we need such hack for tracking processes with inherited fds,
   131  		// because cmd.Wait() waiting for all streams to be copied
   132  		waitF = waitInPIDHost(p, cont)
   133  	}
   134  	ps, err := waitF()
   135  	if err != nil {
   136  		execErr, ok := err.(*exec.ExitError)
   137  		if !ok {
   138  			return execdriver.ExitStatus{ExitCode: -1}, err
   139  		}
   140  		ps = execErr.ProcessState
   141  	}
   142  	cont.Destroy()
   143  	_, oomKill := <-oom
   144  	return execdriver.ExitStatus{ExitCode: utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), OOMKilled: oomKill}, nil
   145  }
   146  
   147  // notifyOnOOM returns a channel that signals if the container received an OOM notification
   148  // for any process.  If it is unable to subscribe to OOM notifications then a closed
   149  // channel is returned as it will be non-blocking and return the correct result when read.
   150  func notifyOnOOM(container libcontainer.Container) <-chan struct{} {
   151  	oom, err := container.NotifyOOM()
   152  	if err != nil {
   153  		logrus.Warnf("Your kernel does not support OOM notifications: %s", err)
   154  		c := make(chan struct{})
   155  		close(c)
   156  		return c
   157  	}
   158  	return oom
   159  }
   160  
   161  func killCgroupProcs(c libcontainer.Container) {
   162  	var procs []*os.Process
   163  	if err := c.Pause(); err != nil {
   164  		logrus.Warn(err)
   165  	}
   166  	pids, err := c.Processes()
   167  	if err != nil {
   168  		// don't care about childs if we can't get them, this is mostly because cgroup already deleted
   169  		logrus.Warnf("Failed to get processes from container %s: %v", c.ID(), err)
   170  	}
   171  	for _, pid := range pids {
   172  		if p, err := os.FindProcess(pid); err == nil {
   173  			procs = append(procs, p)
   174  			if err := p.Kill(); err != nil {
   175  				logrus.Warn(err)
   176  			}
   177  		}
   178  	}
   179  	if err := c.Resume(); err != nil {
   180  		logrus.Warn(err)
   181  	}
   182  	for _, p := range procs {
   183  		if _, err := p.Wait(); err != nil {
   184  			logrus.Warn(err)
   185  		}
   186  	}
   187  }
   188  
   189  func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*os.ProcessState, error) {
   190  	return func() (*os.ProcessState, error) {
   191  		pid, err := p.Pid()
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  
   196  		process, err := os.FindProcess(pid)
   197  		s, err := process.Wait()
   198  		if err != nil {
   199  			execErr, ok := err.(*exec.ExitError)
   200  			if !ok {
   201  				return s, err
   202  			}
   203  			s = execErr.ProcessState
   204  		}
   205  		killCgroupProcs(c)
   206  		p.Wait()
   207  		return s, err
   208  	}
   209  }
   210  
   211  func (d *driver) Kill(c *execdriver.Command, sig int) error {
   212  	active := d.activeContainers[c.ID]
   213  	if active == nil {
   214  		return fmt.Errorf("active container for %s does not exist", c.ID)
   215  	}
   216  	state, err := active.State()
   217  	if err != nil {
   218  		return err
   219  	}
   220  	return syscall.Kill(state.InitProcessPid, syscall.Signal(sig))
   221  }
   222  
   223  func (d *driver) Pause(c *execdriver.Command) error {
   224  	active := d.activeContainers[c.ID]
   225  	if active == nil {
   226  		return fmt.Errorf("active container for %s does not exist", c.ID)
   227  	}
   228  	return active.Pause()
   229  }
   230  
   231  func (d *driver) Unpause(c *execdriver.Command) error {
   232  	active := d.activeContainers[c.ID]
   233  	if active == nil {
   234  		return fmt.Errorf("active container for %s does not exist", c.ID)
   235  	}
   236  	return active.Resume()
   237  }
   238  
   239  func (d *driver) Terminate(c *execdriver.Command) error {
   240  	defer d.cleanContainer(c.ID)
   241  	container, err := d.factory.Load(c.ID)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	defer container.Destroy()
   246  	state, err := container.State()
   247  	if err != nil {
   248  		return err
   249  	}
   250  	pid := state.InitProcessPid
   251  	currentStartTime, err := system.GetProcessStartTime(pid)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	if state.InitProcessStartTime == currentStartTime {
   256  		err = syscall.Kill(pid, 9)
   257  		syscall.Wait4(pid, nil, 0, nil)
   258  	}
   259  	return err
   260  }
   261  
   262  func (d *driver) Info(id string) execdriver.Info {
   263  	return &info{
   264  		ID:     id,
   265  		driver: d,
   266  	}
   267  }
   268  
   269  func (d *driver) Name() string {
   270  	return fmt.Sprintf("%s-%s", DriverName, Version)
   271  }
   272  
   273  func (d *driver) GetPidsForContainer(id string) ([]int, error) {
   274  	d.Lock()
   275  	active := d.activeContainers[id]
   276  	d.Unlock()
   277  
   278  	if active == nil {
   279  		return nil, fmt.Errorf("active container for %s does not exist", id)
   280  	}
   281  	return active.Processes()
   282  }
   283  
   284  func (d *driver) cleanContainer(id string) error {
   285  	d.Lock()
   286  	delete(d.activeContainers, id)
   287  	d.Unlock()
   288  	return os.RemoveAll(filepath.Join(d.root, id))
   289  }
   290  
   291  func (d *driver) createContainerRoot(id string) error {
   292  	return os.MkdirAll(filepath.Join(d.root, id), 0655)
   293  }
   294  
   295  func (d *driver) Clean(id string) error {
   296  	return os.RemoveAll(filepath.Join(d.root, id))
   297  }
   298  
   299  func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
   300  	c := d.activeContainers[id]
   301  	if c == nil {
   302  		return nil, execdriver.ErrNotRunning
   303  	}
   304  	now := time.Now()
   305  	stats, err := c.Stats()
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  	memoryLimit := c.Config().Cgroups.Memory
   310  	// if the container does not have any memory limit specified set the
   311  	// limit to the machines memory
   312  	if memoryLimit == 0 {
   313  		memoryLimit = d.machineMemory
   314  	}
   315  	return &execdriver.ResourceStats{
   316  		Stats:       stats,
   317  		Read:        now,
   318  		MemoryLimit: memoryLimit,
   319  	}, nil
   320  }
   321  
   322  type TtyConsole struct {
   323  	console libcontainer.Console
   324  }
   325  
   326  func NewTtyConsole(console libcontainer.Console, pipes *execdriver.Pipes, rootuid int) (*TtyConsole, error) {
   327  	tty := &TtyConsole{
   328  		console: console,
   329  	}
   330  
   331  	if err := tty.AttachPipes(pipes); err != nil {
   332  		tty.Close()
   333  		return nil, err
   334  	}
   335  
   336  	return tty, nil
   337  }
   338  
   339  func (t *TtyConsole) Master() libcontainer.Console {
   340  	return t.console
   341  }
   342  
   343  func (t *TtyConsole) Resize(h, w int) error {
   344  	return term.SetWinsize(t.console.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
   345  }
   346  
   347  func (t *TtyConsole) AttachPipes(pipes *execdriver.Pipes) error {
   348  	go func() {
   349  		if wb, ok := pipes.Stdout.(interface {
   350  			CloseWriters() error
   351  		}); ok {
   352  			defer wb.CloseWriters()
   353  		}
   354  
   355  		io.Copy(pipes.Stdout, t.console)
   356  	}()
   357  
   358  	if pipes.Stdin != nil {
   359  		go func() {
   360  			io.Copy(t.console, pipes.Stdin)
   361  
   362  			pipes.Stdin.Close()
   363  		}()
   364  	}
   365  
   366  	return nil
   367  }
   368  
   369  func (t *TtyConsole) Close() error {
   370  	return t.console.Close()
   371  }
   372  
   373  func setupPipes(container *configs.Config, processConfig *execdriver.ProcessConfig, p *libcontainer.Process, pipes *execdriver.Pipes) error {
   374  	var term execdriver.Terminal
   375  	var err error
   376  
   377  	if processConfig.Tty {
   378  		rootuid, err := container.HostUID()
   379  		if err != nil {
   380  			return err
   381  		}
   382  		cons, err := p.NewConsole(rootuid)
   383  		if err != nil {
   384  			return err
   385  		}
   386  		term, err = NewTtyConsole(cons, pipes, rootuid)
   387  	} else {
   388  		p.Stdout = pipes.Stdout
   389  		p.Stderr = pipes.Stderr
   390  		r, w, err := os.Pipe()
   391  		if err != nil {
   392  			return err
   393  		}
   394  		if pipes.Stdin != nil {
   395  			go func() {
   396  				io.Copy(w, pipes.Stdin)
   397  				w.Close()
   398  			}()
   399  			p.Stdin = r
   400  		}
   401  		term = &execdriver.StdConsole{}
   402  	}
   403  	if err != nil {
   404  		return err
   405  	}
   406  	processConfig.Terminal = term
   407  	return nil
   408  }