Skip to content

Commit

Permalink
Merge pull request #93 from HsuJv/v0_5_0
Browse files Browse the repository at this point in the history
release version 0.5.0
  • Loading branch information
HsuJv authored Dec 26, 2023
2 parents fbb651f + 963d822 commit 2699b3d
Show file tree
Hide file tree
Showing 24 changed files with 287 additions and 59 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ssh-rs"
version = "0.4.5"
version = "0.5.0"
edition = "2021"
authors = [
"Gao Xiang Kang <[email protected]>",
Expand Down
2 changes: 1 addition & 1 deletion build_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 9 additions & 3 deletions examples/exec/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ fn main() {
let exec = session.open_exec().unwrap();
let vec: Vec<u8> = 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().unwrap());
println!("terminated msg: {}", exec.terminate_msg().unwrap());
let _ = exec.close();

// Close session.
let exec = session.open_exec().unwrap();
let vec: Vec<u8> = exec.send_command("no_command").unwrap();
println!("{}", String::from_utf8(vec).unwrap());
session.close();
}
8 changes: 5 additions & 3 deletions examples/exec_backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -33,7 +33,9 @@ fn main() {

// get command result
let vec: Vec<u8> = 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();
Expand Down
4 changes: 4 additions & 0 deletions examples/shell/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ fn run_shell(shell: &mut LocalShell<std::net::TcpStream>) {
}
}
}

let _ = shell.close();
println!("exit status: {}", shell.exit_status().unwrap());
println!("terminated msg: {}", shell.terminate_msg().unwrap());
}
3 changes: 3 additions & 0 deletions examples/shell_backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
103 changes: 92 additions & 11 deletions src/channel/backend/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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<u8> = 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
}
}
Expand All @@ -182,6 +231,8 @@ pub struct ChannelBroker {
pub(crate) rcv: Receiver<BackendResp>,
pub(crate) snd: Sender<BackendRqst>,
pub(crate) close: bool,
pub(crate) exit_status: u32,
pub(crate) terminate_msg: String,
}

impl ChannelBroker {
Expand All @@ -197,6 +248,8 @@ impl ChannelBroker {
rcv,
snd,
close: false,
exit_status: 0,
terminate_msg: "".to_owned(),
}
}

Expand All @@ -219,13 +272,25 @@ impl ChannelBroker {
ShellBrocker::open(self, tv)
}

/// close the backend channel and consume the channel broker itself
/// <https://datatracker.ietf.org/doc/html/rfc4254#section-6.10>
///
/// Return the command execute status
///
pub fn exit_status(&self) -> SshResult<u32> {
Ok(self.exit_status)
}

/// <https://datatracker.ietf.org/doc/html/rfc4254#section-6.10>
///
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<String> {
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)
Expand All @@ -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 {}",
Expand All @@ -263,14 +328,22 @@ 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
// just set a flag here
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!(),
}
Expand All @@ -279,8 +352,8 @@ impl ChannelBroker {

pub(super) fn try_recv(&mut self) -> SshResult<Option<Vec<u8>>> {
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
Expand All @@ -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 {
Expand All @@ -312,6 +393,6 @@ impl ChannelBroker {

impl Drop for ChannelBroker {
fn drop(&mut self) {
let _ = self.close_no_consue();
let _ = self.close();
}
}
33 changes: 23 additions & 10 deletions src/channel/backend/channel_exec.rs
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -30,23 +46,20 @@ 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<Vec<u8>> {
pub fn get_result(&mut self) -> SshResult<Vec<u8>> {
self.recv_to_end()
}
}

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
}
}
4 changes: 0 additions & 4 deletions src/channel/backend/channel_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 2699b3d

Please sign in to comment.