• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Configure the process resource limits.
2 use cfg_if::cfg_if;
3 use libc::{c_int, c_long, rusage};
4 
5 use crate::errno::Errno;
6 use crate::sys::time::TimeVal;
7 use crate::Result;
8 pub use libc::rlim_t;
9 pub use libc::RLIM_INFINITY;
10 use std::mem;
11 
12 cfg_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 
29 libc_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
getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)>201 pub 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`.
setrlimit( resource: Resource, soft_limit: rlim_t, hard_limit: rlim_t, ) -> Result<()>248 pub 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 
268 libc_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)]
291 pub struct Usage(rusage);
292 
293 impl AsRef<rusage> for Usage {
as_ref(&self) -> &rusage294     fn as_ref(&self) -> &rusage {
295         &self.0
296     }
297 }
298 
299 impl AsMut<rusage> for Usage {
as_mut(&mut self) -> &mut rusage300     fn as_mut(&mut self) -> &mut rusage {
301         &mut self.0
302     }
303 }
304 
305 impl Usage {
306     /// Total amount of time spent executing in user mode.
user_time(&self) -> TimeVal307     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.
system_time(&self) -> TimeVal312     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.
max_rss(&self) -> c_long317     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.
shared_integral(&self) -> c_long323     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.
unshared_data_integral(&self) -> c_long329     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.
unshared_stack_integral(&self) -> c_long335     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.
minor_page_faults(&self) -> c_long341     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).
major_page_faults(&self) -> c_long346     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.
full_swaps(&self) -> c_long351     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.
block_reads(&self) -> c_long356     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.
block_writes(&self) -> c_long361     pub fn block_writes(&self) -> c_long {
362         self.0.ru_oublock
363     }
364 
365     /// Number of IPC messages sent.
ipc_sends(&self) -> c_long366     pub fn ipc_sends(&self) -> c_long {
367         self.0.ru_msgsnd
368     }
369 
370     /// Number of IPC messages received.
ipc_receives(&self) -> c_long371     pub fn ipc_receives(&self) -> c_long {
372         self.0.ru_msgrcv
373     }
374 
375     /// Number of signals received.
signals(&self) -> c_long376     pub fn signals(&self) -> c_long {
377         self.0.ru_nsignals
378     }
379 
380     /// Number of times a context switch was voluntarily invoked.
voluntary_context_switches(&self) -> c_long381     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).
involuntary_context_switches(&self) -> c_long387     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`].
getrusage(who: UsageWho) -> Result<Usage>412 pub 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)]
421 mod test {
422     use super::{getrusage, UsageWho};
423 
424     #[test]
test_self_cpu_time()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 }
444