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} 444