diff --git a/paths_test.go b/paths_test.go index 9dd2898d..0b031952 100644 --- a/paths_test.go +++ b/paths_test.go @@ -102,7 +102,7 @@ func TestEmptySubsystem(t *testing.T) { 1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service 0::/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service` r := strings.NewReader(data) - paths, err := parseCgroupFromReader(r) + paths, unified, err := parseCgroupFromReaderUnified(r) if err != nil { t.Fatal(err) } @@ -111,6 +111,10 @@ func TestEmptySubsystem(t *testing.T) { t.Fatalf("empty subsystem for %q", path) } } + unifiedExpected := "/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service" + if unified != unifiedExpected { + t.Fatalf("expected %q, got %q", unifiedExpected, unified) + } } func TestSystemd240(t *testing.T) { @@ -127,7 +131,7 @@ func TestSystemd240(t *testing.T) { 1:name=systemd:/system.slice/docker.service 0::/system.slice/docker.service` r := strings.NewReader(data) - paths, err := parseCgroupFromReader(r) + paths, unified, err := parseCgroupFromReaderUnified(r) if err != nil { t.Fatal(err) } @@ -140,4 +144,8 @@ func TestSystemd240(t *testing.T) { if err != ErrControllerNotActive { t.Fatalf("expected error %q but received %q", ErrControllerNotActive, err) } + unifiedExpected := "/system.slice/docker.service" + if unified != unifiedExpected { + t.Fatalf("expected %q, got %q", unifiedExpected, unified) + } } diff --git a/utils.go b/utils.go index 2297980d..21713897 100644 --- a/utils.go +++ b/utils.go @@ -261,21 +261,28 @@ func parseKV(raw string) (string, uint64, error) { // "pids": "/user.slice/user-1000.slice" // etc. // -// Note that for cgroup v2 unified hierarchy, there are no per-controller -// cgroup paths, so the resulting map will have a single element where the key -// is empty string ("") and the value is the cgroup path the is in. +// The resulting map does not have an element for cgroup v2 unified hierarchy. +// Use ParseCgroupFileUnified to get the unified path. func ParseCgroupFile(path string) (map[string]string, error) { + x, _, err := ParseCgroupFileUnified(path) + return x, err +} + +// ParseCgroupFileUnified returns legacy subsystem paths as the first value, +// and returns the unified path as the second value. +func ParseCgroupFileUnified(path string) (map[string]string, string, error) { f, err := os.Open(path) if err != nil { - return nil, err + return nil, "", err } defer f.Close() - return parseCgroupFromReader(f) + return parseCgroupFromReaderUnified(f) } -func parseCgroupFromReader(r io.Reader) (map[string]string, error) { +func parseCgroupFromReaderUnified(r io.Reader) (map[string]string, string, error) { var ( cgroups = make(map[string]string) + unified = "" s = bufio.NewScanner(r) ) for s.Scan() { @@ -284,18 +291,20 @@ func parseCgroupFromReader(r io.Reader) (map[string]string, error) { parts = strings.SplitN(text, ":", 3) ) if len(parts) < 3 { - return nil, fmt.Errorf("invalid cgroup entry: %q", text) + return nil, unified, fmt.Errorf("invalid cgroup entry: %q", text) } for _, subs := range strings.Split(parts[1], ",") { - if subs != "" { + if subs == "" { + unified = parts[2] + } else { cgroups[subs] = parts[2] } } } if err := s.Err(); err != nil { - return nil, err + return nil, unified, err } - return cgroups, nil + return cgroups, unified, nil } func getCgroupDestination(subsystem string) (string, error) {