소스 검색

Merge pull request #5515 from crosbymichael/refactor-libcontainer2

Remove CommandFactory and NsInit interface
Guillaume J. Charmes 11 년 전
부모
커밋
fe4a25546a

+ 45 - 53
daemon/execdriver/native/driver.go

@@ -27,10 +27,7 @@ const (
 
 
 func init() {
 func init() {
 	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
 	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
-		var (
-			container *libcontainer.Container
-			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{})
-		)
+		var container *libcontainer.Container
 		f, err := os.Open(filepath.Join(args.Root, "container.json"))
 		f, err := os.Open(filepath.Join(args.Root, "container.json"))
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -41,7 +38,7 @@ func init() {
 		}
 		}
 		f.Close()
 		f.Close()
 
 
-		cwd, err := os.Getwd()
+		rootfs, err := os.Getwd()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -49,7 +46,7 @@ func init() {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
+		if err := nsinit.Init(container, rootfs, args.Console, syncPipe, args.Args); err != nil {
 			return err
 			return err
 		}
 		}
 		return nil
 		return nil
@@ -93,31 +90,44 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	d.activeContainers[c.ID] = &c.Cmd
 	d.activeContainers[c.ID] = &c.Cmd
 
 
 	var (
 	var (
-		term    nsinit.Terminal
-		factory = &dockerCommandFactory{c: c, driver: d}
-		pidRoot = filepath.Join(d.root, c.ID)
-		ns      = nsinit.NewNsInit(factory)
-		args    = append([]string{c.Entrypoint}, c.Arguments...)
+		dataPath = filepath.Join(d.root, c.ID)
+		args     = append([]string{c.Entrypoint}, c.Arguments...)
 	)
 	)
 	if err := d.createContainerRoot(c.ID); err != nil {
 	if err := d.createContainerRoot(c.ID); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
 	defer d.removeContainerRoot(c.ID)
 	defer d.removeContainerRoot(c.ID)
 
 
-	if c.Tty {
-		term = &dockerTtyTerm{
-			pipes: pipes,
-		}
-	} else {
-		term = &dockerStdTerm{
-			pipes: pipes,
-		}
-	}
-	c.Terminal = term
 	if err := d.writeContainerFile(container, c.ID); err != nil {
 	if err := d.writeContainerFile(container, c.ID); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
-	return ns.Exec(container, term, pidRoot, args, func() {
+
+	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 {
 		if startCallback != nil {
 			startCallback(c)
 			startCallback(c)
 		}
 		}
@@ -234,35 +244,17 @@ func getEnv(key string, env []string) string {
 	return ""
 	return ""
 }
 }
 
 
-type dockerCommandFactory struct {
-	c      *execdriver.Command
-	driver *driver
-}
-
-// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
-// defined on the container's configuration and use the current binary as the init with the
-// args provided
-func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
-	// we need to join the rootfs because nsinit will setup the rootfs and chroot
-	initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
-
-	d.c.Path = d.driver.initPath
-	d.c.Args = append([]string{
-		initPath,
-		"-driver", DriverName,
-		"-console", console,
-		"-pipe", "3",
-		"-root", filepath.Join(d.driver.root, d.c.ID),
-		"--",
-	}, args...)
-
-	// set this to nil so that when we set the clone flags anything else is reset
-	d.c.SysProcAttr = nil
-	system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
-	d.c.ExtraFiles = []*os.File{syncFile}
-
-	d.c.Env = container.Env
-	d.c.Dir = d.c.Rootfs
-
-	return &d.c.Cmd
+func getTerminal(c *execdriver.Command, pipes *execdriver.Pipes) nsinit.Terminal {
+	var term nsinit.Terminal
+	if c.Tty {
+		term = &dockerTtyTerm{
+			pipes: pipes,
+		}
+	} else {
+		term = &dockerStdTerm{
+			pipes: pipes,
+		}
+	}
+	c.Terminal = term
+	return term
 }
 }

+ 1 - 150
pkg/libcontainer/README.md

@@ -13,160 +13,11 @@ a `container.json` file is placed with the runtime configuration for how the pro
 should be contained and ran.  Environment, networking, and different capabilities for the 
 should be contained and ran.  Environment, networking, and different capabilities for the 
 process are specified in this file.  The configuration is used for each process executed inside the container.
 process are specified in this file.  The configuration is used for each process executed inside the container.
 
 
-Sample `container.json` file:
-```json
-{
-   "mounts" : [
-      {
-         "type" : "devtmpfs"
-      }
-   ],
-   "tty" : true,
-   "environment" : [
-      "HOME=/",
-      "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin",
-      "container=docker",
-      "TERM=xterm-256color"
-   ],
-   "hostname" : "koye",
-   "cgroups" : {
-      "parent" : "docker",
-      "name" : "docker-koye"
-   },
-   "capabilities_mask" : [
-      {
-         "value" : 8,
-         "key" : "SETPCAP",
-         "enabled" : false
-      },
-      {
-         "enabled" : false,
-         "value" : 16,
-         "key" : "SYS_MODULE"
-      },
-      {
-         "value" : 17,
-         "key" : "SYS_RAWIO",
-         "enabled" : false
-      },
-      {
-         "key" : "SYS_PACCT",
-         "value" : 20,
-         "enabled" : false
-      },
-      {
-         "value" : 21,
-         "key" : "SYS_ADMIN",
-         "enabled" : false
-      },
-      {
-         "value" : 23,
-         "key" : "SYS_NICE",
-         "enabled" : false
-      },
-      {
-         "value" : 24,
-         "key" : "SYS_RESOURCE",
-         "enabled" : false
-      },
-      {
-         "key" : "SYS_TIME",
-         "value" : 25,
-         "enabled" : false
-      },
-      {
-         "enabled" : false,
-         "value" : 26,
-         "key" : "SYS_TTY_CONFIG"
-      },
-      {
-         "key" : "AUDIT_WRITE",
-         "value" : 29,
-         "enabled" : false
-      },
-      {
-         "value" : 30,
-         "key" : "AUDIT_CONTROL",
-         "enabled" : false
-      },
-      {
-         "enabled" : false,
-         "key" : "MAC_OVERRIDE",
-         "value" : 32
-      },
-      {
-         "enabled" : false,
-         "key" : "MAC_ADMIN",
-         "value" : 33
-      },
-      {
-         "key" : "NET_ADMIN",
-         "value" : 12,
-         "enabled" : false
-      },
-      {
-         "value" : 27,
-         "key" : "MKNOD",
-         "enabled" : true
-      }
-   ],
-   "networks" : [
-      {
-         "mtu" : 1500,
-         "address" : "127.0.0.1/0",
-         "type" : "loopback",
-         "gateway" : "localhost"
-      },
-      {
-         "mtu" : 1500,
-         "address" : "172.17.42.2/16",
-         "type" : "veth",
-         "context" : {
-            "bridge" : "docker0",
-            "prefix" : "veth"
-         },
-         "gateway" : "172.17.42.1"
-      }
-   ],
-   "namespaces" : [
-      {
-         "key" : "NEWNS",
-         "value" : 131072,
-         "enabled" : true,
-         "file" : "mnt"
-      },
-      {
-         "key" : "NEWUTS",
-         "value" : 67108864,
-         "enabled" : true,
-         "file" : "uts"
-      },
-      {
-         "enabled" : true,
-         "file" : "ipc",
-         "key" : "NEWIPC",
-         "value" : 134217728
-      },
-      {
-         "file" : "pid",
-         "enabled" : true,
-         "value" : 536870912,
-         "key" : "NEWPID"
-      },
-      {
-         "enabled" : true,
-         "file" : "net",
-         "key" : "NEWNET",
-         "value" : 1073741824
-      }
-   ]
-}
-```
+See the `container.json` file for what the configuration should look like.
 
 
 Using this configuration and the current directory holding the rootfs for a process, one can use libcontainer to exec the container. Running the life of the namespace, a `pid` file 
 Using this configuration and the current directory holding the rootfs for a process, one can use libcontainer to exec the container. Running the life of the namespace, a `pid` file 
 is written to the current directory with the pid of the namespaced process to the external world.  A client can use this pid to wait, kill, or perform other operation with the container.  If a user tries to run a new process inside an existing container with a live namespace, the namespace will be joined by the new process.
 is written to the current directory with the pid of the namespaced process to the external world.  A client can use this pid to wait, kill, or perform other operation with the container.  If a user tries to run a new process inside an existing container with a live namespace, the namespace will be joined by the new process.
 
 
-
 You may also specify an alternate root place where the `container.json` file is read and where the `pid` file will be saved.
 You may also specify an alternate root place where the `container.json` file is read and where the `pid` file will be saved.
 
 
 #### nsinit
 #### nsinit

+ 0 - 47
pkg/libcontainer/nsinit/command.go

@@ -1,47 +0,0 @@
-package nsinit
-
-import (
-	"github.com/dotcloud/docker/pkg/libcontainer"
-	"github.com/dotcloud/docker/pkg/system"
-	"os"
-	"os/exec"
-)
-
-// CommandFactory takes the container's configuration and options passed by the
-// parent processes and creates an *exec.Cmd that will be used to fork/exec the
-// namespaced init process
-type CommandFactory interface {
-	Create(container *libcontainer.Container, console string, syncFd *os.File, args []string) *exec.Cmd
-}
-
-type DefaultCommandFactory struct {
-	Root string
-}
-
-// Create will return an exec.Cmd with the Cloneflags set to the proper namespaces
-// defined on the container's configuration and use the current binary as the init with the
-// args provided
-func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console string, pipe *os.File, args []string) *exec.Cmd {
-	// get our binary name from arg0 so we can always reexec ourself
-	command := exec.Command(os.Args[0], append([]string{
-		"-console", console,
-		"-pipe", "3",
-		"-root", c.Root,
-		"init"}, args...)...)
-
-	system.SetCloneFlags(command, uintptr(GetNamespaceFlags(container.Namespaces)))
-	command.Env = container.Env
-	command.ExtraFiles = []*os.File{pipe}
-	return command
-}
-
-// GetNamespaceFlags parses the container's Namespaces options to set the correct
-// flags on clone, unshare, and setns
-func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
-	for _, ns := range namespaces {
-		if ns.Enabled {
-			flag |= ns.Value
-		}
-	}
-	return flag
-}

+ 10 - 0
pkg/libcontainer/nsinit/create.go

@@ -0,0 +1,10 @@
+package nsinit
+
+import (
+	"os"
+	"os/exec"
+
+	"github.com/dotcloud/docker/pkg/libcontainer"
+)
+
+type CreateCommand func(container *libcontainer.Container, console, rootfs, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd

+ 51 - 26
pkg/libcontainer/nsinit/exec.go

@@ -3,11 +3,8 @@
 package nsinit
 package nsinit
 
 
 import (
 import (
-	"fmt"
-	"io/ioutil"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
-	"path/filepath"
 	"syscall"
 	"syscall"
 
 
 	"github.com/dotcloud/docker/pkg/cgroups"
 	"github.com/dotcloud/docker/pkg/cgroups"
@@ -20,7 +17,7 @@ import (
 
 
 // Exec performes setup outside of a namespace so that a container can be
 // Exec performes setup outside of a namespace so that a container can be
 // executed.  Exec is a high level function for working with container namespaces.
 // executed.  Exec is a high level function for working with container namespaces.
-func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) {
+func Exec(container *libcontainer.Container, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
 	var (
 	var (
 		master  *os.File
 		master  *os.File
 		console string
 		console string
@@ -42,7 +39,7 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoo
 		term.SetMaster(master)
 		term.SetMaster(master)
 	}
 	}
 
 
-	command := ns.commandFactory.Create(container, console, syncPipe.child, args)
+	command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.child, args)
 	if err := term.Attach(command); err != nil {
 	if err := term.Attach(command); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
@@ -56,11 +53,11 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoo
 	if err != nil {
 	if err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
-	if err := WritePid(pidRoot, command.Process.Pid, started); err != nil {
+	if err := WritePid(dataPath, command.Process.Pid, started); err != nil {
 		command.Process.Kill()
 		command.Process.Kill()
 		return -1, err
 		return -1, err
 	}
 	}
-	defer DeletePid(pidRoot)
+	defer DeletePid(dataPath)
 
 
 	// Do this before syncing with child so that no children
 	// Do this before syncing with child so that no children
 	// can escape the cgroup
 	// can escape the cgroup
@@ -90,8 +87,45 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoo
 			return -1, err
 			return -1, err
 		}
 		}
 	}
 	}
-	status := command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
-	return status, err
+	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
+}
+
+// DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
+// defined on the container's configuration and use the current binary as the init with the
+// args provided
+//
+// console: the /dev/console to setup inside the container
+// init: the progam executed inside the namespaces
+// root: the path to the container json file and information
+// pipe: sync pipe to syncronize the parent and child processes
+// args: the arguemnts to pass to the container to run as the user's program
+func DefaultCreateCommand(container *libcontainer.Container, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd {
+	// get our binary name from arg0 so we can always reexec ourself
+	env := []string{
+		"console=" + console,
+		"pipe=3",
+		"data_path=" + dataPath,
+	}
+
+	/*
+	   TODO: move user and wd into env
+	   if user != "" {
+	       env = append(env, "user="+user)
+	   }
+	   if workingDir != "" {
+	       env = append(env, "wd="+workingDir)
+	   }
+	*/
+
+	command := exec.Command(init, append([]string{"init"}, args...)...)
+	// make sure the process is executed inside the context of the rootfs
+	command.Dir = rootfs
+	command.Env = append(os.Environ(), env...)
+
+	system.SetCloneFlags(command, uintptr(GetNamespaceFlags(container.Namespaces)))
+	command.ExtraFiles = []*os.File{pipe}
+
+	return command
 }
 }
 
 
 // SetupCgroups applies the cgroup restrictions to the process running in the contaienr based
 // SetupCgroups applies the cgroup restrictions to the process running in the contaienr based
@@ -123,22 +157,13 @@ func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *Sy
 	return pipe.SendToChild(context)
 	return pipe.SendToChild(context)
 }
 }
 
 
