#[derive(Debug, Default, Eq, PartialEq)]
pub struct Limit {
pub soft: Option<u32>,
pub hard: Option<u32>,
}
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Limits {
pub max_cpu_time: Limit,
pub max_file_size: Limit,
pub max_data_size: Limit,
pub max_stack_size: Limit,
pub max_core_file_size: Limit,
pub max_resident_set: Limit,
pub max_processes: Limit,
pub max_open_files: Limit,
pub max_locked_memory: Limit,
pub max_address_space: Limit,
pub max_file_locks: Limit,
pub max_pending_signals: Limit,
pub max_msgqueue_size: Limit,
pub max_nice_priority: Limit,
pub max_realtime_priority: Limit,
pub max_realtime_timeout: Limit,
}
impl Limits {
pub fn set_property_from_strings(&mut self, name: &str, soft_string: &str, hard_string: &str) {
use std::str::FromStr;
let lower_case = name.to_lowercase();
let soft = if soft_string == "unlimited" {
None
} else {
u32::from_str(soft_string).ok()
};
let hard = if hard_string == "unlimited" {
None
} else {
u32::from_str(hard_string).ok()
};
let new_limit = Limit { soft, hard };
match lower_case.as_str() {
"max cpu time" => self.max_cpu_time = new_limit,
"max file_size" => self.max_file_size = new_limit,
"max data size" => self.max_data_size = new_limit,
"max stack size" => self.max_stack_size = new_limit,
"max core file size" => self.max_core_file_size = new_limit,
"max resident set" => self.max_resident_set = new_limit,
"max processes" => self.max_processes = new_limit,
"max open files" => self.max_open_files = new_limit,
"max locked memory" => self.max_locked_memory = new_limit,
"max address space" => self.max_address_space = new_limit,
"max file locks" => self.max_file_locks = new_limit,
"max pending signals" => self.max_pending_signals = new_limit,
"max msgqueue size" => self.max_msgqueue_size = new_limit,
"max nice priority" => self.max_nice_priority = new_limit,
"max realtime priority" => self.max_realtime_priority = new_limit,
"max realtime timeout" => self.max_realtime_timeout = new_limit,
_ => (),
}
}
}
pub fn get_pid_limits(pid: u32) -> Result<Limits, crate::Error> {
let file_path = format!("/proc/{}/limits", pid);
let file = std::fs::File::open(&file_path)
.map_err(|io_error| crate::Error::ProcFileNotFound(file_path, io_error))?;
let reader = std::io::BufReader::new(file);
get_limits_from_reader(reader)
}
fn get_limits_from_reader<T>(reader: T) -> Result<Limits, crate::Error>
where
T: std::io::BufRead,
{
let mut limits = Limits::default();
let mut lines = std::io::BufRead::lines(reader).filter_map(Result::ok);
lines.next();
for line in lines {
let (property, values) = line.split_at(26);
let property = property.trim();
let values: Vec<&str> = values.split_whitespace().collect();
limits.set_property_from_strings(property, values[0], values[1]);
}
Ok(limits)
}
#[cfg(test)]
mod tests {
use crate::{Limit, Limits};
#[test]
fn test_own_limits_does_not_panic() {
crate::get_own_limits().unwrap();
}
#[test]
fn test_pid_limits_does_not_panic() {
crate::get_pid_limits(1).unwrap();
}
#[test]
fn test_proc_file_not_found() {
let error = format!("{:?}", super::get_pid_limits(std::u32::MAX).unwrap_err());
let expected_error = String::from(
r#"ProcFileNotFound("/proc/4294967295/limits", Os { code: 2, kind: NotFound, message: "No such file or directory" })"#,
);
assert_eq!(error, expected_error);
}
#[test]
fn test_from_empty_string() {
let reader = std::io::Cursor::new("");
let limits = super::get_limits_from_reader(reader).unwrap();
let expected_limits = Limits::default();
assert_eq!(limits, expected_limits);
}
#[test]
fn test_from_correct_string() {
let reader = std::io::Cursor::new(
r#"Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size unlimited unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 62935 62935 processes
Max open files 1024 524288 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 62935 62935 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 99 99
Max realtime timeout unlimited unlimited us"#,
);
let limits = super::get_limits_from_reader(reader).unwrap();
let expected_limits = Limits {
max_cpu_time: Default::default(),
max_file_size: Default::default(),
max_data_size: Default::default(),
max_stack_size: Limit {
soft: Some(8388608),
hard: None,
},
max_core_file_size: Default::default(),
max_resident_set: Default::default(),
max_processes: Limit {
soft: Some(62935),
hard: Some(62935),
},
max_open_files: Limit {
soft: Some(1024),
hard: Some(524288),
},
max_locked_memory: Limit {
soft: Some(65536),
hard: Some(65536),
},
max_address_space: Default::default(),
max_file_locks: Default::default(),
max_pending_signals: Limit {
soft: Some(62935),
hard: Some(62935),
},
max_msgqueue_size: Limit {
soft: Some(819200),
hard: Some(819200),
},
max_nice_priority: Limit {
soft: Some(0),
hard: Some(0),
},
max_realtime_priority: Limit {
soft: Some(99),
hard: Some(99),
},
max_realtime_timeout: Default::default(),
};
assert_eq!(limits, expected_limits);
}
}