github.com/scorpionis/docker@v1.6.0-rc7/daemon/execdriver/native/driver.go (about)

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