use crate::{FileWrapper, ProcError, ProcResult};
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Read};
use std::str::FromStr;
use libc::rlim_t;
impl crate::process::Process {
pub fn limits(&self) -> ProcResult<Limits> {
let path = self.root.join("limits");
let file = FileWrapper::open(&path)?;
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(rlim_t),
}
impl LimitValue {
#[cfg(test)]
pub(crate) fn as_rlim_t(&self) -> libc::rlim_t {
match self {
LimitValue::Unlimited => libc::RLIM_INFINITY,
LimitValue::Value(v) => *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!(rlim_t, s)))
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn test_limits() {
let me = process::Process::myself().unwrap();
let limits = me.limits().unwrap();
println!("{:#?}", limits);
let mut libc_lim = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_CPU, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_cpu_time.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_cpu_time.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_FSIZE, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_file_size.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_file_size.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_DATA, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_data_size.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_data_size.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_STACK, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_stack_size.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_stack_size.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_CORE, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_core_file_size.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_core_file_size.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_RSS, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_resident_set.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_resident_set.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_NPROC, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_processes.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_processes.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_open_files.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_open_files.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_MEMLOCK, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_locked_memory.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_locked_memory.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_AS, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_address_space.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_address_space.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_LOCKS, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_file_locks.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_file_locks.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_SIGPENDING, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_pending_signals.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_pending_signals.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_MSGQUEUE, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_msgqueue_size.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_msgqueue_size.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_NICE, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_nice_priority.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_nice_priority.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_RTPRIO, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_realtime_priority.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_realtime_priority.hard_limit.as_rlim_t()
);
assert_eq!(
unsafe { libc::getrlimit(libc::RLIMIT_RTTIME, &mut libc_lim) },
0
);
assert_eq!(
libc_lim.rlim_cur,
limits.max_realtime_timeout.soft_limit.as_rlim_t()
);
assert_eq!(
libc_lim.rlim_max,
limits.max_realtime_timeout.hard_limit.as_rlim_t()
);
}
}