use std::path::PathBuf;
use crate::{
parse::{parse, parse_next},
v1::{self, cgroup::CgroupHelper, Cgroup, CgroupPath},
Max, Result,
};
#[derive(Debug)]
pub struct Subsystem {
path: CgroupPath,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Resources {
pub max: Option<Max>,
}
impl_cgroup! {
Subsystem, Pids,
fn apply(&mut self, resources: &v1::Resources) -> Result<()> {
if let Some(max) = resources.pids.max {
self.set_max(max)?;
}
Ok(())
}
}
impl Subsystem {
gen_getter!(
pids,
"the maximum number of processes this cgroup can have",
max: link,
Max,
parse
);
gen_setter!(
pids,
"a maximum number of processes this cgroup can have,",
max: link,
set_max,
Max,
controlgroup::Max::Limit(2)
);
gen_getter!(
pids,
"the number of processes this cgroup currently has",
current,
u32,
parse
);
gen_getter!(
pids,
"the event counter, i.e. a pair of the maximum number of processes,
and the number of times fork failed due to the limit",
events,
(Max, u64),
parse_events
);
}
impl Into<v1::Resources> for Resources {
fn into(self) -> v1::Resources {
v1::Resources {
pids: self,
..v1::Resources::default()
}
}
}
fn parse_events(mut reader: impl std::io::Read) -> Result<(Max, u64)> {
let mut buf = String::new();
reader.read_to_string(&mut buf)?;
let mut entry = buf.split_whitespace();
let max = parse_next(&mut entry)?;
let cnt = parse_next(&mut entry)?;
if entry.next().is_some() {
bail_parse!();
}
Ok((max, cnt))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subsystem_create_file_exists() -> Result<()> {
gen_subsystem_test!(Pids, ["max", "current", "events"])
}
#[test]
fn test_subsystem_apply() -> Result<()> {
gen_subsystem_test!(
Pids,
Resources {
max: Some(Max::Limit(42)),
},
(max, Max::Limit(42)),
)
}
#[test]
fn test_subsystem_max() -> Result<()> {
gen_subsystem_test!(Pids, max, Max::Max, set_max, Max::Limit(42))
}
#[test]
#[ignore] fn test_subsystem_current() -> Result<()> {
let mut cgroup =
Subsystem::new(CgroupPath::new(v1::SubsystemKind::Pids, gen_cgroup_name!()));
cgroup.create()?;
assert_eq!(cgroup.current()?, 0);
let pid = crate::Pid::from(std::process::id());
cgroup.add_proc(pid)?;
assert!(cgroup.current()? > 0);
cgroup.remove_proc(pid)?;
assert_eq!(cgroup.current()?, 0);
cgroup.delete()
}
#[test]
fn test_subsystem_events() -> Result<()> {
gen_subsystem_test!(Pids, events, (Max::Max, 0))
}
#[test]
fn test_parse_events() -> Result<()> {
const CONTENT_OK_MAX: &str = "max 0\n";
assert_eq!(parse_events(CONTENT_OK_MAX.as_bytes())?, (Max::Max, 0));
const CONTENT_OK_LIM: &str = "42 7\n";
assert_eq!(
parse_events(CONTENT_OK_LIM.as_bytes())?,
(Max::Limit(42), 7)
);
const CONTENT_NG_MAX: &str = "invalid 0\n";
const CONTENT_NG_LIM: &str = "max invalid\n";
const CONTENT_NG_MISSING_DATA: &str = "42\n";
const CONTENT_NG_EXTRA_DATA: &str = "max 0 invalid\n";
for case in &[
CONTENT_NG_MAX,
CONTENT_NG_LIM,
CONTENT_NG_MISSING_DATA,
CONTENT_NG_EXTRA_DATA,
"",
] {
assert_eq!(
parse_events(case.as_bytes()).unwrap_err().kind(),
crate::ErrorKind::Parse
);
}
Ok(())
}
}