116 lines
2.8 KiB
Rust
116 lines
2.8 KiB
Rust
use clap::{Parser, Subcommand};
|
|
use std::{path::PathBuf, str::FromStr};
|
|
|
|
/// VM controller for `AirgapOS`
|
|
#[derive(Parser, Clone, Debug)]
|
|
pub struct App {
|
|
// global options go here
|
|
#[arg(long, global = true, default_value = "/var/run/netvm.pid")]
|
|
pub lockfile: PathBuf,
|
|
|
|
//
|
|
#[command(subcommand)]
|
|
pub subcommand: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand, Clone, Debug)]
|
|
pub enum Commands {
|
|
/// Start a headless VM in the background.
|
|
Start {
|
|
#[arg(long, default_value = "1G")]
|
|
memory: String,
|
|
},
|
|
|
|
/// Stop a headless VM.
|
|
Stop,
|
|
|
|
/// Open a VM in the foreground with a serial terminal.
|
|
Shell,
|
|
|
|
/// Get the hostname and uptime of a running VM.
|
|
Status,
|
|
|
|
/// Attach a USB device to a running VM.
|
|
Attach {
|
|
/// The device to attach.
|
|
device: DeviceIdentifier,
|
|
},
|
|
|
|
/// Push a file to a currently running VM.
|
|
Push {
|
|
/// The local path to push.
|
|
local_path: PathBuf,
|
|
|
|
/// The remote path to push to.
|
|
remote_path: PathBuf,
|
|
},
|
|
|
|
/// Pull a file from a currently running VM.
|
|
Pull {
|
|
/// The remote path to pull.
|
|
remote_path: PathBuf,
|
|
|
|
/// The local path to pull to.
|
|
local_path: PathBuf,
|
|
},
|
|
|
|
/// Run a command in a currently running VM.
|
|
Run {
|
|
/// The command to run.
|
|
command: String,
|
|
|
|
/// Arguments to pass to the running command.
|
|
args: Vec<String>,
|
|
},
|
|
}
|
|
|
|
/// An attachable USB device identifier.
|
|
#[derive(Clone, Debug)]
|
|
pub struct DeviceIdentifier {
|
|
/// The Vendor ID.
|
|
pub vendor_id: u16,
|
|
|
|
/// The Device ID.
|
|
pub device_id: u16,
|
|
}
|
|
|
|
/// An error encountered while parsing a USB device identifier
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum DeviceIdentifierFromStrError {
|
|
#[error("could not split input by colon; expected output similar to `lsusb`")]
|
|
CouldNotSplitByColon,
|
|
|
|
#[error("could not parse hex from vendor or device ID")]
|
|
Hex(#[from] hex::FromHexError),
|
|
|
|
#[error("could not decode u64 from bytes: {0:?}")]
|
|
BadBytes(Vec<u8>),
|
|
}
|
|
|
|
impl FromStr for DeviceIdentifier {
|
|
type Err = DeviceIdentifierFromStrError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let Some((first, last)) = s.split_once(':') else {
|
|
return Err(DeviceIdentifierFromStrError::CouldNotSplitByColon);
|
|
};
|
|
|
|
let vendor_id = u16::from_be_bytes(
|
|
hex::decode(first)?
|
|
.try_into()
|
|
.map_err(DeviceIdentifierFromStrError::BadBytes)?,
|
|
);
|
|
|
|
let device_id = u16::from_be_bytes(
|
|
hex::decode(last)?
|
|
.try_into()
|
|
.map_err(DeviceIdentifierFromStrError::BadBytes)?,
|
|
);
|
|
|
|
Ok(Self {
|
|
vendor_id,
|
|
device_id,
|
|
})
|
|
}
|
|
}
|