chj_unix_util/
re_exec.rs

1use std::io::stderr;
2use std::path::PathBuf;
3use std::process::Command;
4use std::{ffi::OsString, os::unix::process::CommandExt};
5
6use anyhow::{anyhow, Context, Result};
7
8pub fn re_exec_with_executable(executable_path: PathBuf) -> std::io::Error {
9    let mut args = std::env::args_os();
10    let arg0 = args.next();
11    let mut cmd = Command::new(&executable_path);
12    cmd.args(args);
13    if let Some(a0) = arg0 {
14        cmd.arg0(a0);
15    }
16    cmd.exec()
17}
18
19pub fn current_exe() -> Result<PathBuf> {
20    let path = std::env::current_exe().context("getting the path to the current executable")?;
21    // On Linux, this gets " (deleted)" appended when the binary was
22    // replaced. Undo that, sigh.
23    let deleted_str = " (deleted)";
24    let s = path.to_string_lossy();
25    if s.ends_with(deleted_str) {
26        let os = path.as_os_str().to_owned();
27        let mut bs = os.into_encoded_bytes();
28        if bs.ends_with(deleted_str.as_bytes()) {
29            bs.truncate(bs.len() - deleted_str.as_bytes().len());
30        } else {
31            use std::io::Write;
32            _ = writeln!(
33                &mut stderr(),
34                "can't find the bytes after a first match, in {path:?}"
35            );
36            return Ok(path);
37        }
38        let os = unsafe { OsString::from_encoded_bytes_unchecked(bs) };
39        Ok(os.into())
40    } else {
41        Ok(path)
42    }
43}
44
45/// Only returns when there is an error.
46pub fn _re_exec() -> Result<()> {
47    let path = current_exe()?;
48    Err(re_exec_with_executable(path.clone()))
49        .with_context(|| anyhow!("executing the binary {path:?}"))
50}
51
52/// Only returns when there is an error.
53pub fn re_exec() -> anyhow::Error {
54    match _re_exec() {
55        Ok(()) => unreachable!(),
56        Err(e) => e,
57    }
58}