quick_file_transfer/ssh/
remote_session.rs1use std::{net::ToSocketAddrs, path::Path, time::Duration};
2
3use ssh::{ExecBroker, SessionBroker, SshError, SshResult};
4
5use super::remote_find_free_port::remote_find_free_port;
6
7pub struct RemoteSshSession {
8 session: SessionBroker,
9 latest_executed_cmd: Option<ExecutedCmd>,
10}
11
12impl RemoteSshSession {
13 pub fn new<A>(
14 username: &str,
15 addr: A,
16 timeout: Option<Duration>,
17 private_key_path: Option<&Path>,
18 private_key_dir: Option<&Path>,
19 ) -> Result<Self, SshError>
20 where
21 A: ToSocketAddrs,
22 {
23 let ssh_private_key =
24 crate::ssh::private_key::get_ssh_private_key_path(private_key_path, private_key_dir)
25 .expect("Failed locating SSH private key path");
26 log::trace!("Private key path: {ssh_private_key:?}");
27 let passwd = super::util::get_remote_password_from_env();
28
29 let session = ssh::create_session()
30 .username(username)
31 .password(passwd.as_deref().unwrap_or("root"))
32 .private_key_path(ssh_private_key)
33 .connect_with_timeout(addr, timeout)?;
34 Ok(Self {
35 session: session.run_backend(),
36 latest_executed_cmd: None,
37 })
38 }
39
40 pub fn find_free_port(&mut self, start_port: u16, end_port: u16) -> anyhow::Result<u16> {
41 remote_find_free_port(&mut self.session, start_port, end_port)
42 }
43
44 fn open_run_exec(&mut self, cmd: &str) -> SshResult<ExecutedCmd> {
45 let executed = ExecutedCmd::new(&mut self.session, cmd)?;
46 Ok(executed)
47 }
48
49 pub fn run_cmd(&mut self, cmd: &str) -> SshResult<u32> {
53 let executed = self.open_run_exec(cmd)?;
54 let exit_status = executed.exit_status()?;
55 log::trace!("Remote command exit status: {exit_status}");
56 if let Some(terminate_msg) = executed.terminate_msg() {
57 log::trace!("Remote command terminate message: {terminate_msg}");
58 }
59
60 self.latest_executed_cmd = Some(executed);
61
62 Ok(exit_status)
63 }
64
65 pub fn run_cmd_get_result(&mut self, cmd: &str) -> SshResult<Vec<u8>> {
69 let mut executed = self.open_run_exec(cmd)?;
70 let exit_status = executed.exit_status()?;
71 log::trace!("Remote command exit status: {exit_status}");
72 if let Some(terminate_msg) = executed.terminate_msg() {
73 log::trace!("Remote command terminate message: {terminate_msg}");
74 }
75
76 let res = executed.results()?;
77 self.latest_executed_cmd = Some(executed);
78 Ok(res)
79 }
80
81 pub fn get_cmd_output(&mut self) -> Option<Vec<u8>> {
85 let mut exec = self.latest_executed_cmd.take()?;
86 exec.results().ok()
87 }
88
89 pub fn close(self) {
91 self.session.close()
92 }
93}
94
95pub struct ExecutedCmd {
96 exec_broker: ExecBroker,
97}
98
99impl ExecutedCmd {
100 pub fn new(session: &mut SessionBroker, cmd: &str) -> SshResult<Self> {
101 let mut exec = session.open_exec()?;
102 exec.send_command(cmd)?;
103 Ok(Self { exec_broker: exec })
104 }
105
106 pub fn exit_status(&self) -> SshResult<u32> {
107 self.exec_broker.exit_status()
108 }
109
110 pub fn terminate_msg(&self) -> Option<String> {
111 let tm = self.exec_broker.terminate_msg().ok()?;
112 if tm.is_empty() {
113 None
114 } else {
115 Some(tm)
116 }
117 }
118
119 pub fn results(&mut self) -> SshResult<Vec<u8>> {
121 self.exec_broker.get_result()
122 }
123}