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