Skip to content

Commit 0fb6859

Browse files
committed
Add getrlimit and setrlimit
This work is a continutation on previou contribution by @kpcyrd and @j1ah0ng.
1 parent 8d36e9a commit 0fb6859

File tree

4 files changed

+222
-0
lines changed

4 files changed

+222
-0
lines changed

src/sys/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ pub mod quota;
5959
#[cfg(any(target_os = "linux"))]
6060
pub mod reboot;
6161

62+
#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
63+
pub mod resource;
64+
6265
#[cfg(not(target_os = "redox"))]
6366
pub mod select;
6467

src/sys/resource.rs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
//! Configure the process resource limits.
2+
use cfg_if::cfg_if;
3+
4+
use crate::errno::Errno;
5+
use crate::Result;
6+
pub use libc::rlim_t;
7+
use std::mem;
8+
9+
cfg_if! {
10+
if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
11+
use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
12+
}else if #[cfg(any(
13+
target_os = "freebsd",
14+
target_os = "openbsd",
15+
target_os = "netbsd",
16+
target_os = "macos",
17+
target_os = "ios",
18+
target_os = "android",
19+
target_os = "dragonfly",
20+
all(target_os = "linux", not(target_env = "gnu"))
21+
))]{
22+
use libc::{c_int, rlimit, RLIM_INFINITY};
23+
}
24+
}
25+
26+
libc_enum! {
27+
/// The Resource enum is platform dependent. Check different platform
28+
/// manuals for more details. Some platform links has been provided for
29+
/// earier reference (non-exhaustive).
30+
///
31+
/// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
32+
/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
33+
34+
// linux-gnu uses u_int as resource enum, which is implemented in libc as
35+
// well.
36+
//
37+
// https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
38+
// https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
39+
#[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
40+
#[cfg_attr(any(
41+
target_os = "freebsd",
42+
target_os = "openbsd",
43+
target_os = "netbsd",
44+
target_os = "macos",
45+
target_os = "ios",
46+
target_os = "android",
47+
target_os = "dragonfly",
48+
all(target_os = "linux", not(target_env = "gnu"))
49+
), repr(i32))]
50+
#[non_exhaustive]
51+
pub enum Resource {
52+
#[cfg(not(any(target_os = "netbsd", target_os = "freebsd")))]
53+
RLIMIT_AS,
54+
RLIMIT_CORE,
55+
RLIMIT_CPU,
56+
RLIMIT_DATA,
57+
RLIMIT_FSIZE,
58+
RLIMIT_NOFILE,
59+
RLIMIT_STACK,
60+
61+
#[cfg(target_os = "freebsd")]
62+
RLIMIT_KQUEUES,
63+
64+
#[cfg(any(target_os = "android", target_os = "linux"))]
65+
RLIMIT_LOCKS,
66+
67+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
68+
RLIMIT_MEMLOCK,
69+
70+
#[cfg(any(target_os = "android", target_os = "linux"))]
71+
RLIMIT_MSGQUEUE,
72+
73+
#[cfg(any(target_os = "android", target_os = "linux"))]
74+
RLIMIT_NICE,
75+
76+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
77+
RLIMIT_NPROC,
78+
79+
#[cfg(target_os = "freebsd")]
80+
RLIMIT_NPTS,
81+
82+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
83+
RLIMIT_RSS,
84+
85+
#[cfg(any(target_os = "android", target_os = "linux"))]
86+
RLIMIT_RTPRIO,
87+
88+
#[cfg(any(target_os = "linux"))]
89+
RLIMIT_RTTIME,
90+
91+
#[cfg(any(target_os = "android", target_os = "linux"))]
92+
RLIMIT_SIGPENDING,
93+
94+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
95+
RLIMIT_SBSIZE,
96+
97+
#[cfg(target_os = "freebsd")]
98+
RLIMIT_SWAP,
99+
100+
#[cfg(target_os = "freebsd")]
101+
RLIMIT_VMEM,
102+
}
103+
}
104+
105+
/// Get the current processes resource limits
106+
///
107+
/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
108+
/// there is no limit.
109+
///
110+
/// # Parameters
111+
///
112+
/// * `resource`: The [`Resource`] that we want to get the limits of.
113+
///
114+
/// # Examples
115+
///
116+
/// ```
117+
/// # use nix::sys::resource::{getrlimit, Resource};
118+
///
119+
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
120+
/// println!("current soft_limit: {:?}", soft_limit);
121+
/// println!("current hard_limit: {:?}", hard_limit);
122+
/// ```
123+
///
124+
/// # References
125+
///
126+
/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
127+
///
128+
/// [`Resource`]: enum.Resource.html
129+
pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
130+
let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
131+
132+
cfg_if! {
133+
if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
134+
let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
135+
}else{
136+
let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
137+
}
138+
}
139+
140+
Errno::result(res).map(|_| {
141+
let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
142+
(Some(rlim_cur), Some(rlim_max))
143+
})
144+
}
145+
146+
/// Set the current processes resource limits
147+
///
148+
/// # Parameters
149+
///
150+
/// * `resource`: The [`Resource`] that we want to set the limits of.
151+
/// * `soft_limit`: The value that the kernel enforces for the corresponding
152+
/// resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
153+
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
154+
/// the current hard limit for non-root users. Note: `None` input will be
155+
/// replaced by constant `RLIM_INFINITY`.
156+
///
157+
/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
158+
/// > results `EPERM` Error. So you will need to set the number explicitly.
159+
///
160+
/// # Examples
161+
///
162+
/// ```
163+
/// # use nix::sys::resource::{setrlimit, Resource};
164+
///
165+
/// let soft_limit = Some(1024);
166+
/// let hard_limit = Some(1048576);
167+
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
168+
/// ```
169+
///
170+
/// # References
171+
///
172+
/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
173+
///
174+
/// [`Resource`]: enum.Resource.html
175+
///
176+
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
177+
pub fn setrlimit(
178+
resource: Resource,
179+
soft_limit: Option<rlim_t>,
180+
hard_limit: Option<rlim_t>,
181+
) -> Result<()> {
182+
let new_rlim = rlimit {
183+
rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
184+
rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
185+
};
186+
cfg_if! {
187+
if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
188+
let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
189+
}else{
190+
let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
191+
}
192+
}
193+
194+
Errno::result(res).map(drop)
195+
}

test/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod test_mq;
2424
#[cfg(not(target_os = "redox"))]
2525
mod test_net;
2626
mod test_nix_path;
27+
mod test_resource;
2728
mod test_poll;
2829
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
2930
mod test_pty;

test/test_resource.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
2+
use nix::sys::resource::{getrlimit, setrlimit, Resource};
3+
4+
/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers
5+
/// to the maximum file descriptor number that can be opened by the process (aka the maximum number
6+
/// of file descriptors that the process can open, since Linux 4.5).
7+
///
8+
/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the
9+
/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit()
10+
/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have
11+
/// been updated.
12+
#[test]
13+
#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
14+
pub fn test_resource_limits_nofile() {
15+
let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
16+
17+
let soft_limit = Some(soft_limit.map_or(1024, |v| v - 1));
18+
assert_ne!(soft_limit, hard_limit);
19+
setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
20+
21+
let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
22+
assert_eq!(new_soft_limit, soft_limit);
23+
}

0 commit comments

Comments
 (0)