-// WritePid writes the namespaced processes pid to pid and it's start time
-// to the path specified
-func WritePid(path string, pid int, startTime string) error {
-	err := ioutil.WriteFile(filepath.Join(path, "pid"), []byte(fmt.Sprint(pid)), 0655)
-	if err != nil {
-		return err
-	}
-	return ioutil.WriteFile(filepath.Join(path, "start"), []byte(startTime), 0655)
-}
-
-// DeletePid removes the pid and started file from disk when the container's process
-// dies and the container is cleanly removed
-func DeletePid(path string) error {
-	err := os.Remove(filepath.Join(path, "pid"))
-	if serr := os.Remove(filepath.Join(path, "start")); err == nil {
-		err = serr
+// GetNamespaceFlags parses the container's Namespaces options to set the correct
+// flags on clone, unshare, and setns
+func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
+	for _, ns := range namespaces {
+		if ns.Enabled {
+			flag |= ns.Value
+		}
 	}
 	}
-	return err
+	return flag
 }
 }

+ 11 - 5
pkg/libcontainer/nsinit/execin.go

@@ -16,16 +16,22 @@ import (
 )
 )
 
 
 // ExecIn uses an existing pid and joins the pid's namespaces with the new command.
 // ExecIn uses an existing pid and joins the pid's namespaces with the new command.
