pub fn inspect_keep_fds(keep_fds: &[libc::c_int]) -> (libc::c_int, bool) {
let mut max_keep_fd = -1;
let mut last_fd = -1;
let mut fds_sorted = true;
for fd in keep_fds.iter().cloned() {
if fd > max_keep_fd {
max_keep_fd = fd;
debug_assert!(last_fd <= fd);
} else if last_fd > fd {
fds_sorted = false;
}
last_fd = fd;
}
(max_keep_fd, fds_sorted)
}
pub fn simplify_keep_fds<'a>(
mut keep_fds: &'a [libc::c_int],
fds_sorted: bool,
minfd: &mut libc::c_int,
) -> &'a [libc::c_int] {
use core::cmp::Ordering;
if fds_sorted {
while let Some((first, rest)) = keep_fds.split_first() {
match first.cmp(&minfd) {
Ordering::Greater => break,
Ordering::Equal => {
keep_fds = rest;
*minfd += 1;
}
Ordering::Less => keep_fds = rest,
}
}
}
keep_fds
}
pub fn check_should_keep(keep_fds: &mut &[libc::c_int], fd: libc::c_int, fds_sorted: bool) -> bool {
if fds_sorted {
if let Some(index) = keep_fds.iter().position(|&x| x >= fd) {
*keep_fds = &(*keep_fds)[index..];
}
keep_fds.first() == Some(&fd)
} else {
keep_fds.contains(&fd)
}
}
#[cfg(target_os = "linux")]
#[inline]
pub fn is_wsl_1() -> bool {
use core::sync::atomic::{AtomicU8, Ordering};
static IS_WSL1: AtomicU8 = AtomicU8::new(2);
match IS_WSL1.load(Ordering::Relaxed) {
1 => true,
0 => false,
_ => {
let mut uname = unsafe { core::mem::zeroed() };
unsafe {
libc::uname(&mut uname);
}
let uname_release_len = uname
.release
.iter()
.position(|c| *c == 0)
.unwrap_or_else(|| uname.release.len());
let uname_release = unsafe {
core::slice::from_raw_parts(uname.release.as_ptr() as *const u8, uname_release_len)
};
let is_wsl1 = uname_release.ends_with(b"Microsoft");
IS_WSL1.store(is_wsl1 as u8, Ordering::Relaxed);
is_wsl1
}
}
}
#[inline]
pub fn is_fd_valid(fd: libc::c_int) -> bool {
unsafe { libc::fcntl(fd, libc::F_GETFD) >= 0 }
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn apply_range<F: FnMut(libc::c_int, libc::c_int) -> Result<(), ()>>(
minfd: libc::c_int,
mut keep_fds: &[libc::c_int],
mut func: F,
) -> Result<(), ()> {
if let Some(index) = keep_fds.iter().position(|&fd| fd >= minfd) {
keep_fds = &keep_fds[index..];
} else {
return func(minfd, libc::c_int::MAX);
}
if keep_fds[0] > minfd {
func(minfd, keep_fds[0] - 1)?;
}
for i in 0..(keep_fds.len() - 1) {
let low = unsafe { *keep_fds.get_unchecked(i) };
let high = unsafe { *keep_fds.get_unchecked(i + 1) };
debug_assert!(high >= low);
if high - low >= 2 {
func(low + 1, high - 1)?;
}
}
func(keep_fds[keep_fds.len() - 1] + 1, libc::c_int::MAX)
}
pub fn set_cloexec(fd: libc::c_int) {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
if flags >= 0 && (flags & libc::FD_CLOEXEC) != libc::FD_CLOEXEC {
unsafe {
libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn with_fd<F: FnOnce(libc::c_int)>(f: F) {
let fd = unsafe { libc::open(b"/\0".as_ptr() as *const _, libc::O_RDONLY) };
assert!(fd >= 0);
f(fd);
unsafe {
libc::close(fd);
}
}
#[test]
fn test_inspect_keep_fds() {
assert_eq!(inspect_keep_fds(&[]), (-1, true));
assert_eq!(inspect_keep_fds(&[0]), (0, true));
assert_eq!(inspect_keep_fds(&[0, 1]), (1, true));
assert_eq!(inspect_keep_fds(&[1, 0]), (1, false));
assert_eq!(inspect_keep_fds(&[0, 1, 2, 5, 7]), (7, true));
assert_eq!(inspect_keep_fds(&[0, 1, 2, 7, 5]), (7, false));
}
#[test]
fn test_check_should_keep_sorted() {
let mut keep_fds: &[libc::c_int] = &[0, 1, 5, 8, 10];
assert!(check_should_keep(&mut keep_fds, 0, true));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
assert!(check_should_keep(&mut keep_fds, 1, true));
assert_eq!(keep_fds, &[1, 5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 2, true));
assert_eq!(keep_fds, &[5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 2, true));
assert_eq!(keep_fds, &[5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 3, true));
assert_eq!(keep_fds, &[5, 8, 10]);
assert!(check_should_keep(&mut keep_fds, 5, true));
assert_eq!(keep_fds, &[5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 6, true));
assert_eq!(keep_fds, &[8, 10]);
}
#[test]
fn test_check_should_keep_not_sorted() {
let mut keep_fds: &[libc::c_int] = &[0, 1, 5, 8, 10];
assert!(check_should_keep(&mut keep_fds, 0, false));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
assert!(check_should_keep(&mut keep_fds, 1, false));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 2, false));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 3, false));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
assert!(check_should_keep(&mut keep_fds, 5, false));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
assert!(!check_should_keep(&mut keep_fds, 6, false));
assert_eq!(keep_fds, &[0, 1, 5, 8, 10]);
}
#[test]
fn test_simplify_keep_fds() {
let mut keep_fds: &[libc::c_int];
let mut minfd;
keep_fds = &[0, 1, 2];
minfd = 3;
assert_eq!(simplify_keep_fds(keep_fds, true, &mut minfd), &[]);
assert_eq!(minfd, 3);
keep_fds = &[2, 3, 4, 5, 6, 7];
minfd = 3;
assert_eq!(simplify_keep_fds(keep_fds, true, &mut minfd), &[]);
assert_eq!(minfd, 8);
keep_fds = &[3, 4, 5, 8, 10];
minfd = 3;
assert_eq!(simplify_keep_fds(keep_fds, true, &mut minfd), &[8, 10]);
assert_eq!(minfd, 6);
keep_fds = &[3, 5, 8, 10];
minfd = 3;
assert_eq!(simplify_keep_fds(keep_fds, true, &mut minfd), &[5, 8, 10]);
assert_eq!(minfd, 4);
keep_fds = &[4, 5, 8, 10];
minfd = 3;
assert_eq!(
simplify_keep_fds(keep_fds, true, &mut minfd),
&[4, 5, 8, 10]
);
assert_eq!(minfd, 3);
keep_fds = &[2, 1, 0];
minfd = 3;
assert_eq!(simplify_keep_fds(keep_fds, false, &mut minfd), &[2, 1, 0]);
assert_eq!(minfd, 3);
keep_fds = &[4, 5, 8, 10, 3];
minfd = 3;
assert_eq!(
simplify_keep_fds(keep_fds, false, &mut minfd),
&[4, 5, 8, 10, 3]
);
assert_eq!(minfd, 3);
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[test]
fn test_apply_range() {
macro_rules! check_ok {
($minfd:expr, [$($keep_fds:expr),* $(,)?], [$($calls:expr),* $(,)?] $(,)?) => {{
let mut ranges = [(0, 0); 100];
let mut len = 0;
apply_range($minfd, &[$($keep_fds),*], |low, high| {
*ranges.get_mut(len).unwrap() = (low, high);
len += 1;
Ok(())
}).unwrap();
assert_eq!(&ranges[..len], [$($calls),*]);
}}
}
check_ok!(0, [], [(0, libc::c_int::MAX)]);
check_ok!(-100, [], [(-100, libc::c_int::MAX)]);
check_ok!(3, [0, 2, 3, 4, 5, 6], [(7, libc::c_int::MAX)]);
check_ok!(3, [0, 2], [(3, libc::c_int::MAX)]);
check_ok!(3, [3, 4, 5, 6], [(7, libc::c_int::MAX)]);
check_ok!(3, [4, 5, 6], [(3, 3), (7, libc::c_int::MAX)]);
check_ok!(3, [5, 6, 9, 10], [(3, 4), (7, 8), (11, libc::c_int::MAX)]);
check_ok!(
3,
[5, 6, 9, 10, 20, 23],
[(3, 4), (7, 8), (11, 19), (21, 22), (24, libc::c_int::MAX)],
);
macro_rules! check_err {
($minfd:expr, [$($keep_fds:expr),* $(,)?], $call:expr $(,)?) => {{
let mut call = None;
apply_range($minfd, &[$($keep_fds),*], |low, high| {
assert!(call.is_none());
call = Some((low, high));
Err(())
}).unwrap_err();
assert_eq!(call.unwrap(), $call);
}}
}
check_err!(0, [], (0, libc::c_int::MAX));
check_err!(-100, [], (-100, libc::c_int::MAX));
check_err!(3, [0, 2, 3, 4, 5, 6], (7, libc::c_int::MAX));
check_err!(3, [0, 2], (3, libc::c_int::MAX));
check_err!(3, [3, 4, 5, 6], (7, libc::c_int::MAX));
check_err!(3, [4, 5, 6], (3, 3));
check_err!(3, [5, 6, 9, 10], (3, 4));
check_err!(3, [5, 6, 9, 10, 20, 23], (3, 4),);
}
#[test]
fn test_is_fd_valid() {
assert!(!is_fd_valid(-1));
assert!(!is_fd_valid(libc::c_int::MAX));
assert!(is_fd_valid(0));
with_fd(|fd| {
assert!(is_fd_valid(fd));
});
}
#[test]
fn test_set_cloexec() {
set_cloexec(-1);
set_cloexec(libc::c_int::MAX);
fn is_cloexec(fd: libc::c_int) -> bool {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
assert!(flags >= 0);
flags & libc::FD_CLOEXEC == libc::FD_CLOEXEC
}
with_fd(|fd| {
assert!(!is_cloexec(fd));
set_cloexec(fd);
assert!(is_cloexec(fd));
});
}
}