func New(quiet bool) *SysInfo { sysInfo := &SysInfo{} if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { if !quiet { log.Printf("WARNING: %s\n", 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 { log.Printf("WARNING: 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 { log.Printf("WARNING: Your kernel does not support cgroup swap limit.") } } content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward") sysInfo.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' if sysInfo.IPv4ForwardingDisabled && !quiet { log.Printf("WARNING: IPv4 forwarding is disabled.") } // Check if AppArmor seems to be enabled on this system. if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { sysInfo.AppArmor = false } else { sysInfo.AppArmor = true } return sysInfo }
// TODO: this can be improved with our driver // there has to be a better way to do this func (d *driver) GetPidsForContainer(id string) ([]int, error) { pids := []int{} subsystem := "devices" 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) { filename = filepath.Join(cgroupRoot, cgroupDir, "docker", 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 GetStats(c *cgroups.Cgroup, subsystem string, pid int) (map[string]float64, error) { 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) } d := &data{ root: cgroupRoot, cgroup: cgroup, c: c, pid: pid, } sys, exists := subsystems[subsystem] if !exists { return nil, fmt.Errorf("subsystem %s does not exist", subsystem) } return sys.Stats(d) }
func (d *driver) GetPidsForContainer(id string) ([]int, error) { pids := []int{} // memory is chosen randomly, any cgroup used by docker works subsystem := "memory" 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 Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { // We have two implementation of cgroups support, one is based on // systemd and the dbus api, and one is based on raw cgroup fs operations // following the pre-single-writer model docs at: // http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/ // // 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) } d := &data{ root: cgroupRoot, cgroup: cgroup, c: c, pid: pid, } for _, sys := range subsystems { if err := sys.Set(d); err != nil { d.Cleanup() return nil, err } } return d, nil }
func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { var ( unitName = c.Parent + "-" + c.Name + ".scope" slice = "system.slice" properties []systemd1.Property ) for _, v := range c.UnitProperties { switch v[0] { case "Slice": slice = v[1] default: return nil, fmt.Errorf("Unknown unit propery %s", v[0]) } } 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)})}, ) if !c.DeviceAccess { properties = append(properties, systemd1.Property{"DevicePolicy", dbus.MakeVariant("strict")}, systemd1.Property{"DeviceAllow", dbus.MakeVariant([]DeviceAllow{ {"/dev/null", "rwm"}, {"/dev/zero", "rwm"}, {"/dev/full", "rwm"}, {"/dev/random", "rwm"}, {"/dev/urandom", "rwm"}, {"/dev/tty", "rwm"}, {"/dev/console", "rwm"}, {"/dev/tty0", "rwm"}, {"/dev/tty1", "rwm"}, {"/dev/pts/ptmx", "rwm"}, // There is no way to add /dev/pts/* here atm, so we hack this manually below // /dev/pts/* (how to add this?) // Same with tuntap, which doesn't exist as a node most of the time })}) } // Always enable accounting, this gets us the same behaviour as the raw 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)}) if c.Memory != 0 { properties = append(properties, systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))}) } if c.MemoryReservation != 0 { properties = append(properties, systemd1.Property{"MemorySoftLimit", dbus.MakeVariant(uint64(c.MemoryReservation))}) } // TODO: 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.DeviceAccess { mountpoint, err := cgroups.FindCgroupMountpoint("devices") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) // /dev/pts/* if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 136:* rwm"), 0700); err != nil { return nil, err } // tuntap if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 10:200 rwm"), 0700); err != nil { return nil, err } } return &systemdCgroup{}, nil }
func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { var ( unitName = c.Parent + "-" + c.Name + ".scope" 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)})}, ) if !c.DeviceAccess { properties = append(properties, systemd1.Property{"DevicePolicy", dbus.MakeVariant("strict")}, systemd1.Property{"DeviceAllow", dbus.MakeVariant([]DeviceAllow{ {"/dev/null", "rwm"}, {"/dev/zero", "rwm"}, {"/dev/full", "rwm"}, {"/dev/random", "rwm"}, {"/dev/urandom", "rwm"}, {"/dev/tty", "rwm"}, {"/dev/console", "rwm"}, {"/dev/tty0", "rwm"}, {"/dev/tty1", "rwm"}, {"/dev/pts/ptmx", "rwm"}, // There is no way to add /dev/pts/* here atm, so we hack this manually below // /dev/pts/* (how to add this?) // Same with tuntap, which doesn't exist as a node most of the time })}) } // 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.DeviceAccess { mountpoint, err := cgroups.FindCgroupMountpoint("devices") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) // /dev/pts/* if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 136:* rwm"), 0700); err != nil { return nil, err } // tuntap if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 10:200 rwm"), 0700); 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 } } } 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 } 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 } foundCpus := false foundMems := false 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 }