Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions cgroups/cgroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package cgroups

import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"slices"
"strings"

rspec "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
)

var (
Expand All @@ -20,6 +22,8 @@ var (

// Cgroup represents interfaces for cgroup validation
type Cgroup interface {
Version() int
ControllerPath(controller string, cgPath string) string
GetBlockIOData(pid int, cgPath string) (*rspec.LinuxBlockIO, error)
GetCPUData(pid int, cgPath string) (*rspec.LinuxCPU, error)
GetDevicesData(pid int, cgPath string) ([]rspec.LinuxDeviceCgroup, error)
Expand All @@ -31,13 +35,18 @@ type Cgroup interface {

// FindCgroup gets cgroup root mountpoint
func FindCgroup() (Cgroup, error) {
var st unix.Statfs_t

if err := unix.Statfs(unifiedPath, &st); err == nil && st.Type == unix.CGROUP2_SUPER_MAGIC {
return &CgroupV2{}, nil
}

f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
}
defer f.Close()

cgroupv2 := false
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := scanner.Text()
Expand All @@ -59,25 +68,18 @@ func FindCgroup() (Cgroup, error) {
MountPath: filepath.Dir(fields[4]),
}
return cg, nil
} else if postSeparatorFields[0] == "cgroup2" {
cgroupv2 = true
continue
// TODO cgroupv2 unimplemented
}
}

if err := scanner.Err(); err != nil {
return nil, err
}

if cgroupv2 {
return nil, fmt.Errorf("cgroupv2 is not supported yet")
}
return nil, fmt.Errorf("cgroup is not found")
}

// GetSubsystemPath gets path of subsystem
func GetSubsystemPath(pid int, subsystem string) (string, error) {
// GetV1SubsystemPath gets the path of a cgroup v1 subsystem
func GetV1SubsystemPath(pid int, subsystem string) (string, error) {
contents, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
if err != nil {
return "", err
Expand All @@ -97,3 +99,25 @@ func GetSubsystemPath(pid int, subsystem string) (string, error) {

return "", fmt.Errorf("subsystem %s not found", subsystem)
}

// GetCgroupV2Path gets the path of a process in the unified hierarchy
func GetCgroupV2Path(pid int) (string, error) {
f, err := os.Open(fmt.Sprintf("/proc/%d/cgroup", pid))
if err != nil {
return "", err
}
defer f.Close()

s := bufio.NewScanner(f)
for s.Scan() {
// "0::/user.slice/user-1001.slice/session-1.scope"
if path, ok := strings.CutPrefix(s.Text(), "0::"); ok {
return path, nil
}
}
if err := s.Err(); err != nil {
return "", err
}

return "", errors.New("cgroup v2 path not found")
}
28 changes: 19 additions & 9 deletions cgroups/cgroups_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ type CgroupV1 struct {
MountPath string
}

// Version returns the hierarchy version this implementation reads
func (cg *CgroupV1) Version() int {
return 1
}

// ControllerPath returns the directory backing an absolute cgroupsPath
func (cg *CgroupV1) ControllerPath(controller string, cgPath string) string {
return filepath.Join(cg.MountPath, controller, cgPath)
}

// HugePageSizeUnitList is a list of the units used by the linux kernel when
// naming the HugePage control files.
// https://www.kernel.org/doc/Documentation/cgroup-v1/hugetlb.txt
Expand Down Expand Up @@ -52,7 +62,7 @@ func (cg *CgroupV1) GetBlockIOData(pid int, cgPath string) (*rspec.LinuxBlockIO,
fileName := strings.Join([]string{"blkio", name}, ".")
filePath := filepath.Join(cg.MountPath, "blkio", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "blkio")
subPath, err := GetV1SubsystemPath(pid, "blkio")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -227,7 +237,7 @@ func (cg *CgroupV1) GetCPUData(pid int, cgPath string) (*rspec.LinuxCPU, error)
fileName := strings.Join([]string{"cpu", name}, ".")
filePath := filepath.Join(cg.MountPath, "cpu", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "cpu")
subPath, err := GetV1SubsystemPath(pid, "cpu")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -294,7 +304,7 @@ func (cg *CgroupV1) GetCPUData(pid int, cgPath string) (*rspec.LinuxCPU, error)
fileName := strings.Join([]string{"cpuset", name}, ".")
filePath := filepath.Join(cg.MountPath, "cpuset", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "cpuset")
subPath, err := GetV1SubsystemPath(pid, "cpuset")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -324,7 +334,7 @@ func (cg *CgroupV1) GetDevicesData(pid int, cgPath string) ([]rspec.LinuxDeviceC
fileName := strings.Join([]string{"devices", "list"}, ".")
filePath := filepath.Join(cg.MountPath, "devices", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "devices")
subPath, err := GetV1SubsystemPath(pid, "devices")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -447,7 +457,7 @@ func (cg *CgroupV1) GetHugepageLimitData(pid int, cgPath string) ([]rspec.LinuxH
maxUsage := strings.Join([]string{"hugetlb", pageSize, "limit_in_bytes"}, ".")
filePath := filepath.Join(cg.MountPath, "hugetlb", cgPath, maxUsage)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "hugetlb")
subPath, err := GetV1SubsystemPath(pid, "hugetlb")
if err != nil {
return lh, err
}
Expand Down Expand Up @@ -494,7 +504,7 @@ func (cg *CgroupV1) GetMemoryData(pid int, cgPath string) (*rspec.LinuxMemory, e
fileName := strings.Join([]string{"memory", name}, ".")
filePath := filepath.Join(cg.MountPath, "memory", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "memory")
subPath, err := GetV1SubsystemPath(pid, "memory")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -587,7 +597,7 @@ func (cg *CgroupV1) GetNetworkData(pid int, cgPath string) (*rspec.LinuxNetwork,
fileName := strings.Join([]string{"net_cls", "classid"}, ".")
filePath := filepath.Join(cg.MountPath, "net_cls", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "net_cls")
subPath, err := GetV1SubsystemPath(pid, "net_cls")
if err != nil {
return nil, err
}
Expand All @@ -614,7 +624,7 @@ func (cg *CgroupV1) GetNetworkData(pid int, cgPath string) (*rspec.LinuxNetwork,
fileName = strings.Join([]string{"net_prio", "ifpriomap"}, ".")
filePath = filepath.Join(cg.MountPath, "net_prio", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "net_prio")
subPath, err := GetV1SubsystemPath(pid, "net_prio")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -658,7 +668,7 @@ func (cg *CgroupV1) GetPidsData(pid int, cgPath string) (*rspec.LinuxPids, error
fileName := strings.Join([]string{"pids", "max"}, ".")
filePath := filepath.Join(cg.MountPath, "pids", cgPath, fileName)
if !filepath.IsAbs(cgPath) {
subPath, err := GetSubsystemPath(pid, "pids")
subPath, err := GetV1SubsystemPath(pid, "pids")
if err != nil {
return nil, err
}
Expand Down
Loading