diff --git a/cgroups/fs/apply_raw.go b/cgroups/fs/apply_raw.go index 2d1a1523..04674077 100644 --- a/cgroups/fs/apply_raw.go +++ b/cgroups/fs/apply_raw.go @@ -21,12 +21,15 @@ var ( "perf_event": &PerfEventGroup{}, "freezer": &FreezerGroup{}, } + cgroupProcesses = "cgroup.procs" ) type subsystem interface { - Set(*data) error - Remove(*data) error + Active(d *data) (bool, error) + Enter(string, string) error GetStats(string, *cgroups.Stats) error + Remove(*data) error + Set(*data) error } type data struct { @@ -116,6 +119,36 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) { return cgroups.ReadProcsFile(dir) } +func EnterPid(c *cgroups.Cgroup, pid int) error { + d, err := getCgroupData(c, 0) + if err != nil { + return err + } + + for sysname, sys := range subsystems { + active, err := sys.Active(d) + if err != nil { + return err + } + if !active { + continue + } + path, err := d.path(sysname) + if err != nil { + // Don't fail if a cgroup hierarchy was not found, just skip this subsystem + if err == cgroups.ErrNotFound { + continue + } + return err + } + if err := sys.Enter(path, strconv.FormatInt(int64(pid), 10)); err != nil { + return err + } + } + + return nil +} + func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) { // we can pick any subsystem to find the root cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu") diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index 38b87577..11337c0b 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -139,3 +139,12 @@ func (s *BlkioGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } + +func (s *BlkioGroup) Active(d *data) (bool, error) { + return true, nil +} + +func (s *BlkioGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} + diff --git a/cgroups/fs/cpu.go b/cgroups/fs/cpu.go index efac9ed1..b51a7e42 100644 --- a/cgroups/fs/cpu.go +++ b/cgroups/fs/cpu.go @@ -70,3 +70,18 @@ func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error { } return nil } + +func (s *CpuGroup) Active(d *data) (bool, error) { + dir, err := d.path("cpu") + if err != nil { + return false, err + } + if FileExists(dir) { + return true, nil + } + return false, nil +} + +func (s *CpuGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} diff --git a/cgroups/fs/cpuacct.go b/cgroups/fs/cpuacct.go index 7761d4c2..00a64bd4 100644 --- a/cgroups/fs/cpuacct.go +++ b/cgroups/fs/cpuacct.go @@ -161,3 +161,11 @@ func getPercpuUsage(path string) ([]uint64, error) { } return percpuUsage, nil } + +func (s *CpuacctGroup) Active(d *data) (bool, error) { + return true, nil +} + +func (s *CpuacctGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} diff --git a/cgroups/fs/cpuset.go b/cgroups/fs/cpuset.go index 9570125f..8a4d769c 100644 --- a/cgroups/fs/cpuset.go +++ b/cgroups/fs/cpuset.go @@ -108,3 +108,20 @@ func (s *CpusetGroup) copyIfNeeded(current, parent string) error { func (s *CpusetGroup) isEmpty(b []byte) bool { return len(bytes.Trim(b, "\n")) == 0 } + +func (s *CpusetGroup) Active(d *data) (bool, error) { + if d.c.CpusetCpus != "" { + dir, err := d.path("cpuset") + if err != nil { + return false, err + } + if FileExists(dir) { + return true, nil + } + } + return false, nil +} + +func (s *CpusetGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} diff --git a/cgroups/fs/devices.go b/cgroups/fs/devices.go index 98d5d2d7..aadd9016 100644 --- a/cgroups/fs/devices.go +++ b/cgroups/fs/devices.go @@ -32,3 +32,11 @@ func (s *DevicesGroup) Remove(d *data) error { func (s *DevicesGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } + +func (s *DevicesGroup) Active(d *data) (bool, error) { + return true, nil +} + +func (s *DevicesGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} diff --git a/cgroups/fs/freezer.go b/cgroups/fs/freezer.go index db2a41ca..675aca8f 100644 --- a/cgroups/fs/freezer.go +++ b/cgroups/fs/freezer.go @@ -48,3 +48,12 @@ func (s *FreezerGroup) Remove(d *data) error { func (s *FreezerGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } + +func (s *FreezerGroup) Active(d *data) (bool, error) { + return true, nil +} + +func (s *FreezerGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} + diff --git a/cgroups/fs/memory.go b/cgroups/fs/memory.go index c27150d2..9368a150 100644 --- a/cgroups/fs/memory.go +++ b/cgroups/fs/memory.go @@ -14,7 +14,7 @@ type MemoryGroup struct { func (s *MemoryGroup) Set(d *data) error { dir, err := d.join("memory") - // only return an error for memory if it was not specified + // only return an error for memory if it was specified if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) { return err } @@ -90,3 +90,19 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } + +func (s *MemoryGroup) Active(d *data) (bool, error) { + dir, err := d.path("memory") + if err != nil { + return false, err + } + if FileExists(dir) { + return true, nil + } + + return false, nil +} + +func (s *MemoryGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} diff --git a/cgroups/fs/perf_event.go b/cgroups/fs/perf_event.go index 5f45678f..ba19ce8e 100644 --- a/cgroups/fs/perf_event.go +++ b/cgroups/fs/perf_event.go @@ -22,3 +22,11 @@ func (s *PerfEventGroup) Remove(d *data) error { func (s *PerfEventGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } + +func (s *PerfEventGroup) Active(d *data) (bool, error) { + return true, nil +} + +func (s *PerfEventGroup) Enter(path, pid string) error { + return writeFile(path, cgroupProcesses, pid) +} diff --git a/cgroups/fs/utils.go b/cgroups/fs/utils.go index f65622a8..a2b679be 100644 --- a/cgroups/fs/utils.go +++ b/cgroups/fs/utils.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/ioutil" + "os" "path/filepath" "strconv" "strings" @@ -38,3 +39,10 @@ func getCgroupParamInt(cgroupPath, cgroupFile string) (uint64, error) { } return strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64) } + +func FileExists(path string) bool { + if _, err := os.Stat(path); err == nil { + return true + } + return false +} diff --git a/namespaces/execin.go b/namespaces/execin.go index 8958332a..44ac15bc 100644 --- a/namespaces/execin.go +++ b/namespaces/execin.go @@ -12,6 +12,7 @@ import ( "syscall" "github.com/docker/libcontainer" + "github.com/docker/libcontainer/cgroups/fs" "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/syncpipe" "github.com/docker/libcontainer/system" @@ -21,6 +22,10 @@ import ( // setns code in a single threaded environment joining the existing containers' namespaces. func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath, action string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) { + // Enter cgroups. + if err := EnterCgroups(container); err != nil { + return err + } args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)} @@ -102,3 +107,7 @@ func FinalizeSetns(container *libcontainer.Config, args []string) error { panic("unreachable") } + +func EnterCgroups(container *libcontainer.Config) error { + return fs.EnterPid(container.Cgroups, os.Getpid()) +}