use crate::error::Error;
use crate::session::{Session, SessionEnd};
use crate::terminal::{enter_raw_mode, install_signal_handlers, restore_terminal};
use crate::Config;
#[derive(Debug, Clone)]
pub struct RunOutput {
pub exit_code: i32,
pub output: String,
}
pub fn run(config: &Config) -> Result<RunOutput, Error> {
if let Some(p) = &config.build_snapshot {
crate::vz::snapshot::build(config, p)?;
return Ok(RunOutput {
exit_code: 0,
output: String::new(),
});
}
if let Some(p) = &config.resume_snapshot {
let code = crate::vz::snapshot::resume(config, p)?;
return Ok(RunOutput {
exit_code: code,
output: String::new(),
});
}
install_signal_handlers();
enter_raw_mode();
let session = match Session::start(config) {
Ok(s) => s,
Err(e) => {
restore_terminal();
return Err(e);
}
};
if !config.quiet {
eprint_banner(config, session.cmdline(), session.vsock_port());
}
let end = session.wait();
restore_terminal();
drop(session);
let exit_code = match end {
SessionEnd::Exited(code) => {
if !config.quiet {
eprintln!("\r\n[vmette] guest stopped (exit {})\r", code);
}
code
}
SessionEnd::TimedOut => {
if !config.quiet {
eprintln!(
"\r\n[vmette] timeout {}s reached; guest force-stopped (exit 124)\r",
config.timeout_seconds.unwrap_or(0)
);
}
124
}
SessionEnd::Stopped => {
if !config.quiet {
eprintln!("\r\n[vmette] guest stopped (exit 0)\r");
}
0
}
SessionEnd::Error(msg) => {
eprintln!("\r\n[vmette] guest stopped with error: {}\r", msg);
1
}
};
Ok(RunOutput {
exit_code,
output: String::new(),
})
}
fn eprint_banner(config: &Config, cmdline: &str, vsock_port: Option<u32>) {
let rootfs = match &config.rootfs {
Some(crate::Rootfs::Block(rb)) => {
format!("{} ({} block, ro)", rb.path.display(), rb.fstype)
}
Some(crate::Rootfs::Share(r)) => format!(
"{}{}",
r.path.display(),
if r.read_only { " (ro)" } else { "" }
),
None => "(none)".into(),
};
let vsock = match vsock_port {
None => "(disabled)".into(),
Some(p) => p.to_string(),
};
let overlay = match config.scratch_mib {
Some(mib) => format!("{} MiB ephemeral ext4 disk", mib),
None => "tmpfs (RAM-backed)".into(),
};
eprintln!(
"[vmette] kernel {}\n\
[vmette] initramfs {}\n\
[vmette] cmdline {}\n\
[vmette] rootfs {}\n\
[vmette] shares {}\n\
[vmette] disks {}\n\
[vmette] overlay {}\n\
[vmette] exec {}\n\
[vmette] vsock-port {}\n\
[vmette] switch-root {}\n\
[vmette] net {}\n\
[vmette] timeout {}s\n\
[vmette] vcpus {}, memMiB {}\n",
config.kernel.display(),
config.initramfs.display(),
cmdline,
rootfs,
config.shares.len(),
config.disks.len(),
overlay,
config.exec_cmd.as_deref().unwrap_or("(none — interactive)"),
vsock,
if config.switch_root { "yes" } else { "no" },
if config.net { "yes (NAT)" } else { "no" },
config.timeout_seconds.unwrap_or(0),
config.vcpus,
config.mem_mib,
);
}