#[cfg(unix)]
use super::profile::ResolvedExecutionProfile;
#[cfg(unix)]
use super::profile::UnixResourceLimits;
#[cfg(unix)]
use anyhow::{Result, anyhow};
#[cfg(unix)]
use std::io;
#[cfg(unix)]
pub(crate) fn apply_unix_resource_limits_to_command(
cmd: &mut tokio::process::Command,
execution_profile: &ResolvedExecutionProfile,
) -> Result<()> {
let Some(limits) = UnixResourceLimits::from_execution_profile(execution_profile) else {
return Ok(());
};
unsafe {
cmd.pre_exec(move || apply_unix_resource_limits(&limits).map_err(io::Error::other));
}
Ok(())
}
#[cfg(unix)]
fn apply_unix_resource_limits(limits: &UnixResourceLimits) -> Result<()> {
if let Some(value) = limits.max_memory_bytes {
set_rlimit(rlimit_resource(libc::RLIMIT_AS as u64)?, value)?;
}
if let Some(value) = limits.max_cpu_seconds {
set_rlimit(rlimit_resource(libc::RLIMIT_CPU as u64)?, value)?;
}
if let Some(value) = limits.max_processes {
set_rlimit(rlimit_resource(libc::RLIMIT_NPROC as u64)?, value)?;
}
if let Some(value) = limits.max_open_files {
set_rlimit(rlimit_resource(libc::RLIMIT_NOFILE as u64)?, value)?;
}
Ok(())
}
#[cfg(unix)]
#[cfg(all(target_os = "linux", target_env = "gnu"))]
type RlimitResource = libc::__rlimit_resource_t;
#[cfg(unix)]
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
type RlimitResource = libc::c_int;
#[cfg(unix)]
fn rlimit_resource(resource: u64) -> Result<RlimitResource> {
RlimitResource::try_from(resource)
.map_err(|_| anyhow!("unsupported rlimit resource selector: {resource}"))
}
#[cfg(unix)]
fn set_rlimit(resource: RlimitResource, value: u64) -> Result<()> {
let limit = libc::rlimit {
rlim_cur: value as libc::rlim_t,
rlim_max: value as libc::rlim_t,
};
let rc = unsafe { libc::setrlimit(resource, &limit) };
if rc == 0 {
Ok(())
} else {
Err(anyhow!(
"setrlimit({resource}) failed: {}",
io::Error::last_os_error()
))
}
}