#![allow(
unsafe_code,
clippy::multiple_unsafe_ops_per_block,
reason = "pre_exec runs between fork and exec where allocation / external calls are prohibited; clippy's 'one unsafe op per block' rule fights async-signal-safety here"
)]
use std::process::Command;
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct PreserveFds {
pub watchdog: Option<i32>,
pub landlock: Option<i32>,
}
#[cfg(not(unix))]
pub fn apply(_cmd: &mut Command, _preserve: PreserveFds, _die_with_parent: bool) {}
#[cfg(unix)]
pub(crate) fn apply(cmd: &mut Command, preserve: PreserveFds, die_with_parent: bool) {
use std::os::unix::process::CommandExt;
unsafe {
cmd.pre_exec(move || {
#[cfg(target_os = "linux")]
if let Some(fd) = preserve.landlock {
let errno = bux_landlock::restrict_self(fd);
if errno != 0 {
return Err(std::io::Error::from_raw_os_error(errno));
}
}
#[cfg(not(target_os = "linux"))]
let _ = preserve.landlock;
#[cfg(target_os = "linux")]
if die_with_parent {
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL);
}
#[cfg(not(target_os = "linux"))]
let _ = die_with_parent;
close_inherited_fds(preserve.watchdog);
Ok(())
});
}
}
#[cfg(unix)]
fn close_inherited_fds(preserve: Option<i32>) {
match preserve {
Some(keep) => close_fds_preserving(keep),
None => close_all_fds(),
}
}
#[cfg(unix)]
fn close_all_fds() {
#[cfg(target_os = "linux")]
{
let ret = unsafe { libc::syscall(libc::SYS_close_range, 3_u32, u32::MAX, 0_u32) };
if ret == 0 {
return;
}
}
close_fd_range(3, max_fd());
}
#[cfg(unix)]
fn close_fds_preserving(keep: i32) {
#[cfg(target_os = "linux")]
{
#[allow(clippy::cast_sign_loss, reason = "keep FD is always non-negative")]
let keep_u = keep as u32;
unsafe {
if keep > 3 {
libc::syscall(libc::SYS_close_range, 3_u32, keep_u - 1, 0_u32);
}
libc::syscall(libc::SYS_close_range, keep_u + 1, u32::MAX, 0_u32);
}
return;
}
#[allow(
unreachable_code,
reason = "fallback path after platform-specific early return"
)]
{
let end = max_fd();
for fd in 3..end {
if fd != keep {
unsafe { libc::close(fd) };
}
}
}
}
#[cfg(unix)]
fn max_fd() -> i32 {
let n = unsafe { libc::sysconf(libc::_SC_OPEN_MAX) };
#[allow(
clippy::cast_possible_truncation,
reason = "sysconf result fits in i32"
)]
if n > 0 { n as i32 } else { 1024 }
}
#[cfg(unix)]
fn close_fd_range(start: i32, end: i32) {
for fd in start..end {
unsafe { libc::close(fd) };
}
}