Attempt to fix compilation error on Macos

Former-commit-id: 12ab4ce918
This commit is contained in:
Kaare Hoff Skovgaard 2025-07-10 20:57:01 +02:00
parent bb93578b88
commit 6d1c9ff2dc
3 changed files with 60 additions and 12 deletions

View file

@ -21,7 +21,7 @@ clap = { version = "4.5.39", default-features = false, features = [
"derive",
] }
log = { version = "0.4.27", default-features = false, features = ["std"] }
nix = { version = "0.30.1", default-features = false, features = ["process"] }
nix = { version = "0.30.1", default-features = false }
serde = { version = "1.0.219", default-features = false, features = [
"derive",
"std",

View file

@ -9,6 +9,6 @@ anyhow = { workspace = true }
clap = { workspace = true }
common = { path = "../../lib/common" }
log = { workspace = true }
nix = { workspace = true }
nix = { workspace = true, features = ["env", "process"] }
serde = { workspace = true }
hakari = { version = "0.1", path = "../../lib/hakari" }

View file

@ -1,4 +1,8 @@
use std::{collections::BTreeSet, ffi::CString};
use std::{
collections::BTreeSet,
convert::Infallible,
ffi::{CStr, CString, OsStr, OsString},
};
use anyhow::Context as _;
use clap::{Parser, Subcommand};
@ -195,18 +199,13 @@ fn wrap_program(wrap_program: WrapProgram) -> anyhow::Result<()> {
return Err(anyhow::format_err!("No command to execute was specified"));
}
let unique: BTreeSet<_> = BTreeSet::from_iter(endpoint);
let mut env = Vec::<CString>::new();
let mut env = Vec::<(OsString, OsString)>::new();
for (key, value) in std::env::vars() {
env.push(
CString::new(format!("{key}={value}"))
.with_context(|| format!("Environment variable {key} contained a null byte"))?,
);
env.push((OsString::from(key), OsString::from(value)));
}
for env_set in unique {
for (key, value) in env_set.try_into_env_data()? {
env.push(CString::new(format!("{key}={value}")).with_context(|| {
format!("Environment variable {key} contained a null byte")
})?);
env.push((OsString::from(key), OsString::from(value)));
}
}
let mut args = Vec::new();
@ -217,7 +216,56 @@ fn wrap_program(wrap_program: WrapProgram) -> anyhow::Result<()> {
}
(args, env)
};
nix::unistd::execvpe(&args[0], args.as_slice(), env.as_slice())?;
unsafe {
execvpe(&args[0], args.as_slice(), env.as_slice())?;
}
// This will never get executed
Ok(())
}
#[cfg(not(target_os = "macos"))]
/// Safety: No other threads may read or write environment variables when this function is called.
/// The easiest way to ensure this is using a single threaded program.
/// Note: On Linux specifically this safety requirement is not needed
unsafe fn execvpe<SA: AsRef<CStr>, SEK: AsRef<OsStr>, SEV: AsRef<OsStr>>(
filename: &CStr,
args: &[SA],
environ: &[(SEK, SEV)],
) -> anyhow::Result<Infallible> {
let environ: Vec<_> = environ
.iter()
.map(|(k, v)| {
CString::new(Format!("{k}={v}"))
.with_context(|| format!("Environment variable {k} contains null bytes"))?
})
.collect();
Ok(nix::unistd::execvpe(filename, args, &environ)?)
}
#[cfg(target_os = "macos")]
/// Safety: No other threads may read or write environment variables when this function is called.
/// The easiest way to ensure this is using a single threaded program.
// Simple "bad" version of execvpe that also works on OSX
unsafe fn execvpe<SA: AsRef<CStr>, SEK: AsRef<OsStr>, SEV: AsRef<OsStr>>(
filename: &CStr,
args: &[SA],
environ: &[(SEK, SEV)],
) -> anyhow::Result<Infallible> {
let current_env = std::env::vars_os();
// Safety: Same as this function
unsafe { nix::env::clearenv()? };
for (key, val) in environ {
// Safety: Same as this function
unsafe { std::env::set_var(key.as_ref(), val.as_ref()) };
}
match nix::unistd::execvp(filename, args) {
Err(err) => {
unsafe { nix::env::clearenv()? };
for (key, val) in current_env {
unsafe { std::env::set_var(key.as_os_str(), val.as_os_str()) };
}
Err(err.into())
}
_ => unreachable!("execvp doesn't return on success"),
}
}