From 10ebae5727598f3626a9c068c4293b009d5e8898 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Wed, 29 Nov 2023 02:26:29 +0000 Subject: [PATCH 1/6] Fix the self-implemented timeout can sometimes exceed 1 sec --- src/config/version.rs | 1 + src/model/timeout.rs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/config/version.rs b/src/config/version.rs index 516babc..1b82abc 100644 --- a/src/config/version.rs +++ b/src/config/version.rs @@ -71,6 +71,7 @@ where } return Ok(outbuf); } + timeout.renew(); } Err(e) => { if let std::io::ErrorKind::WouldBlock = e.kind() { diff --git a/src/model/timeout.rs b/src/model/timeout.rs index 1d1fc99..2eedb52 100644 --- a/src/model/timeout.rs +++ b/src/model/timeout.rs @@ -27,6 +27,21 @@ impl Timeout { if self.wait_tick < NANOS_PER_SEC { self.wait_tick <<= 1; } + + if let Some(timemout) = self.timeout { + let timeout_nanos = timemout.as_nanos(); + let used_nanos = self.instant.elapsed().as_nanos(); + + self.wait_tick = { + if timeout_nanos > used_nanos + && timeout_nanos - used_nanos < self.wait_tick as u128 + { + (timeout_nanos - used_nanos) as u64 + } else { + self.wait_tick + } + }; + } } self.wait_tick } @@ -47,6 +62,6 @@ impl Timeout { } pub fn renew(&mut self) { - self.instant = Instant::now(); + self.wait_tick = 1 } } From e19fd292a0180dd8225ab036480300f1cc4c7958 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Tue, 26 Dec 2023 04:40:15 +0000 Subject: [PATCH 2/6] Fix issue 92 for local channels Referring rfc4254 section-6.10, A channel can return either exit_status or exit_signal when it closed Add API `exit_status` and `terminate_msg` to read them --- examples/exec/src/main.rs | 12 +++-- examples/shell/src/main.rs | 4 ++ src/channel/local/channel.rs | 74 +++++++++++++++++++++++++++++-- src/channel/local/channel_exec.rs | 40 +++++++++++++---- src/channel/local/channel_scp.rs | 4 +- src/channel/local/mod.rs | 2 +- src/channel/mod.rs | 2 +- 7 files changed, 119 insertions(+), 19 deletions(-) diff --git a/examples/exec/src/main.rs b/examples/exec/src/main.rs index b82b2d0..e8060d7 100644 --- a/examples/exec/src/main.rs +++ b/examples/exec/src/main.rs @@ -23,9 +23,15 @@ fn main() { let exec = session.open_exec().unwrap(); let vec: Vec = exec.send_command("ls -all").unwrap(); println!("{}", String::from_utf8(vec).unwrap()); + + let mut exec = session.open_exec().unwrap(); + exec.exec_command("no_command").unwrap(); + let vec = exec.get_output().unwrap(); + println!("output: {}", String::from_utf8(vec).unwrap()); + println!("exit status: {}", exec.exit_status()); + println!("terminated msg: {}", exec.terminate_msg()); + let _ = exec.close(); + // Close session. - let exec = session.open_exec().unwrap(); - let vec: Vec = exec.send_command("no_command").unwrap(); - println!("{}", String::from_utf8(vec).unwrap()); session.close(); } diff --git a/examples/shell/src/main.rs b/examples/shell/src/main.rs index c2d310d..b922f04 100644 --- a/examples/shell/src/main.rs +++ b/examples/shell/src/main.rs @@ -51,4 +51,8 @@ fn run_shell(shell: &mut LocalShell) { } } } + + let _ = shell.close(); + println!("exit status: {}", shell.exit_status()); + println!("terminated msg: {}", shell.terminate_msg()); } diff --git a/src/channel/local/channel.rs b/src/channel/local/channel.rs index c3648bd..ad22f41 100644 --- a/src/channel/local/channel.rs +++ b/src/channel/local/channel.rs @@ -31,6 +31,8 @@ where pub(crate) flow_control: FlowControl, pub(crate) client: RcMut, pub(crate) stream: RcMut, + pub(crate) exit_status: u32, + pub(crate) terminate_msg: String, } impl Channel @@ -52,6 +54,8 @@ where flow_control: FlowControl::new(remote_window), client, stream, + exit_status: 0, + terminate_msg: "".to_owned(), } } @@ -87,6 +91,22 @@ where self.receive_close() } + /// + /// + /// Return the command execute status + /// + pub fn exit_status(&self) -> u32 { + self.exit_status + } + + /// + /// + /// Return the terminate message if the command excution was 'killed' by a signal + /// + pub fn terminate_msg(&self) -> &str { + &self.terminate_msg + } + fn send_close(&mut self) -> SshResult<()> { if self.local_close { return Ok(()); @@ -150,7 +170,7 @@ where /// this method will receive at least one data packet /// pub(super) fn recv(&mut self) -> SshResult> { - while !self.is_close() { + while !self.closed() { let maybe_recv = self.recv_once()?; if let ChannelRead::Data(data) = maybe_recv { @@ -162,7 +182,7 @@ where pub(super) fn recv_to_end(&mut self) -> SshResult> { let mut resp = vec![]; - while !self.is_close() { + while !self.closed() { let mut read_this_time = self.recv()?; resp.append(&mut read_this_time); } @@ -256,7 +276,23 @@ where Ok(ChannelRead::Code(x)) } x @ ssh_connection_code::CHANNEL_REQUEST => { - debug!("Currently ignore message {}", x); + let cc = data.get_u32(); + if cc == self.client_channel_no { + let status: Vec = data.get_u8s(); + if let Ok(status_string) = String::from_utf8(status.clone()) { + match status_string.as_str() { + "exit-status" => { + let _ = self.handle_exit_status(&mut data); + } + "exit-signal" => { + let _ = self.handle_exit_signal(&mut data); + } + s => { + debug!("Currently ignore request {}", s); + } + } + } + } Ok(ChannelRead::Code(x)) } x @ ssh_connection_code::CHANNEL_SUCCESS => { @@ -281,6 +317,34 @@ where } } + fn handle_exit_status(&mut self, data: &mut Data) -> SshResult<()> { + let maybe_false = data.get_u8(); + if maybe_false == 0 { + self.exit_status = data.get_u32() + } + Ok(()) + } + + fn handle_exit_signal(&mut self, data: &mut Data) -> SshResult<()> { + let maybe_false = data.get_u8(); + if maybe_false == 0 { + let sig_name = String::from_utf8(data.get_u8s())?; + self.terminate_msg += + &format!("Current request is terminated by signal: {}\n", sig_name); + let coredumped = data.get_u8(); + self.terminate_msg += &format!("Coredumped: {}\n", { + if coredumped == 0 { + "False" + } else { + "True" + } + }); + let err_msg = String::from_utf8(data.get_u8s())?; + self.terminate_msg += &format!("Error message:\n{}\n", err_msg); + } + Ok(()) + } + fn send_window_adjust(&mut self, to_add: u32) -> SshResult<()> { let mut data = Data::new(); data.put_u8(ssh_connection_code::CHANNEL_WINDOW_ADJUST) @@ -295,7 +359,9 @@ where Ok(()) } - pub(crate) fn is_close(&self) -> bool { + /// Return if the channel is closed + /// + pub fn closed(&self) -> bool { self.local_close && self.remote_close } } diff --git a/src/channel/local/channel_exec.rs b/src/channel/local/channel_exec.rs index c115388..4b5a32a 100644 --- a/src/channel/local/channel_exec.rs +++ b/src/channel/local/channel_exec.rs @@ -1,24 +1,42 @@ use super::channel::Channel; -use crate::constant::{ssh_connection_code, ssh_str}; use crate::error::SshResult; use crate::model::Data; +use crate::{ + constant::{ssh_connection_code, ssh_str}, + SshError, +}; use std::{ io::{Read, Write}, ops::{Deref, DerefMut}, }; -pub struct ChannelExec(Channel); +pub struct ChannelExec { + channel: Channel, + command_send: bool, +} impl ChannelExec where S: Read + Write, { pub(crate) fn open(channel: Channel) -> Self { - ChannelExec(channel) + Self { + channel, + command_send: false, + } } - fn exec_command(&mut self, command: &str) -> SshResult<()> { + /// Send an executable command to the server + /// + pub fn exec_command(&mut self, command: &str) -> SshResult<()> { + if self.command_send { + return Err(SshError::GeneralError( + "An exec channle can only send one command".to_owned(), + )); + } + tracing::debug!("Send command {}", command); + self.command_send = true; let mut data = Data::new(); data.put_u8(ssh_connection_code::CHANNEL_REQUEST) .put_u32(self.server_channel_no) @@ -28,6 +46,13 @@ where self.send(data) } + /// Get the output of the previous command + /// + pub fn get_output(&mut self) -> SshResult> { + let r: Vec = self.recv_to_end()?; + Ok(r) + } + /// Send an executable command to the server /// and get the result /// @@ -37,8 +62,7 @@ where pub fn send_command(mut self, command: &str) -> SshResult> { self.exec_command(command)?; - let r = self.recv_to_end()?; - Ok(r) + self.get_output() } } @@ -48,7 +72,7 @@ where { type Target = Channel; fn deref(&self) -> &Self::Target { - &self.0 + &self.channel } } @@ -57,6 +81,6 @@ where S: Read + Write, { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + &mut self.channel } } diff --git a/src/channel/local/channel_scp.rs b/src/channel/local/channel_scp.rs index e12283a..37ee8f0 100644 --- a/src/channel/local/channel_scp.rs +++ b/src/channel/local/channel_scp.rs @@ -287,7 +287,7 @@ where } fn process_d(&mut self, scp_file: &mut ScpFile, local_path: &Path) -> SshResult<()> { - while !self.is_close() { + while !self.closed() { self.send_end()?; let data = self.recv()?; if data.is_empty() { @@ -401,7 +401,7 @@ where }; self.send_end()?; let mut count = 0; - while !self.is_close() { + while !self.closed() { let data = self.recv()?; if data.is_empty() { continue; diff --git a/src/channel/local/mod.rs b/src/channel/local/mod.rs index 54cd355..a5dd9e7 100644 --- a/src/channel/local/mod.rs +++ b/src/channel/local/mod.rs @@ -2,7 +2,7 @@ mod channel; mod channel_exec; mod channel_shell; -pub(crate) use channel::Channel; +pub use channel::Channel; pub use channel_exec::ChannelExec; pub use channel_shell::ChannelShell; diff --git a/src/channel/mod.rs b/src/channel/mod.rs index 81fe799..1053983 100644 --- a/src/channel/mod.rs +++ b/src/channel/mod.rs @@ -4,7 +4,7 @@ mod local; pub(crate) use backend::Channel as BackendChannel; pub use backend::{ChannelBroker, ExecBroker, ShellBrocker}; -pub(crate) use local::Channel as LocalChannel; +pub use local::Channel as LocalChannel; pub use local::ChannelExec as LocalExec; pub use local::ChannelShell as LocalShell; From d792865d352825fb1aa3a54282e1a895b55a24d7 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Tue, 26 Dec 2023 04:45:01 +0000 Subject: [PATCH 3/6] Add some comments for #91 --- src/session/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/session/mod.rs b/src/session/mod.rs index 0ee63ef..5dd05da 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -142,6 +142,10 @@ impl SessionBuilder { } /// Read/Write timeout for local SSH mode. Use None to disable timeout. + /// This is a global timeout only take effect after the session is established + /// + /// Use `connect_with_timeout` instead if you want to add timeout + /// when connect to the target SSH server pub fn timeout(mut self, timeout: Option) -> Self { self.config.timeout = timeout; self @@ -241,6 +245,8 @@ impl SessionBuilder { self } + /// Create a TCP connection to the target server + /// pub fn connect(self, addr: A) -> SshResult> where A: ToSocketAddrs, @@ -257,6 +263,8 @@ impl SessionBuilder { self.connect_bio(tcp) } + /// Create a TCP connection to the target server, with timeout provided + /// pub fn connect_with_timeout( self, addr: A, From 0420cb1104a228c4357d4ea6b0dafbc4476f6379 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Tue, 26 Dec 2023 08:14:45 +0000 Subject: [PATCH 4/6] Fix issue 92 for backend channels, Referring rfc4254 section-6.10, A channel can return either exit_status or exit_signal when it closed Add API `exit_status` and `terminate_msg` to read them --- build_check.sh | 2 +- examples/exec/src/main.rs | 4 +- examples/exec_backend/src/main.rs | 8 ++- examples/shell/src/main.rs | 4 +- examples/shell_backend/src/main.rs | 3 + src/channel/backend/channel.rs | 103 ++++++++++++++++++++++++--- src/channel/backend/channel_exec.rs | 33 ++++++--- src/channel/backend/channel_shell.rs | 4 -- src/channel/local/channel.rs | 15 ++-- src/model/backend_msg.rs | 2 + src/session/session_broker.rs | 10 +-- tests/exec.rs | 2 +- 12 files changed, 144 insertions(+), 46 deletions(-) diff --git a/build_check.sh b/build_check.sh index 906e845..da38681 100644 --- a/build_check.sh +++ b/build_check.sh @@ -29,5 +29,5 @@ echo done echo echo echo cargo test -cargo test --all-features -- --test-threads 1 > /dev/null +cargo test -- --test-threads 1 > /dev/null echo done \ No newline at end of file diff --git a/examples/exec/src/main.rs b/examples/exec/src/main.rs index e8060d7..c1e8e81 100644 --- a/examples/exec/src/main.rs +++ b/examples/exec/src/main.rs @@ -28,8 +28,8 @@ fn main() { exec.exec_command("no_command").unwrap(); let vec = exec.get_output().unwrap(); println!("output: {}", String::from_utf8(vec).unwrap()); - println!("exit status: {}", exec.exit_status()); - println!("terminated msg: {}", exec.terminate_msg()); + println!("exit status: {}", exec.exit_status().unwrap()); + println!("terminated msg: {}", exec.terminate_msg().unwrap()); let _ = exec.close(); // Close session. diff --git a/examples/exec_backend/src/main.rs b/examples/exec_backend/src/main.rs index 9be9fc4..ea34792 100644 --- a/examples/exec_backend/src/main.rs +++ b/examples/exec_backend/src/main.rs @@ -20,9 +20,9 @@ fn main() { .connect("127.0.0.1:22") .unwrap() .run_backend(); - let exec = session.open_exec().unwrap(); + let mut exec = session.open_exec().unwrap(); - const CMD: &str = "ls -lah"; + const CMD: &str = "no command"; // send the command to server println!("Send command {}", CMD); @@ -33,7 +33,9 @@ fn main() { // get command result let vec: Vec = exec.get_result().unwrap(); - println!("{}", String::from_utf8(vec).unwrap()); + println!("output: {}", String::from_utf8(vec).unwrap()); + println!("exit status: {}", exec.exit_status().unwrap()); + println!("terminated msg: {}", exec.terminate_msg().unwrap()); // Close session. session.close(); diff --git a/examples/shell/src/main.rs b/examples/shell/src/main.rs index b922f04..87a1710 100644 --- a/examples/shell/src/main.rs +++ b/examples/shell/src/main.rs @@ -53,6 +53,6 @@ fn run_shell(shell: &mut LocalShell) { } let _ = shell.close(); - println!("exit status: {}", shell.exit_status()); - println!("terminated msg: {}", shell.terminate_msg()); + println!("exit status: {}", shell.exit_status().unwrap()); + println!("terminated msg: {}", shell.terminate_msg().unwrap()); } diff --git a/examples/shell_backend/src/main.rs b/examples/shell_backend/src/main.rs index 41f5fa9..927e39d 100644 --- a/examples/shell_backend/src/main.rs +++ b/examples/shell_backend/src/main.rs @@ -28,6 +28,9 @@ fn main() { // Close channel. shell.close().unwrap(); + sleep(Duration::from_secs(2)); + println!("exit status: {}", shell.exit_status().unwrap()); + println!("terminated msg: {}", shell.terminate_msg().unwrap()); // Close session. session.close(); } diff --git a/src/channel/backend/channel.rs b/src/channel/backend/channel.rs index 6e94878..8845f93 100644 --- a/src/channel/backend/channel.rs +++ b/src/channel/backend/channel.rs @@ -85,7 +85,7 @@ impl Channel { where S: Read + Write, { - if !self.is_close() { + if !self.closed() { data.pack(client).write_stream(stream) } else { Err(SshError::GeneralError( @@ -165,7 +165,56 @@ impl Channel { Ok(()) } - pub fn is_close(&self) -> bool { + pub fn recv_rqst(&mut self, mut data: Data) -> SshResult<()> { + let status: Vec = data.get_u8s(); + if let Ok(status_string) = String::from_utf8(status.clone()) { + match status_string.as_str() { + "exit-status" => { + let _ = self.handle_exit_status(&mut data); + } + "exit-signal" => { + let _ = self.handle_exit_signal(&mut data); + } + s => { + debug!("Currently ignore request {}", s); + } + } + } + Ok(()) + } + + fn handle_exit_status(&mut self, data: &mut Data) -> SshResult<()> { + let maybe_false = data.get_u8(); + if maybe_false == 0 { + self.snd.send(BackendResp::ExitStatus(data.get_u32()))? + } + Ok(()) + } + + fn handle_exit_signal(&mut self, data: &mut Data) -> SshResult<()> { + let maybe_false = data.get_u8(); + let mut msg = "".to_owned(); + if maybe_false == 0 { + if let Ok(sig_name) = String::from_utf8(data.get_u8s()) { + msg += &format!("Current request is terminated by signal: {sig_name}\n"); + } + let coredumped = data.get_u8(); + msg += &format!("Coredumped: {}\n", { + if coredumped == 0 { + "False" + } else { + "True" + } + }); + if let Ok(err_msg) = String::from_utf8(data.get_u8s()) { + msg += &format!("Error message:\n{err_msg}\n"); + } + } + self.snd.send(BackendResp::TermMsg(msg))?; + Ok(()) + } + + pub fn closed(&self) -> bool { self.local_close && self.remote_close } } @@ -182,6 +231,8 @@ pub struct ChannelBroker { pub(crate) rcv: Receiver, pub(crate) snd: Sender, pub(crate) close: bool, + pub(crate) exit_status: u32, + pub(crate) terminate_msg: String, } impl ChannelBroker { @@ -197,6 +248,8 @@ impl ChannelBroker { rcv, snd, close: false, + exit_status: 0, + terminate_msg: "".to_owned(), } } @@ -219,13 +272,25 @@ impl ChannelBroker { ShellBrocker::open(self, tv) } - /// close the backend channel and consume the channel broker itself + /// + /// + /// Return the command execute status + /// + pub fn exit_status(&self) -> SshResult { + Ok(self.exit_status) + } + + /// /// - pub fn close(mut self) -> SshResult<()> { - self.close_no_consue() + /// Return the terminate message if the command excution was 'killed' by a signal + /// + pub fn terminate_msg(&self) -> SshResult { + Ok(self.terminate_msg.clone()) } - fn close_no_consue(&mut self) -> SshResult<()> { + /// close the backend channel but do not consume + /// + pub fn close(&mut self) -> SshResult<()> { if !self.close { let mut data = Data::new(); data.put_u8(ssh_connection_code::CHANNEL_CLOSE) @@ -247,7 +312,7 @@ impl ChannelBroker { self.snd .send(BackendRqst::Command(self.client_channel_no, data))?; if !self.close { - match self.rcv.recv().unwrap() { + match self.rcv.recv()? { BackendResp::Ok(_) => trace!("{}: control command ok", self.client_channel_no), BackendResp::Fail(msg) => error!( "{}: channel error with reason {}", @@ -263,7 +328,7 @@ impl ChannelBroker { if self.close { Ok(vec![]) } else { - match self.rcv.recv().unwrap() { + match self.rcv.recv()? { BackendResp::Close => { // the remote actively close their end // but we can send close later when the broker get dropped @@ -271,6 +336,14 @@ impl ChannelBroker { self.close = true; Ok(vec![]) } + BackendResp::ExitStatus(status) => { + self.exit_status = status; + Ok(vec![]) + } + BackendResp::TermMsg(msg) => { + self.terminate_msg = msg; + Ok(vec![]) + } BackendResp::Data(data) => Ok(data.into_inner()), _ => unreachable!(), } @@ -279,8 +352,8 @@ impl ChannelBroker { pub(super) fn try_recv(&mut self) -> SshResult>> { if !self.close { - if let Ok(rqst) = self.rcv.try_recv() { - match rqst { + if let Ok(resp) = self.rcv.try_recv() { + match resp { BackendResp::Close => { // the remote actively close their end // but we can send close later when the broker get dropped @@ -289,6 +362,14 @@ impl ChannelBroker { Ok(None) } BackendResp::Data(data) => Ok(Some(data.into_inner())), + BackendResp::ExitStatus(status) => { + self.exit_status = status; + Ok(None) + } + BackendResp::TermMsg(msg) => { + self.terminate_msg = msg; + Ok(None) + } _ => unreachable!(), } } else { @@ -312,6 +393,6 @@ impl ChannelBroker { impl Drop for ChannelBroker { fn drop(&mut self) { - let _ = self.close_no_consue(); + let _ = self.close(); } } diff --git a/src/channel/backend/channel_exec.rs b/src/channel/backend/channel_exec.rs index 69a62ee..524e4ec 100644 --- a/src/channel/backend/channel_exec.rs +++ b/src/channel/backend/channel_exec.rs @@ -1,22 +1,38 @@ use super::channel::ChannelBroker; -use crate::constant::{ssh_connection_code, ssh_str}; use crate::error::SshResult; use crate::model::Data; +use crate::{ + constant::{ssh_connection_code, ssh_str}, + SshError, +}; use std::ops::{Deref, DerefMut}; -pub struct ExecBroker(ChannelBroker); +pub struct ExecBroker { + channel: ChannelBroker, + command_send: bool, +} impl ExecBroker { pub(crate) fn open(channel: ChannelBroker) -> Self { - ExecBroker(channel) + Self { + channel, + command_send: false, + } } /// Send an executable command to the server /// /// This method is non-block as it will not wait the result /// - pub fn send_command(&self, command: &str) -> SshResult<()> { + pub fn send_command(&mut self, command: &str) -> SshResult<()> { + if self.command_send { + return Err(SshError::GeneralError( + "An exec channle can only send one command".to_owned(), + )); + } + tracing::debug!("Send command {}", command); + self.command_send = true; let mut data = Data::new(); data.put_u8(ssh_connection_code::CHANNEL_REQUEST) .put_u32(self.server_channel_no) @@ -30,10 +46,7 @@ impl ExecBroker { /// /// This method will block until the server close the channel /// - /// This method also implicitly consume the channel object, - /// since the exec channel can only execute one command - /// - pub fn get_result(mut self) -> SshResult> { + pub fn get_result(&mut self) -> SshResult> { self.recv_to_end() } } @@ -41,12 +54,12 @@ impl ExecBroker { impl Deref for ExecBroker { type Target = ChannelBroker; fn deref(&self) -> &Self::Target { - &self.0 + &self.channel } } impl DerefMut for ExecBroker { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + &mut self.channel } } diff --git a/src/channel/backend/channel_shell.rs b/src/channel/backend/channel_shell.rs index f3e368e..cb209a0 100644 --- a/src/channel/backend/channel_shell.rs +++ b/src/channel/backend/channel_shell.rs @@ -65,10 +65,6 @@ impl ShellBrocker { self.send_data(buf.to_vec().into())?; Ok(()) } - - pub fn close(self) -> SshResult<()> { - self.0.close() - } } impl Deref for ShellBrocker { diff --git a/src/channel/local/channel.rs b/src/channel/local/channel.rs index ad22f41..6348f83 100644 --- a/src/channel/local/channel.rs +++ b/src/channel/local/channel.rs @@ -83,7 +83,7 @@ where ChannelShell::open(self, tv) } - /// close the channel gracefully, but donnot consume it + /// close the channel gracefully, but do not consume it /// pub fn close(&mut self) -> SshResult<()> { info!("channel close."); @@ -95,16 +95,16 @@ where /// /// Return the command execute status /// - pub fn exit_status(&self) -> u32 { - self.exit_status + pub fn exit_status(&self) -> SshResult { + Ok(self.exit_status) } /// /// /// Return the terminate message if the command excution was 'killed' by a signal /// - pub fn terminate_msg(&self) -> &str { - &self.terminate_msg + pub fn terminate_msg(&self) -> SshResult { + Ok(self.terminate_msg.clone()) } fn send_close(&mut self) -> SshResult<()> { @@ -329,8 +329,7 @@ where let maybe_false = data.get_u8(); if maybe_false == 0 { let sig_name = String::from_utf8(data.get_u8s())?; - self.terminate_msg += - &format!("Current request is terminated by signal: {}\n", sig_name); + self.terminate_msg += &format!("Current request is terminated by signal: {sig_name}\n"); let coredumped = data.get_u8(); self.terminate_msg += &format!("Coredumped: {}\n", { if coredumped == 0 { @@ -340,7 +339,7 @@ where } }); let err_msg = String::from_utf8(data.get_u8s())?; - self.terminate_msg += &format!("Error message:\n{}\n", err_msg); + self.terminate_msg += &format!("Error message:\n{err_msg}\n"); } Ok(()) } diff --git a/src/model/backend_msg.rs b/src/model/backend_msg.rs index 3ad0991..efdfcb3 100644 --- a/src/model/backend_msg.rs +++ b/src/model/backend_msg.rs @@ -13,5 +13,7 @@ pub(crate) enum BackendResp { Ok(u32), Fail(String), Data(Data), + ExitStatus(u32), + TermMsg(String), Close, } diff --git a/src/session/session_broker.rs b/src/session/session_broker.rs index 2732f87..31ef0bd 100644 --- a/src/session/session_broker.rs +++ b/src/session/session_broker.rs @@ -161,7 +161,7 @@ where let channel = channels.get_mut(&id).unwrap(); channel.send(data, &mut client, &mut stream)?; channel.local_close()?; - if channel.is_close() { + if channel.closed() { channels.remove(&id); } } @@ -274,7 +274,7 @@ where info!("Channel {} recv close", id); let channel = channels.get_mut(&id).unwrap(); channel.remote_close()?; - if channel.is_close() { + if channel.closed() { channels.remove(&id); } } @@ -288,8 +288,10 @@ where x @ ssh_connection_code::CHANNEL_EOF => { debug!("Currently ignore message {}", x); } - x @ ssh_connection_code::CHANNEL_REQUEST => { - debug!("Currently ignore message {}", x); + ssh_connection_code::CHANNEL_REQUEST => { + let id = data.get_u32(); + let channel = channels.get_mut(&id).unwrap(); + let _ = channel.recv_rqst(data); } _x @ ssh_connection_code::CHANNEL_SUCCESS => { let id = data.get_u32(); diff --git a/tests/exec.rs b/tests/exec.rs index ed54005..2f20ad1 100644 --- a/tests/exec.rs +++ b/tests/exec.rs @@ -39,7 +39,7 @@ mod tests { .connect(get_server()) .unwrap() .run_backend(); - let exec = session.open_exec().unwrap(); + let mut exec = session.open_exec().unwrap(); const CMD: &str = "ls -lah"; From 436458db760d5898d862e7fca23fb8548c129de6 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Tue, 26 Dec 2023 08:41:28 +0000 Subject: [PATCH 5/6] Update version number --- Cargo.toml | 2 +- src/constant.rs | 2 +- src/lib.rs | 2 +- version | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f26a64..3dcab2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ssh-rs" -version = "0.4.5" +version = "0.5.0" edition = "2021" authors = [ "Gao Xiang Kang <1148118271@qq.com>", diff --git a/src/constant.rs b/src/constant.rs index 46da5a6..9991050 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -1,5 +1,5 @@ /// The client version -pub(crate) const CLIENT_VERSION: &str = "SSH-2.0-SSH_RS-0.4.5"; +pub(crate) const CLIENT_VERSION: &str = "SSH-2.0-SSH_RS-0.5.0"; pub(crate) const SSH_MAGIC: &[u8] = b"SSH-"; /// The constant strings that used for ssh communication diff --git a/src/lib.rs b/src/lib.rs index e119c2c..4895af3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! Dependencies //! ```toml -//! ssh-rs = "0.4.5" +//! ssh-rs = "0.5.0" //! ``` //! //!Rust implementation of ssh2.0 client. diff --git a/version b/version index 0bfccb0..79a2734 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.4.5 +0.5.0 \ No newline at end of file From 963d82296f5b6b55cd86c1a26dbaf6b1d3fbf25d Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Tue, 26 Dec 2023 08:47:59 +0000 Subject: [PATCH 6/6] Update changelog --- changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog b/changelog index f2be1f1..7b017f1 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,9 @@ +v0.5.0 (2023-12-26) + 1. Fix some time the global timeout will exceed + 2. Add new API to support `TcpStream::connect_timeout` + 3. Add new APIs to support getting command exit status + 4. pub `LocalChannel` & `ChannelBroker` some of their methods are necessary + v0.4.5 (2023-11-17) 1. Fix the high cpu usage caused by non_block tcp 2. Fix the failuer of version agreement if the server sends more than one lines