#![deny(
missing_docs,
missing_debug_implementations,
clippy::all,
clippy::restriction,
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(
clippy::as_conversions,
clippy::implicit_return,
clippy::inline_always,
clippy::must_use_candidate
)]
#![allow(clippy::missing_errors_doc)]
#[macro_use]
extern crate cfg_if;
use std::error::Error;
use std::fmt;
use std::io;
cfg_if! {
if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
use libc::__rlimit_resource_t as __resource_t;
}else{
use libc::c_int as __resource_t;
}
}
use libc::rlim_t as __rlim_t;
use libc::rlimit as __rlimit;
use libc::getrlimit as __getrlimit;
use libc::setrlimit as __setrlimit;
#[cfg(target_os = "linux")]
use libc::prlimit as __prlimit;
#[allow(non_camel_case_types)]
pub type rlim = __rlim_t;
pub const RLIM_INFINITY: rlim = libc::RLIM_INFINITY;
pub type RawResource = __resource_t;
#[allow(clippy::cast_possible_wrap)]
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Resource {
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
AS = libc::RLIMIT_AS as _,
CORE = libc::RLIMIT_CORE as _,
CPU = libc::RLIMIT_CPU as _,
DATA = libc::RLIMIT_DATA as _,
FSIZE = libc::RLIMIT_FSIZE as _,
#[cfg(target_os = "linux")]
LOCKS = libc::RLIMIT_LOCKS as _,
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
MEMLOCK = libc::RLIMIT_MEMLOCK as _,
#[cfg(target_os = "linux")]
MSGQUEUE = libc::RLIMIT_MSGQUEUE as _,
#[cfg(target_os = "linux")]
NICE = libc::RLIMIT_NICE as _,
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
NOFILE = libc::RLIMIT_NOFILE as _,
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
NPROC = libc::RLIMIT_NPROC as _,
#[cfg(target_os = "linux")]
RSS = libc::RLIMIT_RSS as _,
#[cfg(target_os = "linux")]
RTPRIO = libc::RLIMIT_RTPRIO as _,
#[cfg(all(target_os = "linux", target_env = "gnu"))]
RTTIME = libc::RLIMIT_RTTIME as _,
#[cfg(target_os = "linux")]
SIGPENDING = libc::RLIMIT_SIGPENDING as _,
STACK = libc::RLIMIT_STACK as _,
}
impl Resource {
#[inline(always)]
pub const fn as_raw_resource(self) -> RawResource {
self as _
}
#[inline(always)]
pub fn set(self, soft: rlim, hard: rlim) -> io::Result<()> {
setrlimit(self, soft, hard)
}
#[inline(always)]
pub fn get(self) -> std::io::Result<(rlim, rlim)> {
getrlimit(self)
}
}
#[allow(clippy::missing_docs_in_private_items)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseResourceError {
_priv: (),
}
impl fmt::Display for ParseResourceError {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"failed to parse Resource".fmt(f)
}
}
impl Error for ParseResourceError {}
impl std::str::FromStr for Resource {
type Err = ParseResourceError;
#[inline(always)]
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
"RLIMIT_AS" => Ok(Self::AS),
"RLIMIT_CORE" => Ok(Self::CORE),
"RLIMIT_CPU" => Ok(Self::CPU),
"RLIMIT_DATA" => Ok(Self::DATA),
"RLIMIT_FSIZE" => Ok(Self::FSIZE),
#[cfg(target_os = "linux")]
"RLIMIT_LOCKS" => Ok(Self::LOCKS),
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
"RLIMIT_MEMLOCK" => Ok(Self::MEMLOCK),
#[cfg(target_os = "linux")]
"RLIMIT_MSGQUEUE" => Ok(Self::MSGQUEUE),
#[cfg(target_os = "linux")]
"RLIMIT_NICE" => Ok(Self::NICE),
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
"RLIMIT_NOFILE" => Ok(Self::NOFILE),
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
"RLIMIT_NPROC" => Ok(Self::NPROC),
#[cfg(target_os = "linux")]
"RLIMIT_RSS" => Ok(Self::RSS),
#[cfg(target_os = "linux")]
"RLIMIT_RTPRIO" => Ok(Self::RTPRIO),
#[cfg(all(target_os = "linux", target_env = "gnu"))]
"RLIMIT_RTTIME" => Ok(Self::RTTIME),
#[cfg(target_os = "linux")]
"RLIMIT_SIGPENDING" => Ok(Self::SIGPENDING),
"RLIMIT_STACK" => Ok(Self::STACK),
_ => Err(ParseResourceError { _priv: () }),
}
}
}
#[inline]
pub fn setrlimit(resource: Resource, soft: rlim, hard: rlim) -> io::Result<()> {
let raw_resource = resource.as_raw_resource();
let limit = __rlimit {
rlim_cur: soft,
rlim_max: hard,
};
let ret = unsafe { __setrlimit(raw_resource as _, &limit) };
if ret == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
#[inline]
pub fn getrlimit(resource: Resource) -> io::Result<(rlim, rlim)> {
let mut limit = std::mem::MaybeUninit::<__rlimit>::uninit();
let ret = unsafe { __getrlimit(resource.as_raw_resource() as _, limit.as_mut_ptr()) };
if ret == 0 {
let limit = unsafe { limit.assume_init() };
Ok((limit.rlim_cur, limit.rlim_max))
} else {
Err(std::io::Error::last_os_error())
}
}
#[allow(clippy::similar_names, clippy::needless_pass_by_value)]
#[inline]
#[cfg(target_os = "linux")]
pub fn prlimit(
pid: libc::pid_t,
resource: Resource,
new_limit: Option<(rlim, rlim)>,
old_limit: Option<&mut (rlim, rlim)>,
) -> io::Result<()> {
use std::mem;
use std::ptr;
let new_rlimit: Option<__rlimit> = new_limit.map(|(soft, hard)| __rlimit {
rlim_cur: soft,
rlim_max: hard,
});
let new_rlimit_ptr: *const __rlimit = new_rlimit.as_ref().map_or(ptr::null(), |r| r);
let mut old_rlimit: mem::MaybeUninit<__rlimit> = mem::MaybeUninit::uninit();
let old_rlimit_ptr: *mut __rlimit = if old_limit.is_some() {
old_rlimit.as_mut_ptr()
} else {
ptr::null_mut()
};
let ret = unsafe {
__prlimit(
pid,
resource.as_raw_resource() as _,
new_rlimit_ptr,
old_rlimit_ptr,
)
};
if ret == 0 {
if let Some((soft, hard)) = old_limit {
let old_rlimit: __rlimit = unsafe { old_rlimit.assume_init() };
*soft = old_rlimit.rlim_cur;
*hard = old_rlimit.rlim_max;
}
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}