Files
sausage/userland/ssh/src/main.rs
T

55 lines
1.6 KiB
Rust

//! One noninteractive SSH command with host-mediated authentication.
use clap::Parser;
use std::{
io::{self, Read, Write},
time::{Duration, Instant},
};
#[derive(Parser)]
struct Args {
#[arg(short = 'p', default_value_t = 22)]
port: u16,
#[arg(short = 'l')]
user: Option<String>,
#[arg(short = 'o')]
options: Vec<String>,
host: String,
#[arg(required = true, trailing_var_arg = true)]
command: Vec<String>,
}
fn run(args: Args) -> io::Result<u32> {
let mut accept_new = false;
for option in args.options {
match option.as_str() {
"StrictHostKeyChecking=accept-new" => accept_new = true,
"StrictHostKeyChecking=yes" => accept_new = false,
_ => return Err(io::Error::other("unsupported SSH option")),
}
}
let (user, host) = match args.host.split_once('@') {
Some((user, host)) => (Some(user), host),
None => (args.user.as_deref(), args.host.as_str()),
};
let session = sausage_ssh::identity::connect(host, args.port, user, accept_new)?;
let mut input = Vec::new();
io::stdin().read_to_end(&mut input)?;
let output = session.execute_input(
args.command.join(" ").as_bytes(),
&input,
usize::MAX,
Instant::now() + Duration::from_secs(300),
)?;
io::stdout().write_all(&output.stdout)?;
io::stderr().write_all(&output.stderr)?;
Ok(output.exit_code.unwrap_or(255))
}
fn main() {
let status = match run(Args::parse()) {
Ok(status) => status,
Err(error) => {
eprintln!("{error}");
255
}
};
std::process::exit(status.min(255) as i32);
}