parent
bb93578b88
commit
6d1c9ff2dc
3 changed files with 60 additions and 12 deletions
|
@ -21,7 +21,7 @@ clap = { version = "4.5.39", default-features = false, features = [
|
||||||
"derive",
|
"derive",
|
||||||
] }
|
] }
|
||||||
log = { version = "0.4.27", default-features = false, features = ["std"] }
|
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 = [
|
serde = { version = "1.0.219", default-features = false, features = [
|
||||||
"derive",
|
"derive",
|
||||||
"std",
|
"std",
|
||||||
|
|
|
@ -9,6 +9,6 @@ anyhow = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
common = { path = "../../lib/common" }
|
common = { path = "../../lib/common" }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
nix = { workspace = true }
|
nix = { workspace = true, features = ["env", "process"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
hakari = { version = "0.1", path = "../../lib/hakari" }
|
hakari = { version = "0.1", path = "../../lib/hakari" }
|
||||||
|
|
|
@ -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 anyhow::Context as _;
|
||||||
use clap::{Parser, Subcommand};
|
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"));
|
return Err(anyhow::format_err!("No command to execute was specified"));
|
||||||
}
|
}
|
||||||
let unique: BTreeSet<_> = BTreeSet::from_iter(endpoint);
|
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() {
|
for (key, value) in std::env::vars() {
|
||||||
env.push(
|
env.push((OsString::from(key), OsString::from(value)));
|
||||||
CString::new(format!("{key}={value}"))
|
|
||||||
.with_context(|| format!("Environment variable {key} contained a null byte"))?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
for env_set in unique {
|
for env_set in unique {
|
||||||
for (key, value) in env_set.try_into_env_data()? {
|
for (key, value) in env_set.try_into_env_data()? {
|
||||||
env.push(CString::new(format!("{key}={value}")).with_context(|| {
|
env.push((OsString::from(key), OsString::from(value)));
|
||||||
format!("Environment variable {key} contained a null byte")
|
|
||||||
})?);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
@ -217,7 +216,56 @@ fn wrap_program(wrap_program: WrapProgram) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
(args, env)
|
(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
|
// This will never get executed
|
||||||
Ok(())
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue