use std::path::{Path, PathBuf};
use std::sync::OnceLock;
pub const ENTITLEMENTS_PLIST: &str =
include_str!("../entitlements.plist");
#[derive(Debug, Clone)]
pub struct AssetPaths {
pub kernel: Option<PathBuf>,
pub init_oci_bin: Option<PathBuf>,
pub init_oci_src: Option<PathBuf>,
pub supermachine_agent: Option<PathBuf>,
}
impl AssetPaths {
pub fn discover() -> Self {
if let Some(dir) = std::env::var_os("SUPERMACHINE_ASSETS_DIR") {
let dir = PathBuf::from(dir);
return Self::from_dir(&dir);
}
let exe = std::env::current_exe().ok();
if let Some(exe) = exe.as_deref() {
if let Some(macos_dir) = exe.parent() {
if macos_dir.file_name().and_then(|s| s.to_str()) == Some("MacOS") {
if let Some(contents) = macos_dir.parent() {
let res = contents.join("Resources");
let probe = Self::from_dir(&res);
if probe.kernel.is_some() {
return probe;
}
}
}
}
}
if let Some(exe) = exe.as_deref() {
for ancestor in exe.ancestors() {
let share = ancestor.join("share/supermachine");
if share.join("kernel").is_file() {
return Self::from_dir(&share);
}
}
}
if let Some(exe) = exe.as_deref() {
for ancestor in exe.ancestors() {
let kernel_crate = ancestor.join("crates/supermachine-kernel");
let main_crate = ancestor.join("crates/supermachine");
let agent_crate = ancestor.join("crates/supermachine-guest-agent");
if kernel_crate.join("kernel").is_file() {
return Self {
kernel: Some(kernel_crate.join("kernel")),
init_oci_bin: Some(main_crate.join("oci/init-oci"))
.filter(|p| p.is_file()),
init_oci_src: Some(main_crate.join("oci/init-oci.c"))
.filter(|p| p.is_file()),
supermachine_agent: Some(agent_crate.join(
"target/aarch64-unknown-linux-musl/release/supermachine-agent",
))
.filter(|p| p.is_file()),
};
}
}
}
if let Some(dir) = ensure_bundled_extracted() {
let probe = Self::from_dir(&dir);
if probe.kernel.is_some() {
return probe;
}
}
Self {
kernel: None,
init_oci_bin: None,
init_oci_src: None,
supermachine_agent: None,
}
}
pub fn from_dir(dir: &Path) -> Self {
Self {
kernel: Some(dir.join("kernel")).filter(|p| p.is_file()),
init_oci_bin: Some(dir.join("init-oci")).filter(|p| p.is_file()),
init_oci_src: Some(dir.join("init-oci.c")).filter(|p| p.is_file()),
supermachine_agent: Some(dir.join("supermachine-agent"))
.filter(|p| p.is_file()),
}
}
pub fn from_app_bundle(app: &Path) -> Self {
Self::from_dir(&app.join("Contents/Resources"))
}
}
fn ensure_bundled_extracted() -> Option<PathBuf> {
let dir = bundled_assets_dir()?;
let kernel_dst = dir.join("kernel");
let init_dst = dir.join("init-oci");
let agent_dst = dir.join("supermachine-agent");
let kernel_hash_dst = dir.join("kernel.hash");
let init_hash_dst = dir.join("init-oci.hash");
let agent_hash_dst = dir.join("supermachine-agent.hash");
let want_kernel = kernel_bytes_hash();
let want_init = init_oci_bytes_hash();
let want_agent = supermachine_agent_bytes_hash();
if kernel_dst.is_file()
&& init_dst.is_file()
&& agent_dst.is_file()
&& read_hash_file(&kernel_hash_dst).as_deref() == Some(want_kernel)
&& read_hash_file(&init_hash_dst).as_deref() == Some(want_init)
&& read_hash_file(&agent_hash_dst).as_deref() == Some(want_agent)
{
return Some(dir);
}
if std::fs::create_dir_all(&dir).is_err() {
return None;
}
if !kernel_dst.is_file()
|| read_hash_file(&kernel_hash_dst).as_deref() != Some(want_kernel)
{
if !atomic_write(&dir, &kernel_dst, "kernel.partial", |tmp| {
std::fs::write(tmp, supermachine_kernel::KERNEL_BYTES)
}) {
return None;
}
if !atomic_write_str(&dir, &kernel_hash_dst, "kernel.hash.partial", want_kernel)
{
return None;
}
}
if !init_dst.is_file()
|| read_hash_file(&init_hash_dst).as_deref() != Some(want_init)
{
if !atomic_write(&dir, &init_dst, "init-oci.partial", |tmp| {
supermachine_kernel::extract_init_oci_to(tmp)
}) {
return None;
}
if !atomic_write_str(&dir, &init_hash_dst, "init-oci.hash.partial", want_init)
{
return None;
}
}
if !agent_dst.is_file()
|| read_hash_file(&agent_hash_dst).as_deref() != Some(want_agent)
{
if !atomic_write(&dir, &agent_dst, "supermachine-agent.partial", |tmp| {
supermachine_kernel::extract_supermachine_agent_to(tmp)
}) {
return None;
}
if !atomic_write_str(
&dir,
&agent_hash_dst,
"supermachine-agent.hash.partial",
want_agent,
) {
return None;
}
}
Some(dir)
}
fn atomic_write<F>(dir: &Path, dst: &Path, tmp_name: &str, write: F) -> bool
where
F: FnOnce(&Path) -> std::io::Result<()>,
{
let tmp = dir.join(tmp_name);
if write(&tmp).is_err() {
let _ = std::fs::remove_file(&tmp);
return false;
}
if std::fs::rename(&tmp, dst).is_err() {
let _ = std::fs::remove_file(&tmp);
return false;
}
true
}
fn atomic_write_str(dir: &Path, dst: &Path, tmp_name: &str, contents: &str) -> bool {
atomic_write(dir, dst, tmp_name, |tmp| std::fs::write(tmp, contents))
}
fn read_hash_file(path: &Path) -> Option<String> {
let bytes = std::fs::read(path).ok()?;
if bytes.len() > 128 {
return None;
}
Some(String::from_utf8(bytes).ok()?.trim().to_owned())
}
fn kernel_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::KERNEL_BYTES))
}
fn init_oci_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::INIT_OCI_BYTES))
}
fn supermachine_agent_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::SUPERMACHINE_AGENT_BYTES))
}
fn short_sha256(bytes: &[u8]) -> String {
let digest = ring::digest::digest(&ring::digest::SHA256, bytes);
let mut s = String::with_capacity(12);
for b in &digest.as_ref()[..6] {
s.push_str(&format!("{:02x}", b));
}
s
}
fn bundled_assets_dir() -> 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"))),
)
}