mod cache;
mod env;
mod ephemeral;
mod fuse;
mod interp;
mod loader;
mod memfd;
mod metadata;
mod multicall;
mod portable;
mod ulexec;
#[cfg(feature = "update")]
mod update;
use std::os::unix::process::CommandExt;
fn main() {
let args: Vec<String> = std::env::args().collect();
let argv0 = args.first().map(|s| s.as_str()).unwrap_or("onelf");
let exec_path = std::fs::read_link("/proc/self/exe")
.ok()
.and_then(|p| p.to_str().map(String::from))
.unwrap_or_default();
let exe_path = std::path::Path::new(&exec_path);
let exe_dir = exe_path.parent().unwrap_or(std::path::Path::new("."));
let exe_name = exe_path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("onelf");
if portable::handle_portable_flags(&args, exe_dir, exe_name) {
return;
}
let mut pkg = match loader::load() {
Ok(p) => p,
Err(e) => {
eprintln!("onelf-rt: failed to load package: {e}");
std::process::exit(1);
}
};
let ep_idx = multicall::resolve_entrypoint(&pkg.manifest, argv0);
if ep_idx >= pkg.manifest.entrypoints.len() {
eprintln!("onelf-rt: no valid entrypoint found");
std::process::exit(1);
}
let ep_name = pkg
.manifest
.get_string(pkg.manifest.entrypoints[ep_idx].name)
.to_string();
if metadata::handle_metadata_flags(&args, &mut pkg, &ep_name) {
return;
}
#[cfg(feature = "update")]
if let Some(flag) = update::parse_flag(&args) {
let Some(url_bytes) = read_package_file(&mut pkg, ".onelf/update-url") else {
eprintln!("onelf-rt: no update URL configured (repack with --update-url)");
std::process::exit(1);
};
let url = match std::str::from_utf8(&url_bytes) {
Ok(s) => s.trim().to_string(),
Err(_) => {
eprintln!("onelf-rt: update URL is not valid UTF-8");
std::process::exit(1);
}
};
let Some(self_path) = update::self_path() else {
eprintln!("onelf-rt: cannot resolve /proc/self/exe");
std::process::exit(1);
};
std::process::exit(update::run(flag, &self_path, &url));
}
let ep_target_entry = pkg.manifest.entrypoints[ep_idx].target_entry as usize;
let ep_working_dir = pkg.manifest.entrypoints[ep_idx].working_dir;
let ep_memfd = pkg.manifest.entrypoints[ep_idx].is_memfd_eligible();
let target_blocks = pkg.manifest.entries[ep_target_entry].blocks.clone();
let ep_args_str = pkg
.manifest
.get_string(pkg.manifest.entrypoints[ep_idx].args)
.to_string();
let extra_args: Vec<String> = if ep_args_str.is_empty() {
Vec::new()
} else {
ep_args_str.split('\x1f').map(String::from).collect()
};
let mut final_args = extra_args;
if args.len() > 1 {
final_args.extend_from_slice(&args[1..]);
}
let forced_mode = std::env::var("ONELF_MODE").ok();
let force = forced_mode.as_deref();
if force == Some("memfd") || (force.is_none() && ep_memfd) {
if let Ok(data) = loader::read_payload_blocks(
&mut pkg.file,
pkg.footer.payload_offset,
&target_blocks,
pkg.dict.as_deref(),
) {
let lib_paths_str = pkg.manifest.lib_dirs().join(":");
env::setup_env(
"",
argv0,
&exec_path,
&ep_name,
"memfd",
&lib_paths_str,
"/proc/self/fd/0",
);
portable::setup_portable(exe_dir, exe_name);
if let Err(e) = memfd::execute_memfd(&data, argv0, &final_args) {
if force == Some("memfd") {
eprintln!("onelf-rt: memfd execution failed: {e}");
std::process::exit(1);
}
}
} else if force == Some("memfd") {
eprintln!("onelf-rt: failed to read payload for memfd");
std::process::exit(1);
}
}
let interp_data = read_package_file(&mut pkg, ".onelf/interp");
if force != Some("cache") && force != Some("tmpfs") {
fuse::execute_fuse(
&mut pkg,
ep_idx,
argv0,
&exec_path,
&final_args,
interp_data.as_deref(),
);
if force == Some("fuse") {
eprintln!("onelf-rt: FUSE mode unavailable");
std::process::exit(1);
}
}
if force != Some("cache") {
ephemeral::execute_tmpfs(
&mut pkg,
ep_idx,
argv0,
&exec_path,
&final_args,
interp_data.as_deref(),
);
if force == Some("tmpfs") {
eprintln!("onelf-rt: tmpfs mode unavailable");
std::process::exit(1);
}
}
let pkg_dir = match cache::ensure_extracted(&mut pkg) {
Ok(d) => d,
Err(e) => {
eprintln!("onelf-rt: extraction failed: {e}");
std::process::exit(1);
}
};
let package_id = cache::hex(&pkg.manifest.header.package_id);
let cache_base = cache::base_dir();
let gc_max_age = std::env::var("ONELF_GC_MAX_AGE")
.ok()
.and_then(|v| v.parse::<u64>().ok())
.unwrap_or(30);
if gc_max_age > 0 {
cache::auto_gc(&cache_base, gc_max_age * 86400, &package_id);
}
let target_path_str = pkg.manifest.entry_path(ep_target_entry);
let target_path = pkg_dir.join(&target_path_str);
if !target_path.exists() {
eprintln!(
"onelf-rt: entrypoint target does not exist: {}",
target_path.display()
);
std::process::exit(1);
}
let pkg_dir_str = pkg_dir.to_str().unwrap_or("");
let lib_paths_str = pkg.manifest.lib_dirs().join(":");
let target_path_s = target_path.to_str().unwrap_or("");
env::setup_env(
pkg_dir_str,
argv0,
&exec_path,
&ep_name,
"cache",
&lib_paths_str,
target_path_s,
);
portable::setup_portable(exe_dir, exe_name);
match ep_working_dir {
onelf_format::WorkingDir::PackageRoot => {
let _ = std::env::set_current_dir(&pkg_dir);
}
onelf_format::WorkingDir::EntrypointParent => {
if let Some(parent) = target_path.parent() {
let _ = std::env::set_current_dir(parent);
}
}
onelf_format::WorkingDir::Inherit => {}
}
let lib_dirs = pkg.manifest.lib_dirs();
let bundled_interp_rel = interp_data
.as_deref()
.and_then(interp::parse_bundled_interp_rel);
if let Some(interp) =
interp::should_use_userland_exec(&target_path, &pkg_dir, bundled_interp_rel)
{
interp::exec_userland(&target_path, &interp, argv0, &final_args);
}
let mut cmd =
interp::build_exec_command(&target_path, &pkg_dir, &lib_dirs, argv0, &final_args);
let err = cmd.exec();
eprintln!("onelf-rt: exec failed: {err}");
std::process::exit(1);
}
fn read_package_file(pkg: &mut loader::PackageData, path: &str) -> Option<Vec<u8>> {
let idx = (0..pkg.manifest.entries.len()).find(|&i| {
pkg.manifest.entries[i].kind == onelf_format::EntryKind::File
&& pkg.manifest.entry_path(i) == path
})?;
loader::read_payload_blocks(
&mut pkg.file,
pkg.footer.payload_offset,
&pkg.manifest.entries[idx].blocks,
pkg.dict.as_deref(),
)
.ok()
}