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}