summaryrefslogtreecommitdiff
path: root/execdriver/lxc/driver.go
diff options
context:
space:
mode:
Diffstat (limited to 'execdriver/lxc/driver.go')
-rw-r--r--execdriver/lxc/driver.go389
1 files changed, 0 insertions, 389 deletions
diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go
deleted file mode 100644
index 765a52ee43..0000000000
--- a/execdriver/lxc/driver.go
+++ /dev/null
@@ -1,389 +0,0 @@
-package lxc
-
-import (
- "fmt"
- "github.com/dotcloud/docker/execdriver"
- "github.com/dotcloud/docker/pkg/cgroups"
- "github.com/dotcloud/docker/utils"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "strconv"
- "strings"
- "syscall"
- "time"
-)
-
-const DriverName = "lxc"
-
-func init() {
- execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
- if err := setupHostname(args); err != nil {
- return err
- }
-
- if err := setupNetworking(args); err != nil {
- return err
- }
-
- if err := setupCapabilities(args); err != nil {
- return err
- }
-
- if err := setupWorkingDirectory(args); err != nil {
- return err
- }
-
- if err := changeUser(args); err != nil {
- return err
- }
-
- path, err := exec.LookPath(args.Args[0])
- if err != nil {
- log.Printf("Unable to locate %v", args.Args[0])
- os.Exit(127)
- }
- if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
- return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
- }
- panic("Unreachable")
- })
-}
-
-type driver struct {
- root string // root path for the driver to use
- apparmor bool
- sharedRoot bool
-}
-
-func NewDriver(root string, apparmor bool) (*driver, error) {
- // setup unconfined symlink
- if err := linkLxcStart(root); err != nil {
- return nil, err
- }
- return &driver{
- apparmor: apparmor,
- root: root,
- sharedRoot: rootIsShared(),
- }, nil
-}
-
-func (d *driver) Name() string {
- version := d.version()
- return fmt.Sprintf("%s-%s", DriverName, version)
-}
-
-func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
- if err := execdriver.SetTerminal(c, pipes); err != nil {
- return -1, err
- }
- configPath, err := d.generateLXCConfig(c)
- if err != nil {
- return -1, err
- }
- params := []string{
- "lxc-start",
- "-n", c.ID,
- "-f", configPath,
- "--",
- c.InitPath,
- "-driver",
- DriverName,
- }
-
- if c.Network != nil {
- params = append(params,
- "-g", c.Network.Gateway,
- "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
- "-mtu", strconv.Itoa(c.Network.Mtu),
- )
- }
-
- if c.User != "" {
- params = append(params, "-u", c.User)
- }
-
- if c.Privileged {
- if d.apparmor {
- params[0] = path.Join(d.root, "lxc-start-unconfined")
-
- }
- params = append(params, "-privileged")
- }
-
- if c.WorkingDir != "" {
- params = append(params, "-w", c.WorkingDir)
- }
-
- params = append(params, "--", c.Entrypoint)
- params = append(params, c.Arguments...)
-
- if d.sharedRoot {
- // lxc-start really needs / to be non-shared, or all kinds of stuff break
- // when lxc-start unmount things and those unmounts propagate to the main
- // mount namespace.
- // What we really want is to clone into a new namespace and then
- // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
- // without exec in go we have to do this horrible shell hack...
- shellString :=
- "mount --make-rslave /; exec " +
- utils.ShellQuoteArguments(params)
-
- params = []string{
- "unshare", "-m", "--", "/bin/sh", "-c", shellString,
- }
- }
-
- var (
- name = params[0]
- arg = params[1:]
- )
- aname, err := exec.LookPath(name)
- if err != nil {
- aname = name
- }
- c.Path = aname
- c.Args = append([]string{name}, arg...)
-
- if err := c.Start(); err != nil {
- return -1, err
- }
-
- var (
- waitErr error
- waitLock = make(chan struct{})
- )
- go func() {
- if err := c.Wait(); err != nil {
- if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
- waitErr = err
- }
- }
- close(waitLock)
- }()
-
- // Poll lxc for RUNNING status
- pid, err := d.waitForStart(c, waitLock)
- if err != nil {
- return -1, err
- }
- c.ContainerPid = pid
-
- if startCallback != nil {
- startCallback(c)
- }
-
- <-waitLock
-
- return getExitCode(c), waitErr
-}
-
-/// Return the exit code of the process
-// if the process has not exited -1 will be returned
-func getExitCode(c *execdriver.Command) int {
- if c.ProcessState == nil {
- return -1
- }
- return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
-}
-
-func (d *driver) Kill(c *execdriver.Command, sig int) error {
- return KillLxc(c.ID, sig)
-}
-
-func (d *driver) version() string {
- var (
- version string
- output []byte
- err error
- )
- if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
- output, err = exec.Command("lxc-version").CombinedOutput()
- } else {
- output, err = exec.Command("lxc-start", "--version").CombinedOutput()
- }
- if err == nil {
- version = strings.TrimSpace(string(output))
- if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
- version = strings.TrimSpace(parts[1])
- }
- }
- return version
-}
-
-func KillLxc(id string, sig int) error {
- var (
- err error
- output []byte
- )
- _, err = exec.LookPath("lxc-kill")
- if err == nil {
- output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
- } else {
- output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
- }
- if err != nil {
- return fmt.Errorf("Err: %s Output: %s", err, output)
- }
- return nil
-}
-
-// wait for the process to start and return the pid for the process
-func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
- var (
- err error
- output []byte
- )
- // We wait for the container to be fully running.
- // Timeout after 5 seconds. In case of broken pipe, just retry.
- // Note: The container can run and finish correctly before
- // the end of this loop
- for now := time.Now(); time.Since(now) < 5*time.Second; {
- select {
- case <-waitLock:
- // If the process dies while waiting for it, just return
- return -1, nil
- default:
- }
-
- output, err = d.getInfo(c.ID)
- if err != nil {
- output, err = d.getInfo(c.ID)
- if err != nil {
- return -1, err
- }
- }
- info, err := parseLxcInfo(string(output))
- if err != nil {
- return -1, err
- }
- if info.Running {
- return info.Pid, nil
- }
- time.Sleep(50 * time.Millisecond)
- }
- return -1, execdriver.ErrNotRunning
-}
-
-func (d *driver) getInfo(id string) ([]byte, error) {
- return exec.Command("lxc-info", "-n", id).CombinedOutput()
-}
-
-type info struct {
- ID string
- driver *driver
-}
-
-func (i *info) IsRunning() bool {
- var running bool
-
- output, err := i.driver.getInfo(i.ID)
- if err != nil {
- utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
- return false
- }
- if strings.Contains(string(output), "RUNNING") {
- running = true
- }
- return running
-}
-
-func (d *driver) Info(id string) execdriver.Info {
- return &info{
- ID: id,
- driver: d,
- }
-}
-
-func (d *driver) GetPidsForContainer(id string) ([]int, error) {
- pids := []int{}
-
- // cpu is chosen because it is the only non optional subsystem in cgroups
- subsystem := "cpu"
- cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
- if err != nil {
- return pids, err
- }
-
- cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
- if err != nil {
- return pids, err
- }
-
- filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
- if _, err := os.Stat(filename); os.IsNotExist(err) {
- // With more recent lxc versions use, cgroup will be in lxc/
- filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
- }
-
- output, err := ioutil.ReadFile(filename)
- if err != nil {
- return pids, err
- }
- for _, p := range strings.Split(string(output), "\n") {
- if len(p) == 0 {
- continue
- }
- pid, err := strconv.Atoi(p)
- if err != nil {
- return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
- }
- pids = append(pids, pid)
- }
- return pids, nil
-}
-
-func linkLxcStart(root string) error {
- sourcePath, err := exec.LookPath("lxc-start")
- if err != nil {
- return err
- }
- targetPath := path.Join(root, "lxc-start-unconfined")
-
- if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
- return err
- } else if err == nil {
- if err := os.Remove(targetPath); err != nil {
- return err
- }
- }
- return os.Symlink(sourcePath, targetPath)
-}
-
-// TODO: This can be moved to the mountinfo reader in the mount pkg
-func rootIsShared() bool {
- if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
- for _, line := range strings.Split(string(data), "\n") {
- cols := strings.Split(line, " ")
- if len(cols) >= 6 && cols[4] == "/" {
- return strings.HasPrefix(cols[6], "shared")
- }
- }
- }
-
- // No idea, probably safe to assume so
- return true
-}
-
-func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
- root := path.Join(d.root, "containers", c.ID, "config.lxc")
- fo, err := os.Create(root)
- if err != nil {
- return "", err
- }
- defer fo.Close()
-
- if err := LxcTemplateCompiled.Execute(fo, struct {
- *execdriver.Command
- AppArmor bool
- }{
- Command: c,
- AppArmor: d.apparmor,
- }); err != nil {
- return "", err
- }
- return root, nil
-}