use std::{net::ToSocketAddrs, path::Path, sync::Arc, time::Duration}; use russh::{client, keys::{key, load_secret_key}, ChannelMsg, Disconnect}; use tokio::io::AsyncWriteExt; use crate::domain::executors::SshClient; pub struct RusshClient; impl SshClient for RusshClient { fn test_connection(&self, username: String, password: String) -> Result<(), crate::domain::executors::ExecutorError> { todo!() //Session::connect(); } } struct Client {} // More SSH event handlers // can be defined in this trait // In this example, we're only using Channel, so these aren't needed. #[async_trait] impl client::Handler for Client { type Error = russh::Error; async fn check_server_key( &mut self, _server_public_key: &key::PublicKey, ) -> Result { Ok(true) } } /// This struct is a convenience wrapper /// around a russh client pub struct Session { session: client::Handle, } impl Session { async fn connect, A: ToSocketAddrs>( key_path: P, user: impl Into, addrs: A, ) -> Result { let key_pair = load_secret_key(key_path, None)?; let config = client::Config { inactivity_timeout: Some(Duration::from_secs(5)), ..<_>::default() }; let config = Arc::new(config); let sh = Client {}; let mut session = client::connect(config, addrs, sh).await?; let auth_res = session .authenticate_publickey(user, Arc::new(key_pair)) .await?; if !auth_res { Err("Authentication failed") } Ok(Self { session }) } async fn call(&mut self, command: &str) -> Result { let mut channel = self.session.channel_open_session().await?; channel.exec(true, command).await?; let mut code = None; let mut stdout = tokio::io::stdout(); loop { // There's an event available on the session channel let Some(msg) = channel.wait().await else { break; }; match msg { // Write data to the terminal ChannelMsg::Data { ref data } => { stdout.write_all(data).await?; stdout.flush().await?; } // The command has returned an exit code ChannelMsg::ExitStatus { exit_status } => { code = Some(exit_status); // cannot leave the loop immediately, there might still be more data to receive } _ => {} } } Ok(code.expect("program did not exit cleanly")) } async fn close(&mut self) -> Result<()> { self.session .disconnect(Disconnect::ByApplication, "", "English") .await?; Ok(()) } }