// New returns a new SysInfo, using the filesystem to detect which features the kernel supports. func New(quiet bool) *SysInfo { sysInfo := &SysInfo{} if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { if !quiet { logrus.Warnf("Your kernel does not support cgroup memory limit: %v", err) } } else { // If memory cgroup is mounted, MemoryLimit is always enabled. sysInfo.MemoryLimit = true _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) sysInfo.SwapLimit = err1 == nil if !sysInfo.SwapLimit && !quiet { logrus.Warn("Your kernel does not support swap memory limit.") } _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.oom_control")) sysInfo.OomKillDisable = err == nil if !sysInfo.OomKillDisable && !quiet { logrus.Warnf("Your kernel does not support oom control.") } } if cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu"); err != nil { if !quiet { logrus.Warnf("%v", err) } } else { _, err1 := ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_quota_us")) sysInfo.CpuCfsQuota = err1 == nil if !sysInfo.CpuCfsQuota && !quiet { logrus.Warn("Your kernel does not support cgroup cfs quotas") } } // Checek if ipv4_forward is disabled. if data, err := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward"); os.IsNotExist(err) { sysInfo.IPv4ForwardingDisabled = true } else { if enabled, _ := strconv.Atoi(strings.TrimSpace(string(data))); enabled == 0 { sysInfo.IPv4ForwardingDisabled = true } else { sysInfo.IPv4ForwardingDisabled = false } } // Check if AppArmor is supported. if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { sysInfo.AppArmor = false } else { sysInfo.AppArmor = true } // Check if Devices cgroup is mounted, it is hard requirement for container security. if _, err := cgroups.FindCgroupMountpoint("devices"); err != nil { logrus.Fatalf("Error mounting devices cgroup: %v", err) } return sysInfo }
// New returns a new SysInfo, using the filesystem to detect which features the kernel supports. func New(quiet bool) *SysInfo { sysInfo := &SysInfo{} if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { if !quiet { logrus.Warnf("%s", err) } } else { _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) sysInfo.MemoryLimit = err1 == nil && err2 == nil if !sysInfo.MemoryLimit && !quiet { logrus.Warnf("Your kernel does not support cgroup memory limit.") } _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) sysInfo.SwapLimit = err == nil if !sysInfo.SwapLimit && !quiet { logrus.Warnf("Your kernel does not support cgroup swap limit.") } } // Check if AppArmor is supported. if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { sysInfo.AppArmor = false } else { sysInfo.AppArmor = true } return sysInfo }
func getPath(id, driver, subsystem string) (string, error) { cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return "", err } cgroupRoot = filepath.Dir(cgroupRoot) if _, err := os.Stat(cgroupRoot); err != nil { return "", fmt.Errorf("cgroups fs not found") } group, err := findGroup(subsystem) if err != nil { return "", err } initPath, err := cgroups.GetInitCgroupDir(group) if err != nil { return "", err } path := path.Join(cgroupRoot, group, initPath, driver, id, subsystem) if _, err := os.Stat(path); err != nil { return "", fmt.Errorf("%s not found", path) } return path, nil }
// TODO(vmarmol): Report error here, we'll probably need to wait for the new API. func init() { // we can pick any subsystem to find the root cpuRoot, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return } cgroupRoot = filepath.Dir(cpuRoot) if _, err := os.Stat(cgroupRoot); err != nil { return } }
func findCgroupRootAndDir(subsystem string) (string, string, error) { cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem) if err != nil { return "", "", err } cgroupDir, err := cgroups.GetThisCgroupDir(subsystem) if err != nil { return "", "", err } return cgroupRoot, cgroupDir, nil }
func init() { useSystemd = systemd.UseSystemd() if !useSystemd { // Second attempt at checking for systemd, check for a "name=systemd" cgroup. mnt, err := cgroups.FindCgroupMountpoint("cpu") if err == nil { // systemd presence does not mean systemd controls cgroups. // If system.slice cgroup exists, then systemd is taking control. // This breaks if user creates system.slice manually :) useSystemd = utils.FileExists(mnt + "/system.slice") } } }
func getFreezerPath(c *cgroups.Cgroup) (string, error) { mountpoint, err := cgroups.FindCgroupMountpoint("freezer") if err != nil { return "", err } initPath, err := cgroups.GetInitCgroupDir("freezer") if err != nil { return "", err } return filepath.Join(mountpoint, initPath, fmt.Sprintf("%s-%s", c.Parent, c.Name)), nil }
func GetPids(c *cgroups.Cgroup) ([]int, error) { unitName := getUnitName(c) mountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return nil, err } props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName)) if err != nil { return nil, err } cgroup := props["ControlGroup"].(string) return cgroups.ReadProcsFile(filepath.Join(mountpoint, cgroup)) }
func validateCgroupMounts() (string, string) { const recommendedMount = "/sys/fs/cgroup" desc := fmt.Sprintf("\tAny cgroup mount point that is detectible and accessible is supported. %s is recommended as a standard location.\n", recommendedMount) mnt, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { out := "Could not locate cgroup mount point.\n" out += desc return Unknown, out } mnt = path.Dir(mnt) if !utils.FileExists(mnt) { out := fmt.Sprintf("Cgroup mount directory %s inaccessible.\n", mnt) out += desc return Unsupported, out } mounts, err := ioutil.ReadDir(mnt) if err != nil { out := fmt.Sprintf("Could not read cgroup mount directory %s.\n", mnt) out += desc return Unsupported, out } mountNames := "\tCgroup mount directories: " for _, mount := range mounts { mountNames += mount.Name() + " " } mountNames += "\n" out := fmt.Sprintf("Cgroups are mounted at %s.\n", mnt) out += mountNames out += desc info, err := ioutil.ReadFile("/proc/mounts") if err != nil { out := fmt.Sprintf("Could not read /proc/mounts.\n") out += desc return Unsupported, out } out += "\tCgroup mounts:\n" for _, line := range strings.Split(string(info), "\n") { if strings.Contains(line, " cgroup ") { out += "\t" + line + "\n" } } if mnt == recommendedMount { return Recommended, out } return Supported, out }
func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) { mountpoint, err := cgroups.FindCgroupMountpoint(subsystem) if err != nil { return "", err } initPath, err := cgroups.GetInitCgroupDir(subsystem) if err != nil { return "", err } slice := "system.slice" if c.Slice != "" { slice = c.Slice } return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil }
// New returns a new SysInfo, using the filesystem to detect which features the kernel supports. func New(quiet bool) *SysInfo { sysInfo := &SysInfo{} sysInfo.cgroupMemInfo = checkCgroupMem(quiet) sysInfo.cgroupCpuInfo = checkCgroupCpu(quiet) _, err := cgroups.FindCgroupMountpoint("devices") sysInfo.CgroupDevicesEnabled = err == nil sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward") sysInfo.BridgeNfCallIptablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables") sysInfo.BridgeNfCallIp6tablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables") // Check if AppArmor is supported. if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) { sysInfo.AppArmor = true } return sysInfo }
func (raw *data) path(subsystem string) (string, error) { _, err := cgroups.FindCgroupMountpoint(subsystem) // If we didn't mount the subsystem, there is no point we make the path. if err != nil { return "", err } // If the cgroup name/path is absolute do not look relative to the cgroup of the init process. if filepath.IsAbs(raw.cgroup) { return filepath.Join(raw.root, subsystem, raw.cgroup), nil } parent, err := raw.parent(subsystem) if err != nil { return "", err } return filepath.Join(parent, raw.cgroup), nil }
func UseSystemd() bool { check.Do(func() { useSystemd = false // Check for system.slice in systemd and cpu cgroup. for _, cgroupType := range []string{"name=systemd", "cpu"} { mnt, err := cgroups.FindCgroupMountpoint(cgroupType) if err == nil { // systemd presence does not mean systemd controls cgroups. // If system.slice cgroup exists, then systemd is taking control. // This breaks if user creates system.slice manually :) if utils.FileExists(path.Join(mnt, "system.slice")) { useSystemd = true break } } } }) return useSystemd }
func checkCgroupCpu(quiet bool) *cgroupCpuInfo { info := &cgroupCpuInfo{} mountPoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { if !quiet { logrus.Warn(err) } return info } info.CpuCfsPeriod = cgroupEnabled(mountPoint, "cpu.cfs_period_us") if !quiet && !info.CpuCfsPeriod { logrus.Warn("Your kernel does not support cgroup cfs period") } info.CpuCfsQuota = cgroupEnabled(mountPoint, "cpu.cfs_quota_us") if !quiet && !info.CpuCfsQuota { logrus.Warn("Your kernel does not support cgroup cfs quotas") } return info }
// Gets the cgroupRoot. func getCgroupRoot() (string, error) { cgroupRootLock.Lock() defer cgroupRootLock.Unlock() if cgroupRoot != "" { return cgroupRoot, nil } // we can pick any subsystem to find the root cpuRoot, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return "", err } root := filepath.Dir(cpuRoot) if _, err := os.Stat(root); err != nil { return "", err } cgroupRoot = root return cgroupRoot, nil }
func checkCgroupMem(quiet bool) *cgroupMemInfo { info := &cgroupMemInfo{} mountPoint, err := cgroups.FindCgroupMountpoint("memory") if err != nil { if !quiet { logrus.Warnf("Your kernel does not support cgroup memory limit: %v", err) } return info } info.MemoryLimit = true info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes") if !quiet && !info.SwapLimit { logrus.Warn("Your kernel does not support swap memory limit.") } info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control") if !quiet && !info.OomKillDisable { logrus.Warnf("Your kernel does not support oom control.") } return info }
func (d *driver) GetPidsForContainer(id string) ([]int, error) { pids := []int{} // cpu is chosen because it is the only non optional subsystem in cgroups subsystem := "cpu" cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem) if err != nil { return pids, err } cgroupDir, err := cgroups.GetThisCgroupDir(subsystem) if err != nil { return pids, err } filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks") if _, err := os.Stat(filename); os.IsNotExist(err) { // With more recent lxc versions use, cgroup will be in lxc/ filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks") } output, err := ioutil.ReadFile(filename) if err != nil { return pids, err } for _, p := range strings.Split(string(output), "\n") { if len(p) == 0 { continue } pid, err := strconv.Atoi(p) if err != nil { return pids, fmt.Errorf("Invalid pid '%s': %s", p, err) } pids = append(pids, pid) } return pids, nil }
func validateMemoryAccounting(available_cgroups map[string]int) string { ok, _ := areCgroupsPresent(available_cgroups, []string{"memory"}) if !ok { return "\tHierarchical memory accounting status unknown: memory cgroup not enabled.\n" } mnt, err := cgroups.FindCgroupMountpoint("memory") if err != nil { return "\tHierarchical memory accounting status unknown: memory cgroup not mounted.\n" } hier, err := ioutil.ReadFile(path.Join(mnt, "memory.use_hierarchy")) if err != nil { return "\tHierarchical memory accounting status unknown: hierarchy interface unavailable.\n" } var enabled int n, err := fmt.Sscanf(string(hier), "%d", &enabled) if err != nil || n != 1 { return "\tHierarchical memory accounting status unknown: hierarchy interface unreadable.\n" } if enabled == 1 { return "\tHierarchical memory accounting enabled. Reported memory usage includes memory used by child containers.\n" } return "\tHierarchical memory accounting disabled. Memory usage does not include usage from child containers.\n" }
func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) { // we can pick any subsystem to find the root cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return nil, err } cgroupRoot = filepath.Dir(cgroupRoot) if _, err := os.Stat(cgroupRoot); err != nil { return nil, fmt.Errorf("cgroups fs not found") } cgroup := c.Name if c.Parent != "" { cgroup = filepath.Join(c.Parent, cgroup) } return &data{ root: cgroupRoot, cgroup: cgroup, c: c, pid: pid, }, nil }
"Test requires underlying root filesystem not be backed by overlay.", } IPv6 = TestRequirement{ func() bool { cmd := exec.Command("test", "-f", "/proc/net/if_inet6") if err := cmd.Run(); err != nil { return true } return false }, "Test requires support for IPv6", } OomControl = TestRequirement{ func() bool { cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory") if err != nil { return false } if _, err := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")); err != nil { return false } if _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.oom_control")); err != nil { return false } return true }, "Test requires Oom control enabled.", }
}, "Test requires the native (libcontainer) exec driver.", } NotOverlay = TestRequirement{ func() bool { cmd := exec.Command("grep", "^overlay / overlay", "/proc/mounts") if err := cmd.Run(); err != nil { return true } return false }, "Test requires underlying root filesystem not be backed by overlay.", } CpuCfsPeriod = TestRequirement{ func() bool { cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return false } if _, err := ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_period_us")); err != nil { return false } return true }, "Test requires an environment that supports cgroup cfs period.", } CpuCfsQuota = TestRequirement{ func() bool { cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return false
// +build !windows package main import ( "io/ioutil" "path" "github.com/docker/libcontainer/cgroups" ) var ( CpuCfsPeriod = TestRequirement{ func() bool { cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return false } if _, err := ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_period_us")); err != nil { return false } return true }, "Test requires an environment that supports cgroup cfs period.", } CpuCfsQuota = TestRequirement{ func() bool { cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return false }
func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { var ( unitName = getUnitName(c) slice = "system.slice" properties []systemd1.Property cpuArgs []cgroupArg cpusetArgs []cgroupArg memoryArgs []cgroupArg res systemdCgroup ) // First set up things not supported by systemd // -1 disables memorySwap if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) { memorySwap := c.MemorySwap if memorySwap == 0 { // By default, MemorySwap is set to twice the size of RAM. memorySwap = c.Memory * 2 } memoryArgs = append(memoryArgs, cgroupArg{"memory.memsw.limit_in_bytes", strconv.FormatInt(memorySwap, 10)}) } if c.CpusetCpus != "" { cpusetArgs = append(cpusetArgs, cgroupArg{"cpuset.cpus", c.CpusetCpus}) } if c.Slice != "" { slice = c.Slice } properties = append(properties, systemd1.Property{"Slice", dbus.MakeVariant(slice)}, systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)}, systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})}, ) // Always enable accounting, this gets us the same behaviour as the fs implementation, // plus the kernel has some problems with joining the memory cgroup at a later time. properties = append(properties, systemd1.Property{"MemoryAccounting", dbus.MakeVariant(true)}, systemd1.Property{"CPUAccounting", dbus.MakeVariant(true)}, systemd1.Property{"BlockIOAccounting", dbus.MakeVariant(true)}) if c.Memory != 0 { properties = append(properties, systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))}) } // TODO: MemoryReservation and MemorySwap not available in systemd if c.CpuShares != 0 { properties = append(properties, systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))}) } if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { return nil, err } // To work around the lack of /dev/pts/* support above we need to manually add these // so, ask systemd for the cgroup used props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName)) if err != nil { return nil, err } cgroup := props["ControlGroup"].(string) if !c.AllowAllDevices { // Atm we can't use the systemd device support because of two missing things: // * Support for wildcards to allow mknod on any device // * Support for wildcards to allow /dev/pts support // // The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is // in wide use. When both these are availalable we will be able to switch, but need to keep the old // implementation for backwards compat. // // Note: we can't use systemd to set up the initial limits, and then change the cgroup // because systemd will re-write the device settings if it needs to re-apply the cgroup context. // This happens at least for v208 when any sibling unit is started. mountpoint, err := cgroups.FindCgroupMountpoint("devices") if err != nil { return nil, err } initPath, err := cgroups.GetInitCgroupDir("devices") if err != nil { return nil, err } dir := filepath.Join(mountpoint, initPath, c.Parent, c.Name) res.cleanupDirs = append(res.cleanupDirs, dir) if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) { return nil, err } if err := ioutil.WriteFile(filepath.Join(dir, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil { return nil, err } if err := writeFile(dir, "devices.deny", "a"); err != nil { return nil, err } for _, dev := range c.AllowedDevices { if err := writeFile(dir, "devices.allow", dev.GetCgroupAllowString()); err != nil { return nil, err } } } if len(cpuArgs) != 0 { mountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) for _, arg := range cpuArgs { if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil { return nil, err } } } if len(memoryArgs) != 0 { mountpoint, err := cgroups.FindCgroupMountpoint("memory") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) for _, arg := range memoryArgs { if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil { return nil, err } } } // we need to manually join the freezer cgroup in systemd because it does not currently support it // via the dbus api freezerPath, err := joinFreezer(c, pid) if err != nil { return nil, err } res.cleanupDirs = append(res.cleanupDirs, freezerPath) if len(cpusetArgs) != 0 { // systemd does not atm set up the cpuset controller, so we must manually // join it. Additionally that is a very finicky controller where each // level must have a full setup as the default for a new directory is "no cpus", // so we avoid using any hierarchies here, creating a toplevel directory. mountpoint, err := cgroups.FindCgroupMountpoint("cpuset") if err != nil { return nil, err } initPath, err := cgroups.GetInitCgroupDir("cpuset") if err != nil { return nil, err } var ( foundCpus bool foundMems bool rootPath = filepath.Join(mountpoint, initPath) path = filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name) ) res.cleanupDirs = append(res.cleanupDirs, path) if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { return nil, err } for _, arg := range cpusetArgs { if arg.File == "cpuset.cpus" { foundCpus = true } if arg.File == "cpuset.mems" { foundMems = true } if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil { return nil, err } } // These are required, if not specified inherit from parent if !foundCpus { s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.cpus")) if err != nil { return nil, err } if err := ioutil.WriteFile(filepath.Join(path, "cpuset.cpus"), s, 0700); err != nil { return nil, err } } // These are required, if not specified inherit from parent if !foundMems { s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.mems")) if err != nil { return nil, err } if err := ioutil.WriteFile(filepath.Join(path, "cpuset.mems"), s, 0700); err != nil { return nil, err } } if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil { return nil, err } } return &res, nil }