use nix::errno::Errno;
use std::fs;
use std::os::fd::RawFd;
const FIRST_INHERITED_FD: RawFd = 3;
const FALLBACK_FD_LIMIT: RawFd = 1_048_576;
pub(crate) fn close_inherited_fds() {
let first = u32::try_from(FIRST_INHERITED_FD).expect("fd lower bound fits u32");
let rc = unsafe { libc::syscall(libc::SYS_close_range, first, u32::MAX, 0_u32) };
if rc == 0 {
return;
}
match Errno::last() {
Errno::ENOSYS | Errno::EINVAL => {}
error => log::debug!("fd cleanup: close_range errno={}", error as i32),
}
match fs::read_dir("/proc/self/fd") {
Ok(entries) => {
let mut entries = entries;
let mut fds = Vec::new();
for entry in &mut entries {
let entry = match entry {
Ok(entry) => entry,
Err(error) => {
log::debug!("fd cleanup: proc entry: {error}");
fds.clear();
break;
}
};
let name = entry.file_name();
let Some(name) = name.to_str() else {
continue;
};
let Ok(fd) = name.parse::<RawFd>() else {
continue;
};
if fd >= FIRST_INHERITED_FD {
fds.push(fd);
}
}
drop(entries);
if !fds.is_empty() {
fds.sort_unstable();
fds.dedup();
for fd in fds {
close_fd(fd);
}
return;
}
}
Err(error) => log::debug!("fd cleanup: open /proc/self/fd: {error}"),
}
let mut rlimit = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
let mut limit = FALLBACK_FD_LIMIT;
let rc = unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlimit) };
if rc == 0 {
if let Ok(fallback) = libc::rlim_t::try_from(FALLBACK_FD_LIMIT) {
if let Ok(capped) = RawFd::try_from(rlimit.rlim_cur.min(fallback)) {
limit = capped;
}
}
}
for fd in FIRST_INHERITED_FD..limit {
close_fd(fd);
}
}
fn close_fd(fd: RawFd) {
if fd < FIRST_INHERITED_FD {
return;
}
let rc = unsafe { libc::close(fd) };
if rc == 0 {
return;
}
let error = Errno::last();
if error != Errno::EBADF {
log::debug!("fd cleanup: close({fd}) errno={}", error as i32);
}
}