diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index 812389c1bd..26c7d90474 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -29,7 +29,7 @@ func init() { execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error { var ( container *libcontainer.Container - ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}) + ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}) ) f, err := os.Open(filepath.Join(args.Root, "container.json")) if err != nil { @@ -93,15 +93,11 @@ 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} - stateWriter = &dockerStateWriter{ - callback: startCallback, - c: c, - dsw: &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)}, - } - ns = nsinit.NewNsInit(factory, stateWriter) - args = append([]string{c.Entrypoint}, c.Arguments...) + 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...) ) if err := d.createContainerRoot(c.ID); err != nil { return -1, err @@ -121,7 +117,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba if err := d.writeContainerFile(container, c.ID); err != nil { return -1, err } - return ns.Exec(container, term, args) + return ns.Exec(container, term, pidRoot, args, func() { + if startCallback != nil { + startCallback(c) + } + }) } func (d *driver) Kill(p *execdriver.Command, sig int) error { @@ -266,22 +266,3 @@ func (d *dockerCommandFactory) Create(container *libcontainer.Container, console return &d.c.Cmd } - -type dockerStateWriter struct { - dsw nsinit.StateWriter - c *execdriver.Command - callback execdriver.StartCallback -} - -func (d *dockerStateWriter) WritePid(pid int, started string) error { - d.c.ContainerPid = pid - err := d.dsw.WritePid(pid, started) - if d.callback != nil { - d.callback(d.c) - } - return err -} - -func (d *dockerStateWriter) DeletePid() error { - return d.dsw.DeletePid() -} diff --git a/pkg/libcontainer/nsinit/exec.go b/pkg/libcontainer/nsinit/exec.go index 17201b6def..64d35e51c3 100644 --- a/pkg/libcontainer/nsinit/exec.go +++ b/pkg/libcontainer/nsinit/exec.go @@ -3,8 +3,11 @@ package nsinit import ( + "fmt" + "io/ioutil" "os" "os/exec" + "path/filepath" "syscall" "github.com/dotcloud/docker/pkg/cgroups" @@ -17,7 +20,7 @@ import ( // 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. -func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) { +func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) { var ( master *os.File console string @@ -53,24 +56,24 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [ if err != nil { return -1, err } - if err := ns.stateWriter.WritePid(command.Process.Pid, started); err != nil { + if err := WritePid(pidRoot, command.Process.Pid, started); err != nil { command.Process.Kill() return -1, err } - defer ns.stateWriter.DeletePid() + defer DeletePid(pidRoot) // Do this before syncing with child so that no children // can escape the cgroup - activeCgroup, err := ns.SetupCgroups(container, command.Process.Pid) + cleaner, err := SetupCgroups(container, command.Process.Pid) if err != nil { command.Process.Kill() return -1, err } - if activeCgroup != nil { - defer activeCgroup.Cleanup() + if cleaner != nil { + defer cleaner.Cleanup() } - if err := ns.InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil { + if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil { command.Process.Kill() return -1, err } @@ -78,6 +81,10 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [ // Sync with child syncPipe.Close() + if startCallback != nil { + startCallback() + } + if err := command.Wait(); err != nil { if _, ok := err.(*exec.ExitError); !ok { return -1, err @@ -87,7 +94,9 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [ return status, err } -func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) { +// SetupCgroups applies the cgroup restrictions to the process running in the contaienr based +// on the container's configuration +func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) { if container.Cgroups != nil { c := container.Cgroups if systemd.UseSystemd() { @@ -98,7 +107,9 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (c return nil, nil } -func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { +// InitializeNetworking creates the container's network stack outside of the namespace and moves +// interfaces into the container's net namespaces if necessary +func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { context := libcontainer.Context{} for _, config := range container.Networks { strategy, err := network.GetStrategy(config.Type) @@ -111,3 +122,23 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid } 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 +} diff --git a/pkg/libcontainer/nsinit/nsinit.go b/pkg/libcontainer/nsinit/nsinit.go index 6aed9c9dbc..506a39eaed 100644 --- a/pkg/libcontainer/nsinit/nsinit.go +++ b/pkg/libcontainer/nsinit/nsinit.go @@ -5,7 +5,7 @@ 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, args []string) (int, error) + 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 } @@ -13,12 +13,10 @@ type NsInit interface { type linuxNs struct { root string commandFactory CommandFactory - stateWriter StateWriter } -func NewNsInit(command CommandFactory, state StateWriter) NsInit { +func NewNsInit(command CommandFactory) NsInit { return &linuxNs{ commandFactory: command, - stateWriter: state, } } diff --git a/pkg/libcontainer/nsinit/nsinit/main.go b/pkg/libcontainer/nsinit/nsinit/main.go index 615e31d6db..bcb0068ba9 100644 --- a/pkg/libcontainer/nsinit/nsinit/main.go +++ b/pkg/libcontainer/nsinit/nsinit/main.go @@ -56,7 +56,7 @@ func main() { exitCode, err = ns.ExecIn(container, nspid, flag.Args()[1:]) } else { term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty) - exitCode, err = ns.Exec(container, term, flag.Args()[1:]) + exitCode, err = ns.Exec(container, term, root, flag.Args()[1:], nil) } if err != nil { log.Fatalf("Failed to exec: %s", err) @@ -109,5 +109,5 @@ func readPid() (int, error) { } func newNsInit() (nsinit.NsInit, error) { - return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}, &nsinit.DefaultStateWriter{root}), nil + return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}), nil } diff --git a/pkg/libcontainer/nsinit/state.go b/pkg/libcontainer/nsinit/state.go deleted file mode 100644 index 26d7fa4230..0000000000 --- a/pkg/libcontainer/nsinit/state.go +++ /dev/null @@ -1,36 +0,0 @@ -package nsinit - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" -) - -// StateWriter handles writing and deleting the pid file -// on disk -type StateWriter interface { - WritePid(pid int, startTime string) error - DeletePid() error -} - -type DefaultStateWriter struct { - Root string -} - -// writePidFile writes the namespaced processes pid to pid in the rootfs for the container -func (d *DefaultStateWriter) WritePid(pid int, startTime string) error { - err := ioutil.WriteFile(filepath.Join(d.Root, "pid"), []byte(fmt.Sprint(pid)), 0655) - if err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(d.Root, "start"), []byte(startTime), 0655) -} - -func (d *DefaultStateWriter) DeletePid() error { - err := os.Remove(filepath.Join(d.Root, "pid")) - if serr := os.Remove(filepath.Join(d.Root, "start")); err == nil { - err = serr - } - return err -} diff --git a/pkg/libcontainer/nsinit/unsupported.go b/pkg/libcontainer/nsinit/unsupported.go index 2412223d28..135c0ef314 100644 --- a/pkg/libcontainer/nsinit/unsupported.go +++ b/pkg/libcontainer/nsinit/unsupported.go @@ -6,7 +6,7 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer" ) -func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) { +func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) { return -1, libcontainer.ErrUnsupported }