From 8ad63194e7e8e1cdd4c29155c342499d7815e14f Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 18 Sep 2025 15:13:10 +0200 Subject: [PATCH] Re-execute itself with DYLD_LIBRARY_PATH After libkrun stopped linking directly against libkrunfw, we depend on dlopen() and the system's linker to do a sane job at looking for the library. This poses a problem on macOS, since the library path from the brew prefix is not used for looking up libraries by default. So work around this limitation, look if the DYLD_LIBRARY_PATH environment variable is present. If it isn't, obtain the brew prefix and re-execute krunvm with pushing that variable with brew's library path into the list of environment variables. Fixes: #70 Signed-off-by: Sergio Lopez --- Cargo.toml | 2 +- src/main.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1462c47..80f19a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ libc = "0.2.82" serde = "1.0.120" serde_derive = "1.0.120" text_io = "0.1.8" -nix = {version = "0.27.1", features = ["socket", "fs"]} +nix = {version = "0.27.1", features = ["socket", "fs", "process"]} diff --git a/src/main.rs b/src/main.rs index 3be6e5a..d6f336d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,15 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 use std::collections::HashMap; +use std::env; +use std::ffi::CString; #[cfg(target_os = "macos")] use std::fs::File; #[cfg(target_os = "macos")] -use std::io::{self, Read, Write}; +use std::io::{self, Error, ErrorKind, Read, Write}; +use std::os::unix::ffi::OsStringExt; use crate::commands::{ ChangeVmCmd, ConfigCmd, CreateCmd, DeleteCmd, InspectCmd, ListCmd, StartCmd, }; use clap::{Parser, Subcommand}; +use nix::unistd::execve; use serde_derive::{Deserialize, Serialize}; #[cfg(target_os = "macos")] use text_io::read; @@ -169,7 +173,62 @@ enum Command { Config(ConfigCmd), } +#[cfg(target_os = "macos")] +fn get_brew_prefix() -> Option { + let output = std::process::Command::new("brew") + .arg("--prefix") + .stderr(std::process::Stdio::inherit()) + .output() + .ok()?; + + let exit_code = output.status.code().unwrap_or(-1); + if exit_code != 0 { + return None; + } + + Some(std::str::from_utf8(&output.stdout).ok()?.trim().to_string()) +} + +#[cfg(target_os = "macos")] +fn reexec() -> Result<(), Error> { + let args: Vec = env::args_os() + .map(|arg| CString::new(arg.into_vec()).unwrap()) + .collect(); + + let mut envs: Vec = env::vars_os() + .map(|(key, value)| { + CString::new(format!( + "{}={}", + key.into_string().unwrap(), + value.into_string().unwrap() + )) + .unwrap() + }) + .collect(); + let brew_prefix = get_brew_prefix().ok_or(ErrorKind::NotFound)?; + envs.push(CString::new(format!( + "DYLD_LIBRARY_PATH={brew_prefix}/lib" + ))?); + + // Use execve to replace the current process. This function only returns + // if an error occurs. + match execve(&args[0], &args, &envs) { + Ok(_) => Ok(()), + Err(e) => { + eprintln!("Error re-executing krunvm: {}", e); + std::process::exit(-1); + } + } +} + fn main() { + #[cfg(target_os = "macos")] + { + if env::var("DYLD_LIBRARY_PATH").is_err() { + _ = reexec(); + } + } + let mut cfg: KrunvmConfig = confy::load(APP_NAME).unwrap(); let cli_args = Cli::parse();