bootc_internal_utils/
reexec.rs1use std::os::unix::process::CommandExt;
2use std::path::PathBuf;
3use std::process::Command;
4
5use anyhow::Result;
6
7pub 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
15pub const ORIG: &str = "_BOOTC_ORIG_EXE";
17
18pub 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
29pub 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}