use std::io;
use std::os::unix::process::CommandExt;
use std::process::Command;
use rlimit::{setrlimit, Resource};
#[cfg(test)]
#[path = "resource_limits_test.rs"]
pub mod test;
struct RLimit {
resource: Resource,
soft_limit: u64,
hard_limit: u64,
units: String,
}
impl RLimit {
fn set(&self) -> io::Result<()> {
eprintln!(
"Setting {:?} limits: {} {} soft limit; {} {} hard limit.",
self.resource, self.soft_limit, self.units, self.hard_limit, self.units
);
setrlimit(self.resource, self.soft_limit, self.hard_limit)
}
}
pub struct ResourceLimits {
cpu_time: Option<RLimit>,
file_size: Option<RLimit>,
memory_size: Option<RLimit>,
}
impl ResourceLimits {
pub fn new(
cpu_time: Option<u64>,
file_size: Option<u64>,
memory_size: Option<u64>,
) -> ResourceLimits {
ResourceLimits {
cpu_time: cpu_time.map(|t| RLimit {
resource: Resource::CPU,
soft_limit: t,
hard_limit: t,
units: "seconds".to_string(),
}),
file_size: file_size.map(|x| RLimit {
resource: Resource::FSIZE,
soft_limit: x,
hard_limit: x,
units: "bytes".to_string(),
}),
memory_size: memory_size.map(|y| RLimit {
resource: Resource::AS,
soft_limit: y,
hard_limit: y,
units: "bytes".to_string(),
}),
}
}
pub fn set(&self) -> io::Result<()> {
[self.cpu_time.as_ref(), self.file_size.as_ref(), self.memory_size.as_ref()]
.iter()
.flatten()
.try_for_each(|resource_limit| resource_limit.set())
}
pub fn apply(self, command: &mut Command) -> &mut Command {
if self.cpu_time.is_none() && self.file_size.is_none() && self.memory_size.is_none() {
return command;
}
unsafe {
command.pre_exec(move || self.set())
}
}
}