diff --git a/crates/shim/Cargo.toml b/crates/shim/Cargo.toml index ebd5d8aa..9b3c6fad 100644 --- a/crates/shim/Cargo.toml +++ b/crates/shim/Cargo.toml @@ -63,7 +63,7 @@ signal-hook-tokio = { version = "0.3.1", optional = true, features = [ tokio = { workspace = true, features = ["full"], optional = true } [target.'cfg(target_os = "linux")'.dependencies] -cgroups-rs = "0.3.3" +cgroups-rs = "0.3.4" [target.'cfg(unix)'.dependencies] command-fds = "0.3.0" diff --git a/crates/shim/src/cgroup.rs b/crates/shim/src/cgroup.rs index a556a70b..5e4222c0 100644 --- a/crates/shim/src/cgroup.rs +++ b/crates/shim/src/cgroup.rs @@ -16,7 +16,11 @@ #![cfg(target_os = "linux")] -use std::{fs, io::Read, path::Path}; +use std::{ + fs, + io::Read, + path::{Path, PathBuf}, +}; use cgroups_rs::{ cgroup::get_cgroups_relative_paths_by_pid, hierarchies, Cgroup, CgroupPid, MaxValue, Subsystem, @@ -178,18 +182,8 @@ pub fn collect_metrics(pid: u32) -> Result { fn get_cgroup(pid: u32) -> Result { let hierarchies = hierarchies::auto(); let cgroup = if hierarchies.v2() { - let path = format!("/proc/{}/cgroup", pid); - let content = fs::read_to_string(path).map_err(io_error!(e, "read cgroup"))?; - let content = content.strip_suffix('\n').unwrap_or_default(); - - let parts: Vec<&str> = content.split("::").collect(); - let path_parts: Vec<&str> = parts[1].split('/').collect(); - let namespace = path_parts[1]; - let cgroup_name = path_parts[2]; - Cgroup::load( - hierarchies, - format!("/sys/fs/cgroup/{namespace}/{cgroup_name}").as_str(), - ) + let path = get_cgroups_v2_path_by_pid(pid)?; + Cgroup::load(hierarchies, path) } else { // get container main process cgroup let path = get_cgroups_relative_paths_by_pid(pid) @@ -199,6 +193,34 @@ fn get_cgroup(pid: u32) -> Result { Ok(cgroup) } +/// Get the cgroups v2 path given a PID +pub fn get_cgroups_v2_path_by_pid(pid: u32) -> Result { + // todo: should upstream to cgroups-rs + let path = format!("/proc/{}/cgroup", pid); + let content = fs::read_to_string(path).map_err(io_error!(e, "read cgroup"))?; + let content = content.lines().next().unwrap_or(""); + + let Ok(path) = parse_cgroups_v2_path(content)?.canonicalize() else { + return Err(Error::Other("cgroup path not found".to_string())); + }; + Ok(path) +} + +// https://github.com/opencontainers/runc/blob/1950892f69597aa844cbf000fbdf77610dda3a44/libcontainer/cgroups/fs2/defaultpath.go#L83 +fn parse_cgroups_v2_path(content: &str) -> Result { + // the entry for cgroup v2 is always in the format like `0::$PATH` + // where 0 is the hierarchy ID, the controller name is omitted in cgroup v2 + // and $PATH is the cgroup path + // see https://docs.kernel.org/admin-guide/cgroup-v2.html + let Some(path) = content.strip_prefix("0::") else { + return Err(Error::Other(format!("invalid cgroup path: {}", content))); + }; + + let path = path.trim_start_matches('/'); + + Ok(PathBuf::from(format!("/sys/fs/cgroup/{}", path))) +} + /// Update process cgroup limits pub fn update_resources(pid: u32, resources: &LinuxResources) -> Result<()> { // get container main process cgroup @@ -305,8 +327,11 @@ pub fn update_resources(pid: u32, resources: &LinuxResources) -> Result<()> { #[cfg(test)] mod tests { + use std::path::PathBuf; + use cgroups_rs::{hierarchies, Cgroup, CgroupPid}; + use super::parse_cgroups_v2_path; use crate::cgroup::{ add_task_to_cgroup, adjust_oom_score, read_process_oom_score, OOM_SCORE_ADJ_MAX, }; @@ -342,4 +367,31 @@ mod tests { assert_eq!(new, OOM_SCORE_ADJ_MAX) } } + + #[test] + fn test_parse_cgroups_v2_path() { + let path = "0::/user.slice/user-1000.slice/session-2.scope"; + assert_eq!( + parse_cgroups_v2_path(path).unwrap(), + PathBuf::from("/sys/fs/cgroup/user.slice/user-1000.slice/session-2.scope") + ); + } + + #[test] + fn test_parse_cgroups_v2_path_empty() { + let path = "0::"; + assert_eq!( + parse_cgroups_v2_path(path).unwrap(), + PathBuf::from("/sys/fs/cgroup/") + ); + } + + #[test] + fn test_parse_cgroups_v2_path_kube() { + let path = "0::/kubepods-besteffort-pod8.slice:cri-containerd:8"; + assert_eq!( + parse_cgroups_v2_path(path).unwrap(), + PathBuf::from("/sys/fs/cgroup/kubepods-besteffort-pod8.slice:cri-containerd:8") + ); + } }