Skip to main content

proot/
config.rs

1//! Runtime configuration shared by both command-line front ends.
2
3use std::ffi::CString;
4
5/// One bind mount: the host path visible at `guest` inside the guest root.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct Binding {
8    /// Absolute host path, without a trailing slash (except for `/`).
9    pub host: Vec<u8>,
10    /// Absolute guest path, without a trailing slash (except for `/`).
11    pub guest: Vec<u8>,
12    /// Whether writes through this binding are refused with `EROFS`.
13    pub read_only: bool,
14}
15
16/// Everything the launcher needs to start the traced command.
17#[derive(Clone, Debug)]
18pub struct Config {
19    /// Host directory used as the guest `/`.
20    pub root: Vec<u8>,
21    /// Bind mounts, in command-line order; later entries win on equal prefix.
22    pub bindings: Vec<Binding>,
23    /// Initial working directory, as a guest path.
24    pub cwd: Vec<u8>,
25    /// `cwd` was not given on the command line: it is the caller's working
26    /// directory, to be kept only where the guest can see it.
27    pub cwd_default: bool,
28    /// Kill every tracee when the supervisor exits.
29    pub kill_on_exit: bool,
30    /// Verbosity, 0 is quiet.
31    pub verbose: i32,
32    /// Command and arguments to run inside the guest; `argv[0]` first.
33    pub command: Vec<CString>,
34    /// Environment for the command, or `None` to inherit the supervisor's.
35    pub env: Option<Vec<CString>>,
36    /// Value reported by `uname(2)` in the `release` field, if faked.
37    pub kernel_release: Option<Vec<u8>>,
38    /// Pretend to be root (fake_id0).  Accepted but not implemented yet.
39    pub fake_root: bool,
40    /// Number of supervisor threads answering seccomp notifications.
41    pub threads: usize,
42    /// Microseconds a supervisor thread spins before parking.
43    pub spin_us: u64,
44    /// Directories to remove recursively once the tracer returns (bwrap mode).
45    pub cleanup_dirs: Vec<Vec<u8>>,
46    /// Extra classic-BPF seccomp programs (raw `sock_filter` bytes) the first
47    /// tracee installs after ours (bwrap `--seccomp`/`--add-seccomp-fd`).
48    pub seccomp_programs: Vec<Vec<u8>>,
49    /// Descriptors that receive `{"child-pid": N}` once the child is forked
50    /// (bwrap `--info-fd`).
51    pub info_fds: Vec<i32>,
52    /// Descriptors that receive `{"child-pid": N}` and later `{"exit-code": N}`
53    /// (bwrap `--json-status-fd`).
54    pub json_status_fds: Vec<i32>,
55    /// Run the command in a new session (bwrap `--new-session`).
56    pub new_session: bool,
57    /// Hostname to report (bwrap `--hostname`).  Not applied yet.
58    pub hostname: Option<Vec<u8>>,
59    /// `argv[0]` to give the command when it differs from `command[0]` (bwrap `--argv0`).
60    pub argv0: Option<CString>,
61    /// Invoked through the bubblewrap front end: failures are worded as bwrap words them.
62    pub bwrap_mode: bool,
63}
64
65impl Default for Config {
66    /// Builds the configuration equivalent to running `proot` with no option.
67    fn default() -> Self {
68        let threads = std::env::var("PROOT_THREADS")
69            .ok()
70            .and_then(|v| v.parse().ok())
71            .unwrap_or(4);
72        let spin_us = std::env::var("PROOT_SPIN_US")
73            .ok()
74            .and_then(|v| v.parse().ok())
75            .unwrap_or(200);
76        // bwrap mode has no verbosity flag of its own; the environment serves both front ends.
77        let verbose = std::env::var("PROOT_VERBOSE")
78            .ok()
79            .and_then(|v| v.parse().ok())
80            .unwrap_or(0);
81        Config {
82            root: b"/".to_vec(),
83            bindings: Vec::new(),
84            cwd: b".".to_vec(),
85            cwd_default: false,
86            kill_on_exit: false,
87            verbose,
88            command: Vec::new(),
89            env: None,
90            kernel_release: None,
91            fake_root: false,
92            threads,
93            spin_us,
94            cleanup_dirs: Vec::new(),
95            seccomp_programs: Vec::new(),
96            info_fds: Vec::new(),
97            json_status_fds: Vec::new(),
98            new_session: false,
99            hostname: None,
100            argv0: None,
101            bwrap_mode: false,
102        }
103    }
104}
105
106/// Strips trailing slashes from a path, keeping a lone `/`.
107///
108/// # Arguments
109/// * `path` - Any path.
110///
111/// # Returns
112/// The same path without trailing slashes.
113pub fn trim_slashes(mut path: Vec<u8>) -> Vec<u8> {
114    while path.len() > 1 && path.ends_with(b"/") {
115        path.pop();
116    }
117    if path.is_empty() {
118        path.push(b'/');
119    }
120    path
121}