use crate::{FileWrapper, ProcError, ProcResult};
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Read};
use std::str::FromStr;
impl crate::process::Process {
pub fn limits(&self) -> ProcResult<Limits> {
let file = FileWrapper::open_at(&self.root, &self.fd, "limits")?;
Limits::from_reader(file)
}
}
#[derive(Debug, Clone)]
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 {
fn from_reader<R: Read>(r: R) -> ProcResult<Limits> {
let bufread = BufReader::new(r);
let mut lines = bufread.lines();
let mut map = HashMap::new();
while let Some(Ok(line)) = lines.next() {
let line = line.trim();
if line.starts_with("Limit") {
continue;
}
let s: Vec<_> = line.split_whitespace().collect();
let l = s.len();
let (hard_limit, soft_limit, name) =
if line.starts_with("Max nice priority") || line.starts_with("Max realtime priority") {
let hard_limit = expect!(s.get(l - 1)).to_owned();
let soft_limit = expect!(s.get(l - 2)).to_owned();
let name = s[0..l - 2].join(" ");
(hard_limit, soft_limit, name)
} else {
let hard_limit = expect!(s.get(l - 2)).to_owned();
let soft_limit = expect!(s.get(l - 3)).to_owned();
let name = s[0..l - 3].join(" ");
(hard_limit, soft_limit, name)
};
let _units = expect!(s.get(l - 1));
map.insert(name.to_owned(), (soft_limit.to_owned(), hard_limit.to_owned()));
}
let limits = Limits {
max_cpu_time: Limit::from_pair(expect!(map.remove("Max cpu time")))?,
max_file_size: Limit::from_pair(expect!(map.remove("Max file size")))?,
max_data_size: Limit::from_pair(expect!(map.remove("Max data size")))?,
max_stack_size: Limit::from_pair(expect!(map.remove("Max stack size")))?,
max_core_file_size: Limit::from_pair(expect!(map.remove("Max core file size")))?,
max_resident_set: Limit::from_pair(expect!(map.remove("Max resident set")))?,
max_processes: Limit::from_pair(expect!(map.remove("Max processes")))?,
max_open_files: Limit::from_pair(expect!(map.remove("Max open files")))?,
max_locked_memory: Limit::from_pair(expect!(map.remove("Max locked memory")))?,
max_address_space: Limit::from_pair(expect!(map.remove("Max address space")))?,
max_file_locks: Limit::from_pair(expect!(map.remove("Max file locks")))?,
max_pending_signals: Limit::from_pair(expect!(map.remove("Max pending signals")))?,
max_msgqueue_size: Limit::from_pair(expect!(map.remove("Max msgqueue size")))?,
max_nice_priority: Limit::from_pair(expect!(map.remove("Max nice priority")))?,
max_realtime_priority: Limit::from_pair(expect!(map.remove("Max realtime priority")))?,
max_realtime_timeout: Limit::from_pair(expect!(map.remove("Max realtime timeout")))?,
};
if cfg!(test) {
assert!(map.is_empty(), "Map isn't empty: {:?}", map);
}
Ok(limits)
}
}
#[derive(Debug, Copy, Clone)]
pub struct Limit {
pub soft_limit: LimitValue,
pub hard_limit: LimitValue,
}
impl Limit {
fn from_pair(l: (String, String)) -> ProcResult<Limit> {
let (soft, hard) = l;
Ok(Limit {
soft_limit: LimitValue::from_str(&soft)?,
hard_limit: LimitValue::from_str(&hard)?,
})
}
}
#[derive(Debug, Copy, Clone)]
pub enum LimitValue {
Unlimited,
Value(u64),
}
impl LimitValue {
#[cfg(test)]
pub(crate) fn as_limit(&self) -> Option<u64> {
match self {
LimitValue::Unlimited => None,
LimitValue::Value(v) => Some(*v),
}
}
}
impl FromStr for LimitValue {
type Err = ProcError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "unlimited" {
Ok(LimitValue::Unlimited)
} else {
Ok(LimitValue::Value(from_str!(u64, s)))
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
use rustix::process::Resource;
#[test]
fn test_limits() {
let me = process::Process::myself().unwrap();
let limits = me.limits().unwrap();
println!("{:#?}", limits);
let lim = rustix::process::getrlimit(Resource::Cpu);
assert_eq!(lim.current, limits.max_cpu_time.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_cpu_time.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Fsize);
assert_eq!(lim.current, limits.max_file_size.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_file_size.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Data);
assert_eq!(lim.current, limits.max_data_size.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_data_size.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Stack);
assert_eq!(lim.current, limits.max_stack_size.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_stack_size.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Core);
assert_eq!(lim.current, limits.max_core_file_size.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_core_file_size.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Rss);
assert_eq!(lim.current, limits.max_resident_set.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_resident_set.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Nproc);
assert_eq!(lim.current, limits.max_processes.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_processes.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Nofile);
assert_eq!(lim.current, limits.max_open_files.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_open_files.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Memlock);
assert_eq!(lim.current, limits.max_locked_memory.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_locked_memory.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::As);
assert_eq!(lim.current, limits.max_address_space.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_address_space.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Locks);
assert_eq!(lim.current, limits.max_file_locks.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_file_locks.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Sigpending);
assert_eq!(lim.current, limits.max_pending_signals.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_pending_signals.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Msgqueue);
assert_eq!(lim.current, limits.max_msgqueue_size.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_msgqueue_size.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Nice);
assert_eq!(lim.current, limits.max_nice_priority.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_nice_priority.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Rtprio);
assert_eq!(lim.current, limits.max_realtime_priority.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_realtime_priority.hard_limit.as_limit());
let lim = rustix::process::getrlimit(Resource::Rttime);
assert_eq!(lim.current, limits.max_realtime_timeout.soft_limit.as_limit());
assert_eq!(lim.maximum, limits.max_realtime_timeout.hard_limit.as_limit());
}
}