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