-func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
+func ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
+	// clear the current processes env and replace it with the environment
+	// defined on the container
+	if err := LoadContainerEnvironment(container); err != nil {
+		return -1, err
+	}
+
 	for _, nsv := range container.Namespaces {
 	for _, nsv := range container.Namespaces {
 		// skip the PID namespace on unshare because it it not supported
 		// skip the PID namespace on unshare because it it not supported
-		if nsv.Key != "NEWPID" {
+		if nsv.Enabled && nsv.Key != "NEWPID" {
 			if err := system.Unshare(nsv.Value); err != nil {
 			if err := system.Unshare(nsv.Value); err != nil {
 				return -1, err
 				return -1, err
 			}
 			}
 		}
 		}
 	}
 	}
-	fds, err := ns.getNsFds(nspid, container)
+	fds, err := getNsFds(nspid, container)
 	closeFds := func() {
 	closeFds := func() {
 		for _, f := range fds {
 		for _, f := range fds {
 			system.Closefd(f)
 			system.Closefd(f)
@@ -82,7 +88,7 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s
 		os.Exit(state.Sys().(syscall.WaitStatus).ExitStatus())
 		os.Exit(state.Sys().(syscall.WaitStatus).ExitStatus())
 	}
 	}
 dropAndExec:
 dropAndExec:
-	if err := finalizeNamespace(container); err != nil {
+	if err := FinalizeNamespace(container); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
 	err = label.SetProcessLabel(processLabel)
 	err = label.SetProcessLabel(processLabel)
@@ -95,7 +101,7 @@ dropAndExec:
 	panic("unreachable")
 	panic("unreachable")
 }
 }
 
 
-func (ns *linuxNs) getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
+func getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
 	fds := make([]uintptr, len(container.Namespaces))
 	fds := make([]uintptr, len(container.Namespaces))
 	for i, ns := range container.Namespaces {
 	for i, ns := range container.Namespaces {
 		f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", ns.File), os.O_RDONLY, 0)
 		f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", ns.File), os.O_RDONLY, 0)

+ 26 - 9
pkg/libcontainer/nsinit/init.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
+	"strings"
 	"syscall"
 	"syscall"
 
 
 	"github.com/dotcloud/docker/pkg/apparmor"
 	"github.com/dotcloud/docker/pkg/apparmor"
@@ -22,12 +23,18 @@ import (
 
 
 // Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
 // Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
 // and other options required for the new container.
 // and other options required for the new container.
-func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error {
+func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error {
 	rootfs, err := utils.ResolveRootfs(uncleanRootfs)
 	rootfs, err := utils.ResolveRootfs(uncleanRootfs)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	// clear the current processes env and replace it with the environment
+	// defined on the container
+	if err := LoadContainerEnvironment(container); err != nil {
+		return err
+	}
+
 	// We always read this as it is a way to sync with the parent as well
 	// We always read this as it is a way to sync with the parent as well
 	context, err := syncPipe.ReadFromParent()
 	context, err := syncPipe.ReadFromParent()
 	if err != nil {
 	if err != nil {
@@ -54,23 +61,22 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
 	}
 	}
 
 
 	label.Init()
 	label.Init()
+
 	if err := mount.InitializeMountNamespace(rootfs, consolePath, container); err != nil {
 	if err := mount.InitializeMountNamespace(rootfs, consolePath, container); err != nil {
 		return fmt.Errorf("setup mount namespace %s", err)
 		return fmt.Errorf("setup mount namespace %s", err)
 	}
 	}
 	if err := system.Sethostname(container.Hostname); err != nil {
 	if err := system.Sethostname(container.Hostname); err != nil {
 		return fmt.Errorf("sethostname %s", err)
 		return fmt.Errorf("sethostname %s", err)
 	}
 	}
-	if err := finalizeNamespace(container); err != nil {
+	if err := FinalizeNamespace(container); err != nil {
 		return fmt.Errorf("finalize namespace %s", err)
 		return fmt.Errorf("finalize namespace %s", err)
 	}
 	}
 
 
-	if profile := container.Context["apparmor_profile"]; profile != "" {
-		if err := apparmor.ApplyProfile(os.Getpid(), profile); err != nil {
-			return err
-		}
-	}
 	runtime.LockOSThread()
 	runtime.LockOSThread()
 
 
+	if err := apparmor.ApplyProfile(os.Getpid(), container.Context["apparmor_profile"]); err != nil {
+		return err
+	}
 	if err := label.SetProcessLabel(container.Context["process_label"]); err != nil {
 	if err := label.SetProcessLabel(container.Context["process_label"]); err != nil {
 		return fmt.Errorf("set process label %s", err)
 		return fmt.Errorf("set process label %s", err)
 	}
 	}
@@ -113,10 +119,10 @@ func setupNetwork(container *libcontainer.Container, context libcontainer.Contex
 	return nil
 	return nil
 }
 }
 
 
-// finalizeNamespace drops the caps, sets the correct user
+// FinalizeNamespace drops the caps, sets the correct user
 // and working dir, and closes any leaky file descriptors
 // and working dir, and closes any leaky file descriptors
 // before execing the command inside the namespace
 // before execing the command inside the namespace
-func finalizeNamespace(container *libcontainer.Container) error {
+func FinalizeNamespace(container *libcontainer.Container) error {
 	if err := capabilities.DropCapabilities(container); err != nil {
 	if err := capabilities.DropCapabilities(container); err != nil {
 		return fmt.Errorf("drop capabilities %s", err)
 		return fmt.Errorf("drop capabilities %s", err)
 	}
 	}
@@ -133,3 +139,14 @@ func finalizeNamespace(container *libcontainer.Container) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func LoadContainerEnvironment(container *libcontainer.Container) error {
+	os.Clearenv()
+	for _, pair := range container.Env {
+		p := strings.SplitN(pair, "=", 2)
+		if err := os.Setenv(p[0], p[1]); err != nil {
+			return err
+		}
+	}
+	return nil
+}

+ 0 - 22
pkg/libcontainer/nsinit/nsinit.go

@@ -1,22 +0,0 @@
-package nsinit
-
-import "github.com/dotcloud/docker/pkg/libcontainer"
-
-// NsInit is an interface with the public facing methods to provide high level
-// exec operations on a container
-type NsInit interface {
-	Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error)
-	ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
-	Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error
-}
-
-type linuxNs struct {
-	root           string
-	commandFactory CommandFactory
-}
-
-func NewNsInit(command CommandFactory) NsInit {
-	return &linuxNs{
-		commandFactory: command,
-	}
-}

+ 29 - 45
pkg/libcontainer/nsinit/nsinit/main.go

@@ -2,7 +2,6 @@ package main
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
-	"flag"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
 	"os"
 	"os"
@@ -14,76 +13,65 @@ import (
 )
 )
 
 
 var (
 var (
-	root, console, logs string
-	pipeFd              int
+	dataPath  = os.Getenv("data_path")
+	console   = os.Getenv("console")
+	rawPipeFd = os.Getenv("pipe")
 )
 )
 
 
-func registerFlags() {
-	flag.StringVar(&console, "console", "", "console (pty slave) path")
-	flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
-	flag.StringVar(&root, "root", ".", "root for storing configuration data")
-	flag.StringVar(&logs, "log", "none", "set stderr or a filepath to enable logging")
-
-	flag.Parse()
-}
-
 func main() {
 func main() {
-	registerFlags()
-
-	if flag.NArg() < 1 {
-		log.Fatalf("wrong number of arguments %d", flag.NArg())
-	}
-	container, err := loadContainer()
-	if err != nil {
-		log.Fatalf("Unable to load container: %s", err)
+	if len(os.Args) < 2 {
+		log.Fatalf("invalid number of arguments %d", len(os.Args))
 	}
 	}
 
 
-	ns, err := newNsInit()
+	container, err := loadContainer()
 	if err != nil {
 	if err != nil {
-		log.Fatalf("Unable to initialize nsinit: %s", err)
+		log.Fatalf("unable to load container: %s", err)
 	}
 	}
 
 
-	switch flag.Arg(0) {
+	switch os.Args[1] {
 	case "exec": // this is executed outside of the namespace in the cwd
 	case "exec": // this is executed outside of the namespace in the cwd
-		var exitCode int
-		nspid, err := readPid()
-		if err != nil {
-			if !os.IsNotExist(err) {
-				log.Fatalf("Unable to read pid: %s", err)
-			}
+		var nspid, exitCode int
+		if nspid, err = readPid(); err != nil && !os.IsNotExist(err) {
+			log.Fatalf("unable to read pid: %s", err)
 		}
 		}
+
 		if nspid > 0 {
 		if nspid > 0 {
-			exitCode, err = ns.ExecIn(container, nspid, flag.Args()[1:])
+			exitCode, err = nsinit.ExecIn(container, nspid, os.Args[2:])
 		} else {
 		} else {
 			term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
 			term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
-			exitCode, err = ns.Exec(container, term, root, flag.Args()[1:], nil)
+			exitCode, err = nsinit.Exec(container, term, "", dataPath, os.Args[2:], nsinit.DefaultCreateCommand, nil)
 		}
 		}
+
 		if err != nil {
 		if err != nil {
-			log.Fatalf("Failed to exec: %s", err)
+			log.Fatalf("failed to exec: %s", err)
 		}
 		}
 		os.Exit(exitCode)
 		os.Exit(exitCode)
 	case "init": // this is executed inside of the namespace to setup the container
 	case "init": // this is executed inside of the namespace to setup the container
-		cwd, err := os.Getwd()
+		// by default our current dir is always our rootfs
+		rootfs, err := os.Getwd()
 		if err != nil {
 		if err != nil {
 			log.Fatal(err)
 			log.Fatal(err)
 		}
 		}
-		if flag.NArg() < 2 {
-			log.Fatalf("wrong number of arguments %d", flag.NArg())
+
+		pipeFd, err := strconv.Atoi(rawPipeFd)
+		if err != nil {
+			log.Fatal(err)
 		}
 		}
 		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd))
 		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd))
 		if err != nil {
 		if err != nil {
-			log.Fatalf("Unable to create sync pipe: %s", err)
+			log.Fatalf("unable to create sync pipe: %s", err)
 		}
 		}
-		if err := ns.Init(container, cwd, console, syncPipe, flag.Args()[1:]); err != nil {
-			log.Fatalf("Unable to initialize for container: %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:
 	default:
-		log.Fatalf("command not supported for nsinit %s", flag.Arg(0))
+		log.Fatalf("command not supported for nsinit %s", os.Args[0])
 	}
 	}
 }
 }
 
 
 func loadContainer() (*libcontainer.Container, error) {
 func loadContainer() (*libcontainer.Container, error) {
-	f, err := os.Open(filepath.Join(root, "container.json"))
+	f, err := os.Open(filepath.Join(dataPath, "container.json"))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -97,7 +85,7 @@ func loadContainer() (*libcontainer.Container, error) {
 }
 }
 
 
 func readPid() (int, error) {
 func readPid() (int, error) {
-	data, err := ioutil.ReadFile(filepath.Join(root, "pid"))
+	data, err := ioutil.ReadFile(filepath.Join(dataPath, "pid"))
 	if err != nil {
 	if err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
@@ -107,7 +95,3 @@ func readPid() (int, error) {
 	}
 	}
 	return pid, nil
 	return pid, nil
 }
 }
-
-func newNsInit() (nsinit.NsInit, error) {
-	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}), nil
-}

+ 28 - 0
pkg/libcontainer/nsinit/pid.go

@@ -0,0 +1,28 @@
+package nsinit
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+// WritePid writes the namespaced processes pid to pid and it's start time
+// to the path specified
+func WritePid(path string, pid int, startTime string) error {
+	err := ioutil.WriteFile(filepath.Join(path, "pid"), []byte(fmt.Sprint(pid)), 0655)
+	if err != nil {
+		return err
+	}
+	return ioutil.WriteFile(filepath.Join(path, "start"), []byte(startTime), 0655)
+}
+
+// DeletePid removes the pid and started file from disk when the container's process
+// dies and the container is cleanly removed
+func DeletePid(path string) error {
+	err := os.Remove(filepath.Join(path, "pid"))
+	if serr := os.Remove(filepath.Join(path, "start")); err == nil {
+		err = serr
+	}
+	return err
+}

+ 49 - 0
pkg/libcontainer/nsinit/std_term.go

@@ -0,0 +1,49 @@
+package nsinit
+
+import (
+	"io"
+	"os"
+	"os/exec"
+)
+
+type StdTerminal struct {
+	stdin          io.Reader
+	stdout, stderr io.Writer
+}
+
+func (s *StdTerminal) SetMaster(*os.File) {
+	// no need to set master on non tty
+}
+
+func (s *StdTerminal) Close() error {
+	return nil
+}
+
+func (s *StdTerminal) Resize(h, w int) error {
+	return nil
+}
+
+func (s *StdTerminal) Attach(command *exec.Cmd) error {
+	inPipe, err := command.StdinPipe()
+	if err != nil {
+		return err
+	}
+	outPipe, err := command.StdoutPipe()
+	if err != nil {
+		return err
+	}
+	errPipe, err := command.StderrPipe()
+	if err != nil {
+		return err
+	}
+
+	go func() {
+		defer inPipe.Close()
+		io.Copy(inPipe, s.stdin)
+	}()
+
+	go io.Copy(s.stdout, outPipe)
+	go io.Copy(s.stderr, errPipe)
+
+	return nil
+}

+ 10 - 1
pkg/libcontainer/nsinit/sync_pipe.go

@@ -3,9 +3,10 @@ package nsinit
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/pkg/libcontainer"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
+
+	"github.com/dotcloud/docker/pkg/libcontainer"
 )
 )
 
 
 // SyncPipe allows communication to and from the child processes
 // SyncPipe allows communication to and from the child processes
