#![deny(unsafe_code)]
use crate::unix::pid_t;
use std::fs;
use std::io::{self, BufRead};
use std::num::ParseIntError;
use std::path::Path;
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct ProcLimits {
pub max_cpu_time: Option<ProcLimit>,
pub max_file_size: Option<ProcLimit>,
pub max_data_size: Option<ProcLimit>,
pub max_stack_size: Option<ProcLimit>,
pub max_core_file_size: Option<ProcLimit>,
pub max_resident_set: Option<ProcLimit>,
pub max_processes: Option<ProcLimit>,
pub max_open_files: Option<ProcLimit>,
pub max_locked_memory: Option<ProcLimit>,
pub max_address_space: Option<ProcLimit>,
pub max_file_locks: Option<ProcLimit>,
pub max_pending_signals: Option<ProcLimit>,
pub max_msgqueue_size: Option<ProcLimit>,
pub max_nice_priority: Option<ProcLimit>,
pub max_realtime_priority: Option<ProcLimit>,
pub max_realtime_timeout: Option<ProcLimit>,
}
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ProcLimit {
pub soft_limit: Option<u64>,
pub hard_limit: Option<u64>,
}
impl ProcLimits {
pub fn read_self() -> io::Result<Self> {
Self::read_proc_fs("/proc/self/limits")
}
pub fn read_process(pid: pid_t) -> io::Result<Self> {
if pid < 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"ProcLimits: pid must be non-negative",
));
}
Self::read_proc_fs(format!("/proc/{}/limits", pid))
}
fn read_proc_fs(limits_path: impl AsRef<Path>) -> io::Result<Self> {
fn parse_head(head: &str) -> Option<(usize, usize, usize)> {
let s_idx = head.find('S')?;
let h_idx = head[s_idx..].find('H')?;
let u_idx = head[s_idx + h_idx..].find('U')?;
Some((s_idx, h_idx, u_idx))
}
fn parse_limit_number(s: &str) -> Result<Option<u64>, ParseIntError> {
match s {
"unlimited" => Ok(None),
_ => match s.parse::<u64>() {
Ok(n) => Ok(Some(n)),
Err(e) => Err(e),
},
}
}
fn error_missing_table_head() -> io::Error {
io::Error::new(io::ErrorKind::Other, "ProcLimits: missing table head")
}
fn error_invalid_table_head() -> io::Error {
io::Error::new(io::ErrorKind::Other, "ProcLimits: invalid table head")
}
fn error_invalid_limit_number(e: ParseIntError) -> io::Error {
let ans = io::Error::new(
io::ErrorKind::Other,
format!("ProcLimits: invalid limit number: {}", e),
);
drop(e);
ans
}
fn error_duplicate_limit_field() -> io::Error {
io::Error::new(io::ErrorKind::Other, "ProcLimits: duplicate limit field")
}
fn error_unknown_limit_field(s: &str) -> io::Error {
io::Error::new(
io::ErrorKind::Other,
format!("ProcLimits: unknown limit field: {:?}", s),
)
}
let reader = io::BufReader::new(fs::File::open(limits_path)?);
let mut lines = reader.lines();
let head = lines.next().ok_or_else(error_missing_table_head)??;
let (name_len, soft_len, hard_len) =
parse_head(&head).ok_or_else(error_invalid_table_head)?;
let mut ans = Self::default();
let sorted_table: [(&str, &mut Option<ProcLimit>); 16] = [
("max address space", &mut ans.max_address_space),
("max core file size", &mut ans.max_core_file_size),
("max cpu time", &mut ans.max_cpu_time),
("max data size", &mut ans.max_data_size),
("max file locks", &mut ans.max_file_locks),
("max file size", &mut ans.max_file_size),
("max locked memory", &mut ans.max_locked_memory),
("max msgqueue size", &mut ans.max_msgqueue_size),
("max nice priority", &mut ans.max_nice_priority),
("max open files", &mut ans.max_open_files),
("max pending signals", &mut ans.max_pending_signals),
("max processes", &mut ans.max_processes),
("max realtime priority", &mut ans.max_realtime_priority),
("max realtime timeout", &mut ans.max_realtime_timeout),
("max resident set", &mut ans.max_resident_set),
("max stack size", &mut ans.max_stack_size),
];
for line in lines {
let line = line?;
let (name, line) = line.split_at(name_len);
let (soft, line) = line.split_at(soft_len);
let (hard, _) = line.split_at(hard_len);
let name = name.trim().to_lowercase();
let soft_limit = parse_limit_number(soft.trim()).map_err(error_invalid_limit_number)?;
let hard_limit = parse_limit_number(hard.trim()).map_err(error_invalid_limit_number)?;
let limit = ProcLimit {
soft_limit,
hard_limit,
};
match sorted_table.binary_search_by_key(&name.as_str(), |&(s, _)| s) {
Ok(idx) => {
let field = &mut *sorted_table[idx].1;
if field.is_some() {
return Err(error_duplicate_limit_field());
}
*field = Some(limit);
}
Err(_) => return Err(error_unknown_limit_field(&name)),
}
}
Ok(ans)
}
}