Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release version 0.5.0 #93

Merged
merged 6 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <1148118271@qq.com>",
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