use std::fmt;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use anyhow::{Result, bail};
use oxdock_func_macro::oxdock_type;
use tokio::sync::mpsc;
use crate::state::{DownMsg, ServerState, UpMsg};
#[oxdock_type(name = "SSH_SERVER")]
#[derive(Debug, Clone)]
pub struct SshServerTag {
state: Arc<ServerState>,
}
impl SshServerTag {
pub fn new(state: Arc<ServerState>) -> Self {
Self { state }
}
pub fn state(&self) -> &Arc<ServerState> {
&self.state
}
}
impl PartialEq for SshServerTag {
fn eq(&self, other: &Self) -> bool {
self.state.id() == other.state.id()
}
}
impl fmt::Display for SshServerTag {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"SSH_SERVER({}, {})",
self.state.id(),
self.state.addr_text()
)
}
}
static SESSION_IDS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
#[derive(Debug)]
struct SshSessionInner {
id: u64,
command: Option<String>,
username: Option<String>,
peer_addr: Option<SocketAddr>,
pty_size: crate::state::SharedPtySize,
up_rx: Mutex<Option<mpsc::Receiver<UpMsg>>>,
down_tx: Mutex<Option<mpsc::Sender<DownMsg>>>,
}
#[oxdock_type(name = "SSH_SESSION")]
#[derive(Debug, Clone)]
pub struct SshSessionTag {
inner: Arc<SshSessionInner>,
}
impl SshSessionTag {
pub fn new(
command: Option<String>,
username: Option<String>,
peer_addr: Option<SocketAddr>,
pty_size: crate::state::SharedPtySize,
up_rx: mpsc::Receiver<UpMsg>,
down_tx: mpsc::Sender<DownMsg>,
) -> Self {
Self {
inner: Arc::new(SshSessionInner {
id: SESSION_IDS.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
command,
username,
peer_addr,
pty_size,
up_rx: Mutex::new(Some(up_rx)),
down_tx: Mutex::new(Some(down_tx)),
}),
}
}
pub fn command(&self) -> Option<String> {
self.inner.command.clone()
}
pub fn username(&self) -> Option<String> {
self.inner.username.clone()
}
pub fn peer_addr(&self) -> Option<SocketAddr> {
self.inner.peer_addr
}
pub fn pty_size(&self) -> crate::state::PtySize {
crate::state::snapshot_pty_size(&self.inner.pty_size).size
}
pub fn pty_size_handle(&self) -> crate::state::SharedPtySize {
Arc::clone(&self.inner.pty_size)
}
pub fn take_pump_ends(&self) -> Result<(mpsc::Receiver<UpMsg>, mpsc::Sender<DownMsg>)> {
let mut up = self.inner.up_rx.lock().unwrap_or_else(|p| p.into_inner());
let mut down = self.inner.down_tx.lock().unwrap_or_else(|p| p.into_inner());
if up.is_none() || down.is_none() {
bail!("SSH session already pumped");
}
Ok((up.take().unwrap(), down.take().unwrap()))
}
}
impl PartialEq for SshSessionTag {
fn eq(&self, other: &Self) -> bool {
self.inner.id == other.inner.id
}
}
impl fmt::Display for SshSessionTag {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let peer = self
.inner
.peer_addr
.map(|addr| addr.to_string())
.unwrap_or_default();
match &self.inner.username {
Some(user) => write!(
formatter,
"SSH_SESSION(sess-{} {}@{})",
self.inner.id, user, peer
),
None => write!(formatter, "SSH_SESSION(sess-{} {})", self.inner.id, peer),
}
}
}