nix/sys/
resource.rs

1//! Configure the process resource limits.
2use cfg_if::cfg_if;
3use libc::{c_int, c_long, rusage};
4
5use crate::errno::Errno;
6use crate::sys::time::TimeVal;
7use crate::Result;
8pub use libc::rlim_t;
9pub use libc::RLIM_INFINITY;
10use std::mem;
11
12cfg_if! {
13    if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
14        use libc::{__rlimit_resource_t, rlimit};
15    } else if #[cfg(any(
16        target_os = "freebsd",
17        target_os = "openbsd",
18        target_os = "netbsd",
19        target_os = "macos",
20        target_os = "ios",
21        target_os = "android",
22        target_os = "dragonfly",
23        all(target_os = "linux", not(target_env = "gnu"))
24    ))]{
25        use libc::rlimit;
26    }
27}
28
29libc_enum! {
30    /// Types of process resources.
31    ///
32    /// The Resource enum is platform dependent. Check different platform
33    /// manuals for more details. Some platform links have been provided for
34    /// easier reference (non-exhaustive).
35    ///
36    /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
37    /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
38    /// * [NetBSD](https://man.netbsd.org/setrlimit.2)
39
40    // linux-gnu uses u_int as resource enum, which is implemented in libc as
41    // well.
42    //
43    // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
44    // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
45    #[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))]
46    #[cfg_attr(any(
47            target_os = "freebsd",
48            target_os = "openbsd",
49            target_os = "netbsd",
50            target_os = "macos",
51            target_os = "ios",
52            target_os = "android",
53            target_os = "dragonfly",
54            all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
55        ), repr(i32))]
56    #[non_exhaustive]
57    pub enum Resource {
58        #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
59        #[cfg_attr(docsrs, doc(cfg(all())))]
60        /// The maximum amount (in bytes) of virtual memory the process is
61        /// allowed to map.
62        RLIMIT_AS,
63        /// The largest size (in bytes) core(5) file that may be created.
64        RLIMIT_CORE,
65        /// The maximum amount of cpu time (in seconds) to be used by each
66        /// process.
67        RLIMIT_CPU,
68        /// The maximum size (in bytes) of the data segment for a process
69        RLIMIT_DATA,
70        /// The largest size (in bytes) file that may be created.
71        RLIMIT_FSIZE,
72        /// The maximum number of open files for this process.
73        RLIMIT_NOFILE,
74        /// The maximum size (in bytes) of the stack segment for a process.
75        RLIMIT_STACK,
76
77        #[cfg(target_os = "freebsd")]
78        #[cfg_attr(docsrs, doc(cfg(all())))]
79        /// The maximum number of kqueues this user id is allowed to create.
80        RLIMIT_KQUEUES,
81
82        #[cfg(any(target_os = "android", target_os = "linux"))]
83        #[cfg_attr(docsrs, doc(cfg(all())))]
84        /// A limit on the combined number of flock locks and fcntl leases that
85        /// this process may establish.
86        RLIMIT_LOCKS,
87
88        #[cfg(any(
89            target_os = "android",
90            target_os = "freebsd",
91            target_os = "openbsd",
92            target_os = "linux",
93            target_os = "netbsd"
94        ))]
95        #[cfg_attr(docsrs, doc(cfg(all())))]
96        /// The maximum size (in bytes) which a process may lock into memory
97        /// using the mlock(2) system call.
98        RLIMIT_MEMLOCK,
99
100        #[cfg(any(target_os = "android", target_os = "linux"))]
101        #[cfg_attr(docsrs, doc(cfg(all())))]
102        /// A limit on the number of bytes that can be allocated for POSIX
103        /// message queues  for  the  real  user  ID  of  the  calling process.
104        RLIMIT_MSGQUEUE,
105
106        #[cfg(any(target_os = "android", target_os = "linux"))]
107        #[cfg_attr(docsrs, doc(cfg(all())))]
108        /// A ceiling to which the process's nice value can be raised using
109        /// setpriority or nice.
110        RLIMIT_NICE,
111
112        #[cfg(any(
113            target_os = "android",
114            target_os = "freebsd",
115            target_os = "netbsd",
116            target_os = "openbsd",
117            target_os = "linux",
118        ))]
119        #[cfg_attr(docsrs, doc(cfg(all())))]
120        /// The maximum number of simultaneous processes for this user id.
121        RLIMIT_NPROC,
122
123        #[cfg(target_os = "freebsd")]
124        #[cfg_attr(docsrs, doc(cfg(all())))]
125        /// The maximum number of pseudo-terminals this user id is allowed to
126        /// create.
127        RLIMIT_NPTS,
128
129        #[cfg(any(target_os = "android",
130            target_os = "freebsd",
131            target_os = "netbsd",
132            target_os = "openbsd",
133            target_os = "linux",
134        ))]
135        #[cfg_attr(docsrs, doc(cfg(all())))]
136        /// When there is memory pressure and swap is available, prioritize
137        /// eviction of a process' resident pages beyond this amount (in bytes).
138        RLIMIT_RSS,
139
140        #[cfg(any(target_os = "android", target_os = "linux"))]
141        #[cfg_attr(docsrs, doc(cfg(all())))]
142        /// A ceiling on the real-time priority that may be set for this process
143        /// using sched_setscheduler and  sched_set‐ param.
144        RLIMIT_RTPRIO,
145
146        #[cfg(any(target_os = "linux"))]
147        #[cfg_attr(docsrs, doc(cfg(all())))]
148        /// A limit (in microseconds) on the amount of CPU time that a process
149        /// scheduled under a real-time scheduling policy may con‐ sume without
150        /// making a blocking system call.
151        RLIMIT_RTTIME,
152
153        #[cfg(any(target_os = "android", target_os = "linux"))]
154        #[cfg_attr(docsrs, doc(cfg(all())))]
155        /// A limit on the number of signals that may be queued for the real
156        /// user ID of the  calling  process.
157        RLIMIT_SIGPENDING,
158
159        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
160        #[cfg_attr(docsrs, doc(cfg(all())))]
161        /// The maximum size (in bytes) of socket buffer usage for this user.
162        RLIMIT_SBSIZE,
163
164        #[cfg(target_os = "freebsd")]
165        #[cfg_attr(docsrs, doc(cfg(all())))]
166        /// The maximum size (in bytes) of the swap space that may be reserved
167        /// or used by all of this user id's processes.
168        RLIMIT_SWAP,
169
170        #[cfg(target_os = "freebsd")]
171        #[cfg_attr(docsrs, doc(cfg(all())))]
172        /// An alias for RLIMIT_AS.
173        RLIMIT_VMEM,
174    }
175}
176
177/// Get the current processes resource limits
178///
179/// The special value [`RLIM_INFINITY`] indicates that no limit will be
180/// enforced.
181///
182/// # Parameters
183///
184/// * `resource`: The [`Resource`] that we want to get the limits of.
185///
186/// # Examples
187///
188/// ```
189/// # use nix::sys::resource::{getrlimit, Resource};
190///
191/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
192/// println!("current soft_limit: {}", soft_limit);
193/// println!("current hard_limit: {}", hard_limit);
194/// ```
195///
196/// # References
197///
198/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
199///
200/// [`Resource`]: enum.Resource.html
201pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
202    let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
203
204    cfg_if! {
205        if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
206            let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
207        } else {
208            let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
209        }
210    }
211
212    Errno::result(res).map(|_| {
213        let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
214        (rlim_cur, rlim_max)
215    })
216}
217
218/// Set the current processes resource limits
219///
220/// # Parameters
221///
222/// * `resource`: The [`Resource`] that we want to set the limits of.
223/// * `soft_limit`: The value that the kernel enforces for the corresponding
224///   resource.
225/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
226///   the current hard limit for non-root users.
227///
228/// The special value [`RLIM_INFINITY`] indicates that no limit will be
229/// enforced.
230///
231/// # Examples
232///
233/// ```
234/// # use nix::sys::resource::{setrlimit, Resource};
235///
236/// let soft_limit = 512;
237/// let hard_limit = 1024;
238/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
239/// ```
240///
241/// # References
242///
243/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
244///
245/// [`Resource`]: enum.Resource.html
246///
247/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
248pub fn setrlimit(
249    resource: Resource,
250    soft_limit: rlim_t,
251    hard_limit: rlim_t,
252) -> Result<()> {
253    let new_rlim = rlimit {
254        rlim_cur: soft_limit,
255        rlim_max: hard_limit,
256    };
257    cfg_if! {
258        if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
259            let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
260        }else{
261            let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
262        }
263    }
264
265    Errno::result(res).map(drop)
266}
267
268libc_enum! {
269    /// Whose resource usage should be returned by [`getrusage`].
270    #[repr(i32)]
271    #[non_exhaustive]
272    pub enum UsageWho {
273        /// Resource usage for the current process.
274        RUSAGE_SELF,
275
276        /// Resource usage for all the children that have terminated and been waited for.
277        RUSAGE_CHILDREN,
278
279        #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
280        #[cfg_attr(docsrs, doc(cfg(all())))]
281        /// Resource usage for the calling thread.
282        RUSAGE_THREAD,
283    }
284}
285
286/// Output of `getrusage` with information about resource usage. Some of the fields
287/// may be unused in some platforms, and will be always zeroed out. See their manuals
288/// for details.
289#[repr(transparent)]
290#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
291pub struct Usage(rusage);
292
293impl AsRef<rusage> for Usage {
294    fn as_ref(&self) -> &rusage {
295        &self.0
296    }
297}
298
299impl AsMut<rusage> for Usage {
300    fn as_mut(&mut self) -> &mut rusage {
301        &mut self.0
302    }
303}
304
305impl Usage {
306    /// Total amount of time spent executing in user mode.
307    pub fn user_time(&self) -> TimeVal {
308        TimeVal::from(self.0.ru_utime)
309    }
310
311    /// Total amount of time spent executing in kernel mode.
312    pub fn system_time(&self) -> TimeVal {
313        TimeVal::from(self.0.ru_stime)
314    }
315
316    /// The resident set size at its peak, in kilobytes.
317    pub fn max_rss(&self) -> c_long {
318        self.0.ru_maxrss
319    }
320
321    /// Integral value expressed in kilobytes times ticks of execution indicating
322    /// the amount of text memory shared with other processes.
323    pub fn shared_integral(&self) -> c_long {
324        self.0.ru_ixrss
325    }
326
327    /// Integral value expressed in kilobytes times ticks of execution indicating
328    /// the amount of unshared memory used by data.
329    pub fn unshared_data_integral(&self) -> c_long {
330        self.0.ru_idrss
331    }
332
333    /// Integral value expressed in kilobytes times ticks of execution indicating
334    /// the amount of unshared memory used for stack space.
335    pub fn unshared_stack_integral(&self) -> c_long {
336        self.0.ru_isrss
337    }
338
339    /// Number of page faults that were served without resorting to I/O, with pages
340    /// that have been allocated previously by the kernel.
341    pub fn minor_page_faults(&self) -> c_long {
342        self.0.ru_minflt
343    }
344
345    /// Number of page faults that were served through I/O (i.e. swap).
346    pub fn major_page_faults(&self) -> c_long {
347        self.0.ru_majflt
348    }
349
350    /// Number of times all of the memory was fully swapped out.
351    pub fn full_swaps(&self) -> c_long {
352        self.0.ru_nswap
353    }
354
355    /// Number of times a read was done from a block device.
356    pub fn block_reads(&self) -> c_long {
357        self.0.ru_inblock
358    }
359
360    /// Number of times a write was done to a block device.
361    pub fn block_writes(&self) -> c_long {
362        self.0.ru_oublock
363    }
364
365    /// Number of IPC messages sent.
366    pub fn ipc_sends(&self) -> c_long {
367        self.0.ru_msgsnd
368    }
369
370    /// Number of IPC messages received.
371    pub fn ipc_receives(&self) -> c_long {
372        self.0.ru_msgrcv
373    }
374
375    /// Number of signals received.
376    pub fn signals(&self) -> c_long {
377        self.0.ru_nsignals
378    }
379
380    /// Number of times a context switch was voluntarily invoked.
381    pub fn voluntary_context_switches(&self) -> c_long {
382        self.0.ru_nvcsw
383    }
384
385    /// Number of times a context switch was imposed by the kernel (usually due to
386    /// time slice expiring or preemption by a higher priority process).
387    pub fn involuntary_context_switches(&self) -> c_long {
388        self.0.ru_nivcsw
389    }
390}
391
392/// Get usage information for a process, its children or the current thread
393///
394/// Real time information can be obtained for either the current process or (in some
395/// systems) thread, but information about children processes is only provided for
396/// those that have terminated and been waited for (see [`super::wait::wait`]).
397///
398/// Some information may be missing depending on the platform, and the way information
399/// is provided for children may also vary. Check the manuals for details.
400///
401/// # References
402///
403/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
404/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
405/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
406/// * [NetBSD](https://man.netbsd.org/getrusage.2)
407/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
408///
409/// [`UsageWho`]: enum.UsageWho.html
410///
411/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
412pub fn getrusage(who: UsageWho) -> Result<Usage> {
413    unsafe {
414        let mut rusage = mem::MaybeUninit::<rusage>::uninit();
415        let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
416        Errno::result(res).map(|_| Usage(rusage.assume_init()))
417    }
418}
419
420#[cfg(test)]
421mod test {
422    use super::{getrusage, UsageWho};
423
424    #[test]
425    pub fn test_self_cpu_time() {
426        // Make sure some CPU time is used.
427        let mut numbers: Vec<i32> = (1..1_000_000).collect();
428        numbers.iter_mut().for_each(|item| *item *= 2);
429
430        // FIXME: this is here to help ensure the compiler does not optimize the whole
431        // thing away. Replace the assert with test::black_box once stabilized.
432        assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
433
434        let usage = getrusage(UsageWho::RUSAGE_SELF)
435            .expect("Failed to call getrusage for SELF");
436        let rusage = usage.as_ref();
437
438        let user = usage.user_time();
439        assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
440        assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
441        assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
442    }
443}