diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index 26c7d90474..c73eb0aec5 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -27,10 +27,7 @@ const ( func init() { 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")) if err != nil { return err @@ -41,7 +38,7 @@ func init() { } f.Close() - cwd, err := os.Getwd() + rootfs, err := os.Getwd() if err != nil { return err } @@ -49,7 +46,7 @@ func init() { if err != nil { 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 nil @@ -93,35 +90,87 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba d.activeContainers[c.ID] = &c.Cmd 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...) + master *os.File + console string + + 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 c.Tty { - term = &dockerTtyTerm{ - pipes: pipes, - } - } else { - term = &dockerStdTerm{ - pipes: pipes, - } - } - c.Terminal = term if err := d.writeContainerFile(container, c.ID); err != nil { return -1, err } - return ns.Exec(container, term, pidRoot, args, func() { - if startCallback != nil { - startCallback(c) + + // create a pipe so that we can syncronize with the namespaced process and + // pass the veth name to the child + syncPipe, err := nsinit.NewSyncPipe() + if err != nil { + return -1, err + } + term := getTerminal(c, pipes) + + if container.Tty { + master, console, err = system.CreateMasterAndConsole() + if err != nil { + return -1, err } - }) + term.SetMaster(master) + } + + setupCommand(d, c, container, console, syncPipe.Child(), args) + if err := term.Attach(&c.Cmd); err != nil { + return -1, err + } + defer term.Close() + + if err := c.Start(); err != nil { + return -1, err + } + + started, err := system.GetProcessStartTime(c.Process.Pid) + if err != nil { + return -1, err + } + if err := nsinit.WritePid(dataPath, c.Process.Pid, started); err != nil { + c.Process.Kill() + return -1, err + } + defer nsinit.DeletePid(dataPath) + + // Do this before syncing with child so that no children + // can escape the cgroup + cleaner, err := nsinit.SetupCgroups(container, c.Process.Pid) + if err != nil { + c.Process.Kill() + return -1, err + } + if cleaner != nil { + defer cleaner.Cleanup() + } + + if err := nsinit.InitializeNetworking(container, c.Process.Pid, syncPipe); err != nil { + c.Process.Kill() + return -1, err + } + + // Sync with child + syncPipe.Close() + + if startCallback != nil { + startCallback(c) + } + + if err := c.Wait(); err != nil { + if _, ok := err.(*exec.ExitError); !ok { + return -1, err + } + } + return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil + } func (d *driver) Kill(p *execdriver.Command, sig int) error { @@ -234,35 +283,40 @@ func getEnv(key string, env []string) string { return "" } -type dockerCommandFactory struct { - c *execdriver.Command - driver *driver +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 } -// 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 { +func setupCommand(d *driver, c *execdriver.Command, container *libcontainer.Container, console string, syncFile *os.File, args []string) { // we need to join the rootfs because nsinit will setup the rootfs and chroot - initPath := filepath.Join(d.c.Rootfs, d.c.InitPath) + initPath := filepath.Join(c.Rootfs, c.InitPath) - d.c.Path = d.driver.initPath - d.c.Args = append([]string{ + c.Path = d.initPath + c.Args = append([]string{ initPath, "-driver", DriverName, "-console", console, "-pipe", "3", - "-root", filepath.Join(d.driver.root, d.c.ID), + "-root", filepath.Join(d.root, 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} + c.SysProcAttr = nil + system.SetCloneFlags(&c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces))) + c.ExtraFiles = []*os.File{syncFile} - d.c.Env = container.Env - d.c.Dir = d.c.Rootfs - - return &d.c.Cmd + c.Env = container.Env + c.Dir = c.Rootfs } diff --git a/pkg/libcontainer/nsinit/exec.go b/pkg/libcontainer/nsinit/exec.go index 5aa98af58e..078f277e80 100644 --- a/pkg/libcontainer/nsinit/exec.go +++ b/pkg/libcontainer/nsinit/exec.go @@ -3,11 +3,8 @@ package nsinit import ( - "fmt" - "io/ioutil" "os" "os/exec" - "path/filepath" "syscall" "github.com/dotcloud/docker/pkg/cgroups" @@ -160,26 +157,6 @@ func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *Sy 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 - } - return err -} - // GetNamespaceFlags parses the container's Namespaces options to set the correct // flags on clone, unshare, and setns func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) { diff --git a/pkg/libcontainer/nsinit/pid.go b/pkg/libcontainer/nsinit/pid.go new file mode 100644 index 0000000000..bba2f10e1b --- /dev/null +++ b/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 +} diff --git a/pkg/libcontainer/nsinit/unsupported.go b/pkg/libcontainer/nsinit/unsupported.go index 972d905cbb..c99d881a59 100644 --- a/pkg/libcontainer/nsinit/unsupported.go +++ b/pkg/libcontainer/nsinit/unsupported.go @@ -3,9 +3,22 @@ package nsinit import ( + "github.com/dotcloud/docker/pkg/cgroups" "github.com/dotcloud/docker/pkg/libcontainer" ) +func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error { + return libcontainer.ErrUnsupported +} + +func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { + 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 } diff --git a/pkg/system/unsupported.go b/pkg/system/unsupported.go index 4ae2a488aa..96ebc858f5 100644 --- a/pkg/system/unsupported.go +++ b/pkg/system/unsupported.go @@ -3,6 +3,7 @@ package system import ( + "os" "os/exec" ) @@ -23,3 +24,7 @@ func GetClockTicks() int { // just return 100 return 100 } + +func CreateMasterAndConsole() (*os.File, string, error) { + return nil, "", ErrNotSupportedPlatform +}