use std::{ io::{self, Read, Write}, os::fd::AsRawFd, path::PathBuf, sync::Arc, }; use sparse_05_common::messages::Capabilities; use tokio::net::UdpSocket; use super::{commands, Connection}; macro_rules! libc_try { ($expr:expr) => { if unsafe { $expr } == -1 { return Err(std::io::Error::last_os_error())?; } }; } #[cfg(unix)] fn get_term_attrs(fd: &F) -> anyhow::Result { let mut termios = unsafe { std::mem::zeroed() }; libc_try!(libc::tcgetattr(fd.as_raw_fd(), &mut termios)); Ok(termios) } #[cfg(unix)] fn set_term_attrs(fd: &mut F, attrs: &libc::termios) -> anyhow::Result<()> { libc_try!(libc::tcsetattr(fd.as_raw_fd(), 0, attrs)); Ok(()) } #[cfg(unix)] fn convert_termios_raw(attrs: &mut libc::termios) -> anyhow::Result<()> { unsafe { libc::cfmakeraw(attrs) }; Ok(()) } pub(super) async fn shell( connection: Arc, mut capabilities: Capabilities, ) -> anyhow::Result<()> { println!("Type #help to view a list of sparse commands\n"); let mut stdin = io::stdin(); let mut stdout = io::stdout(); let backup_term_attrs = get_term_attrs(&stdin)?; let mut raw_term_attrs = get_term_attrs(&stdin)?; convert_termios_raw(&mut raw_term_attrs)?; macro_rules! cmd_matcher { ($input:expr, $(($check:ident, $matcher:expr) => {$($body:tt)*}),+, _ => {$($ebody:tt)*}) => { match &$input[..] { $($check if $check.len() >= $matcher.len() && $check[..$matcher.len()] == $matcher[..] => { let $check = &$check[$matcher.len()..]; $($body)* }),+ _ => { $($ebody)* } } } } let mut cwd = "/".to_string(); loop { print!( "{}@{}:{} {} ", capabilities.userent.as_deref().unwrap_or("unknown user!"), match &capabilities.hostname { Some(n) => n.clone(), None => format!("{}", connection.ip.ip()), }, cwd, if capabilities.root { "#" } else { "$" } ); stdout.flush().unwrap(); let mut cmd = [0u8; 256]; let amount = stdin.read(&mut cmd)?; if amount == 0 { break; } cmd_matcher!( cmd[..amount], (_sysinfo, b"#sysinfo") => { commands::sysinfo::print_capabilities(&capabilities) }, (_help, b"#help") => {}, (_exit, b"#exit") => { break; }, _ => {} ); } Ok(()) }