guestctl/src/main.rs

104 lines
3.7 KiB
Rust

#![allow(clippy::redundant_else)]
use clap::Parser;
use eyre::WrapErr;
use std::io::Write;
mod cli;
mod vm;
use vm::{SpawnArguments, VirtualMachine};
fn main() -> eyre::Result<()> {
color_eyre::install()?;
let mut args = std::env::args().collect::<Vec<_>>();
let ignore_opts = String::from("--");
if let Some(run_pos) = args.iter().position(|e| e == "run") {
if !args.contains(&ignore_opts) && args.get(run_pos + 1).is_some_and(|arg| arg != "--help")
{
args.insert(run_pos + 1, ignore_opts);
}
}
let opts = cli::App::parse_from(args);
match opts.subcommand {
cli::Commands::Start { memory } => {
let spawn_args = SpawnArguments {
memory: memory.clone(),
..Default::default()
};
let vm = VirtualMachine::start(spawn_args)?;
let pid = vm.pid();
std::fs::write(&opts.lockfile, pid.to_string()).with_context(|| {
format!(
"could not write PID {pid} to {lockfile}",
lockfile = opts.lockfile.display(),
)
})?;
}
cli::Commands::Stop => {
let spawn_arguments = SpawnArguments::default();
let vm = VirtualMachine::load(spawn_arguments, None)?;
vm.kill()?;
}
cli::Commands::Shell => {
todo!("custom args to starting a VM and piping stdin/stdout are not yet implemented");
}
cli::Commands::Status => {
let spawn_arguments = SpawnArguments::default();
let mut vm = VirtualMachine::load(spawn_arguments, None)?;
let result = vm.execute("guest-get-host-name", serde_json::json!({}))?;
let hostname = result
.get("host-name")
.ok_or(eyre::eyre!("no hostname"))?
.as_str()
.ok_or(eyre::eyre!("hostname is not str"))?;
let uptime = vm.run_command("uptime", [])?;
eprintln!("hostname: {hostname}");
eprint!("{}", String::from_utf8_lossy(&uptime.0));
}
cli::Commands::Attach { device } => {
let spawn_arguments = SpawnArguments::default();
let mut vm = VirtualMachine::load(spawn_arguments, None)?;
vm.execute_host("qmp_capabilities", serde_json::json!({}))?;
vm.execute_host(
"device_add",
serde_json::json!({
"driver": "usb-host",
"bus": "usb.0",
"vendorid": device.vendor_id,
"productid": device.device_id,
}),
)?;
}
cli::Commands::Push {
local_path,
remote_path,
} => {
let spawn_arguments = SpawnArguments::default();
let mut vm = VirtualMachine::load(spawn_arguments, None)?;
vm.push(local_path, remote_path)?;
}
cli::Commands::Pull {
remote_path,
local_path,
} => {
let spawn_arguments = SpawnArguments::default();
let mut vm = VirtualMachine::load(spawn_arguments, None)?;
vm.pull(remote_path, local_path)?;
}
cli::Commands::Run { command, args } => {
let spawn_arguments = SpawnArguments::default();
let mut vm = VirtualMachine::load(spawn_arguments, None)?;
let (response, exit_code) = vm.run_command(&command, args)?;
std::io::stdout().write_all(&response)?;
std::process::exit(
i32::try_from(exit_code).context(eyre::eyre!("bad PID: pid < i32::MAX << 1"))?,
);
}
}
Ok(())
}