Skip to main content

bootc_internal_utils/
reexec.rs

1use std::os::unix::process::CommandExt;
2use std::path::PathBuf;
3use std::process::Command;
4
5use anyhow::Result;
6
7/// Set up `cmd` to re-execute ourself: pass along our arguments and `argv[0]`,
8/// and set `extra_env` (state that must survive the re-exec) in the child.
9pub fn prepare_reexec(cmd: &mut Command, extra_env: &[(&str, &str)]) {
10    cmd.envs(extra_env.iter().copied());
11    cmd.args(std::env::args_os().skip(1));
12    cmd.arg0(crate::NAME);
13}
14
15/// Environment variable holding a reference to our original binary
16pub const ORIG: &str = "_BOOTC_ORIG_EXE";
17
18/// Return the path to our own executable. In some cases (SELinux) we may have
19/// performed a re-exec with a temporary copy of the binary and
20/// this environment variable will hold the path to the original binary.
21pub fn executable_path() -> Result<PathBuf> {
22    if let Some(p) = std::env::var_os(ORIG) {
23        Ok(p.into())
24    } else {
25        std::env::current_exe().map_err(Into::into)
26    }
27}
28
29/// Re-execute the current process if the provided environment variable is not set.
30pub fn reexec_with_guardenv(
31    k: &str,
32    prefix_args: &[&str],
33    extra_env: &[(&str, &str)],
34) -> Result<()> {
35    if std::env::var_os(k).is_some() {
36        tracing::trace!("Skipping re-exec due to env var {k}");
37        return Ok(());
38    }
39    let self_exe = executable_path()?;
40    let mut prefix_args = prefix_args.iter();
41    let mut cmd = if let Some(p) = prefix_args.next() {
42        let mut c = Command::new(p);
43        c.args(prefix_args);
44        c.arg(self_exe);
45        c
46    } else {
47        Command::new(self_exe)
48    };
49    cmd.env(k, "1");
50    prepare_reexec(&mut cmd, extra_env);
51    tracing::debug!("Re-executing current process for {k}");
52    Err(cmd.exec().into())
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn test_prepare_reexec_env() {
61        let mut cmd = Command::new("true");
62        prepare_reexec(&mut cmd, &[("_BOOTC_TEST_A", "1"), ("_BOOTC_TEST_B", "2")]);
63        let env: Vec<_> = cmd
64            .get_envs()
65            .filter_map(|(k, v)| Some((k.to_str()?, v?.to_str()?)))
66            .collect();
67        assert_eq!(env, [("_BOOTC_TEST_A", "1"), ("_BOOTC_TEST_B", "2")]);
68    }
69}