Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 1 addition & 40 deletions crates/blockdev/src/blockdev.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::collections::HashMap;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::sync::OnceLock;

Expand Down Expand Up @@ -247,7 +246,7 @@ impl LoopbackDevice {
/// if the parent process dies unexpectedly
fn spawn_cleanup_helper(device_path: &str) -> Result<LoopbackCleanupHandle> {
// Try multiple strategies to find the bootc binary
let bootc_path = Self::find_bootc_binary()
let bootc_path = bootc_utils::reexec::executable_path()
.context("Failed to locate bootc binary for cleanup helper")?;

// Create the helper process
Expand All @@ -270,44 +269,6 @@ impl LoopbackDevice {
Ok(LoopbackCleanupHandle { child })
}

/// Find the bootc binary using multiple strategies
fn find_bootc_binary() -> Result<PathBuf> {
// Strategy 1: Try /proc/self/exe (works in most cases)
if let Ok(exe_path) = std::fs::read_link("/proc/self/exe") {
if exe_path.exists() {
return Ok(exe_path);
} else {
tracing::warn!("/proc/self/exe points to non-existent path: {:?}", exe_path);
}
} else {
tracing::warn!("Failed to read /proc/self/exe");
}

// Strategy 2: Try argv[0] from std::env
if let Some(argv0) = std::env::args().next() {
let argv0_path = PathBuf::from(argv0);
if argv0_path.is_absolute() && argv0_path.exists() {
return Ok(argv0_path);
}
// If it's relative, try to resolve it
if let Ok(canonical) = argv0_path.canonicalize() {
return Ok(canonical);
}
}

// Strategy 3: Try common installation paths
let common_paths = ["/usr/bin/bootc", "/usr/local/bin/bootc"];

for path in &common_paths {
let path_buf = PathBuf::from(path);
if path_buf.exists() {
return Ok(path_buf);
}
}

anyhow::bail!("Could not locate bootc binary using any available strategy")
}

// Shared backend for our `close` and `drop` implementations.
fn impl_close(&mut self) -> Result<()> {
// SAFETY: This is the only place we take the option
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ pub(crate) fn ensure_self_unshared_mount_namespace() -> Result<()> {
anyhow::bail!("Failed to unshare mount namespace");
}
}
crate::reexec::reexec_with_guardenv(recurse_env, &["unshare", "-m", "--"])
bootc_utils::reexec::reexec_with_guardenv(recurse_env, &["unshare", "-m", "--"])
}

/// Acquire a locked sysroot.
Expand Down
6 changes: 3 additions & 3 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,11 +914,11 @@ async fn install_container(
}

/// Run a command in the host mount namespace
pub(crate) fn run_in_host_mountns(cmd: &str) -> Command {
let mut c = Command::new("/proc/self/exe");
pub(crate) fn run_in_host_mountns(cmd: &str) -> Result<Command> {
let mut c = Command::new(bootc_utils::reexec::executable_path()?);
c.lifecycle_bind()
.args(["exec-in-host-mount-namespace", cmd]);
c
Ok(c)
}

#[context("Re-exec in host mountns")]
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/install/baseline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub(crate) fn udev_settle() -> Result<()> {
// our way out of this.
std::thread::sleep(std::time::Duration::from_millis(200));

let st = super::run_in_host_mountns("udevadm")
let st = super::run_in_host_mountns("udevadm")?
.arg("settle")
.status()?;
if !st.success() {
Expand Down
1 change: 0 additions & 1 deletion crates/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub(crate) mod metadata;
mod podman;
mod progress_jsonl;
mod reboot;
mod reexec;
pub mod spec;
mod status;
mod store;
Expand Down
4 changes: 3 additions & 1 deletion crates/lib/src/lsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ pub(crate) fn selinux_ensure_install() -> Result<bool> {
// to match that of /usr/bin/ostree, and then re-exec. This is really a gross
// hack; we can't always rely on https://github.com/fedora-selinux/selinux-policy/pull/1500/commits/67eb283c46d35a722636d749e5b339615fe5e7f5
let mut tmpf = tempfile::NamedTempFile::new()?;
let mut src = std::fs::File::open("/proc/self/exe")?;
let srcpath = std::env::current_exe()?;
let mut src = std::fs::File::open(&srcpath)?;
let meta = src.metadata()?;
std::io::copy(&mut src, &mut tmpf).context("Copying self to tempfile for selinux re-exec")?;
tmpf.as_file_mut()
Expand All @@ -107,6 +108,7 @@ pub(crate) fn selinux_ensure_install() -> Result<bool> {

let mut cmd = Command::new(&tmpf);
cmd.env(guardenv, tmpf);
cmd.env(bootc_utils::reexec::ORIG, srcpath);
cmd.args(std::env::args_os().skip(1));
cmd.log_debug();
Err(anyhow::Error::msg(cmd.exec()).context("execve"))
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/podman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub(crate) struct ImageListEntry {
/// Given an image ID, return its manifest digest
pub(crate) fn imageid_to_digest(imgid: &str) -> Result<String> {
use bootc_utils::CommandRunExt;
let o: Vec<Inspect> = crate::install::run_in_host_mountns("podman")
let o: Vec<Inspect> = crate::install::run_in_host_mountns("podman")?
.args(["inspect", imgid])
.run_and_parse_json()?;
let i = o
Expand Down
2 changes: 1 addition & 1 deletion crates/ostree-ext/src/container/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub async fn deploy(

// Note that the sysroot is provided as `.` but we use cwd_dir to
// make the process current working directory the sysroot.
let st = std::process::Command::new("/proc/self/exe")
let st = std::process::Command::new(std::env::current_exe()?)
.args(["internals", "bootc-install-completion", ".", stateroot])
.cwd_dir(sysroot_dir.try_clone()?)
.lifecycle_bind()
Expand Down
2 changes: 2 additions & 0 deletions crates/utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ mod timestamp;
pub use timestamp::*;
mod tracing_util;
pub use tracing_util::*;
/// Re-execute the current process
pub mod reexec;
21 changes: 17 additions & 4 deletions crates/lib/src/reexec.rs → crates/utils/src/reexec.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::Command;

use anyhow::Result;
use fn_error_context::context;

/// Environment variable holding a reference to our original binary
pub const ORIG: &str = "_BOOTC_ORIG_EXE";

/// Return the path to our own executable. In some cases (SELinux) we may have
/// performed a re-exec with a temporary copy of the binary and
/// this environment variable will hold the path to the original binary.
pub fn executable_path() -> Result<PathBuf> {
if let Some(p) = std::env::var_os(ORIG) {
Ok(p.into())
} else {
std::env::current_exe().map_err(Into::into)
}
}

/// Re-execute the current process if the provided environment variable is not set.
#[context("Reexec self")]
pub(crate) fn reexec_with_guardenv(k: &str, prefix_args: &[&str]) -> Result<()> {
pub fn reexec_with_guardenv(k: &str, prefix_args: &[&str]) -> Result<()> {
if std::env::var_os(k).is_some() {
tracing::trace!("Skipping re-exec due to env var {k}");
return Ok(());
}
let self_exe = std::fs::read_link("/proc/self/exe")?;
let self_exe = executable_path()?;
let mut prefix_args = prefix_args.iter();
let mut cmd = if let Some(p) = prefix_args.next() {
let mut c = Command::new(p);
Expand Down