1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Helper functions for interfacing with platform specific resource limit APIs.

use crate::error::SysErr;

cfg_if::cfg_if!(
    if #[cfg(target_env = "gnu")] {
        /// Type of rlimit resource identifiers.
        pub type RlimitResource = u32;
    } else {
        /// Type of rlimit resource identifiers.
        pub type RlimitResource = i32;
    }
);

/// Pair of soft and hard limit on a resource.
#[cfg(unix)]
pub struct Rlimit(libc::rlimit);

#[cfg(unix)]
impl Rlimit {
    /// Create [`Rlimit`] pair from soft and hard limits.
    #[must_use]
    pub fn new(rlim_cur: libc::rlim_t, rlim_max: libc::rlim_t) -> Self {
        debug_assert!(rlim_cur <= rlim_max);
        Self(libc::rlimit { rlim_cur, rlim_max })
    }

    /// Get the soft limit from an [`Rlimit`] pair.
    #[cfg(feature = "rlimit")]
    #[must_use]
    pub const fn soft_limit(&self) -> &libc::rlim_t {
        &self.0.rlim_cur
    }

    /// Get the hard limit from an [`Rlimit`] pair.
    #[cfg(feature = "rlimit")]
    #[must_use]
    pub const fn hard_limit(&self) -> &libc::rlim_t {
        &self.0.rlim_max
    }
}

#[cfg(all(feature = "rlimit", unix))]
/// Get resource limit for `resource` to the limit pair pointed to by `rlim`.
///
/// # Safety
/// `resource` must be a valid resource identifier for the platform.
pub unsafe fn get_rlimit<E: SysErr>(resource: RlimitResource, rlim: &mut Rlimit) -> Result<(), E> {
    let rlim_ptr = &mut rlim.0 as *mut libc::rlimit;
    // SAFETY: `rlim_ptr` points to a valid `libc::rlimit` instance
    let res: i32 = unsafe { libc::setrlimit(resource, rlim_ptr) };
    if res == 0 {
        Ok(())
    } else {
        Err(E::create())
    }
}

#[cfg(unix)]
/// Set resource limit for `resource` to the limit pair pointed to by `rlim`.
///
/// # Safety
/// `resource` must be a valid resource identifier for the platform.
pub unsafe fn set_rlimit<E: SysErr>(resource: RlimitResource, rlim: &Rlimit) -> Result<(), E> {
    let res: i32 = unsafe { libc::setrlimit(resource, &rlim.0 as *const libc::rlimit) };
    if res == 0 {
        Ok(())
    } else {
        Err(E::create())
    }
}

#[cfg(unix)]
/// Set resource limit for core dumps to the limit pair pointed to by `rlim`.
pub fn set_coredump_rlimit<E: SysErr>(rlim: &Rlimit) -> Result<(), E> {
    // SAFETY: `libc::RLIMIT_CORE` is a valid resource
    unsafe { set_rlimit(libc::RLIMIT_CORE, rlim) }
}

#[cfg(test)]
mod tests {
    use super::*;
    cfg_if::cfg_if!(
        if #[cfg(feature = "std")] {
            use crate::error::StdSystemError as TestSysErr;
        } else {
            use crate::error::EmptySystemError as TestSysErr;
        }
    );

    #[cfg(unix)]
    #[test]
    fn test_resource_nodump() {
        let rlim = Rlimit::new(0, 0);
        assert!(set_coredump_rlimit::<TestSysErr>(&rlim).is_ok());
    }
}