-
Notifications
You must be signed in to change notification settings - Fork 674
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) #1190
Closed
Closed
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7daf66b
Add getrlimit(2) and setrlimit(2)
kpcyrd c10d071
Update API
kpcyrd a015fc1
Use libc_enum
kpcyrd 0a29195
Add freebsd specific rlimits
kpcyrd d78b32e
Use explicit target_os list
kpcyrd a96627c
Add docs
kpcyrd d211451
Increase test coverage
kpcyrd 65541c9
Add examples
kpcyrd 28718e6
Update documentation and tests
kpcyrd e8e833f
Codestyle and docs
kpcyrd b900fa0
Document Resource enum
kpcyrd 88c0581
Commenting for clarity
j1ah0ng b07fc51
Set nolimit (infinity) as the error mode since this is Rust-specific
j1ah0ng bfd2228
Making types consistent with what libc expects
j1ah0ng 7309246
Conditional reprs based on the target_os (incomplete)
j1ah0ng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
//! Configure the process resource limits. | ||
use std::mem; | ||
|
||
#[cfg(not(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "macos")))] | ||
use libc::{self, rlimit, __rlimit_resource_t, RLIM_INFINITY}; | ||
|
||
#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "macos"))] | ||
use libc::{self, rlimit, c_int, RLIM_INFINITY}; | ||
|
||
pub use libc::rlim_t; | ||
|
||
use {Errno, Result}; | ||
|
||
#[cfg(target_os = "linux")] | ||
libc_enum!{ | ||
/// A resource that limits apply to | ||
#[repr(u32)] | ||
pub enum Resource { | ||
// POSIX | ||
/// This is the maximum size of the process's virtual memory (address space). The limit is | ||
/// specified in bytes, and is rounded down to the system page size. | ||
RLIMIT_AS, | ||
/// This is the maximum size of a core file (see core(5)) in bytes that the process may | ||
/// dump. | ||
RLIMIT_CORE, | ||
/// This is a limit, in seconds, on the amount of CPU time that the process can consume. | ||
RLIMIT_CPU, | ||
/// This is the maximum size of the process's data segment (initialized data, uninitialized | ||
/// data, and heap). The limit is specified in bytes, and is rounded down to the system | ||
/// page size. | ||
RLIMIT_DATA, | ||
/// This is the maximum size in bytes of files that the process may create. Attempts to | ||
/// extend a file beyond this limit result in delivery of a SIGXFSZ signal. | ||
RLIMIT_FSIZE, | ||
/// This specifies a value one greater than the maximum file descriptor number that can be | ||
/// opened by this process. | ||
RLIMIT_NOFILE, | ||
/// This is the maximum size of the process stack, in bytes. Upon reaching this limit, a | ||
/// SIGSEGV signal is generated. | ||
RLIMIT_STACK, | ||
|
||
// BSDs and Linux | ||
/// This is the maximum number of bytes of memory that may be locked into RAM. This limit | ||
/// is in effect rounded down to the nearest multiple of the system page size. | ||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] | ||
RLIMIT_MEMLOCK, | ||
/// This is a limit on the number of extant process (or, more precisely on Linux, threads) | ||
/// for the real user ID of the calling process. | ||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] | ||
RLIMIT_NPROC, | ||
/// This is a limit (in bytes) on the process's resident set (the number of virtual pages resident in RAM). | ||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] | ||
RLIMIT_RSS, | ||
|
||
// Android and Linux only | ||
/// This is a limit on the combined number of flock(2) locks and fcntl(2) leases that this process may establish. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_LOCKS, | ||
/// This is a limit on the number of bytes that can be allocated for POSIX message queues | ||
/// for the real user ID of the calling process. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_MSGQUEUE, | ||
/// This specifies a ceiling to which the process's nice value can be raised using | ||
/// setpriority(2) or nice(2). The actual ceiling for the nice value is calculated as 20 - | ||
/// rlim_cur. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_NICE, | ||
/// This specifies a ceiling on the real-time priority that may be set for this process | ||
/// using sched_setscheduler(2) and sched_setparam(2). | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_RTPRIO, | ||
/// This is a limit (in microseconds) on the amount of CPU time that a process scheduled | ||
/// under a real-time scheduling policy may consume without making a blocking system call. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_RTTIME, | ||
/// This is a limit on the number of signals that may be queued for the real user ID of the | ||
/// calling process. Both standard and real-time signals are counted for the purpose of | ||
/// checking this limit. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_SIGPENDING, | ||
|
||
// Available on some BSD | ||
/// The maximum number of kqueues this user id is allowed to create. | ||
#[cfg(target_os = "freebsd")] | ||
RLIMIT_KQUEUES, | ||
/// The maximum number of pseudo-terminals this user id is allowed to create. | ||
#[cfg(target_os = "freebsd")] | ||
RLIMIT_NPTS, | ||
/// The maximum size (in bytes) of socket buffer usage for this user. | ||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] | ||
RLIMIT_SBSIZE, | ||
/// The maximum size (in bytes) of the swap space that may be reserved or used by all of this user id's processes. | ||
#[cfg(target_os = "freebsd")] | ||
RLIMIT_SWAP, | ||
} | ||
} | ||
|
||
#[cfg(not(target_os = "linux"))] | ||
libc_enum!{ | ||
/// A resource that limits apply to | ||
#[repr(i32)] | ||
pub enum Resource { | ||
// POSIX | ||
/// This is the maximum size of the process's virtual memory (address space). The limit is | ||
/// specified in bytes, and is rounded down to the system page size. | ||
RLIMIT_AS, | ||
/// This is the maximum size of a core file (see core(5)) in bytes that the process may | ||
/// dump. | ||
RLIMIT_CORE, | ||
/// This is a limit, in seconds, on the amount of CPU time that the process can consume. | ||
RLIMIT_CPU, | ||
/// This is the maximum size of the process's data segment (initialized data, uninitialized | ||
/// data, and heap). The limit is specified in bytes, and is rounded down to the system | ||
/// page size. | ||
RLIMIT_DATA, | ||
/// This is the maximum size in bytes of files that the process may create. Attempts to | ||
/// extend a file beyond this limit result in delivery of a SIGXFSZ signal. | ||
RLIMIT_FSIZE, | ||
/// This specifies a value one greater than the maximum file descriptor number that can be | ||
/// opened by this process. | ||
RLIMIT_NOFILE, | ||
/// This is the maximum size of the process stack, in bytes. Upon reaching this limit, a | ||
/// SIGSEGV signal is generated. | ||
RLIMIT_STACK, | ||
|
||
// BSDs and Linux | ||
/// This is the maximum number of bytes of memory that may be locked into RAM. This limit | ||
/// is in effect rounded down to the nearest multiple of the system page size. | ||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] | ||
RLIMIT_MEMLOCK, | ||
/// This is a limit on the number of extant process (or, more precisely on Linux, threads) | ||
/// for the real user ID of the calling process. | ||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] | ||
RLIMIT_NPROC, | ||
/// This is a limit (in bytes) on the process's resident set (the number of virtual pages resident in RAM). | ||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] | ||
RLIMIT_RSS, | ||
|
||
// Android and Linux only | ||
/// This is a limit on the combined number of flock(2) locks and fcntl(2) leases that this process may establish. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_LOCKS, | ||
/// This is a limit on the number of bytes that can be allocated for POSIX message queues | ||
/// for the real user ID of the calling process. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_MSGQUEUE, | ||
/// This specifies a ceiling to which the process's nice value can be raised using | ||
/// setpriority(2) or nice(2). The actual ceiling for the nice value is calculated as 20 - | ||
/// rlim_cur. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_NICE, | ||
/// This specifies a ceiling on the real-time priority that may be set for this process | ||
/// using sched_setscheduler(2) and sched_setparam(2). | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_RTPRIO, | ||
/// This is a limit (in microseconds) on the amount of CPU time that a process scheduled | ||
/// under a real-time scheduling policy may consume without making a blocking system call. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_RTTIME, | ||
/// This is a limit on the number of signals that may be queued for the real user ID of the | ||
/// calling process. Both standard and real-time signals are counted for the purpose of | ||
/// checking this limit. | ||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
RLIMIT_SIGPENDING, | ||
|
||
// Available on some BSD | ||
/// The maximum number of kqueues this user id is allowed to create. | ||
#[cfg(target_os = "freebsd")] | ||
RLIMIT_KQUEUES, | ||
/// The maximum number of pseudo-terminals this user id is allowed to create. | ||
#[cfg(target_os = "freebsd")] | ||
RLIMIT_NPTS, | ||
/// The maximum size (in bytes) of socket buffer usage for this user. | ||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] | ||
RLIMIT_SBSIZE, | ||
/// The maximum size (in bytes) of the swap space that may be reserved or used by all of this user id's processes. | ||
#[cfg(target_os = "freebsd")] | ||
RLIMIT_SWAP, | ||
} | ||
} | ||
|
||
/// Get the current processes resource limits | ||
/// | ||
/// A value of None indicates that there's no limit. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
/// | ||
/// # Parameters | ||
/// | ||
/// * `resource`: The [`Resource`] that we want to get the limits of. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// # use nix::sys::resource::{getrlimit, Resource}; | ||
/// | ||
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); | ||
/// println!("current soft_limit: {:?}", soft_limit); | ||
/// println!("current hard_limit: {:?}", hard_limit); | ||
/// ``` | ||
/// | ||
/// # References | ||
/// | ||
/// [getrlimit(2)](https://linux.die.net/man/2/getrlimit) | ||
/// | ||
/// [`Resource`]: enum.Resource.html | ||
pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> { | ||
let mut rlim = mem::MaybeUninit::<&rlimit>::uninit(); | ||
|
||
#[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "openbsd", target_os = "netbsd")))] | ||
let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, rlim.as_mut_ptr() as *mut _) }; | ||
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "openbsd", target_os = "netbsd"))] | ||
let res = unsafe { libc::getrlimit(resource as c_int, rlim.as_mut_ptr() as *mut _) }; | ||
|
||
let rlim = unsafe { rlim.assume_init() }; | ||
// TODO: use Option::filter after it has been stabilized | ||
Errno::result(res).map(|_| { | ||
(if rlim.rlim_cur != RLIM_INFINITY { Some(rlim.rlim_cur) } else { None }, | ||
if rlim.rlim_max != RLIM_INFINITY { Some(rlim.rlim_max) } else { None }) | ||
}) | ||
} | ||
|
||
/// Set the current processes resource limits | ||
/// | ||
/// A value of None indicates that there's no limit. | ||
/// | ||
/// # Parameters | ||
/// | ||
/// * `resource`: The [`Resource`] that we want to set the limits of. | ||
/// * `soft_limit`: The value that the kernel enforces for the corresponding resource. | ||
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to the current hard limit | ||
/// for non-root users. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ```no_run | ||
/// # use nix::sys::resource::{setrlimit, Resource}; | ||
/// | ||
/// let soft_limit = Some(1024); | ||
/// let hard_limit = None; | ||
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); | ||
/// ``` | ||
/// | ||
/// # References | ||
/// | ||
/// [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<()> { | ||
let mut rlim: rlimit = unsafe { mem::zeroed() }; | ||
rlim.rlim_cur = soft_limit.unwrap_or(RLIM_INFINITY); | ||
rlim.rlim_max = hard_limit.unwrap_or(RLIM_INFINITY); | ||
|
||
#[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "openbsd", target_os = "netbsd")))] | ||
let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &rlim as *const _) }; | ||
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "openbsd", target_os = "netbsd"))] | ||
let res = unsafe { libc::setrlimit(resource as c_int, &rlim as *const _) }; | ||
|
||
Errno::result(res).map(|_| ()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
use nix::sys::resource::{Resource, getrlimit, setrlimit}; | ||
|
||
/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers | ||
/// to the maximum file descriptor number that can be opened by the process (aka the maximum number | ||
/// of file descriptors that the process can open, since Linux 4.5). | ||
/// | ||
/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the | ||
/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit() | ||
/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have | ||
/// been updated. | ||
#[test] | ||
pub fn test_resource_limits_nofile() { | ||
let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); | ||
|
||
// make sure the soft limit and hard limit are not equal | ||
let soft_limit = match soft_limit { | ||
Some(nofile) => Some(nofile - 1), | ||
None => Some(1024), | ||
}; | ||
setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); | ||
|
||
let (new_soft_limit, _new_hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); | ||
assert_eq!(new_soft_limit, soft_limit); | ||
} | ||
|
||
/// Tests the RLIMIT_STACK functionality of getrlimit(), where the resource RLIMIT_STACK refers to | ||
/// the maximum stack size that can be spawned by the current process before SIGSEGV is generated. | ||
/// | ||
/// We first save the current stack limits, then newly set the soft limit to the same size as the | ||
/// hard limit. We check to make sure these limits have been updated properly. We then set the | ||
/// stack limits back to the original values, and make sure they have been updated properly. | ||
#[test] | ||
pub fn test_resource_limits_stack() { | ||
let (mut soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_STACK).unwrap(); | ||
let orig_limit = (soft_limit, hard_limit); | ||
|
||
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); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't use a negative config check here. Nix could add a new platform at any time, and it's easier to maintain checks like this if they're all written as positive checks.