Skip to main content

proot/
main.rs

1//! PRoot reimplemented in Rust.
2//!
3//! The program runs a command inside a guest root filesystem without any
4//! privilege.  Path-taking syscalls are routed through seccomp user
5//! notification and answered by a small pool of supervisor threads; only the
6//! syscalls that must change the tracee's own state in place (`execve`,
7//! `chdir`, unix socket `bind`/`connect`) take a ptrace stop.  Everything else
8//! runs untraced at native speed.  Where ptrace is filtered out, those few
9//! syscalls are served through notifications too, with in-place rewrites
10//! (`syscall::direct`).
11//!
12//! The binary behaves like bubblewrap when invoked through a name ending in
13//! `bwrap`, busybox style, so a symlink is enough to make it a drop-in.
14
15mod abi;
16mod access;
17mod cli;
18mod config;
19mod exec;
20mod fs;
21mod guest_stub;
22mod launch;
23mod procs;
24mod seccomp;
25mod state;
26mod sys;
27mod syscall;
28mod tracee;
29mod tracer;
30
31use std::ffi::OsString;
32use std::os::unix::ffi::OsStrExt;
33
34/// Program entry point.
35///
36/// Dispatches on the basename of `argv[0]`: a name ending in `bwrap` selects
37/// the bubblewrap-compatible front end, anything else the PRoot front end.
38/// Exits with the traced command's exit status, or 1 on internal error.
39fn main() {
40    let mut args: Vec<OsString> = std::env::args_os().collect();
41    let argv0 = args.remove(0);
42    let base = std::path::Path::new(&argv0)
43        .file_name()
44        .map(|f| f.as_bytes().to_vec())
45        .unwrap_or_default();
46    let parsed = if base.ends_with(b"bwrap") {
47        cli::bwrap::parse(args, &cli::host::Local)
48    } else {
49        cli::parse(args, &cli::host::Local)
50    };
51    let config = match parsed {
52        Ok(c) => c,
53        Err(cli::CliError::Exit { code, text }) => {
54            print!("{text}");
55            std::process::exit(code)
56        }
57        Err(cli::CliError::Usage(msg)) => {
58            eprintln!("proot: {msg}");
59            std::process::exit(125);
60        }
61    };
62    match launch::run(config) {
63        Ok(code) => std::process::exit(code),
64        Err(e) => {
65            eprintln!("proot: {e}");
66            std::process::exit(1);
67        }
68    }
69}