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