1mod assert;
2pub mod image;
3pub mod machine;
4pub mod ports;
5
6use std::process::Stdio;
7
8use anyhow::Result;
9
10pub struct HostMemory {
12 pub total_mb: u64,
13 pub available_mb: u64,
14 pub swap_total_mb: u64,
15 pub swap_used_mb: u64,
16}
17
18pub fn read_host_memory() -> Option<HostMemory> {
20 let meminfo = std::fs::read_to_string("/proc/meminfo").ok()?;
21 let parse_kb = |key: &str| -> Option<u64> {
22 meminfo
23 .lines()
24 .find(|l| l.starts_with(key))
25 .and_then(|l| l.split_whitespace().nth(1))
26 .and_then(|v| v.parse::<u64>().ok())
27 };
28 let total_kb = parse_kb("MemTotal:")?;
29 let avail_kb = parse_kb("MemAvailable:")?;
30 let swap_total_kb = parse_kb("SwapTotal:").unwrap_or(0);
31 let swap_free_kb = parse_kb("SwapFree:").unwrap_or(0);
32 Some(HostMemory {
33 total_mb: total_kb / 1024,
34 available_mb: avail_kb / 1024,
35 swap_total_mb: swap_total_kb / 1024,
36 swap_used_mb: swap_total_kb.saturating_sub(swap_free_kb) / 1024,
37 })
38}
39
40pub fn check_prerequisites(use_kvm: bool) -> Result<()> {
42 let required = [
43 "qemu-system-aarch64",
44 "qemu-img",
45 "ssh",
46 "scp",
47 "ssh-keygen",
48 "curl",
49 ];
50 let mut missing: Vec<&str> = required
51 .iter()
52 .filter(|c| !has_command(c))
53 .copied()
54 .collect();
55
56 if !has_any_iso_tool() {
57 missing.push("genisoimage");
58 }
59
60 if !missing.is_empty() {
61 anyhow::bail!(
62 "missing required tools: {}\n\
63 Install with:\n \
64 sudo apt install qemu-system-arm qemu-utils qemu-efi-aarch64 \\\n \
65 genisoimage openssh-client curl # Debian/Ubuntu\n \
66 sudo dnf install qemu-system-aarch64 qemu-img edk2-aarch64 \\\n \
67 genisoimage openssh-clients curl # Fedora\n \
68 sudo pacman -S qemu-system-aarch64 qemu-img edk2-aarch64 \\\n \
69 cdrtools openssh curl # Arch",
70 missing.join(", ")
71 );
72 }
73
74 if use_kvm {
75 check_kvm()?;
76 }
77
78 Ok(())
79}
80
81fn has_command(cmd: &str) -> bool {
82 std::process::Command::new("which")
83 .arg(cmd)
84 .stdout(Stdio::null())
85 .stderr(Stdio::null())
86 .status()
87 .map(|s| s.success())
88 .unwrap_or(false)
89}
90
91fn has_any_iso_tool() -> bool {
92 ["genisoimage", "mkisofs"]
93 .iter()
94 .any(|cmd| has_command(cmd))
95}
96
97fn check_kvm() -> Result<()> {
99 let kvm = std::path::Path::new("/dev/kvm");
100 if !kvm.exists() {
101 anyhow::bail!(
102 "/dev/kvm not found — KVM is not available on this machine.\n\
103 Run with --no-kvm to use software emulation (slower), or \
104 run on a machine with KVM support."
105 );
106 }
107 let accessible = std::fs::File::open(kvm).is_ok();
108 if !accessible {
109 anyhow::bail!(
110 "/dev/kvm exists but is not accessible — permission denied.\n\
111 Add your user to the kvm group and re-login:\n \
112 sudo usermod -aG kvm $USER\n \
113 # then log out and back in, or run: newgrp kvm"
114 );
115 }
116 Ok(())
117}
118
119pub fn accel_args() -> &'static [&'static str] {
121 &["-enable-kvm"]
122}