use std::ffi::CString;
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{DirBuilderExt, MetadataExt, PermissionsExt};
use std::os::unix::process::CommandExt;
use std::path::{Path, PathBuf};
pub const APE_NEEDS_LOADER: bool = true;
pub const APE_SHELL: &str = "/bin/sh";
pub const APE_SYSTEM_LOADERS: &[&str] = &["/usr/local/bin/ape"];
pub const APE_LOADER_HOST: crate::platform::ape::LoaderHost = crate::platform::ape::LoaderHost::Macos;
pub const APE_EXECVP_SHELL_FALLBACK: bool = true;
pub fn is_exec_format_error(error: &io::Error) -> bool {
error.raw_os_error() == Some(libc::ENOEXEC)
}
pub fn is_executable(metadata: &std::fs::Metadata) -> bool {
metadata.permissions().mode() & 0o111 != 0
}
pub fn default_loader_dirs() -> Vec<PathBuf> {
let set = |value: Option<std::ffi::OsString>| value.filter(|value| !value.is_empty());
let mut dirs = Vec::new();
if let Some(cache) = set(crate::env_vars::XDG_CACHE_HOME.os()) {
dirs.push(PathBuf::from(cache).join("running-process").join("ape"));
}
if let Some(home) = set(crate::env_vars::HOME.os()) {
dirs.push(
PathBuf::from(home)
.join("Library")
.join("Caches")
.join("running-process")
.join("ape"),
);
}
let uid = unsafe { libc::geteuid() };
dirs.push(std::env::temp_dir().join(format!("running-process-ape-{uid}")));
dirs
}
pub fn private_exec_dir(dir: &Path) -> bool {
let _ = std::fs::DirBuilder::new()
.recursive(true)
.mode(0o700)
.create(dir);
let uid = unsafe { libc::geteuid() };
let private = std::fs::symlink_metadata(dir).is_ok_and(|meta| {
meta.file_type().is_dir() && meta.uid() == uid && meta.mode() & 0o022 == 0
});
private && mount_allows_exec(dir)
}
fn mount_allows_exec(dir: &Path) -> bool {
let Ok(path) = CString::new(dir.as_os_str().as_bytes()) else {
return false;
};
let mut stats = std::mem::MaybeUninit::<libc::statfs>::uninit();
if unsafe { libc::statfs(path.as_ptr(), stats.as_mut_ptr()) } != 0 {
return false;
}
let stats = unsafe { stats.assume_init() };
stats.f_flags & (libc::MNT_NOEXEC as u32) == 0
}
pub fn anonymous_executable(_bytes: &[u8], _name: &str) -> Option<PathBuf> {
None
}
pub fn mark_executable(path: &Path) -> io::Result<()> {
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755))
}
pub fn route_through_execvp(command: &mut std::process::Command) {
unsafe {
command.pre_exec(|| Ok(()));
}
}
#[cfg(feature = "async-process")]
pub fn route_tokio_through_execvp(command: &mut tokio::process::Command) {
unsafe {
command.pre_exec(|| Ok(()));
}
}
#[cfg(test)]
#[path = "ape_tests.rs"]
mod tests;