use std::{net::ToSocketAddrs, path::Path, time::Duration};
use ssh::{ExecBroker, SessionBroker, SshError, SshResult};
use super::remote_find_free_port::remote_find_free_port;
pub struct RemoteSshSession {
session: SessionBroker,
latest_executed_cmd: Option<ExecutedCmd>,
}
impl RemoteSshSession {
pub fn new<A>(
username: &str,
addr: A,
timeout: Option<Duration>,
private_key_path: Option<&Path>,
private_key_dir: Option<&Path>,
) -> Result<Self, SshError>
where
A: ToSocketAddrs,
{
let ssh_private_key =
crate::ssh::private_key::get_ssh_private_key_path(private_key_path, private_key_dir)
.expect("Failed locating SSH private key path");
log::trace!("Private key path: {ssh_private_key:?}");
let passwd = super::util::get_remote_password_from_env();
let session = ssh::create_session()
.username(username)
.password(passwd.as_deref().unwrap_or("root"))
.private_key_path(ssh_private_key)
.connect_with_timeout(addr, timeout)?;
Ok(Self {
session: session.run_backend(),
latest_executed_cmd: None,
})
}
pub fn find_free_port(&mut self, start_port: u16, end_port: u16) -> anyhow::Result<u16> {
remote_find_free_port(&mut self.session, start_port, end_port)
}
fn open_run_exec(&mut self, cmd: &str) -> SshResult<ExecutedCmd> {
let executed = ExecutedCmd::new(&mut self.session, cmd)?;
Ok(executed)
}
pub fn run_cmd(&mut self, cmd: &str) -> SshResult<u32> {
let executed = self.open_run_exec(cmd)?;
let exit_status = executed.exit_status()?;
log::trace!("Remote command exit status: {exit_status}");
if let Some(terminate_msg) = executed.terminate_msg() {
log::trace!("Remote command terminate message: {terminate_msg}");
}
self.latest_executed_cmd = Some(executed);
Ok(exit_status)
}
pub fn run_cmd_get_result(&mut self, cmd: &str) -> SshResult<Vec<u8>> {
let mut executed = self.open_run_exec(cmd)?;
let exit_status = executed.exit_status()?;
log::trace!("Remote command exit status: {exit_status}");
if let Some(terminate_msg) = executed.terminate_msg() {
log::trace!("Remote command terminate message: {terminate_msg}");
}
let res = executed.results()?;
self.latest_executed_cmd = Some(executed);
Ok(res)
}
pub fn get_cmd_output(&mut self) -> Option<Vec<u8>> {
let mut exec = self.latest_executed_cmd.take()?;
exec.results().ok()
}
pub fn close(self) {
self.session.close()
}
}
pub struct ExecutedCmd {
exec_broker: ExecBroker,
}
impl ExecutedCmd {
pub fn new(session: &mut SessionBroker, cmd: &str) -> SshResult<Self> {
let mut exec = session.open_exec()?;
exec.send_command(cmd)?;
Ok(Self { exec_broker: exec })
}
pub fn exit_status(&self) -> SshResult<u32> {
self.exec_broker.exit_status()
}
pub fn terminate_msg(&self) -> Option<String> {
let tm = self.exec_broker.terminate_msg().ok()?;
if tm.is_empty() {
None
} else {
Some(tm)
}
}
pub fn results(&mut self) -> SshResult<Vec<u8>> {
self.exec_broker.get_result()
}
}