posix_resources/
lib.rs

1use libc;
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
4pub enum ResourceLimit {
5    Infinity,
6    Unknown,
7    Value(libc::rlim_t),
8}
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
11pub struct ResourceLimits {
12    pub soft_limit: ResourceLimit,
13    pub hard_limit: ResourceLimit,
14}
15
16impl From<libc::rlimit> for ResourceLimits {
17    fn from(rs: libc::rlimit) -> Self {
18        let soft_limit = match rs.rlim_cur {
19            libc::RLIM_INFINITY => ResourceLimit::Infinity,
20            other => {
21                if libc::RLIM_SAVED_MAX != libc::RLIM_INFINITY && other == libc::RLIM_SAVED_MAX {
22                    ResourceLimit::Unknown
23                } else if libc::RLIM_SAVED_CUR != libc::RLIM_INFINITY
24                    && other == libc::RLIM_SAVED_CUR
25                {
26                    ResourceLimit::Unknown
27                } else {
28                    ResourceLimit::Value(other)
29                }
30            }
31        };
32        let hard_limit = match rs.rlim_max {
33            libc::RLIM_INFINITY => ResourceLimit::Infinity,
34            other => {
35                if libc::RLIM_SAVED_MAX != libc::RLIM_INFINITY && other == libc::RLIM_SAVED_MAX {
36                    ResourceLimit::Unknown
37                } else if libc::RLIM_SAVED_CUR != libc::RLIM_INFINITY
38                    && other == libc::RLIM_SAVED_CUR
39                {
40                    ResourceLimit::Unknown
41                } else {
42                    ResourceLimit::Value(other)
43                }
44            }
45        };
46        ResourceLimits {
47            soft_limit,
48            hard_limit,
49        }
50    }
51}
52
53impl Into<libc::rlimit> for ResourceLimits {
54    fn into(self: ResourceLimits) -> libc::rlimit {
55        let rlim_cur = match self.soft_limit {
56            ResourceLimit::Infinity => libc::RLIM_INFINITY,
57            ResourceLimit::Unknown => libc::RLIM_SAVED_CUR,
58            ResourceLimit::Value(n) => n,
59        };
60        let rlim_max = match self.hard_limit {
61            ResourceLimit::Infinity => libc::RLIM_INFINITY,
62            ResourceLimit::Unknown => libc::RLIM_SAVED_MAX,
63            ResourceLimit::Value(n) => n,
64        };
65        libc::rlimit { rlim_cur, rlim_max }
66    }
67}
68
69pub enum Resource {
70    CoreFileSize,
71    CPUTime,
72    DataSize,
73    FileSize,
74    OpenFiles,
75    StackSize,
76    TotalMemory,
77}
78
79impl Into<libc::__rlimit_resource_t> for Resource {
80    fn into(self: Resource) -> libc::__rlimit_resource_t {
81        match self {
82            Resource::CoreFileSize => libc::RLIMIT_CORE,
83            Resource::CPUTime => libc::RLIMIT_CPU,
84            Resource::DataSize => libc::RLIMIT_DATA,
85            Resource::FileSize => libc::RLIMIT_FSIZE,
86            Resource::OpenFiles => libc::RLIMIT_NOFILE,
87            Resource::StackSize => libc::RLIMIT_STACK,
88            Resource::TotalMemory => libc::RLIMIT_AS,
89        }
90    }
91}
92
93impl From<libc::__rlimit_resource_t> for Resource {
94    fn from(r: libc::__rlimit_resource_t) -> Self {
95        match r {
96            libc::RLIMIT_CORE => Resource::CoreFileSize,
97            libc::RLIMIT_CPU => Resource::CPUTime,
98            libc::RLIMIT_DATA => Resource::DataSize,
99            libc::RLIMIT_FSIZE => Resource::FileSize,
100            libc::RLIMIT_NOFILE => Resource::OpenFiles,
101            libc::RLIMIT_STACK => Resource::StackSize,
102            libc::RLIMIT_AS => Resource::TotalMemory,
103            _ => panic!("Invalid resource type code"),
104        }
105    }
106}
107
108/// An error value returned from the failure of ['get_resource_limit'].
109#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
110pub enum GetRLimitError {
111    /// [EINVAL] An invalid resource was specified; or in a setrlimit() call,
112    /// the new rlim_cur exceeds the new rlim_max.
113    Invalid,
114    /// [EPERM] The limit specified to setrlimit() would have raised the maximum
115    /// limit value, and the calling process does not have appropriate
116    /// privileges.
117    Permission,
118}
119
120impl std::fmt::Display for GetRLimitError {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        match *self {
123            Self::Invalid => write!(f, "EINVAL"),
124            Self::Permission => write!(f, "EPERM"),
125        }
126    }
127}
128
129impl std::error::Error for GetRLimitError {
130    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
131        match *self {
132            Self::Invalid => None,
133            Self::Permission => None,
134        }
135    }
136}
137
138/// Get the limit values for a particular resource.
139pub fn get_resource_limit(resource: Resource) -> Result<ResourceLimits, GetRLimitError> {
140    let mut rlimit: libc::rlimit = libc::rlimit {
141        rlim_cur: 0,
142        rlim_max: 0,
143    };
144    unsafe {
145        match libc::getrlimit(resource.into(), &mut rlimit) {
146            0 => Ok(rlimit.into()),
147            -1 => {
148                let errno: *mut libc::c_int = libc::__errno_location();
149                Err(match *errno {
150                    libc::EINVAL => GetRLimitError::Invalid,
151                    libc::EPERM => GetRLimitError::Permission,
152                    _ => panic!("Invalid error code"),
153                })
154            }
155            _ => panic!("Invalid error return"),
156        }
157    }
158}
159
160/// An error value returned from the failure of ['set_resource_limit'].
161#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
162pub enum SetRLimitError {
163    /// [EINVAL] The limit specified cannot be lowered because current usage is
164    /// already higher than the limit.
165    Invalid,
166}
167
168/// Set the limit values for a particular resource.
169pub fn set_resource_limit(
170    resource: Resource,
171    r_limit: ResourceLimits,
172) -> Result<(), SetRLimitError> {
173    unsafe {
174        match libc::setrlimit(resource.into(), &r_limit.into()) {
175            0 => Ok(()),
176            -1 => {
177                let errno: *mut libc::c_int = libc::__errno_location();
178                Err(match *errno {
179                    libc::EINVAL => SetRLimitError::Invalid,
180                    _ => panic!("Invalid error code"),
181                })
182            }
183            _ => panic!("Invalid error return"),
184        }
185    }
186}