From fab8cca3fedef695db775b838280bc0bef773198 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 2 Jun 2024 12:55:50 +0800 Subject: [PATCH] Implement Session Exit for client --- async/src/cmdline_client.rs | 4 ++++ src/channel.rs | 37 ++++++++++++++++++++++++++++--------- src/conn.rs | 8 +++++++- src/event.rs | 17 +++++++++++++++-- src/packets.rs | 2 +- src/runner.rs | 7 ++++++- 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/async/src/cmdline_client.rs b/async/src/cmdline_client.rs index fabee2c..aa20b29 100644 --- a/async/src/cmdline_client.rs +++ b/async/src/cmdline_client.rs @@ -289,7 +289,11 @@ impl CmdlineClient { // TODO is there a better way launch_chan.send((io.clone().unwrap(), extin.clone(), self.pty_guard.take())).await; } + CliEvent::SessionExit(ex) => { + debug!("TODO handle session exit {ex:?}") + } CliEvent::Defunct => { + trace!("break defunct"); break Ok::<_, Error>(()) } } diff --git a/src/channel.rs b/src/channel.rs index 4f3dcdd..e845ac0 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,3 +1,5 @@ +use self::{event::ChanRequest, packets::ExitSignal}; + #[allow(unused_imports)] use { crate::error::{Error, Result, TrapBug}, @@ -722,23 +724,18 @@ impl Channel { fn dispatch_client_request( &mut self, p: &packets::ChannelRequest, - s: &mut TrafSend, + _s: &mut TrafSend, ) -> Result { if !matches!(self.ty, ChanType::Session) { return Err(Error::SSHProtoUnsupported) } match &p.req { - ChannelReqType::ExitStatus(_st) => { - // TODO - self.handle_close(s)?; - // Ok(DispatchEvent::CliEvent(Exit(st) - Err(Error::SSHProtoUnsupported) + ChannelReqType::ExitStatus(_) => { + Ok(DispatchEvent::CliEvent(CliEventId::SessionExit)) } ChannelReqType::ExitSignal(_sig) => { - // TODO - self.handle_close(s)?; - Err(Error::SSHProtoUnsupported) + Ok(DispatchEvent::CliEvent(CliEventId::SessionExit)) } _ => { if let ChannelReqType::Unknown(u) = &p.req { @@ -954,3 +951,25 @@ impl core::fmt::Debug for CliSessionOpener<'_, '_> { f.debug_struct("CliSessionOpener").finish() } } + +#[derive(Debug)] +pub enum CliSessionExit<'g> { + /// Remote process exited with an exit status code + Status(u32), + /// Remote process exited by signal + Signal(ExitSignal<'g>) +} + +impl<'g> CliSessionExit<'g> { + pub fn new(p: &Packet<'g>) -> Result { + match p { + Packet::ChannelRequest(ChannelRequest { req: ChannelReqType::ExitStatus(e), .. }) => { + Ok(Self::Status(e.status)) + } + Packet::ChannelRequest(ChannelRequest { req: ChannelReqType::ExitSignal(e), .. }) => { + Ok(Self::Signal(e.clone())) + } + _ => Err(Error::bug()) + } + } +} diff --git a/src/conn.rs b/src/conn.rs index 4cd5294..73b72e2 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -20,7 +20,7 @@ use client::Client; use packets::{Packet,ParseContext}; use server::Server; use traffic::TrafSend; -use channel::Channels; +use channel::{Channels, CliSessionExit}; use config::MAX_CHANNELS; use kex::{Kex, SessId, AlgoConfig}; use event::{CliEvent, ServEvent}; @@ -532,6 +532,12 @@ impl Conn { } } + pub(crate) fn fetch_cli_session_exit<'p>(&mut self, payload: &'p [u8]) -> Result> { + self.client()?; + let packet = self.packet(payload)?; + CliSessionExit::new(&packet) + } + pub(crate) fn resume_servhostkeys(&mut self, payload: &[u8], s: &mut TrafSend, keys: &[&SignKey]) -> Result<()> { self.server()?; diff --git a/src/event.rs b/src/event.rs index f82e316..9a5f14b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -16,7 +16,7 @@ use core::fmt::Debug; use crate::*; use sshwire::TextString; use packets::Packet; -use channel::CliSessionOpener; +use channel::{CliSessionOpener, CliSessionExit}; #[derive(Debug)] pub enum Event<'g, 'a> { @@ -55,8 +55,10 @@ pub enum CliEvent<'g, 'a> Password(RequestPassword<'g, 'a>), Authenticated, SessionOpened(CliSessionOpener<'g, 'a>), + /// Remote process exited + SessionExit(CliSessionExit<'g>), - /// The SSH session is no longer running + /// The SSH connection is no longer running Defunct, // ChanRequest(ChanRequest<'g, 'a>), @@ -71,6 +73,7 @@ impl Debug for CliEvent<'_, '_> { Self::Password(_) => "Password", Self::Authenticated => "Authenticated", Self::SessionOpened(_) => "SessionOpened", + Self::SessionExit(_) => "SessionExit", Self::Defunct => "Defunct", }; write!(f, "CliEvent({e})") @@ -115,6 +118,11 @@ impl CheckHostkey<'_, '_> { } } +// impl CliExit<''_, '_> { +// pub fn + +// } + #[derive(Debug, Clone, Copy)] pub(crate) enum CliEventId { Hostkey, @@ -122,6 +130,7 @@ pub(crate) enum CliEventId { Password, Authenticated, SessionOpened(ChanNum), + SessionExit, Defunct // TODO: @@ -155,6 +164,9 @@ impl CliEventId { // (Self::Banner, Packet::UserauthBanner(p)) => { // CliEvent::Banner { banner: p.message, language: p.lang } // } + Self::SessionExit => { + Ok(CliEvent::SessionExit(runner.fetch_cli_session_exit()?)) + } Self::Defunct => error::BadUsage.fail() } } @@ -165,6 +177,7 @@ impl CliEventId { match self { | Self::Authenticated | Self::SessionOpened(_) + | Self::SessionExit | Self::Defunct => false, | Self::Hostkey diff --git a/src/packets.rs b/src/packets.rs index 41de51d..92159c1 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -719,7 +719,7 @@ pub struct ExitStatus { pub status: u32, } -#[derive(Debug, SSHEncode, SSHDecode)] +#[derive(Debug, SSHEncode, SSHDecode, Clone)] pub struct ExitSignal<'a> { pub signal: &'a str, pub core: bool, diff --git a/src/runner.rs b/src/runner.rs index a70fbbb..113151c 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,4 +1,4 @@ -use self::{channel::CliSessionOpener, event::Event}; +use self::{channel::{CliSessionExit, CliSessionOpener}, event::Event}; #[allow(unused_imports)] use { @@ -440,6 +440,11 @@ impl<'a> Runner<'a> { }) } + pub(crate) fn fetch_cli_session_exit(&mut self) -> Result { + let (payload, _seq) = self.traf_in.payload().trap()?; + self.conn.fetch_cli_session_exit(payload) + } + fn wake(&mut self) { if self.is_input_ready() { trace!("wake ready_input, waker {:?}", self.input_waker);