allow input in `trvmctl run`

This commit is contained in:
Ryan Heywood 2025-05-12 00:01:10 -04:00
parent cfe35b3edb
commit bc41781df0
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
3 changed files with 28 additions and 3 deletions

View File

@ -56,6 +56,10 @@ pub enum Commands {
},
/// Run a command in a currently running VM.
///
/// If standard input is not a terminal, all standard input is collected before invoking the
/// program. All standard output is collected before returning from the function. Interactive
/// programs are not possible at this time..
Run {
/// The command to run.
command: String,

View File

@ -2,13 +2,14 @@
use clap::Parser;
use eyre::WrapErr;
use std::io::Write;
use std::io::{IsTerminal, Read, Write};
mod cli;
mod vm;
use vm::{SpawnArguments, VirtualMachine};
#[allow(clippy::too_many_lines)]
fn main() -> eyre::Result<()> {
color_eyre::install()?;
@ -54,7 +55,7 @@ fn main() -> eyre::Result<()> {
.ok_or(eyre::eyre!("no hostname"))?
.as_str()
.ok_or(eyre::eyre!("hostname is not str"))?;
let uptime = vm.run_command("uptime", [])?;
let uptime = vm.run_command("uptime", [], None)?;
eprintln!("hostname: {hostname}");
eprint!("{}", String::from_utf8_lossy(&uptime.0));
}
@ -91,7 +92,24 @@ fn main() -> eyre::Result<()> {
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)?;
let mut stdin = std::io::stdin();
let buffered_stdin = if stdin.is_terminal() {
None
} else {
let mut bytes = vec![];
stdin
.read_to_end(&mut bytes)
.context(eyre::eyre!("could not read input"))?;
if bytes.is_empty() {
None
} else {
Some(bytes)
}
};
let (response, exit_code) =
vm.run_command(&command, args, buffered_stdin.as_deref())?;
std::io::stdout().write_all(&response)?;
std::process::exit(
i32::try_from(exit_code).context(eyre::eyre!("bad PID: pid < i32::MAX << 1"))?,

View File

@ -574,13 +574,16 @@ impl VirtualMachine {
&mut self,
command: &str,
args: impl IntoIterator<Item = String>,
stdin: Option<&[u8]>,
) -> Result<(Vec<u8>, u64)> {
let args = args.into_iter().collect::<Vec<_>>();
let input = stdin.map(|bytes| BASE64_STANDARD.encode(bytes));
let payload = serde_json::json!({
"path": command,
"arg": args,
"capture-output": true,
"input-data": input,
});
let bar = spinner(format!("Running: {command:?} {args:?}"));