pub fn close_fds_from(first_fd: i32) -> Result<(), i32> {
#[cfg(target_os = "linux")]
{
let result = unsafe {
libc::syscall(
libc::SYS_close_range,
first_fd as libc::c_uint,
libc::c_uint::MAX,
0 as libc::c_uint,
)
};
if result == 0 {
return Ok(());
}
for fd in first_fd..1024 {
unsafe { libc::close(fd) };
}
Ok(())
}
#[cfg(target_os = "macos")]
{
for fd in first_fd..4096 {
unsafe { libc::close(fd) };
}
Ok(())
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = first_fd;
Err(libc::ENOSYS)
}
}
pub fn close_inherited_fds_raw() -> Result<(), i32> {
close_fds_from(3)
}
#[cfg(test)]
mod tests {
use super::*;
const STDOUT_FD: i32 = 1;
const STDERR_FD: i32 = 2;
fn run_in_child(test_name: &str, f: fn() -> i32) {
let pid = unsafe { libc::fork() };
assert!(pid >= 0, "fork failed for {}", test_name);
if pid == 0 {
let code = f();
unsafe { libc::_exit(code) };
}
let mut status = 0;
let waited = unsafe { libc::waitpid(pid, &mut status, 0) };
assert_eq!(waited, pid, "waitpid failed for {}", test_name);
assert!(
libc::WIFEXITED(status),
"{} child did not exit normally (status={})",
test_name,
status
);
assert_eq!(
libc::WEXITSTATUS(status),
0,
"{} child failed (status={})",
test_name,
status
);
}
fn child_close_fds_raw_succeeds() -> i32 {
let fd = unsafe { libc::dup(STDOUT_FD) };
if fd <= STDERR_FD {
return 1;
}
if close_inherited_fds_raw().is_err() {
return 2;
}
let result = unsafe { libc::fcntl(fd, libc::F_GETFD) };
if result != -1 {
return 3;
}
0
}
#[test]
fn test_close_fds_raw_succeeds() {
run_in_child("test_close_fds_raw_succeeds", child_close_fds_raw_succeeds);
}
fn child_stdin_stdout_stderr_preserved() -> i32 {
if close_inherited_fds_raw().is_err() {
return 1;
}
if unsafe { libc::fcntl(0, libc::F_GETFD) } < 0 {
return 2;
}
if unsafe { libc::fcntl(1, libc::F_GETFD) } < 0 {
return 3;
}
if unsafe { libc::fcntl(2, libc::F_GETFD) } < 0 {
return 4;
}
0
}
#[test]
fn test_stdin_stdout_stderr_preserved() {
run_in_child(
"test_stdin_stdout_stderr_preserved",
child_stdin_stdout_stderr_preserved,
);
}
fn child_close_fds_from_preserves_below() -> i32 {
let fd_a = unsafe { libc::dup(STDOUT_FD) };
let fd_b = unsafe { libc::dup(STDOUT_FD) };
if fd_a < 3 {
return 1;
}
if fd_b <= fd_a {
return 2;
}
if close_fds_from(fd_b).is_err() {
return 3;
}
let result = unsafe { libc::fcntl(fd_a, libc::F_GETFD) };
if result < 0 {
return 4;
}
let result = unsafe { libc::fcntl(fd_b, libc::F_GETFD) };
if result != -1 {
return 5;
}
unsafe { libc::close(fd_a) };
0
}
#[test]
fn test_close_fds_from_preserves_below() {
run_in_child(
"test_close_fds_from_preserves_below",
child_close_fds_from_preserves_below,
);
}
fn child_close_fds_from_closes_target_and_above() -> i32 {
let fd = unsafe { libc::dup(STDOUT_FD) };
if fd < 3 {
return 1;
}
if close_fds_from(fd).is_err() {
return 2;
}
let result = unsafe { libc::fcntl(fd, libc::F_GETFD) };
if result != -1 {
return 3;
}
0
}
#[test]
fn test_close_fds_from_closes_target_and_above() {
run_in_child(
"test_close_fds_from_closes_target_and_above",
child_close_fds_from_closes_target_and_above,
);
}
}