Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getrlimit(2) and setrlimit(2) #879

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/sys/mod.rs
Expand Up @@ -39,6 +39,8 @@ pub mod quota;
#[cfg(any(target_os = "linux"))]
pub mod reboot;

pub mod resource;

pub mod select;

// TODO: Add support for dragonfly, freebsd, and ios/macos.
Expand Down
70 changes: 70 additions & 0 deletions src/sys/resource.rs
@@ -0,0 +1,70 @@
use std::mem;

use libc::{self, c_int, rlimit, RLIM_INFINITY};
pub use libc::rlim_t;

use {Errno, Result};

libc_enum!{
#[repr(i32)]
pub enum Resource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enum needs a doccomment as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// POSIX
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide doc comments for every entry here to give users an understanding of what they mean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

RLIMIT_AS,
RLIMIT_CORE,
RLIMIT_CPU,
RLIMIT_DATA,
RLIMIT_FSIZE,
RLIMIT_NOFILE,
RLIMIT_STACK,

// BSDs and Linux
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
RLIMIT_MEMLOCK,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
RLIMIT_NPROC,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
RLIMIT_RSS,

// Android and Linux only
#[cfg(any(target_os = "android", target_os = "linux"))]
RLIMIT_LOCKS,
#[cfg(any(target_os = "android", target_os = "linux"))]
RLIMIT_MSGQUEUE,
#[cfg(any(target_os = "android", target_os = "linux"))]
RLIMIT_NICE,
#[cfg(any(target_os = "android", target_os = "linux"))]
RLIMIT_RTPRIO,
#[cfg(any(target_os = "android", target_os = "linux"))]
RLIMIT_RTTIME,
#[cfg(any(target_os = "android", target_os = "linux"))]
RLIMIT_SIGPENDING,

// Non-Linux
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment makes it sound like these symbols are available everywhere except Linux.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to // Available on some BSD

#[cfg(target_os = "freebsd")]
RLIMIT_KQUEUES,
#[cfg(target_os = "freebsd")]
RLIMIT_NPTS,
#[cfg(target_os = "freebsd")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RLIMIT_SBSIZE is also available on Dragonfly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

RLIMIT_SBSIZE,
#[cfg(target_os = "freebsd")]
RLIMIT_SWAP,
}
}

pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a doccomment, should have an example as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

let mut rlim: rlimit = unsafe { mem::uninitialized() };
let res = unsafe { libc::getrlimit(resource as c_int, &mut rlim as *mut _) };
Errno::result(res).map(|_| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugly, but it works. Can you put a TODO in here to switch over to that other API you mentioned once it hits stable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

(if rlim.rlim_cur != RLIM_INFINITY { Some(rlim.rlim_cur) } else { None },
if rlim.rlim_max != RLIM_INFINITY { Some(rlim.rlim_max) } else { None })
})
}

pub fn setrlimit(resource: Resource, soft_limit: Option<rlim_t>, hard_limit: Option<rlim_t>) -> Result<()> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came in here to suggest changing this to

pub fn setrlimit<S=rlim_t, H=rlim_t>(resource: Resource, soft_limit: Option<rlim_t>, hard_limit: Option<rlim_t>) -> Result<()>
    where S: Into<Option<rlim_t>>, H: Into<Option<rlim_t>> { // ...

playground example

to take advantage for the impl<T> From<T> for Option<T> impl added in 1.12: rust-lang/rust#34828.

but it turns out we can't use default type params in functions on stable. There's a WG looking into this, so maybe we can open it up later this year. (This would allow setrlimit(RLIMIT_RSS, 1024 * 1024, None) without the Some wrapping on the first arg.)

let mut rlim: rlimit = unsafe { mem::uninitialized() };
rlim.rlim_cur = soft_limit.unwrap_or(RLIM_INFINITY);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be checked to be less than rlim_max or should that be deferred to the setrlimit call?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to leave this to the kernel.

rlim.rlim_max = hard_limit.unwrap_or(RLIM_INFINITY);

let res = unsafe { libc::setrlimit(resource as c_int, &rlim as *const _) };
Errno::result(res).map(drop)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do Errno::result(res).map(|_| ()) as it's more clear about what it's actually doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be map(|_| ()).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
1 change: 1 addition & 0 deletions test/test.rs
Expand Up @@ -20,6 +20,7 @@ mod test_fcntl;
mod test_mq;
mod test_net;
mod test_nix_path;
mod test_resource;
mod test_poll;
mod test_pty;
#[cfg(any(target_os = "linux", target_os = "android"))]
Expand Down
20 changes: 20 additions & 0 deletions test/test_resource.rs
@@ -0,0 +1,20 @@
use nix::sys::resource::{Resource, getrlimit, setrlimit};

#[test]
pub fn test_resource_limits() {
let (mut soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_STACK).unwrap();
let orig_limit = (soft_limit, hard_limit);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your changes to the test do fix the previous failure, but the coverage isn't very good, since the soft and hard limits are often identical. How about setting RLIMIT_NOFILE to a hard-coded value of 1024 if the current soft limit is unlimited, or the current softlimit - 1 otherwise?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need two tests? Does the stack test provide any extra coverage over the RLIMIT_NOFILE test?


soft_limit = hard_limit;
setrlimit(Resource::RLIMIT_STACK, soft_limit, hard_limit).unwrap();

let limit2 = getrlimit(Resource::RLIMIT_STACK).unwrap();
assert_eq!(soft_limit, limit2.0);
assert_eq!(hard_limit, limit2.1);

setrlimit(Resource::RLIMIT_STACK, orig_limit.0, orig_limit.1).unwrap();

let final_limit = getrlimit(Resource::RLIMIT_STACK).unwrap();
assert_eq!(orig_limit.0, final_limit.0);
assert_eq!(orig_limit.1, final_limit.1);
}