#[cfg(any(unix, all(windows, feature = "std")))]
use crate::Error;
#[cfg(all(unix, feature = "std"))]
pub mod unix_shmem_server;
#[cfg(unix)]
pub mod unix_signals;
#[cfg(unix)]
pub use unix_signals::CTRL_C_EXIT;
#[cfg(all(unix, feature = "alloc"))]
pub mod pipes;
#[cfg(all(unix, feature = "std"))]
use alloc::borrow::Cow;
#[cfg(all(unix, feature = "std"))]
use core::ffi::CStr;
#[cfg(feature = "std")]
use std::{env, process::Command};
#[cfg(all(unix, feature = "std"))]
use std::{ffi::CString, os::fd::RawFd};
#[cfg(all(unix, feature = "std"))]
use std::{fs::File, os::fd::AsRawFd, sync::OnceLock};
#[cfg(all(windows, feature = "std"))]
#[allow(missing_docs, overflowing_literals)]
pub mod windows_exceptions;
#[cfg(unix)]
use libc::pid_t;
#[cfg(all(windows, feature = "std"))]
pub use windows_exceptions::CTRL_C_EXIT;
#[cfg(all(feature = "std", unix))]
static NULL_FILE: OnceLock<File> = OnceLock::new();
#[cfg(unix)]
#[derive(Debug)]
pub struct ChildHandle {
pub pid: pid_t,
}
#[cfg(unix)]
impl ChildHandle {
#[must_use]
pub fn status(&self) -> i32 {
let mut status = -1;
unsafe {
libc::waitpid(self.pid, &mut status, 0);
}
libc::WEXITSTATUS(status)
}
}
#[cfg(unix)]
#[derive(Debug)]
pub enum ForkResult {
Parent(ChildHandle),
Child,
}
#[cfg(unix)]
pub unsafe fn fork() -> Result<ForkResult, Error> {
match libc::fork() {
pid if pid > 0 => Ok(ForkResult::Parent(ChildHandle { pid })),
pid if pid < 0 => {
#[cfg(feature = "std")]
{
let err_str = CString::new("Fork failed").unwrap();
libc::perror(err_str.as_ptr());
}
Err(Error::unknown(format!("Fork failed ({pid})")))
}
_ => Ok(ForkResult::Child),
}
}
#[cfg(feature = "std")]
pub fn startable_self() -> Result<Command, Error> {
let mut startable = Command::new(env::current_exe()?);
startable
.current_dir(env::current_dir()?)
.args(env::args().skip(1));
Ok(startable)
}
#[cfg(all(unix, feature = "std"))]
pub fn dup(fd: RawFd) -> Result<RawFd, Error> {
match unsafe { libc::dup(fd) } {
-1 => Err(Error::last_os_error(format!("Error calling dup({fd})"))),
new_fd => Ok(new_fd),
}
}
#[cfg(all(unix, feature = "std"))]
pub fn peak_rss_mb_child_processes() -> Result<i64, Error> {
use core::mem;
use std::io;
use libc::{rusage, RUSAGE_CHILDREN};
let rss = unsafe {
let mut rusage = mem::MaybeUninit::<rusage>::uninit();
if libc::getrusage(RUSAGE_CHILDREN, rusage.as_mut_ptr()) == -1 {
Err(io::Error::last_os_error())
} else {
Ok(rusage.assume_init())
}
}?;
Ok(rss.ru_maxrss >> 10)
}
#[cfg(all(unix, feature = "std"))]
pub fn dup2(fd: RawFd, device: RawFd) -> Result<(), Error> {
match unsafe { libc::dup2(fd, device) } {
-1 => Err(Error::last_os_error(format!(
"Error calling dup2({fd}, {device})"
))),
_ => Ok(()),
}
}
#[cfg(all(unix, feature = "std"))]
#[must_use]
pub fn last_error_str<'a>() -> Option<Cow<'a, str>> {
std::io::Error::last_os_error().raw_os_error().map(|errno| {
unsafe { CStr::from_ptr(libc::strerror(errno)).to_string_lossy() }
})
}
#[cfg(all(unix, feature = "std"))]
pub fn null_fd() -> Result<RawFd, Error> {
if let Some(file) = NULL_FILE.get() {
Ok(file.as_raw_fd())
} else {
let null_file = File::open("/dev/null")?;
Ok(NULL_FILE.get_or_init(move || null_file).as_raw_fd())
}
}