1 #![deny(unsafe_code)] 2 3 use std::error::Error; 4 use std::fmt; 5 use std::io; 6 use std::str::FromStr; 7 8 #[cfg(all(target_os = "linux", target_env = "gnu"))] 9 use libc::__rlimit_resource_t as resource_t; 10 11 #[cfg(not(all(target_os = "linux", target_env = "gnu")))] 12 use libc::c_int as resource_t; 13 14 /// Integer type used for resource values. 15 /// 16 /// The actual type of [`RawResource`][RawResource] can be different on different platforms. 17 /// 18 /// [RawResource]: type.RawResource.html 19 #[allow(clippy::module_name_repetitions)] 20 pub type RawResource = resource_t; 21 22 /// A kind of resource. 23 /// 24 /// **Be careful**: The documentation of [`Resource`][Resource] constants are based on a few systems. 25 /// It may be inconsistent with other platforms. 26 /// 27 /// # References 28 /// Linux: <https://man7.org/linux/man-pages/man2/getrlimit.2.html> 29 /// 30 /// FreeBSD: <https://www.freebsd.org/cgi/man.cgi?query=getrlimit> 31 /// 32 /// NetBSD: <https://man.netbsd.org/getrlimit.2> 33 /// 34 /// [Resource]: struct.Resource.html 35 /// 36 #[allow(clippy::doc_markdown)] 37 #[derive(Clone, Copy, PartialEq, Eq, Hash)] 38 pub struct Resource { 39 tag: u16, 40 value: u16, 41 } 42 43 impl fmt::Debug for Resource { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 45 let idx = Self::VALUE_TABLE.iter().position(|v| v == self).unwrap(); 46 write!(f, "Resource::{}", Self::IDENT_TABLE[idx]) 47 } 48 } 49 50 impl FromStr for Resource { 51 type Err = ParseResourceError; 52 from_str(s: &str) -> Result<Self, Self::Err>53 fn from_str(s: &str) -> Result<Self, Self::Err> { 54 let pos = Self::NAME_TABLE.iter().position(|&name| s == name); 55 match pos { 56 Some(idx) => Ok(Self::VALUE_TABLE[idx]), 57 None => Err(ParseResourceError { _priv: () }), 58 } 59 } 60 } 61 62 /// An error returned when parsing a `Resource` using [`from_str`] fails 63 #[derive(Debug, Clone, PartialEq, Eq)] 64 pub struct ParseResourceError { 65 /// private place holder 66 _priv: (), 67 } 68 69 impl fmt::Display for ParseResourceError { 70 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 write!(f, "failed to parse Resource") 73 } 74 } 75 76 impl Error for ParseResourceError {} 77 78 macro_rules! declare_resource { 79 {$($(#[$attr:meta])* $id:ident = $tag:expr => $c_enum:ident,)+} => { 80 impl Resource{ 81 $( 82 $(#[$attr])* 83 pub const $id: Self = Self{ tag: $tag, value: libc::$c_enum as u16 }; 84 )+ 85 } 86 87 #[allow(unused_doc_comments)] 88 impl Resource{ 89 const NAME_TABLE: &'static [&'static str] = &[ 90 $( 91 $(#[$attr])* 92 { 93 stringify!($c_enum) 94 }, 95 )+ 96 ]; 97 98 const VALUE_TABLE: &'static [Self] = &[ 99 $( 100 $(#[$attr])* 101 { 102 Self::$id 103 }, 104 )+ 105 ]; 106 107 const IDENT_TABLE: &'static [&'static str] = &[ 108 $( 109 $(#[$attr])* 110 { 111 stringify!($id) 112 }, 113 )+ 114 ]; 115 } 116 117 #[cfg(test)] 118 mod tests{ 119 use super::*; 120 121 #[allow(unused_comparisons)] 122 #[allow(unused_doc_comments)] 123 #[test] 124 fn name_value(){ 125 $( 126 $(#[$attr])* 127 { 128 assert_eq!(Resource::$id.as_name(), stringify!($c_enum)); 129 assert_eq!(Resource::from_str(stringify!($c_enum)).unwrap(), Resource::$id); 130 assert!((0..=128).contains(&libc::$c_enum)); 131 } 132 )+ 133 } 134 135 #[allow(unused_doc_comments)] 136 #[test] 137 fn unique_tag(){ 138 use std::collections::HashSet; 139 140 let tags = [ 141 $( 142 $(#[$attr])* 143 { $tag }, 144 )+ 145 ]; 146 147 let s: HashSet<u16> = tags.iter().copied().collect(); 148 assert_eq!(s.len(), Resource::NAME_TABLE.len()); 149 } 150 151 #[allow(unused_doc_comments)] 152 #[test] 153 fn raw_eq(){ 154 $( 155 $(#[$attr])* 156 { 157 assert_eq!(Resource::$id.as_raw(), libc::$c_enum); 158 } 159 )+ 160 } 161 162 #[allow(unused_doc_comments)] 163 #[test] 164 fn from_str(){ 165 $( 166 $(#[$attr])* 167 { 168 assert_eq!(Resource::from_str(stringify!($c_enum)), Ok(Resource::$id)); 169 } 170 )+ 171 172 assert!(Resource::from_str("asdqwe").is_err()); 173 } 174 175 #[test] 176 fn available(){ 177 assert_eq!( 178 Resource::available_names().len(), 179 Resource::available_resources().len() 180 ); 181 } 182 } 183 }; 184 } 185 186 impl Resource { 187 /// Set resource limits. 188 /// # Errors 189 /// See [`setrlimit`](fn.setrlimit.html) 190 #[inline] set(self, soft: u64, hard: u64) -> io::Result<()>191 pub fn set(self, soft: u64, hard: u64) -> io::Result<()> { 192 super::setrlimit(self, soft, hard) 193 } 194 195 /// Get resource limits. 196 /// # Errors 197 /// See [`getrlimit`](fn.getrlimit.html) 198 #[inline] get(self) -> io::Result<(u64, u64)>199 pub fn get(self) -> io::Result<(u64, u64)> { 200 super::getrlimit(self) 201 } 202 203 /// Returns the name of the resource. 204 /// 205 /// # Example 206 /// ``` 207 /// # use rlimit::Resource; 208 /// assert_eq!(Resource::NOFILE.as_name(), "RLIMIT_NOFILE"); 209 /// ``` 210 #[must_use] 211 #[allow(clippy::missing_panics_doc)] // this method should never panic as_name(self) -> &'static str212 pub fn as_name(self) -> &'static str { 213 let idx = Self::VALUE_TABLE.iter().position(|&v| v == self).unwrap(); 214 Self::NAME_TABLE[idx] 215 } 216 217 /// Returns available resource names. 218 #[must_use] available_names() -> &'static [&'static str]219 pub const fn available_names() -> &'static [&'static str] { 220 Self::NAME_TABLE 221 } 222 223 /// Returns available resources. 224 #[must_use] available_resources() -> &'static [Self]225 pub const fn available_resources() -> &'static [Self] { 226 Self::VALUE_TABLE 227 } 228 229 /// Returns the raw resource type. 230 /// 231 /// **Be careful**: The actual type of [`RawResource`][RawResource] can be different on different platforms. 232 /// 233 /// [RawResource]: type.RawResource.html 234 #[inline] 235 #[must_use] as_raw(self) -> RawResource236 pub const fn as_raw(self) -> RawResource { 237 self.value as _ 238 } 239 } 240 241 // #begin-codegen 242 // generated from rust-lang/libc 6568dacc81b2dd2edae571ab97bbca94bc662595 243 declare_resource! { 244 245 /// The maximum size (in bytes) 246 /// of the process's virtual memory (address space). 247 #[cfg(any( 248 all(target_os = "linux", target_env = "gnu"), 249 all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "mips", target_arch = "powerpc", target_arch = "hexagon", target_arch = "arm")), 250 all(target_os = "linux", target_env = "musl", any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64")), 251 all(target_os = "linux", target_env = "uclibc", any(target_arch = "mips", target_arch = "mips64")), 252 any(target_os = "freebsd", target_os = "dragonfly"), 253 any(target_os = "macos", target_os = "ios"), 254 target_os = "android", 255 target_os = "emscripten", 256 target_os = "fuchsia", 257 target_os = "haiku", 258 target_os = "netbsd", 259 target_os = "solarish", 260 ))] 261 AS = 1 => RLIMIT_AS, 262 263 264 /// The maximum size (in bytes) 265 /// of a core file that the process may dump. 266 #[cfg(any( 267 all(target_os = "linux", target_env = "gnu"), 268 all(target_os = "linux", target_env = "musl"), 269 all(target_os = "linux", target_env = "uclibc"), 270 any(target_os = "freebsd", target_os = "dragonfly"), 271 any(target_os = "macos", target_os = "ios"), 272 any(target_os = "openbsd", target_os = "netbsd"), 273 target_os = "android", 274 target_os = "emscripten", 275 target_os = "fuchsia", 276 target_os = "haiku", 277 target_os = "solarish", 278 ))] 279 CORE = 2 => RLIMIT_CORE, 280 281 282 /// A limit (in seconds) 283 /// on the amount of CPU time that the process can consume. 284 #[cfg(any( 285 all(target_os = "linux", target_env = "gnu"), 286 all(target_os = "linux", target_env = "musl"), 287 all(target_os = "linux", target_env = "uclibc"), 288 any(target_os = "freebsd", target_os = "dragonfly"), 289 any(target_os = "macos", target_os = "ios"), 290 any(target_os = "openbsd", target_os = "netbsd"), 291 target_os = "android", 292 target_os = "emscripten", 293 target_os = "fuchsia", 294 target_os = "haiku", 295 target_os = "solarish", 296 ))] 297 CPU = 3 => RLIMIT_CPU, 298 299 300 /// The maximum size (in bytes) 301 /// of the process's data segment 302 /// (initialized data, uninitialized data, and heap). 303 #[cfg(any( 304 all(target_os = "linux", target_env = "gnu"), 305 all(target_os = "linux", target_env = "musl"), 306 all(target_os = "linux", target_env = "uclibc"), 307 any(target_os = "freebsd", target_os = "dragonfly"), 308 any(target_os = "macos", target_os = "ios"), 309 any(target_os = "openbsd", target_os = "netbsd"), 310 target_os = "android", 311 target_os = "emscripten", 312 target_os = "fuchsia", 313 target_os = "haiku", 314 target_os = "solarish", 315 ))] 316 DATA = 4 => RLIMIT_DATA, 317 318 319 /// The maximum size (in bytes) 320 /// of files that the process may create. 321 #[cfg(any( 322 all(target_os = "linux", target_env = "gnu"), 323 all(target_os = "linux", target_env = "musl"), 324 all(target_os = "linux", target_env = "uclibc"), 325 any(target_os = "freebsd", target_os = "dragonfly"), 326 any(target_os = "macos", target_os = "ios"), 327 any(target_os = "openbsd", target_os = "netbsd"), 328 target_os = "android", 329 target_os = "emscripten", 330 target_os = "fuchsia", 331 target_os = "haiku", 332 target_os = "solarish", 333 ))] 334 FSIZE = 5 => RLIMIT_FSIZE, 335 336 337 /// The maximum number of kqueues this user id is allowed to create. 338 #[cfg(any( 339 target_os = "freebsd", 340 ))] 341 KQUEUES = 6 => RLIMIT_KQUEUES, 342 343 344 /// (early Linux 2.4 only) 345 /// 346 /// A limit on the combined number 347 /// of `flock(2)` locks and `fcntl(2)` leases 348 /// that this process may establish. 349 #[cfg(any( 350 all(target_os = "linux", target_env = "gnu"), 351 all(target_os = "linux", target_env = "musl"), 352 all(target_os = "linux", target_env = "uclibc"), 353 target_os = "android", 354 target_os = "emscripten", 355 target_os = "fuchsia", 356 ))] 357 LOCKS = 7 => RLIMIT_LOCKS, 358 359 360 /// The maximum number (in bytes) 361 /// of memory that may be locked into RAM. 362 #[cfg(any( 363 all(target_os = "linux", target_env = "gnu"), 364 all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "mips", target_arch = "powerpc", target_arch = "hexagon", target_arch = "arm")), 365 all(target_os = "linux", target_env = "musl", any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64")), 366 all(target_os = "linux", target_env = "uclibc", any(target_arch = "mips", target_arch = "mips64")), 367 any(target_os = "freebsd", target_os = "dragonfly"), 368 any(target_os = "macos", target_os = "ios"), 369 any(target_os = "openbsd", target_os = "netbsd"), 370 target_os = "android", 371 target_os = "emscripten", 372 target_os = "fuchsia", 373 ))] 374 MEMLOCK = 8 => RLIMIT_MEMLOCK, 375 376 377 /// A limit on the number 378 /// of bytes that can be allocated for POSIX message queues 379 /// for the real user ID of the calling process. 380 #[cfg(any( 381 all(target_os = "linux", target_env = "gnu"), 382 all(target_os = "linux", target_env = "musl"), 383 all(target_os = "linux", target_env = "uclibc"), 384 target_os = "android", 385 target_os = "emscripten", 386 target_os = "fuchsia", 387 ))] 388 MSGQUEUE = 9 => RLIMIT_MSGQUEUE, 389 390 391 /// This specifies a ceiling 392 /// to which the process's nice value can be raised 393 /// using `setpriority(2)` or `nice(2)`. 394 #[cfg(any( 395 all(target_os = "linux", target_env = "gnu"), 396 all(target_os = "linux", target_env = "musl"), 397 all(target_os = "linux", target_env = "uclibc"), 398 target_os = "android", 399 target_os = "emscripten", 400 target_os = "fuchsia", 401 ))] 402 NICE = 10 => RLIMIT_NICE, 403 404 405 /// This specifies a value 406 /// one greater than the maximum file descriptor number 407 /// that can be opened by this process. 408 #[cfg(any( 409 all(target_os = "linux", target_env = "gnu"), 410 all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "mips", target_arch = "powerpc", target_arch = "hexagon", target_arch = "arm")), 411 all(target_os = "linux", target_env = "musl", any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64")), 412 all(target_os = "linux", target_env = "uclibc", any(target_arch = "mips", target_arch = "mips64")), 413 any(target_os = "freebsd", target_os = "dragonfly"), 414 any(target_os = "macos", target_os = "ios"), 415 any(target_os = "openbsd", target_os = "netbsd"), 416 target_os = "android", 417 target_os = "emscripten", 418 target_os = "fuchsia", 419 target_os = "haiku", 420 target_os = "solarish", 421 ))] 422 NOFILE = 11 => RLIMIT_NOFILE, 423 424 425 /// The number of open vnode monitors. 426 #[cfg(any( 427 target_os = "haiku", 428 ))] 429 NOVMON = 12 => RLIMIT_NOVMON, 430 431 432 /// A limit on the number of extant process (or, more precisely on Linux, threads) 433 /// for the real user ID of the calling process. 434 #[cfg(any( 435 all(target_os = "linux", target_env = "gnu"), 436 all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "mips", target_arch = "powerpc", target_arch = "hexagon", target_arch = "arm")), 437 all(target_os = "linux", target_env = "musl", any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64")), 438 all(target_os = "linux", target_env = "uclibc", any(target_arch = "mips", target_arch = "mips64")), 439 any(target_os = "freebsd", target_os = "dragonfly"), 440 any(target_os = "macos", target_os = "ios"), 441 any(target_os = "openbsd", target_os = "netbsd"), 442 target_os = "android", 443 target_os = "emscripten", 444 target_os = "fuchsia", 445 ))] 446 NPROC = 13 => RLIMIT_NPROC, 447 448 449 /// The maximum number of pseudo-terminals this user id is allowed to create. 450 #[cfg(any( 451 target_os = "freebsd", 452 ))] 453 NPTS = 14 => RLIMIT_NPTS, 454 455 456 /// The maximum number of simultaneous threads (Lightweight 457 /// Processes) for this user id. Kernel threads and the 458 /// first thread of each process are not counted against this 459 /// limit. 460 #[cfg(any( 461 target_os = "netbsd", 462 ))] 463 NTHR = 15 => RLIMIT_NTHR, 464 465 466 /// The maximum number of POSIX-type advisory-mode locks available to this user. 467 #[cfg(any( 468 target_os = "dragonfly", 469 ))] 470 POSIXLOCKS = 16 => RLIMIT_POSIXLOCKS, 471 472 473 /// A limit (in bytes) 474 /// on the process's resident set 475 /// (the number of virtual pages resident in RAM). 476 #[cfg(any( 477 all(target_os = "linux", target_env = "gnu"), 478 all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "mips", target_arch = "powerpc", target_arch = "hexagon", target_arch = "arm")), 479 all(target_os = "linux", target_env = "musl", any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64")), 480 all(target_os = "linux", target_env = "uclibc", any(target_arch = "mips", target_arch = "mips64")), 481 any(target_os = "freebsd", target_os = "dragonfly"), 482 any(target_os = "macos", target_os = "ios"), 483 any(target_os = "openbsd", target_os = "netbsd"), 484 target_os = "android", 485 target_os = "emscripten", 486 target_os = "fuchsia", 487 ))] 488 RSS = 17 => RLIMIT_RSS, 489 490 491 /// This specifies a ceiling on the real-time priority 492 /// that may be set for this process 493 /// using `sched_setscheduler(2)` and `sched_setparam(2)`. 494 #[cfg(any( 495 all(target_os = "linux", target_env = "gnu"), 496 all(target_os = "linux", target_env = "musl"), 497 all(target_os = "linux", target_env = "uclibc"), 498 target_os = "android", 499 target_os = "emscripten", 500 target_os = "fuchsia", 501 ))] 502 RTPRIO = 18 => RLIMIT_RTPRIO, 503 504 505 /// A limit (in microseconds) on the amount of CPU time 506 /// that a process scheduled under a real-time scheduling policy 507 /// may consume without making a blocking system call. 508 #[cfg(any( 509 all(target_os = "linux", target_env = "gnu"), 510 all(target_os = "linux", target_env = "musl"), 511 target_os = "fuchsia", 512 ))] 513 RTTIME = 19 => RLIMIT_RTTIME, 514 515 516 /// The maximum size (in bytes) of socket buffer usage for 517 /// this user. This limits the amount of network memory, and 518 /// hence the amount of mbufs, that this user may hold at any 519 /// time. 520 #[cfg(any( 521 any(target_os = "freebsd", target_os = "dragonfly"), 522 target_os = "netbsd", 523 ))] 524 SBSIZE = 20 => RLIMIT_SBSIZE, 525 526 527 /// A limit on the number 528 /// of signals that may be queued 529 /// for the real user ID of the calling process. 530 #[cfg(any( 531 all(target_os = "linux", target_env = "gnu"), 532 all(target_os = "linux", target_env = "musl"), 533 all(target_os = "linux", target_env = "uclibc"), 534 target_os = "android", 535 target_os = "emscripten", 536 target_os = "fuchsia", 537 ))] 538 SIGPENDING = 21 => RLIMIT_SIGPENDING, 539 540 541 /// The maximum size (in bytes) 542 /// of the process stack. 543 #[cfg(any( 544 all(target_os = "linux", target_env = "gnu"), 545 all(target_os = "linux", target_env = "musl"), 546 all(target_os = "linux", target_env = "uclibc"), 547 any(target_os = "freebsd", target_os = "dragonfly"), 548 any(target_os = "macos", target_os = "ios"), 549 any(target_os = "openbsd", target_os = "netbsd"), 550 target_os = "android", 551 target_os = "emscripten", 552 target_os = "fuchsia", 553 target_os = "haiku", 554 target_os = "solarish", 555 ))] 556 STACK = 22 => RLIMIT_STACK, 557 558 559 /// The maximum size (in bytes) of the swap space that may be 560 /// reserved or used by all of this user id's processes. 561 #[cfg(any( 562 target_os = "freebsd", 563 ))] 564 SWAP = 23 => RLIMIT_SWAP, 565 566 567 /// The number of shared locks a given user may create simultaneously. 568 #[cfg(any( 569 target_os = "freebsd", 570 ))] 571 UMTXP = 24 => RLIMIT_UMTXP, 572 573 574 /// An alias for RLIMIT_AS. The maximum size of a process's mapped address space in bytes. 575 #[cfg(any( 576 any(target_os = "freebsd", target_os = "dragonfly"), 577 target_os = "solarish", 578 ))] 579 VMEM = 25 => RLIMIT_VMEM, 580 581 } 582 // #end-codegen 583