use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::OnceLock;
pub fn ensure_worker_signed(worker_path: &Path) -> Result<(), String> {
static IN_PROCESS_DONE: OnceLock<()> = OnceLock::new();
if IN_PROCESS_DONE.get().is_some() {
return Ok(());
}
let stat_marker = |path: &Path| -> Option<String> {
let meta = std::fs::metadata(path).ok()?;
let mtime = meta
.modified()
.ok()?
.duration_since(std::time::UNIX_EPOCH)
.ok()?
.as_secs();
Some(format!(
"size={}\nmtime={}\npath={}\n",
meta.len(),
mtime,
path.display()
))
};
let current_marker = stat_marker(worker_path).ok_or_else(|| {
format!("stat {}: file disappeared", worker_path.display())
})?;
if let Some(sentinel) = sentinel_path() {
if let Ok(existing) = std::fs::read_to_string(&sentinel) {
if existing == current_marker {
IN_PROCESS_DONE.set(()).ok();
return Ok(());
}
}
}
let plist = std::env::temp_dir().join(format!(
"supermachine-entitlements-{}.plist",
std::process::id()
));
std::fs::write(&plist, crate::assets::ENTITLEMENTS_PLIST)
.map_err(|e| format!("write entitlements plist: {e}"))?;
let status = Command::new("codesign")
.args(["-s", "-", "--entitlements"])
.arg(&plist)
.arg("--force")
.arg(worker_path)
.stdout(Stdio::null())
.stderr(Stdio::piped())
.status();
let _ = std::fs::remove_file(&plist);
match status {
Ok(s) if s.success() => {
if let (Some(sentinel), Some(post_marker)) =
(sentinel_path(), stat_marker(worker_path))
{
if let Some(parent) = sentinel.parent() {
let _ = std::fs::create_dir_all(parent);
}
let _ = std::fs::write(&sentinel, post_marker);
}
IN_PROCESS_DONE.set(()).ok();
Ok(())
}
Ok(s) => Err(format!(
"codesign exited with {:?} for {}",
s.code(),
worker_path.display()
)),
Err(e) => Err(format!(
"failed to spawn codesign for {}: {e}\n\
(codesign ships with macOS by default; if missing, \
reinstall Xcode Command Line Tools)",
worker_path.display()
)),
}
}
pub fn locate_worker_bin() -> Option<PathBuf> {
if let Some(p) = std::env::var_os("SUPERMACHINE_WORKER_BIN") {
let p = PathBuf::from(p);
if p.is_file() {
return Some(p);
}
}
if let Ok(exe) = std::env::current_exe() {
if let Some(p) = sibling_worker(&exe) {
return Some(p);
}
if let Ok(canonical) = std::fs::canonicalize(&exe) {
if canonical != exe {
if let Some(p) = sibling_worker(&canonical) {
return Some(p);
}
}
}
}
if let Some(p) = cargo_bin_worker() {
return Some(p);
}
if let Some(p) = path_walk_worker() {
return Some(p);
}
if let Ok(exe) = std::env::current_exe() {
for ancestor in exe.ancestors() {
let p = ancestor.join("target/release/supermachine-worker");
if p.is_file() {
return Some(p);
}
}
if let Ok(canonical) = std::fs::canonicalize(&exe) {
for ancestor in canonical.ancestors() {
let p = ancestor.join("target/release/supermachine-worker");
if p.is_file() {
return Some(p);
}
}
}
}
None
}
fn sibling_worker(exe: &Path) -> Option<PathBuf> {
let dir = exe.parent()?;
let p = dir.join("supermachine-worker");
if p.is_file() {
Some(p)
} else {
None
}
}
fn cargo_bin_worker() -> Option<PathBuf> {
let bin_dir = if let Some(cargo) = std::env::var_os("CARGO_HOME") {
PathBuf::from(cargo).join("bin")
} else if let Some(home) = std::env::var_os("HOME") {
PathBuf::from(home).join(".cargo").join("bin")
} else {
return None;
};
let p = bin_dir.join("supermachine-worker");
if p.is_file() {
Some(p)
} else {
None
}
}
fn path_walk_worker() -> Option<PathBuf> {
let path = std::env::var_os("PATH")?;
for dir in std::env::split_paths(&path) {
let p = dir.join("supermachine-worker");
if p.is_file() {
return Some(p);
}
}
None
}
pub fn check_self_has_hvf_entitlement() -> Result<(), String> {
static CACHED: OnceLock<Result<(), String>> = OnceLock::new();
CACHED
.get_or_init(check_self_has_hvf_entitlement_uncached)
.clone()
}
fn check_self_has_hvf_entitlement_uncached() -> Result<(), String> {
let exe = std::env::current_exe().map_err(|e| {
format!(
"could not resolve current_exe to check HVF entitlement: {e} \
(your binary may need to be codesigned with \
`cargo supermachine build`)"
)
})?;
let output = Command::new("codesign")
.args(["--display", "--entitlements", "-", "--xml"])
.arg(&exe)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
let output = match output {
Ok(o) => o,
Err(_) => return Ok(()), };
if !output.status.success() {
return Err(missing_entitlement_message(&exe));
}
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.contains("com.apple.security.hypervisor") {
Ok(())
} else {
Err(missing_entitlement_message(&exe))
}
}
fn missing_entitlement_message(exe: &Path) -> String {
format!(
"this binary lacks the `com.apple.security.hypervisor` entitlement, \
so `Vm::start` cannot call `hv_vm_create` (it would return HV_DENIED).\n\
\n\
Two ways to fix:\n\
\n\
(a) Use `Image::acquire` / `Image::acquire_with` instead of \
`Vm::start`. The library spawns a pre-signed \
`supermachine-worker` subprocess that handles HVF on your \
behalf, so your own binary never calls into HVF and doesn't \
need codesigning. This is the recommended path for embedders.\n\
(b) Build your binary with the bundled cargo plugin:\n\
cargo supermachine build --release\n\
which wraps `cargo build` and codesigns the output with the \
HVF entitlement. Use this if you specifically want the \
in-process VM thread (`Vm::start`).\n\
\n\
Path: {}",
exe.display()
)
}
fn sentinel_path() -> Option<PathBuf> {
let base = if let Some(d) = std::env::var_os("XDG_DATA_HOME") {
PathBuf::from(d)
} else if let Some(h) = std::env::var_os("HOME") {
PathBuf::from(h).join(".local/share")
} else {
return None;
};
Some(
base.join("supermachine")
.join(format!("v{}", env!("CARGO_PKG_VERSION")))
.join(".worker-signed"),
)
}