func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { // take the Command and populate the libcontainer.Container from it container, err := d.createContainer(c) if err != nil { return -1, err } d.Lock() d.activeContainers[c.ID] = &activeContainer{ container: container, cmd: &c.Cmd, } d.Unlock() var ( dataPath = filepath.Join(d.root, c.ID) args = append([]string{c.Entrypoint}, c.Arguments...) ) if err := d.createContainerRoot(c.ID); err != nil { return -1, err } defer d.removeContainerRoot(c.ID) if err := d.writeContainerFile(container, c.ID); err != nil { return -1, err } term := getTerminal(c, pipes) return nsinit.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Container, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd { // we need to join the rootfs because nsinit will setup the rootfs and chroot initPath := filepath.Join(c.Rootfs, c.InitPath) c.Path = d.initPath c.Args = append([]string{ initPath, "-driver", DriverName, "-console", console, "-pipe", "3", "-root", filepath.Join(d.root, c.ID), "--", }, args...) // set this to nil so that when we set the clone flags anything else is reset c.SysProcAttr = nil system.SetCloneFlags(&c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces))) c.ExtraFiles = []*os.File{child} c.Env = container.Env c.Dir = c.Rootfs return &c.Cmd }, func() { if startCallback != nil { c.ContainerPid = c.Process.Pid startCallback(c) } }) }
func main() { if len(os.Args) < 2 { log.Fatalf("invalid number of arguments %d", len(os.Args)) } container, err := loadContainer() if err != nil { log.Fatalf("unable to load container: %s", err) } switch os.Args[1] { case "exec": // this is executed outside of the namespace in the cwd var nspid, exitCode int if nspid, err = readPid(); err != nil && !os.IsNotExist(err) { log.Fatalf("unable to read pid: %s", err) } if nspid > 0 { exitCode, err = nsinit.ExecIn(container, nspid, os.Args[2:]) } else { term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty) exitCode, err = nsinit.Exec(container, term, "", dataPath, os.Args[2:], nsinit.DefaultCreateCommand, nil) } if err != nil { log.Fatalf("failed to exec: %s", err) } os.Exit(exitCode) case "init": // this is executed inside of the namespace to setup the container // by default our current dir is always our rootfs rootfs, err := os.Getwd() if err != nil { log.Fatal(err) } pipeFd, err := strconv.Atoi(rawPipeFd) if err != nil { log.Fatal(err) } syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd)) if err != nil { log.Fatalf("unable to create sync pipe: %s", err) } if err := nsinit.Init(container, rootfs, console, syncPipe, os.Args[2:]); err != nil { log.Fatalf("unable to initialize for container: %s", err) } default: log.Fatalf("command not supported for nsinit %s", os.Args[0]) } }
// startContainer starts the container. Returns the exit status or -1 and an // error. // // Signals sent to the current process will be forwarded to container. func startContainer(container *libcontainer.Container, term nsinit.Terminal, dataPath string, args []string) (int, error) { var ( cmd *exec.Cmd sigc = make(chan os.Signal, 10) ) signal.Notify(sigc) createCommand := func(container *libcontainer.Container, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { cmd = nsinit.DefaultCreateCommand(container, console, rootfs, dataPath, init, pipe, args) return cmd } startCallback := func() { go func() { for sig := range sigc { cmd.Process.Signal(sig) } }() } return nsinit.Exec(container, term, "", dataPath, args, createCommand, startCallback) }