use cfg_if::cfg_if;
use libc::{c_int, c_long, rusage};
use crate::errno::Errno;
use crate::sys::time::TimeVal;
use crate::Result;
pub use libc::rlim_t;
pub use libc::RLIM_INFINITY;
use std::mem;
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))]{
use libc::{__rlimit_resource_t, rlimit};
} else if #[cfg(any(
bsd,
target_os = "android",
target_os = "aix",
target_os = "illumos",
all(target_os = "linux", not(target_env = "gnu")),
target_os = "cygwin"
))]{
use libc::rlimit;
}
}
libc_enum! {
#[cfg_attr(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
), repr(u32))]
#[cfg_attr(any(
bsd,
target_os = "android",
target_os = "aix",
target_os = "illumos",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))),
target_os = "cygwin"
), repr(i32))]
#[non_exhaustive]
pub enum Resource {
#[cfg(not(any(target_os = "freebsd", netbsdlike)))]
RLIMIT_AS,
RLIMIT_CORE,
RLIMIT_CPU,
RLIMIT_DATA,
RLIMIT_FSIZE,
RLIMIT_NOFILE,
RLIMIT_STACK,
#[cfg(target_os = "freebsd")]
RLIMIT_KQUEUES,
#[cfg(linux_android)]
RLIMIT_LOCKS,
#[cfg(any(linux_android, target_os = "freebsd", netbsdlike))]
RLIMIT_MEMLOCK,
#[cfg(linux_android)]
RLIMIT_MSGQUEUE,
#[cfg(linux_android)]
RLIMIT_NICE,
#[cfg(any(
linux_android,
target_os = "freebsd",
netbsdlike,
target_os = "aix",
))]
RLIMIT_NPROC,
#[cfg(target_os = "freebsd")]
RLIMIT_NPTS,
#[cfg(any(linux_android,
target_os = "freebsd",
netbsdlike,
target_os = "aix",
))]
RLIMIT_RSS,
#[cfg(linux_android)]
RLIMIT_RTPRIO,
#[cfg(any(target_os = "linux"))]
RLIMIT_RTTIME,
#[cfg(linux_android)]
RLIMIT_SIGPENDING,
#[cfg(freebsdlike)]
RLIMIT_SBSIZE,
#[cfg(target_os = "freebsd")]
RLIMIT_SWAP,
#[cfg(target_os = "freebsd")]
RLIMIT_VMEM,
}
}
pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))] {
let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
} else {
let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
}
}
Errno::result(res).map(|_| {
let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
(rlim_cur, rlim_max)
})
}
pub fn setrlimit(
resource: Resource,
soft_limit: rlim_t,
hard_limit: rlim_t,
) -> Result<()> {
let new_rlim = rlimit {
rlim_cur: soft_limit,
rlim_max: hard_limit,
};
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd",
))]{
let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
}else{
let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
}
}
Errno::result(res).map(drop)
}
libc_enum! {
#[repr(i32)]
#[non_exhaustive]
pub enum UsageWho {
RUSAGE_SELF,
RUSAGE_CHILDREN,
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
RUSAGE_THREAD,
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Usage(rusage);
impl AsRef<rusage> for Usage {
fn as_ref(&self) -> &rusage {
&self.0
}
}
impl AsMut<rusage> for Usage {
fn as_mut(&mut self) -> &mut rusage {
&mut self.0
}
}
impl Usage {
pub fn user_time(&self) -> TimeVal {
TimeVal::from(self.0.ru_utime)
}
pub fn system_time(&self) -> TimeVal {
TimeVal::from(self.0.ru_stime)
}
#[cfg_attr(apple_targets, doc = " in bytes.")]
#[cfg_attr(not(apple_targets), doc = " in kilobytes.")]
pub fn max_rss(&self) -> c_long {
self.0.ru_maxrss
}
pub fn shared_integral(&self) -> c_long {
self.0.ru_ixrss
}
pub fn unshared_data_integral(&self) -> c_long {
self.0.ru_idrss
}
pub fn unshared_stack_integral(&self) -> c_long {
self.0.ru_isrss
}
pub fn minor_page_faults(&self) -> c_long {
self.0.ru_minflt
}
pub fn major_page_faults(&self) -> c_long {
self.0.ru_majflt
}
pub fn full_swaps(&self) -> c_long {
self.0.ru_nswap
}
pub fn block_reads(&self) -> c_long {
self.0.ru_inblock
}
pub fn block_writes(&self) -> c_long {
self.0.ru_oublock
}
pub fn ipc_sends(&self) -> c_long {
self.0.ru_msgsnd
}
pub fn ipc_receives(&self) -> c_long {
self.0.ru_msgrcv
}
pub fn signals(&self) -> c_long {
self.0.ru_nsignals
}
pub fn voluntary_context_switches(&self) -> c_long {
self.0.ru_nvcsw
}
pub fn involuntary_context_switches(&self) -> c_long {
self.0.ru_nivcsw
}
}
pub fn getrusage(who: UsageWho) -> Result<Usage> {
unsafe {
let mut rusage = mem::MaybeUninit::<rusage>::uninit();
let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
Errno::result(res).map(|_| Usage(rusage.assume_init()))
}
}