// InitializeMountNamespace sets up the devices, mount points, and filesystems for use inside a // new mount namespace. func InitializeMountNamespace(rootfs, console string, sysReadonly bool, mountConfig *MountConfig) error { var ( err error flag = syscall.MS_PRIVATE ) if mountConfig.NoPivotRoot { flag = syscall.MS_SLAVE } if err := syscall.Mount("", "/", "", uintptr(flag|syscall.MS_REC), ""); err != nil { return fmt.Errorf("mounting / with flags %X %s", (flag | syscall.MS_REC), err) } if err := syscall.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mouting %s as bind %s", rootfs, err) } if err := mountSystem(rootfs, sysReadonly, mountConfig); err != nil { return fmt.Errorf("mount system %s", err) } if err := setupBindmounts(rootfs, mountConfig); err != nil { return fmt.Errorf("bind mounts %s", err) } if err := nodes.CreateDeviceNodes(rootfs, mountConfig.DeviceNodes); err != nil { return fmt.Errorf("create device nodes %s", err) } if err := SetupPtmx(rootfs, console, mountConfig.MountLabel); err != nil { return err } // stdin, stdout and stderr could be pointing to /dev/null from parent namespace. // Re-open them inside this namespace. if err := reOpenDevNull(rootfs); err != nil { return fmt.Errorf("Failed to reopen /dev/null %s", err) } if err := setupDevSymlinks(rootfs); err != nil { return fmt.Errorf("dev symlinks %s", err) } if err := syscall.Chdir(rootfs); err != nil { return fmt.Errorf("chdir into %s %s", rootfs, err) } if mountConfig.NoPivotRoot { err = MsMoveRoot(rootfs) } else { err = PivotRoot(rootfs) } if err != nil { return err } if mountConfig.ReadonlyFs { if err := SetReadonly(); err != nil { return fmt.Errorf("set readonly %s", err) } } syscall.Umask(0022) return nil }
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { var ( term execdriver.Terminal err error ) if c.Tty { term, err = NewTtyConsole(c, pipes) } else { term, err = execdriver.NewStdConsole(c, pipes) } c.Terminal = term c.Mounts = append(c.Mounts, execdriver.Mount{ Source: d.initPath, Destination: c.InitPath, Writable: false, Private: true, }) if err := d.generateEnvConfig(c); 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, } if c.Network.Interface != nil { params = append(params, "-g", c.Network.Interface.Gateway, "-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), ) } params = append(params, "-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) } if len(c.CapAdd) > 0 { params = append(params, fmt.Sprintf("-cap-add=%s", strings.Join(c.CapAdd, ":"))) } if len(c.CapDrop) > 0 { params = append(params, fmt.Sprintf("-cap-drop=%s", strings.Join(c.CapDrop, ":"))) } 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 := nodes.CreateDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil { return -1, err } 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 { if c.Process != nil { c.Process.Kill() c.Wait() } return -1, err } c.ContainerPid = pid if startCallback != nil { startCallback(c) } <-waitLock return getExitCode(c), waitErr }