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

Conversation

kpcyrd
Copy link
Contributor

@kpcyrd kpcyrd commented Apr 1, 2018

This is a followup of #364 by @kamalmarhubi. The api has been changed to (Option<rlim_t>, Option<rlim_t>) to hide the rlim struct. This allows:

let (rlim_cur, rlim_max) = getrlimit()?;

Resolves #878.

#[cfg(any(target_os = "linux",
target_os = "openbsd",
target_os = "netbsd",
target_os = "bitrig"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Bitrig no longer exists, do go ahead and drop this. But please add fuchsia and empscripten, as they also support this API.

Additionally these targets should be alphabetized.

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, removed the import.


#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[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.

Please use the libc_enum! macro instead.

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

}
}

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

Ok((rlim_to_option(rlim.rlim_cur), rlim_to_option(rlim.rlim_max)))
}

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

Choose a reason for hiding this comment

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

I'm not a fan of this API because it's hard to know what limit means. I'd make this take a soft_limit and a hard_limit which are both Option<rlim_t>s so it's more explicit.

Additionally this should have doccomments and an example showing how this would be used (can be same as the one for getrlimit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've changed the api to

setrlimit(resource: Resource, soft_limit: Option<rlim_t>, hard_limit: Option<rlim_t>) -> Result<()>

I'd prefer to delay documentation until the api itself is approved.

rlim.rlim_max = limit.1.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

RLIMIT_STACK = libc::RLIMIT_STACK,
RLIMIT_AS = libc::RLIMIT_AS,
// BSDs and Linux
#[cfg(all(unix, not(target_os = "solaris")))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't use not, explicitly list targets here instead.

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

}

#[inline]
fn rlim_to_option(rlim: 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.

Is there not a better API to do this? I would think this would be a common enough idiom that there's a function somewhere in the Rust API to do this already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've changed it to a rust-style ternary expression. Option.filter would work, but is nightly only.

#[cfg(all(unix, not(target_os = "solaris")))]
RLIMIT_RSS = libc::RLIMIT_RSS,
// Linux-only
#[cfg(any(target_os = "linux", target_os = "android"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

These also need to be alphabetized.

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_NPROC = libc::RLIMIT_NPROC,
#[cfg(all(unix, not(target_os = "solaris")))]
RLIMIT_RSS = libc::RLIMIT_RSS,
// Linux-only
Copy link
Contributor

Choose a reason for hiding this comment

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

"Android and Linux only"

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

#[test]
pub fn test_resource_limits() {
let mut limit = getrlimit(Resource::RLIMIT_STACK).unwrap();
assert!(limit.0 != limit.1);
Copy link
Member

Choose a reason for hiding this comment

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

This assertion need not hold universally. On FreeBSD for example, the default hard limits and soft limits seem to be identical.

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

use std::mem;

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

Choose a reason for hiding this comment

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

I understand why you're reexporting rlim_t, but why reexport the others?

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, limited the reexport to rlim_t

target_os = "openbsd",
target_os = "netbsd",
target_os = "bitrig"))]
pub use libc::{RLIM_SAVED_CUR, RLIM_SAVED_MAX};
Copy link
Member

Choose a reason for hiding this comment

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

Why reexport these symbols? For that matter, why import them? It seems like nothing is using them.

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, removed the import.

RLIMIT_RTTIME = libc::RLIMIT_RTTIME,
#[cfg(any(target_os = "linux", target_os = "android"))]
RLIMIT_SIGPENDING = libc::RLIMIT_SIGPENDING,
}
Copy link
Member

Choose a reason for hiding this comment

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

There are more resources on non-Linux platforms, such as RLIMIT_KQUEUES, RLIMIT_NPTS, RLIMIT_SBSIZE, and RLIMIT_SWAP.

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

@asomers asomers left a comment

Choose a reason for hiding this comment

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

I'm happy with the API now.

match rlim {
RLIM_INFINITY => None,
rlim => Some(rlim),
// 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

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

let mut limit = getrlimit(Resource::RLIMIT_STACK).unwrap();
assert!(limit.0 != limit.1);
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?

/// # Examples
///
/// ```
/// use nix::sys::resource::{getrlimit, Resource};
Copy link
Member

Choose a reason for hiding this comment

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

In the doc test, you should put a # before the use, fn main, and final } lines so they will be hidden from the documentation. Likewise, omit the blank line before main.

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

/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to set the limits of.
/// * `soft_limit`: The value that the kenrel enforces for the corresponding resource.
Copy link
Member

Choose a reason for hiding this comment

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

s/kenrel/kernel

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

setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();

let (new_soft_limit, new_hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
assert!(new_soft_limit != new_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.

This should be assert!(new_soft_limit, soft_limit)

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 limit = getrlimit(Resource::RLIMIT_STACK).unwrap();
assert!(limit.0 != limit.1);
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.

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

libc_enum!{
#[repr(i32)]
pub enum Resource {
// 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


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


/// Get the current processes resource limits
///
/// A value of `None` corresponds to `RLIM_INFINITY`, which means there's no limit.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd just remove the RLIM_INFINITY reference. For the user that implementation detail is completely hidden: "A value of None indicates that there's no limit."

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

///
/// ```
/// # use nix::sys::resource::{getrlimit, Resource};
/// # fn main() {
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't need to declare main() here.

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

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.

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

pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
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


// make sure the soft limit and hard limit are not equal
let soft_limit = match soft_limit {
Some(nofile) => Some(nofile -1),
Copy link
Contributor

Choose a reason for hiding this comment

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

Need a space after the -

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

use nix::sys::resource::{Resource, getrlimit, setrlimit};

#[test]
pub fn test_resource_limits_nofile() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you summarize what this test is doing? Makes it easier to fix or modify them later.

/// # fn main() {
/// let soft_limit = Some(1024);
/// let hard_limit = None;
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a realistic use case auser might actually do? If not, we should try to find one more common to use as an example and provide a nice description of it before the codeblock.

}

#[test]
pub fn test_resource_limits_stack() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you provide some comments or a general summarizing comment here? It makes it easier for us reviewers to understand and to modify/fix them later.

@kpcyrd
Copy link
Contributor Author

kpcyrd commented Apr 14, 2018

I'm having trouble coming up with a cross plattform compatible test that is also a realistic test case. Could you suggest one?

@Susurrus
Copy link
Contributor

A test doesn't need to run on all platforms, though it's nice if it does. It'd be good to get at least a test working on one of our Tier 1 platforms so it'll run regularly in CI tho.

/// [setrlimit(2)](https://linux.die.net/man/2/setrlimit)
///
/// [`Resource`]: enum.Resource.html
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.)

/// [`Resource`]: enum.Resource.html
pub fn setrlimit(resource: Resource, soft_limit: Option<rlim_t>, hard_limit: Option<rlim_t>) -> Result<()> {
let mut rlim: rlimit = unsafe { mem::uninitialized() };
rlim.rlim_cur = soft_limit.unwrap_or(RLIM_INFINITY);
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.

@Susurrus
Copy link
Contributor

Susurrus commented Jun 2, 2018

@kpcyrd friendly pin on this.

@kpcyrd
Copy link
Contributor Author

kpcyrd commented Jun 19, 2018

This issue is up for grabs if anybody is interested in it, but I might come back to it later. I'm currently using my own solution in the program I was working on when filing this PR.

j1ah0ng referenced this pull request in j1ah0ng/nix Mar 6, 2020
@rtzoeller
Copy link
Collaborator

Implemented by #1302.

@rtzoeller rtzoeller closed this Nov 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add getrlimit(2)
5 participants