github.com/alexandrev/docker@v1.9.0/daemon/execdriver/lxc/driver.go (about)

     1  // +build linux
     2  
     3  package lxc
     4  
     5  import (
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path"
    14  	"path/filepath"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"syscall"
    20  	"time"
    21  
    22  	"github.com/Sirupsen/logrus"
    23  	"github.com/docker/docker/daemon/execdriver"
    24  	"github.com/docker/docker/pkg/stringutils"
    25  	sysinfo "github.com/docker/docker/pkg/system"
    26  	"github.com/docker/docker/pkg/term"
    27  	"github.com/docker/docker/pkg/version"
    28  	"github.com/kr/pty"
    29  	"github.com/opencontainers/runc/libcontainer"
    30  	"github.com/opencontainers/runc/libcontainer/cgroups"
    31  	"github.com/opencontainers/runc/libcontainer/configs"
    32  	"github.com/opencontainers/runc/libcontainer/system"
    33  	"github.com/opencontainers/runc/libcontainer/user"
    34  	"github.com/vishvananda/netns"
    35  )
    36  
    37  // DriverName for lxc driver
    38  const DriverName = "lxc"
    39  
    40  // ErrExec defines unsupported error message
    41  var ErrExec = errors.New("Unsupported: Exec is not supported by the lxc driver")
    42  
    43  // Driver contains all information for lxc driver,
    44  // it implements execdriver.Driver
    45  type Driver struct {
    46  	root             string // root path for the driver to use
    47  	libPath          string
    48  	initPath         string
    49  	apparmor         bool
    50  	sharedRoot       bool
    51  	activeContainers map[string]*activeContainer
    52  	machineMemory    int64
    53  	sync.Mutex
    54  }
    55  
    56  type activeContainer struct {
    57  	container *configs.Config
    58  	cmd       *exec.Cmd
    59  }
    60  
    61  // NewDriver returns a new lxc driver, called from NewDriver of execdriver
    62  func NewDriver(root, libPath, initPath string, apparmor bool) (*Driver, error) {
    63  	if err := os.MkdirAll(root, 0700); err != nil {
    64  		return nil, err
    65  	}
    66  	// setup unconfined symlink
    67  	if err := linkLxcStart(root); err != nil {
    68  		return nil, err
    69  	}
    70  	meminfo, err := sysinfo.ReadMemInfo()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	return &Driver{
    75  		apparmor:         apparmor,
    76  		root:             root,
    77  		libPath:          libPath,
    78  		initPath:         initPath,
    79  		sharedRoot:       rootIsShared(),
    80  		activeContainers: make(map[string]*activeContainer),
    81  		machineMemory:    meminfo.MemTotal,
    82  	}, nil
    83  }
    84  
    85  // Name implements the exec driver Driver interface.
    86  func (d *Driver) Name() string {
    87  	version := d.version()
    88  	return fmt.Sprintf("%s-%s", DriverName, version)
    89  }
    90  
    91  func setupNetNs(nsPath string) (*os.Process, error) {
    92  	runtime.LockOSThread()
    93  	defer runtime.UnlockOSThread()
    94  
    95  	origns, err := netns.Get()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	defer origns.Close()
   100  
   101  	f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
   102  	if err != nil {
   103  		return nil, fmt.Errorf("failed to get network namespace %q: %v", nsPath, err)
   104  	}
   105  	defer f.Close()
   106  
   107  	nsFD := f.Fd()
   108  	if err := netns.Set(netns.NsHandle(nsFD)); err != nil {
   109  		return nil, fmt.Errorf("failed to set network namespace %q: %v", nsPath, err)
   110  	}
   111  	defer netns.Set(origns)
   112  
   113  	cmd := exec.Command("/bin/sh", "-c", "while true; do sleep 1; done")
   114  	if err := cmd.Start(); err != nil {
   115  		return nil, fmt.Errorf("failed to start netns process: %v", err)
   116  	}
   117  
   118  	return cmd.Process, nil
   119  }
   120  
   121  func killNetNsProc(proc *os.Process) {
   122  	proc.Kill()
   123  	proc.Wait()
   124  }
   125  
   126  // Run implements the exec driver Driver interface,
   127  // it calls 'exec.Cmd' to launch lxc commands to run a container.
   128  func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
   129  	var (
   130  		term     execdriver.Terminal
   131  		err      error
   132  		dataPath = d.containerDir(c.ID)
   133  	)
   134  
   135  	if c.Network == nil || (c.Network.NamespacePath == "" && c.Network.ContainerID == "") {
   136  		return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("empty namespace path for non-container network")
   137  	}
   138  
   139  	container, err := d.createContainer(c)
   140  	if err != nil {
   141  		return execdriver.ExitStatus{ExitCode: -1}, err
   142  	}
   143  
   144  	if c.ProcessConfig.Tty {
   145  		term, err = NewTtyConsole(&c.ProcessConfig, pipes)
   146  	} else {
   147  		term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes)
   148  	}
   149  	if err != nil {
   150  		return execdriver.ExitStatus{ExitCode: -1}, err
   151  	}
   152  	c.ProcessConfig.Terminal = term
   153  
   154  	d.Lock()
   155  	d.activeContainers[c.ID] = &activeContainer{
   156  		container: container,
   157  		cmd:       &c.ProcessConfig.Cmd,
   158  	}
   159  	d.Unlock()
   160  
   161  	c.Mounts = append(c.Mounts, execdriver.Mount{
   162  		Source:      d.initPath,
   163  		Destination: c.InitPath,
   164  		Writable:    false,
   165  		Private:     true,
   166  	})
   167  
   168  	if err := d.generateEnvConfig(c); err != nil {
   169  		return execdriver.ExitStatus{ExitCode: -1}, err
   170  	}
   171  	configPath, err := d.generateLXCConfig(c)
   172  	if err != nil {
   173  		return execdriver.ExitStatus{ExitCode: -1}, err
   174  	}
   175  	params := []string{
   176  		"lxc-start",
   177  		"-n", c.ID,
   178  		"-f", configPath,
   179  		"-q",
   180  	}
   181  
   182  	// From lxc>=1.1 the default behavior is to daemonize containers after start
   183  	lxcVersion := version.Version(d.version())
   184  	if lxcVersion.GreaterThanOrEqualTo(version.Version("1.1")) {
   185  		params = append(params, "-F")
   186  	}
   187  
   188  	proc := &os.Process{}
   189  	if c.Network.ContainerID != "" {
   190  		params = append(params,
   191  			"--share-net", c.Network.ContainerID,
   192  		)
   193  	} else {
   194  		proc, err = setupNetNs(c.Network.NamespacePath)
   195  		if err != nil {
   196  			return execdriver.ExitStatus{ExitCode: -1}, err
   197  		}
   198  
   199  		pidStr := fmt.Sprintf("%d", proc.Pid)
   200  		params = append(params,
   201  			"--share-net", pidStr)
   202  	}
   203  	if c.Ipc != nil {
   204  		if c.Ipc.ContainerID != "" {
   205  			params = append(params,
   206  				"--share-ipc", c.Ipc.ContainerID,
   207  			)
   208  		} else if c.Ipc.HostIpc {
   209  			params = append(params,
   210  				"--share-ipc", "1",
   211  			)
   212  		}
   213  	}
   214  
   215  	params = append(params,
   216  		"--",
   217  		c.InitPath,
   218  	)
   219  
   220  	if c.ProcessConfig.User != "" {
   221  		params = append(params, "-u", c.ProcessConfig.User)
   222  	}
   223  
   224  	if c.ProcessConfig.Privileged {
   225  		if d.apparmor {
   226  			params[0] = path.Join(d.root, "lxc-start-unconfined")
   227  
   228  		}
   229  		params = append(params, "-privileged")
   230  	}
   231  
   232  	if c.WorkingDir != "" {
   233  		params = append(params, "-w", c.WorkingDir)
   234  	}
   235  
   236  	params = append(params, "--", c.ProcessConfig.Entrypoint)
   237  	params = append(params, c.ProcessConfig.Arguments...)
   238  
   239  	if d.sharedRoot {
   240  		// lxc-start really needs / to be non-shared, or all kinds of stuff break
   241  		// when lxc-start unmount things and those unmounts propagate to the main
   242  		// mount namespace.
   243  		// What we really want is to clone into a new namespace and then
   244  		// mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
   245  		// without exec in go we have to do this horrible shell hack...
   246  		shellString :=
   247  			"mount --make-rslave /; exec " +
   248  				stringutils.ShellQuoteArguments(params)
   249  
   250  		params = []string{
   251  			"unshare", "-m", "--", "/bin/sh", "-c", shellString,
   252  		}
   253  	}
   254  	logrus.Debugf("lxc params %s", params)
   255  	var (
   256  		name = params[0]
   257  		arg  = params[1:]
   258  	)
   259  	aname, err := exec.LookPath(name)
   260  	if err != nil {
   261  		aname = name
   262  	}
   263  	c.ProcessConfig.Path = aname
   264  	c.ProcessConfig.Args = append([]string{name}, arg...)
   265  
   266  	if err := createDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
   267  		killNetNsProc(proc)
   268  		return execdriver.ExitStatus{ExitCode: -1}, err
   269  	}
   270  
   271  	if err := c.ProcessConfig.Start(); err != nil {
   272  		killNetNsProc(proc)
   273  		return execdriver.ExitStatus{ExitCode: -1}, err
   274  	}
   275  
   276  	var (
   277  		waitErr  error
   278  		waitLock = make(chan struct{})
   279  	)
   280  
   281  	go func() {
   282  		if err := c.ProcessConfig.Wait(); err != nil {
   283  			if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
   284  				waitErr = err
   285  			}
   286  		}
   287  		close(waitLock)
   288  	}()
   289  
   290  	terminate := func(terr error) (execdriver.ExitStatus, error) {
   291  		if c.ProcessConfig.Process != nil {
   292  			c.ProcessConfig.Process.Kill()
   293  			c.ProcessConfig.Wait()
   294  		}
   295  		return execdriver.ExitStatus{ExitCode: -1}, terr
   296  	}
   297  	// Poll lxc for RUNNING status
   298  	pid, err := d.waitForStart(c, waitLock)
   299  	if err != nil {
   300  		killNetNsProc(proc)
   301  		return terminate(err)
   302  	}
   303  	killNetNsProc(proc)
   304  
   305  	cgroupPaths, err := cgroupPaths(c.ID)
   306  	if err != nil {
   307  		return terminate(err)
   308  	}
   309  
   310  	state := &libcontainer.State{
   311  		InitProcessPid: pid,
   312  		CgroupPaths:    cgroupPaths,
   313  	}
   314  
   315  	f, err := os.Create(filepath.Join(dataPath, "state.json"))
   316  	if err != nil {
   317  		return terminate(err)
   318  	}
   319  	defer f.Close()
   320  
   321  	if err := json.NewEncoder(f).Encode(state); err != nil {
   322  		return terminate(err)
   323  	}
   324  
   325  	c.ContainerPid = pid
   326  
   327  	if hooks.Start != nil {
   328  		logrus.Debugf("Invoking startCallback")
   329  		chOOM := make(chan struct{})
   330  		close(chOOM)
   331  		hooks.Start(&c.ProcessConfig, pid, chOOM)
   332  	}
   333  
   334  	oomKillNotification := notifyChannelOOM(cgroupPaths)
   335  
   336  	<-waitLock
   337  	exitCode := getExitCode(c)
   338  
   339  	_, oomKill := <-oomKillNotification
   340  	logrus.Debugf("oomKill error: %v, waitErr: %v", oomKill, waitErr)
   341  
   342  	// check oom error
   343  	if oomKill {
   344  		exitCode = 137
   345  	}
   346  
   347  	return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: oomKill}, waitErr
   348  }
   349  
   350  func notifyChannelOOM(paths map[string]string) <-chan struct{} {
   351  	oom, err := notifyOnOOM(paths)
   352  	if err != nil {
   353  		logrus.Warnf("Your kernel does not support OOM notifications: %s", err)
   354  		c := make(chan struct{})
   355  		close(c)
   356  		return c
   357  	}
   358  	return oom
   359  }
   360  
   361  // copy from libcontainer
   362  func notifyOnOOM(paths map[string]string) (<-chan struct{}, error) {
   363  	dir := paths["memory"]
   364  	if dir == "" {
   365  		return nil, fmt.Errorf("There is no path for %q in state", "memory")
   366  	}
   367  	oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control"))
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  	fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
   372  	if syserr != 0 {
   373  		oomControl.Close()
   374  		return nil, syserr
   375  	}
   376  
   377  	eventfd := os.NewFile(fd, "eventfd")
   378  
   379  	eventControlPath := filepath.Join(dir, "cgroup.event_control")
   380  	data := fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
   381  	if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
   382  		eventfd.Close()
   383  		oomControl.Close()
   384  		return nil, err
   385  	}
   386  	ch := make(chan struct{})
   387  	go func() {
   388  		defer func() {
   389  			close(ch)
   390  			eventfd.Close()
   391  			oomControl.Close()
   392  		}()
   393  		buf := make([]byte, 8)
   394  		for {
   395  			if _, err := eventfd.Read(buf); err != nil {
   396  				logrus.Warn(err)
   397  				return
   398  			}
   399  			// When a cgroup is destroyed, an event is sent to eventfd.
   400  			// So if the control path is gone, return instead of notifying.
   401  			if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
   402  				logrus.Warn(err)
   403  				return
   404  			}
   405  			ch <- struct{}{}
   406  		}
   407  	}()
   408  	return ch, nil
   409  }
   410  
   411  // createContainer populates and configures the container type with the
   412  // data provided by the execdriver.Command
   413  func (d *Driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
   414  	container := execdriver.InitContainer(c)
   415  	if err := execdriver.SetupCgroups(container, c); err != nil {
   416  		return nil, err
   417  	}
   418  	return container, nil
   419  }
   420  
   421  // Return an map of susbystem -> absolute container cgroup path
   422  func cgroupPaths(containerID string) (map[string]string, error) {
   423  	subsystems, err := cgroups.GetAllSubsystems()
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  	logrus.Debugf("subsystems: %s", subsystems)
   428  	paths := make(map[string]string)
   429  	for _, subsystem := range subsystems {
   430  		cgroupRoot, cgroupDir, err := findCgroupRootAndDir(subsystem)
   431  		logrus.Debugf("cgroup path %s %s", cgroupRoot, cgroupDir)
   432  		if err != nil {
   433  			//unsupported subystem
   434  			continue
   435  		}
   436  		// if we are running dind
   437  		dockerPathIdx := strings.LastIndex(cgroupDir, "docker")
   438  		if dockerPathIdx != -1 {
   439  			cgroupDir = cgroupDir[:dockerPathIdx-1]
   440  		}
   441  		path := filepath.Join(cgroupRoot, cgroupDir, "lxc", containerID)
   442  		paths[subsystem] = path
   443  	}
   444  
   445  	return paths, nil
   446  }
   447  
   448  // this is copy from old libcontainer nodes.go
   449  func createDeviceNodes(rootfs string, nodesToCreate []*configs.Device) error {
   450  	oldMask := syscall.Umask(0000)
   451  	defer syscall.Umask(oldMask)
   452  
   453  	for _, node := range nodesToCreate {
   454  		if err := createDeviceNode(rootfs, node); err != nil {
   455  			return err
   456  		}
   457  	}
   458  	return nil
   459  }
   460  
   461  // Creates the device node in the rootfs of the container.
   462  func createDeviceNode(rootfs string, node *configs.Device) error {
   463  	var (
   464  		dest   = filepath.Join(rootfs, node.Path)
   465  		parent = filepath.Dir(dest)
   466  	)
   467  
   468  	if err := os.MkdirAll(parent, 0755); err != nil {
   469  		return err
   470  	}
   471  
   472  	fileMode := node.FileMode
   473  	switch node.Type {
   474  	case 'c':
   475  		fileMode |= syscall.S_IFCHR
   476  	case 'b':
   477  		fileMode |= syscall.S_IFBLK
   478  	default:
   479  		return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
   480  	}
   481  
   482  	if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil && !os.IsExist(err) {
   483  		return fmt.Errorf("mknod %s %s", node.Path, err)
   484  	}
   485  
   486  	if err := syscall.Chown(dest, int(node.Uid), int(node.Gid)); err != nil {
   487  		return fmt.Errorf("chown %s to %d:%d", node.Path, node.Uid, node.Gid)
   488  	}
   489  
   490  	return nil
   491  }
   492  
   493  // setupUser changes the groups, gid, and uid for the user inside the container
   494  // copy from libcontainer, cause not it's private
   495  func setupUser(userSpec string) error {
   496  	// Set up defaults.
   497  	defaultExecUser := user.ExecUser{
   498  		Uid:  syscall.Getuid(),
   499  		Gid:  syscall.Getgid(),
   500  		Home: "/",
   501  	}
   502  	passwdPath, err := user.GetPasswdPath()
   503  	if err != nil {
   504  		return err
   505  	}
   506  	groupPath, err := user.GetGroupPath()
   507  	if err != nil {
   508  		return err
   509  	}
   510  	execUser, err := user.GetExecUserPath(userSpec, &defaultExecUser, passwdPath, groupPath)
   511  	if err != nil {
   512  		return err
   513  	}
   514  	if err := syscall.Setgroups(execUser.Sgids); err != nil {
   515  		return err
   516  	}
   517  	if err := system.Setgid(execUser.Gid); err != nil {
   518  		return err
   519  	}
   520  	if err := system.Setuid(execUser.Uid); err != nil {
   521  		return err
   522  	}
   523  	// if we didn't get HOME already, set it based on the user's HOME
   524  	if envHome := os.Getenv("HOME"); envHome == "" {
   525  		if err := os.Setenv("HOME", execUser.Home); err != nil {
   526  			return err
   527  		}
   528  	}
   529  	return nil
   530  }
   531  
   532  // getExitCode returns the exit code of the process.
   533  // If the process has not exited -1 will be returned.
   534  func getExitCode(c *execdriver.Command) int {
   535  	if c.ProcessConfig.ProcessState == nil {
   536  		return -1
   537  	}
   538  	return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
   539  }
   540  
   541  // Kill implements the exec driver Driver interface.
   542  func (d *Driver) Kill(c *execdriver.Command, sig int) error {
   543  	if sig == 9 || c.ProcessConfig.Process == nil {
   544  		return killLxc(c.ID, sig)
   545  	}
   546  
   547  	return c.ProcessConfig.Process.Signal(syscall.Signal(sig))
   548  }
   549  
   550  // Pause implements the exec driver Driver interface,
   551  // it executes lxc-freeze to pause a container.
   552  func (d *Driver) Pause(c *execdriver.Command) error {
   553  	_, err := exec.LookPath("lxc-freeze")
   554  	if err == nil {
   555  		output, errExec := exec.Command("lxc-freeze", "-n", c.ID).CombinedOutput()
   556  		if errExec != nil {
   557  			return fmt.Errorf("Err: %s Output: %s", errExec, output)
   558  		}
   559  	}
   560  
   561  	return err
   562  }
   563  
   564  // Unpause implements the exec driver Driver interface,
   565  // it executes lxc-unfreeze to unpause a container.
   566  func (d *Driver) Unpause(c *execdriver.Command) error {
   567  	_, err := exec.LookPath("lxc-unfreeze")
   568  	if err == nil {
   569  		output, errExec := exec.Command("lxc-unfreeze", "-n", c.ID).CombinedOutput()
   570  		if errExec != nil {
   571  			return fmt.Errorf("Err: %s Output: %s", errExec, output)
   572  		}
   573  	}
   574  
   575  	return err
   576  }
   577  
   578  // Terminate implements the exec driver Driver interface.
   579  func (d *Driver) Terminate(c *execdriver.Command) error {
   580  	return killLxc(c.ID, 9)
   581  }
   582  
   583  func (d *Driver) version() string {
   584  	var (
   585  		version string
   586  		output  []byte
   587  		err     error
   588  	)
   589  	if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
   590  		output, err = exec.Command("lxc-version").CombinedOutput()
   591  	} else {
   592  		output, err = exec.Command("lxc-start", "--version").CombinedOutput()
   593  	}
   594  	if err == nil {
   595  		version = strings.TrimSpace(string(output))
   596  		if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
   597  			version = strings.TrimSpace(parts[1])
   598  		}
   599  	}
   600  	return version
   601  }
   602  
   603  func killLxc(id string, sig int) error {
   604  	var (
   605  		err    error
   606  		output []byte
   607  	)
   608  	_, err = exec.LookPath("lxc-kill")
   609  	if err == nil {
   610  		output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
   611  	} else {
   612  		// lxc-stop does not take arbitrary signals like lxc-kill does
   613  		output, err = exec.Command("lxc-stop", "-k", "-n", id).CombinedOutput()
   614  	}
   615  	if err != nil {
   616  		return fmt.Errorf("Err: %s Output: %s", err, output)
   617  	}
   618  	return nil
   619  }
   620  
   621  // wait for the process to start and return the pid for the process
   622  func (d *Driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
   623  	var (
   624  		err    error
   625  		output []byte
   626  	)
   627  	// We wait for the container to be fully running.
   628  	// Timeout after 5 seconds. In case of broken pipe, just retry.
   629  	// Note: The container can run and finish correctly before
   630  	// the end of this loop
   631  	for now := time.Now(); time.Since(now) < 5*time.Second; {
   632  		select {
   633  		case <-waitLock:
   634  			// If the process dies while waiting for it, just return
   635  			return -1, nil
   636  		default:
   637  		}
   638  
   639  		output, err = d.getInfo(c.ID)
   640  		if err == nil {
   641  			info, err := parseLxcInfo(string(output))
   642  			if err != nil {
   643  				return -1, err
   644  			}
   645  			if info.Running {
   646  				return info.Pid, nil
   647  			}
   648  		}
   649  		time.Sleep(50 * time.Millisecond)
   650  	}
   651  	return -1, execdriver.ErrNotRunning
   652  }
   653  
   654  func (d *Driver) getInfo(id string) ([]byte, error) {
   655  	return exec.Command("lxc-info", "-n", id).CombinedOutput()
   656  }
   657  
   658  type info struct {
   659  	ID     string
   660  	driver *Driver
   661  }
   662  
   663  func (i *info) IsRunning() bool {
   664  	var running bool
   665  
   666  	output, err := i.driver.getInfo(i.ID)
   667  	if err != nil {
   668  		logrus.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
   669  		return false
   670  	}
   671  	if strings.Contains(string(output), "RUNNING") {
   672  		running = true
   673  	}
   674  	return running
   675  }
   676  
   677  // Info implements the exec driver Driver interface.
   678  func (d *Driver) Info(id string) execdriver.Info {
   679  	return &info{
   680  		ID:     id,
   681  		driver: d,
   682  	}
   683  }
   684  
   685  func findCgroupRootAndDir(subsystem string) (string, string, error) {
   686  	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
   687  	if err != nil {
   688  		return "", "", err
   689  	}
   690  
   691  	cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
   692  	if err != nil {
   693  		return "", "", err
   694  	}
   695  	return cgroupRoot, cgroupDir, nil
   696  }
   697  
   698  // GetPidsForContainer implements the exec driver Driver interface.
   699  func (d *Driver) GetPidsForContainer(id string) ([]int, error) {
   700  	pids := []int{}
   701  
   702  	// cpu is chosen because it is the only non optional subsystem in cgroups
   703  	subsystem := "cpu"
   704  	cgroupRoot, cgroupDir, err := findCgroupRootAndDir(subsystem)
   705  	if err != nil {
   706  		return pids, err
   707  	}
   708  
   709  	filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
   710  	if _, err := os.Stat(filename); os.IsNotExist(err) {
   711  		// With more recent lxc versions use, cgroup will be in lxc/
   712  		filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
   713  	}
   714  
   715  	output, err := ioutil.ReadFile(filename)
   716  	if err != nil {
   717  		return pids, err
   718  	}
   719  	for _, p := range strings.Split(string(output), "\n") {
   720  		if len(p) == 0 {
   721  			continue
   722  		}
   723  		pid, err := strconv.Atoi(p)
   724  		if err != nil {
   725  			return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
   726  		}
   727  		pids = append(pids, pid)
   728  	}
   729  	return pids, nil
   730  }
   731  
   732  func linkLxcStart(root string) error {
   733  	sourcePath, err := exec.LookPath("lxc-start")
   734  	if err != nil {
   735  		return err
   736  	}
   737  	targetPath := path.Join(root, "lxc-start-unconfined")
   738  
   739  	if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
   740  		return err
   741  	} else if err == nil {
   742  		if err := os.Remove(targetPath); err != nil {
   743  			return err
   744  		}
   745  	}
   746  	return os.Symlink(sourcePath, targetPath)
   747  }
   748  
   749  // TODO: This can be moved to the mountinfo reader in the mount pkg
   750  func rootIsShared() bool {
   751  	if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
   752  		for _, line := range strings.Split(string(data), "\n") {
   753  			cols := strings.Split(line, " ")
   754  			if len(cols) >= 6 && cols[4] == "/" {
   755  				return strings.HasPrefix(cols[6], "shared")
   756  			}
   757  		}
   758  	}
   759  
   760  	// No idea, probably safe to assume so
   761  	return true
   762  }
   763  
   764  func (d *Driver) containerDir(containerID string) string {
   765  	return path.Join(d.libPath, "containers", containerID)
   766  }
   767  
   768  func (d *Driver) generateLXCConfig(c *execdriver.Command) (string, error) {
   769  	root := path.Join(d.containerDir(c.ID), "config.lxc")
   770  
   771  	fo, err := os.Create(root)
   772  	if err != nil {
   773  		return "", err
   774  	}
   775  	defer fo.Close()
   776  
   777  	if err := lxcTemplateCompiled.Execute(fo, struct {
   778  		*execdriver.Command
   779  		AppArmor bool
   780  	}{
   781  		Command:  c,
   782  		AppArmor: d.apparmor,
   783  	}); err != nil {
   784  		return "", err
   785  	}
   786  
   787  	return root, nil
   788  }
   789  
   790  func (d *Driver) generateEnvConfig(c *execdriver.Command) error {
   791  	data, err := json.Marshal(c.ProcessConfig.Env)
   792  	if err != nil {
   793  		return err
   794  	}
   795  	p := path.Join(d.libPath, "containers", c.ID, "config.env")
   796  	c.Mounts = append(c.Mounts, execdriver.Mount{
   797  		Source:      p,
   798  		Destination: "/.dockerenv",
   799  		Writable:    false,
   800  		Private:     true,
   801  	})
   802  
   803  	return ioutil.WriteFile(p, data, 0600)
   804  }
   805  
   806  // Clean implements the exec driver Driver interface,
   807  // it's not implemented by lxc.
   808  func (d *Driver) Clean(id string) error {
   809  	return nil
   810  }
   811  
   812  // TtyConsole implements the exec driver Terminal interface,
   813  // it stores the master and slave ends of the container's pty.
   814  type TtyConsole struct {
   815  	MasterPty *os.File
   816  	SlavePty  *os.File
   817  }
   818  
   819  // NewTtyConsole returns a new TtyConsole struct.
   820  // Wired up to the provided process config and stdin/stdout/stderr pipes.
   821  func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
   822  	// lxc is special in that we cannot create the master outside of the container without
   823  	// opening the slave because we have nothing to provide to the cmd.  We have to open both then do
   824  	// the crazy setup on command right now instead of passing the console path to lxc and telling it
   825  	// to open up that console.  we save a couple of openfiles in the native driver because we can do
   826  	// this.
   827  	ptyMaster, ptySlave, err := pty.Open()
   828  	if err != nil {
   829  		return nil, err
   830  	}
   831  
   832  	tty := &TtyConsole{
   833  		MasterPty: ptyMaster,
   834  		SlavePty:  ptySlave,
   835  	}
   836  
   837  	if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
   838  		tty.Close()
   839  		return nil, err
   840  	}
   841  
   842  	processConfig.Console = tty.SlavePty.Name()
   843  
   844  	return tty, nil
   845  }
   846  
   847  // Resize implements Resize method of Terminal interface
   848  func (t *TtyConsole) Resize(h, w int) error {
   849  	return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
   850  }
   851  
   852  // AttachPipes attaches given pipes to exec.Cmd
   853  func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
   854  	command.Stdout = t.SlavePty
   855  	command.Stderr = t.SlavePty
   856  
   857  	go func() {
   858  		if wb, ok := pipes.Stdout.(interface {
   859  			CloseWriters() error
   860  		}); ok {
   861  			defer wb.CloseWriters()
   862  		}
   863  
   864  		io.Copy(pipes.Stdout, t.MasterPty)
   865  	}()
   866  
   867  	if pipes.Stdin != nil {
   868  		command.Stdin = t.SlavePty
   869  		command.SysProcAttr.Setctty = true
   870  
   871  		go func() {
   872  			io.Copy(t.MasterPty, pipes.Stdin)
   873  
   874  			pipes.Stdin.Close()
   875  		}()
   876  	}
   877  	return nil
   878  }
   879  
   880  // Close implements Close method of Terminal interface
   881  func (t *TtyConsole) Close() error {
   882  	t.SlavePty.Close()
   883  	return t.MasterPty.Close()
   884  }
   885  
   886  // Exec implements the exec driver Driver interface,
   887  // it is not implemented by lxc.
   888  func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
   889  	return -1, ErrExec
   890  }
   891  
   892  // Stats implements the exec driver Driver interface.
   893  // Lxc doesn't implement it's own Stats, it does some trick by implementing
   894  // execdriver.Stats to get stats info by libcontainer APIs.
   895  func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
   896  	if _, ok := d.activeContainers[id]; !ok {
   897  		return nil, fmt.Errorf("%s is not a key in active containers", id)
   898  	}
   899  	return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
   900  }
   901  
   902  // SupportsHooks implements the execdriver Driver interface.
   903  // The LXC execdriver does not support the hook mechanism, which is currently unique to runC/libcontainer.
   904  func (d *Driver) SupportsHooks() bool {
   905  	return false
   906  }