diff --git a/nsinit/exec.go b/nsinit/exec.go index 17201b6d..64d35e51 100644 --- a/nsinit/exec.go +++ b/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/nsinit/nsinit.go b/nsinit/nsinit.go index 6aed9c9d..506a39ea 100644 --- a/nsinit/nsinit.go +++ b/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/nsinit/nsinit/main.go b/nsinit/nsinit/main.go index 615e31d6..bcb0068b 100644 --- a/nsinit/nsinit/main.go +++ b/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/nsinit/state.go b/nsinit/state.go deleted file mode 100644 index 26d7fa42..00000000 --- a/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/nsinit/unsupported.go b/nsinit/unsupported.go index 2412223d..135c0ef3 100644 --- a/nsinit/unsupported.go +++ b/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 }