#[cfg(test)]
use assert_cmd as _;
#[cfg(not(feature = "fuse"))]
use libc as _;
#[cfg(test)]
use nfs3_server as _;
#[cfg(test)]
use predicates as _;
#[cfg(test)]
use proptest as _;
mod cli;
mod engine;
mod output;
mod proto;
mod report;
mod util;
#[cfg(feature = "fuse")]
mod fuse;
mod shell;
use clap::Parser;
use tracing_subscriber::EnvFilter;
fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).with_target(false).init();
let cli = cli::Cli::parse();
let globals = cli.global_opts();
output::apply_no_color(globals.no_color);
#[cfg(feature = "fuse")]
if let cli::Command::Mount(args) = &cli.command {
cli::mount::preflight(args)?;
eprintln!("{}", output::status_info(&format!("Mounting at {} (background; unmount with `fusermount3 -u {}`)", args.mountpoint, args.mountpoint)));
cli::emit_replay(&globals);
#[expect(unsafe_code, reason = "libc::daemon is the only POSIX way to detach without dropping into raw fork(); confined to this single call site")]
let rc = unsafe { libc::daemon(1, 1) };
if rc != 0 {
anyhow::bail!("daemonize failed: {}", std::io::Error::last_os_error());
}
}
let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
rt.block_on(async move {
match cli.command {
cli::Command::Scan(args) => cli::scan::run(args, &globals).await,
cli::Command::Analyze(args) => cli::analyze::run(args, &globals).await,
#[cfg(feature = "fuse")]
cli::Command::Mount(args) => cli::mount::run(args, &globals).await,
cli::Command::Shell(args) => cli::shell::run(args, &globals).await,
cli::Command::Escape(args) => cli::escape::run(args, &globals).await,
cli::Command::BruteHandle(args) => cli::brute_handle::run(args, &globals).await,
cli::Command::UidSpray(args) => cli::uid_spray::run(args, &globals).await,
cli::Command::Convert(args) => cli::convert::run(&args, &globals),
cli::Command::Completions(args) => {
cli::completions(&args);
Ok(())
},
}
})
}