use color_eyre::eyre;
use color_eyre::Result;
use std::path::Path;
#[cfg(target_os = "linux")]
use std::os::fd::OwnedFd;
use crate::run::RunOptions;
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub fn try_vmm_backends(
env_root: &Path,
run_options: &RunOptions,
guest_command: &Path,
vm_socket_path: Option<&Path>,
vmm_order: &[String],
#[cfg(target_os = "linux")] vm_daemon_ready_fd: Option<&OwnedFd>,
#[cfg(not(target_os = "linux"))] _vm_daemon_ready_fd: Option<&()>,
) -> Result<()> {
#[cfg(target_os = "linux")]
let _ = crate::qemu::ensure_vmm_log_dir();
let order: Vec<String> = if !vmm_order.is_empty() {
vmm_order.to_vec()
} else {
default_vmm_order()
};
let mut last_err: Option<eyre::Report> = None;
for backend in &order {
match backend.as_str() {
"libkrun" => {
#[cfg(target_os = "linux")]
{
if let Err(e) = try_krun_backend(env_root, run_options, guest_command, vm_daemon_ready_fd) {
log::warn!("libkrun backend failed, will try next VMM if any: {}", e);
last_err = Some(e);
continue;
}
}
#[cfg(not(target_os = "linux"))]
{
if let Err(e) = try_krun_backend(env_root, run_options, guest_command) {
log::warn!("libkrun backend failed, will try next VMM if any: {}", e);
last_err = Some(e);
continue;
}
}
return Ok(());
}
"qemu" => {
#[cfg(target_os = "linux")]
{
if let Err(e) = try_qemu_backend(env_root, run_options, guest_command, vm_socket_path, vm_daemon_ready_fd) {
log::warn!("qemu backend failed, will try next VMM if any: {}", e);
last_err = Some(e);
continue;
}
}
#[cfg(not(target_os = "linux"))]
{
if let Err(e) = try_qemu_backend(env_root, run_options, guest_command, vm_socket_path) {
log::warn!("qemu backend failed, will try next VMM if any: {}", e);
last_err = Some(e);
continue;
}
}
return Ok(());
}
other => {
log::warn!("Unknown VMM backend '{}' in --vmm list, skipping", other);
}
}
}
if let Some(e) = last_err {
return Err(eyre::eyre!(
"All requested VMM backends failed (order: {:?}); last error: {}",
order,
e
));
}
Err(eyre::eyre!(
"No usable VMM backend found for order {:?}. \
Specify --vmm=libkrun,qemu or --vmm=qemu and ensure dependencies are installed.",
order
))
}
#[cfg_attr(not(target_os = "linux"), allow(unused_mut))]
fn default_vmm_order() -> Vec<String> {
let mut order = Vec::new();
#[cfg(feature = "libkrun")]
{
order.push("libkrun".to_string());
}
#[cfg(target_os = "linux")]
{
order.push("qemu".to_string());
}
order
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub fn try_krun_backend(
env_root: &Path,
run_options: &RunOptions,
guest_command: &Path,
#[cfg(target_os = "linux")] vm_daemon_ready_fd: Option<&OwnedFd>,
) -> Result<()> {
#[cfg(feature = "libkrun")]
{
log::debug!("Trying VMM backend: libkrun");
#[cfg(target_os = "linux")]
let result = crate::libkrun::run_command_in_krun(env_root, run_options, guest_command, vm_daemon_ready_fd);
#[cfg(not(target_os = "linux"))]
let result = crate::libkrun::run_command_in_krun(env_root, run_options, guest_command);
match result {
Ok(()) => Ok(()),
Err(e) => {
let msg = e.to_string();
if msg.contains("HostAddressNotAvailable") || msg.contains("GuestMemoryMmap") {
return Err(eyre::eyre!(
"{}. Hint: host address-space layout can cause this; try --memory 4096 or EPKG_VM_MEMORY=4096, or use --vmm=qemu.",
msg
));
}
Err(e)
}
}
}
#[cfg(not(feature = "libkrun"))]
{
#[cfg(target_os = "linux")]
let _ = (env_root, run_options, guest_command, vm_daemon_ready_fd);
#[cfg(not(target_os = "linux"))]
let _ = (env_root, run_options, guest_command);
log::debug!("VMM backend 'libkrun' requested but libkrun feature is disabled; skipping");
Err(eyre::eyre!("libkrun feature disabled"))
}
}
#[cfg(target_os = "linux")]
pub fn try_qemu_backend(
env_root: &Path,
run_options: &RunOptions,
guest_command: &Path,
vm_socket_path: Option<&Path>,
vm_daemon_ready_fd: Option<&OwnedFd>,
) -> Result<()> {
log::debug!("Trying VMM backend: qemu");
crate::qemu::run_command_in_qemu(env_root, run_options, guest_command, vm_socket_path, vm_daemon_ready_fd)
}
#[cfg(not(target_os = "linux"))]
#[allow(dead_code)]
pub fn try_qemu_backend(
_env_root: &Path,
_run_options: &RunOptions,
_guest_command: &Path,
_vm_socket_path: Option<&Path>,
) -> Result<()> {
log::debug!("VMM backend 'qemu' is only supported on Linux");
Err(eyre::eyre!("qemu backend is only supported on Linux"))
}