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();