use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};
use serde::Serialize;
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum CgroupDriver {
Systemd,
Cgroupfs,
}
impl fmt::Display for CgroupDriver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Systemd => write!(f, "systemd"),
Self::Cgroupfs => write!(f, "cgroupfs"),
}
}
}
pub struct CgroupManager {
driver: Option<CgroupDriver>,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct CgroupPath {
pub path: PathBuf,
pub driver: CgroupDriver,
}
impl Default for CgroupManager {
fn default() -> Self { Self::new() }
}
impl CgroupManager {
#[must_use]
pub const fn new() -> Self { Self { driver: None } }
pub fn get_cgroup(&mut self, slices: &[&str], construct: bool) -> Option<CgroupPath> {
match self.driver {
Some(driver) => {
let path = make(driver, slices, construct);
match cgroup_exists(&path) {
true => Some(CgroupPath { path, driver }),
false => None,
}
},
None => self
.try_resolve(CgroupDriver::Systemd, slices, construct)
.or_else(|| self.try_resolve(CgroupDriver::Cgroupfs, slices, construct)),
}
}
pub fn get_cgroup_divided(
&mut self,
systemd_slices: &[&str],
cgroupfs_slices: &[&str],
construct: bool,
) -> Option<CgroupPath> {
match self.driver {
Some(driver) => {
let path = match driver {
CgroupDriver::Systemd { .. } => make(driver, systemd_slices, construct),
CgroupDriver::Cgroupfs => make(driver, cgroupfs_slices, construct),
};
match cgroup_exists(&path) {
true => Some(CgroupPath { path, driver }),
false => None,
}
},
None => self
.try_resolve(CgroupDriver::Systemd, systemd_slices, construct)
.or_else(|| self.try_resolve(CgroupDriver::Cgroupfs, cgroupfs_slices, construct)),
}
}
fn try_resolve(
&mut self,
driver: CgroupDriver,
slices: &[&str],
construct: bool,
) -> Option<CgroupPath> {
let path = make(driver, slices, construct);
match cgroup_exists(&path) {
false => None,
true => {
self.driver = Some(driver);
Some(CgroupPath { path, driver })
},
}
}
#[must_use]
pub const fn driver(&self) -> Option<CgroupDriver> { self.driver }
}
#[must_use]
pub fn make(driver: CgroupDriver, slices: &[&str], construct: bool) -> PathBuf {
match driver {
CgroupDriver::Cgroupfs => make_cgroupfs(slices),
CgroupDriver::Systemd => make_systemd(slices, construct),
}
}
const SYSTEMD_SLICE_SUFFIX: &str = ".slice";
#[must_use]
fn make_systemd(slices: &[&str], construct: bool) -> PathBuf {
if slices.is_empty() || slices.len() == 1 && slices[0].is_empty() {
return PathBuf::from("");
}
match construct {
false => slices.iter().collect::<PathBuf>(),
true => {
let escaped = slices.iter().map(|&s| escape_systemd(s));
let mut path: PathBuf = PathBuf::new();
let mut accumulator: String = String::new();
let mut working: String = String::new();
for slice in escaped {
working += &accumulator;
working += &slice;
working += SYSTEMD_SLICE_SUFFIX;
path.push(&working);
working.clear();
accumulator += &slice;
accumulator += "-";
}
path
},
}
}
#[must_use]
pub fn escape_systemd(slice: &str) -> String { slice.replace("-", "_") }
#[must_use]
fn make_cgroupfs(slices: &[&str]) -> PathBuf { slices.iter().collect() }
pub const INVALID_CGROUP_MOUNT_MESSAGE: &str =
"rAdvisor expects cgroups to be mounted in /sys/fs/cgroup. If this is\nthe case, make sure \
that the 'cpuacct' resource controller has not been disabled.";
#[must_use]
pub fn cgroups_mounted_properly() -> bool {
cgroup_exists("")
}
pub const LINUX_CGROUP_ROOT: &str = "/sys/fs/cgroup";
#[must_use]
fn cgroup_exists<C: AsRef<Path>>(path: C) -> bool {
let mut full_path: PathBuf = [LINUX_CGROUP_ROOT, "cpuacct"].iter().collect();
full_path.push(path);
match fs::metadata(full_path) {
Err(_) => false,
Ok(metadata) => metadata.is_dir(),
}
}