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