//! Configure the process resource limits. 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(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{ use libc::{__rlimit_resource_t, rlimit}; } else if #[cfg(any( target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "macos", target_os = "ios", target_os = "android", target_os = "dragonfly", all(target_os = "linux", not(target_env = "gnu")) ))]{ use libc::rlimit; } } libc_enum! { /// Types of process resources. /// /// The Resource enum is platform dependent. Check different platform /// manuals for more details. Some platform links have been provided for /// easier reference (non-exhaustive). /// /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html) /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit) /// * [NetBSD](https://man.netbsd.org/setrlimit.2) // linux-gnu uses u_int as resource enum, which is implemented in libc as // well. // // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs #[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))] #[cfg_attr(any( target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "macos", target_os = "ios", target_os = "android", target_os = "dragonfly", all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))) ), repr(i32))] #[non_exhaustive] pub enum Resource { #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum amount (in bytes) of virtual memory the process is /// allowed to map. RLIMIT_AS, /// The largest size (in bytes) core(5) file that may be created. RLIMIT_CORE, /// The maximum amount of cpu time (in seconds) to be used by each /// process. RLIMIT_CPU, /// The maximum size (in bytes) of the data segment for a process RLIMIT_DATA, /// The largest size (in bytes) file that may be created. RLIMIT_FSIZE, /// The maximum number of open files for this process. RLIMIT_NOFILE, /// The maximum size (in bytes) of the stack segment for a process. RLIMIT_STACK, #[cfg(target_os = "freebsd")] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum number of kqueues this user id is allowed to create. RLIMIT_KQUEUES, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// A limit on the combined number of flock locks and fcntl leases that /// this process may establish. RLIMIT_LOCKS, #[cfg(any( target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux", target_os = "netbsd" ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum size (in bytes) which a process may lock into memory /// using the mlock(2) system call. RLIMIT_MEMLOCK, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// A limit on the number of bytes that can be allocated for POSIX /// message queues for the real user ID of the calling process. RLIMIT_MSGQUEUE, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// A ceiling to which the process's nice value can be raised using /// setpriority or nice. RLIMIT_NICE, #[cfg(any( target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "linux", ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum number of simultaneous processes for this user id. RLIMIT_NPROC, #[cfg(target_os = "freebsd")] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum number of pseudo-terminals this user id is allowed to /// create. RLIMIT_NPTS, #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "linux", ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// When there is memory pressure and swap is available, prioritize /// eviction of a process' resident pages beyond this amount (in bytes). RLIMIT_RSS, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// A ceiling on the real-time priority that may be set for this process /// using sched_setscheduler and sched_set‐ param. RLIMIT_RTPRIO, #[cfg(any(target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// A limit (in microseconds) on the amount of CPU time that a process /// scheduled under a real-time scheduling policy may con‐ sume without /// making a blocking system call. RLIMIT_RTTIME, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// A limit on the number of signals that may be queued for the real /// user ID of the calling process. RLIMIT_SIGPENDING, #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum size (in bytes) of socket buffer usage for this user. RLIMIT_SBSIZE, #[cfg(target_os = "freebsd")] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum size (in bytes) of the swap space that may be reserved /// or used by all of this user id's processes. RLIMIT_SWAP, #[cfg(target_os = "freebsd")] #[cfg_attr(docsrs, doc(cfg(all())))] /// An alias for RLIMIT_AS. RLIMIT_VMEM, } } /// Get the current processes resource limits /// /// The special value [`RLIM_INFINITY`] indicates that no limit will be /// enforced. /// /// # Parameters /// /// * `resource`: The [`Resource`] that we want to get the limits of. /// /// # Examples /// /// ``` /// # use nix::sys::resource::{getrlimit, Resource}; /// /// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); /// println!("current soft_limit: {}", soft_limit); /// println!("current hard_limit: {}", hard_limit); /// ``` /// /// # References /// /// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215) /// /// [`Resource`]: enum.Resource.html pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> { let mut old_rlim = mem::MaybeUninit::::uninit(); cfg_if! { if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{ 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) }) } /// Set the current processes resource limits /// /// # Parameters /// /// * `resource`: The [`Resource`] that we want to set the limits of. /// * `soft_limit`: The value that the kernel enforces for the corresponding /// resource. /// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to /// the current hard limit for non-root users. /// /// The special value [`RLIM_INFINITY`] indicates that no limit will be /// enforced. /// /// # Examples /// /// ``` /// # use nix::sys::resource::{setrlimit, Resource}; /// /// let soft_limit = 512; /// let hard_limit = 1024; /// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); /// ``` /// /// # References /// /// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215) /// /// [`Resource`]: enum.Resource.html /// /// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`. 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(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{ 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! { /// Whose resource usage should be returned by [`getrusage`]. #[repr(i32)] #[non_exhaustive] pub enum UsageWho { /// Resource usage for the current process. RUSAGE_SELF, /// Resource usage for all the children that have terminated and been waited for. RUSAGE_CHILDREN, #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Resource usage for the calling thread. RUSAGE_THREAD, } } /// Output of `getrusage` with information about resource usage. Some of the fields /// may be unused in some platforms, and will be always zeroed out. See their manuals /// for details. #[repr(transparent)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Usage(rusage); impl AsRef for Usage { fn as_ref(&self) -> &rusage { &self.0 } } impl AsMut for Usage { fn as_mut(&mut self) -> &mut rusage { &mut self.0 } } impl Usage { /// Total amount of time spent executing in user mode. pub fn user_time(&self) -> TimeVal { TimeVal::from(self.0.ru_utime) } /// Total amount of time spent executing in kernel mode. pub fn system_time(&self) -> TimeVal { TimeVal::from(self.0.ru_stime) } /// The resident set size at its peak, in kilobytes. pub fn max_rss(&self) -> c_long { self.0.ru_maxrss } /// Integral value expressed in kilobytes times ticks of execution indicating /// the amount of text memory shared with other processes. pub fn shared_integral(&self) -> c_long { self.0.ru_ixrss } /// Integral value expressed in kilobytes times ticks of execution indicating /// the amount of unshared memory used by data. pub fn unshared_data_integral(&self) -> c_long { self.0.ru_idrss } /// Integral value expressed in kilobytes times ticks of execution indicating /// the amount of unshared memory used for stack space. pub fn unshared_stack_integral(&self) -> c_long { self.0.ru_isrss } /// Number of page faults that were served without resorting to I/O, with pages /// that have been allocated previously by the kernel. pub fn minor_page_faults(&self) -> c_long { self.0.ru_minflt } /// Number of page faults that were served through I/O (i.e. swap). pub fn major_page_faults(&self) -> c_long { self.0.ru_majflt } /// Number of times all of the memory was fully swapped out. pub fn full_swaps(&self) -> c_long { self.0.ru_nswap } /// Number of times a read was done from a block device. pub fn block_reads(&self) -> c_long { self.0.ru_inblock } /// Number of times a write was done to a block device. pub fn block_writes(&self) -> c_long { self.0.ru_oublock } /// Number of IPC messages sent. pub fn ipc_sends(&self) -> c_long { self.0.ru_msgsnd } /// Number of IPC messages received. pub fn ipc_receives(&self) -> c_long { self.0.ru_msgrcv } /// Number of signals received. pub fn signals(&self) -> c_long { self.0.ru_nsignals } /// Number of times a context switch was voluntarily invoked. pub fn voluntary_context_switches(&self) -> c_long { self.0.ru_nvcsw } /// Number of times a context switch was imposed by the kernel (usually due to /// time slice expiring or preemption by a higher priority process). pub fn involuntary_context_switches(&self) -> c_long { self.0.ru_nivcsw } } /// Get usage information for a process, its children or the current thread /// /// Real time information can be obtained for either the current process or (in some /// systems) thread, but information about children processes is only provided for /// those that have terminated and been waited for (see [`super::wait::wait`]). /// /// Some information may be missing depending on the platform, and the way information /// is provided for children may also vary. Check the manuals for details. /// /// # References /// /// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html) /// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html) /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage) /// * [NetBSD](https://man.netbsd.org/getrusage.2) /// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html) /// /// [`UsageWho`]: enum.UsageWho.html /// /// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`]. pub fn getrusage(who: UsageWho) -> Result { unsafe { let mut rusage = mem::MaybeUninit::::uninit(); let res = libc::getrusage(who as c_int, rusage.as_mut_ptr()); Errno::result(res).map(|_| Usage(rusage.assume_init())) } } #[cfg(test)] mod test { use super::{getrusage, UsageWho}; #[test] pub fn test_self_cpu_time() { // Make sure some CPU time is used. let mut numbers: Vec = (1..1_000_000).collect(); numbers.iter_mut().for_each(|item| *item *= 2); // FIXME: this is here to help ensure the compiler does not optimize the whole // thing away. Replace the assert with test::black_box once stabilized. assert_eq!(numbers[100..200].iter().sum::(), 30_100); let usage = getrusage(UsageWho::RUSAGE_SELF) .expect("Failed to call getrusage for SELF"); let rusage = usage.as_ref(); let user = usage.user_time(); assert!(user.tv_sec() > 0 || user.tv_usec() > 0); assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec); assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec); } }