@@ -36,6 +37,14 @@ func NewSyncPipeFromFd(parendFd, childFd uintptr) (*SyncPipe, error) {
 	return s, nil
 	return s, nil
 }
 }
 
 
+func (s *SyncPipe) Child() *os.File {
+	return s.child
+}
+
+func (s *SyncPipe) Parent() *os.File {
+	return s.parent
+}
+
 func (s *SyncPipe) SendToChild(context libcontainer.Context) error {
 func (s *SyncPipe) SendToChild(context libcontainer.Context) error {
 	data, err := json.Marshal(context)
 	data, err := json.Marshal(context)
 	if err != nil {
 	if err != nil {

+ 0 - 89
pkg/libcontainer/nsinit/term.go

@@ -1,7 +1,6 @@
 package nsinit
 package nsinit
 
 
 import (
 import (
-	"github.com/dotcloud/docker/pkg/term"
 	"io"
 	"io"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
@@ -28,91 +27,3 @@ func NewTerminal(stdin io.Reader, stdout, stderr io.Writer, tty bool) Terminal {
 		stderr: stderr,
 		stderr: stderr,
 	}
 	}
 }
 }
-
-type TtyTerminal struct {
-	stdin          io.Reader
-	stdout, stderr io.Writer
-	master         *os.File
-	state          *term.State
-}
-
-func (t *TtyTerminal) Resize(h, w int) error {
-	return term.SetWinsize(t.master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
-}
-
-func (t *TtyTerminal) SetMaster(master *os.File) {
-	t.master = master
-}
-
-func (t *TtyTerminal) Attach(command *exec.Cmd) error {
-	go io.Copy(t.stdout, t.master)
-	go io.Copy(t.master, t.stdin)
-
-	state, err := t.setupWindow(t.master, os.Stdin)
-	if err != nil {
-		command.Process.Kill()
-		return err
-	}
-	t.state = state
-	return err
-}
-
-// SetupWindow gets the parent window size and sets the master
-// pty to the current size and set the parents mode to RAW
-func (t *TtyTerminal) setupWindow(master, parent *os.File) (*term.State, error) {
-	ws, err := term.GetWinsize(parent.Fd())
-	if err != nil {
-		return nil, err
-	}
-	if err := term.SetWinsize(master.Fd(), ws); err != nil {
-		return nil, err
-	}
-	return term.SetRawTerminal(parent.Fd())
-}
-
-func (t *TtyTerminal) Close() error {
-	term.RestoreTerminal(os.Stdin.Fd(), t.state)
-	return t.master.Close()
-}
-
-type StdTerminal struct {
-	stdin          io.Reader
-	stdout, stderr io.Writer
-}
-
-func (s *StdTerminal) SetMaster(*os.File) {
-	// no need to set master on non tty
-}
-
-func (s *StdTerminal) Close() error {
-	return nil
-}
-
-func (s *StdTerminal) Resize(h, w int) error {
-	return nil
-}
-
-func (s *StdTerminal) Attach(command *exec.Cmd) error {
-	inPipe, err := command.StdinPipe()
-	if err != nil {
-		return err
-	}
-	outPipe, err := command.StdoutPipe()
-	if err != nil {
-		return err
-	}
-	errPipe, err := command.StderrPipe()
-	if err != nil {
-		return err
-	}
-
-	go func() {
-		defer inPipe.Close()
-		io.Copy(inPipe, s.stdin)
-	}()
-
-	go io.Copy(s.stdout, outPipe)
-	go io.Copy(s.stderr, errPipe)
-
-	return nil
-}

+ 55 - 0
pkg/libcontainer/nsinit/tty_term.go

@@ -0,0 +1,55 @@
+package nsinit
+
+import (
+	"io"
+	"os"
+	"os/exec"
+
+	"github.com/dotcloud/docker/pkg/term"
+)
+
+type TtyTerminal struct {
+	stdin          io.Reader
+	stdout, stderr io.Writer
+	master         *os.File
+	state          *term.State
+}
+
+func (t *TtyTerminal) Resize(h, w int) error {
+	return term.SetWinsize(t.master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
+}
+
+func (t *TtyTerminal) SetMaster(master *os.File) {
+	t.master = master
+}
+
+func (t *TtyTerminal) Attach(command *exec.Cmd) error {
+	go io.Copy(t.stdout, t.master)
+	go io.Copy(t.master, t.stdin)
+
+	state, err := t.setupWindow(t.master, os.Stdin)
+	if err != nil {
+		command.Process.Kill()
+		return err
+	}
+	t.state = state
+	return err
+}
+
+// SetupWindow gets the parent window size and sets the master
+// pty to the current size and set the parents mode to RAW
+func (t *TtyTerminal) setupWindow(master, parent *os.File) (*term.State, error) {
+	ws, err := term.GetWinsize(parent.Fd())
+	if err != nil {
+		return nil, err
+	}
+	if err := term.SetWinsize(master.Fd(), ws); err != nil {
+		return nil, err
+	}
+	return term.SetRawTerminal(parent.Fd())
+}
+
+func (t *TtyTerminal) Close() error {
+	term.RestoreTerminal(os.Stdin.Fd(), t.state)
+	return t.master.Close()
+}

+ 13 - 4
pkg/libcontainer/nsinit/unsupported.go

@@ -3,17 +3,26 @@
 package nsinit
 package nsinit
 
 
 import (
 import (
+	"github.com/dotcloud/docker/pkg/cgroups"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 )
 )
 
 
-func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) {
+func Exec(container *libcontainer.Container, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
 	return -1, libcontainer.ErrUnsupported
 	return -1, libcontainer.ErrUnsupported
 }
 }
 
 
-func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
-	return -1, libcontainer.ErrUnsupported
+func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error {
+	return libcontainer.ErrUnsupported
 }
 }
 
 
-func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
+func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
 	return libcontainer.ErrUnsupported
 	return libcontainer.ErrUnsupported
 }
 }
+
+func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
+	return nil, libcontainer.ErrUnsupported
+}
+
+func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
+	return 0
+}

+ 5 - 0
pkg/system/unsupported.go

@@ -3,6 +3,7 @@
 package system
 package system
 
 
 import (
 import (
+	"os"
 	"os/exec"
 	"os/exec"
 )
 )
 
 
@@ -23,3 +24,7 @@ func GetClockTicks() int {
 	// just return 100
 	// just return 100
 	return 100
 	return 100
 }
 }
+
+func CreateMasterAndConsole() (*os.File, string, error) {
+	return nil, "", ErrNotSupportedPlatform
+}