use std::{
io,
os::{fd::AsRawFd, unix::process::CommandExt},
};
pub fn pause_before_exec(
command: &mut std::process::Command,
observed_fd: i32,
) -> io::Result<(std::io::PipeReader, std::io::PipeWriter)> {
let (ready_reader, ready_writer) = io::pipe()?;
let (release_reader, release_writer) = io::pipe()?;
#[allow(unsafe_code)]
unsafe {
command.pre_exec(move || {
let flags = libc::fcntl(observed_fd, libc::F_GETFD);
if flags == -1 {
return Err(io::Error::last_os_error());
}
let mut ready = [0u8; 8];
ready[..4].copy_from_slice(&libc::getpid().to_ne_bytes());
ready[4..].copy_from_slice(&flags.to_ne_bytes());
let mut written = 0;
while written < ready.len() {
let count = libc::write(
ready_writer.as_raw_fd(),
ready[written..].as_ptr().cast(),
ready.len() - written,
);
if count > 0 {
written += count as usize;
} else if count == -1
&& io::Error::last_os_error().kind() == io::ErrorKind::Interrupted
{
continue;
} else {
return Err(io::Error::last_os_error());
}
}
let mut release = [0u8; 1];
loop {
match libc::read(release_reader.as_raw_fd(), release.as_mut_ptr().cast(), 1) {
1 => return Ok(()),
-1 if io::Error::last_os_error().kind() == io::ErrorKind::Interrupted => {}
0 => return Err(io::Error::from_raw_os_error(libc::EPIPE)),
_ => return Err(io::Error::last_os_error()),
}
}
});
}
Ok((ready_reader, release_writer))
}
pub fn flags_are_close_on_exec(flags: i32) -> bool {
flags & libc::FD_CLOEXEC != 0
}
#[cfg(target_os = "macos")]
pub fn wait_for_child_exit_without_reaping(pid: u32) -> io::Result<()> {
#[allow(unsafe_code)]
unsafe {
let mut info: libc::siginfo_t = std::mem::zeroed();
loop {
if libc::waitid(libc::P_PID, pid, &mut info, libc::WEXITED | libc::WNOWAIT) == 0 {
return Ok(());
}
let error = io::Error::last_os_error();
if error.kind() != io::ErrorKind::Interrupted {
return Err(error);
}
}
}
}