make server logging show messages in the frontend

now you can use the log crate and get messages in the frontend.
This commit is contained in:
Brandon W Maister 2024-07-31 10:43:02 -04:00
parent 8135f163f2
commit 18da61ed32
6 changed files with 91 additions and 25 deletions

26
Cargo.lock generated
View file

@ -261,6 +261,25 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"env_filter",
"log",
]
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.9" version = "0.3.9"
@ -302,6 +321,7 @@ dependencies = [
"bytes", "bytes",
"copypasta", "copypasta",
"crossterm", "crossterm",
"env_logger",
"home", "home",
"indoc", "indoc",
"log", "log",
@ -446,7 +466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.48.5", "windows-targets 0.52.5",
] ]
[[package]] [[package]]
@ -473,9 +493,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.20" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "malloc_buf" name = "malloc_buf"

View file

@ -18,6 +18,7 @@ anyhow = "1.0"
bytes = "1" bytes = "1"
copypasta = "0.10.1" copypasta = "0.10.1"
crossterm = { version = "0.25", features = ["event-stream"] } crossterm = { version = "0.25", features = ["event-stream"] }
env_logger = { version = "0.11.5", default-features = false }
home = "0.5.4" home = "0.5.4"
indoc = "1" indoc = "1"
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }

View file

@ -323,6 +323,7 @@ async fn client_main<Reader: AsyncRead + Unpin, Writer: AsyncWrite + Unpin>(
async fn spawn_ssh( async fn spawn_ssh(
server: &str, server: &str,
sudo: bool, sudo: bool,
log_filter: &str,
) -> Result<(tokio::process::Child, u16), std::io::Error> { ) -> Result<(tokio::process::Child, u16), std::io::Error> {
let socks_port = { let socks_port = {
let listener = TcpListener::bind("127.0.0.1:0").await?; let listener = TcpListener::bind("127.0.0.1:0").await?;
@ -337,7 +338,9 @@ async fn spawn_ssh(
if sudo { if sudo {
cmd.arg("sudo"); cmd.arg("sudo");
} }
cmd.arg("fwd").arg("--server"); cmd.arg(format!("FWD_LOG={log_filter}"))
.arg("fwd")
.arg("--server");
cmd.stdout(std::process::Stdio::piped()); cmd.stdout(std::process::Stdio::piped());
cmd.stdin(std::process::Stdio::piped()); cmd.stdin(std::process::Stdio::piped());
@ -367,13 +370,15 @@ fn is_sigint(status: std::process::ExitStatus) -> bool {
async fn client_connect_loop( async fn client_connect_loop(
remote: &str, remote: &str,
sudo: bool, sudo: bool,
log_filter: &str,
events: mpsc::Sender<ui::UIEvent>, events: mpsc::Sender<ui::UIEvent>,
) { ) {
loop { loop {
_ = events.send(ui::UIEvent::Disconnected).await; _ = events.send(ui::UIEvent::Disconnected).await;
let (mut child, socks_port) = let (mut child, socks_port) = spawn_ssh(remote, sudo, log_filter)
spawn_ssh(remote, sudo).await.expect("failed to spawn"); .await
.expect("failed to spawn");
let mut stderr = child let mut stderr = child
.stderr .stderr
@ -431,7 +436,7 @@ async fn client_connect_loop(
} }
} }
pub async fn run_client(remote: &str, sudo: bool) { pub async fn run_client(remote: &str, sudo: bool, log_filter: &str) {
let (event_sender, event_receiver) = mpsc::channel(1024); let (event_sender, event_receiver) = mpsc::channel(1024);
_ = log::set_boxed_logger(ui::Logger::new(event_sender.clone())); _ = log::set_boxed_logger(ui::Logger::new(event_sender.clone()));
log::set_max_level(LevelFilter::Info); log::set_max_level(LevelFilter::Info);
@ -449,7 +454,7 @@ pub async fn run_client(remote: &str, sudo: bool) {
// Start the reconnect loop. // Start the reconnect loop.
tokio::select! { tokio::select! {
_ = ui.run() => (), _ = ui.run() => (),
_ = client_connect_loop(remote, sudo, event_sender) => () _ = client_connect_loop(remote, sudo, log_filter, event_sender) => ()
} }
} }

View file

@ -26,6 +26,10 @@ Options:
run as root), but requires sudo access on the server and run as root), but requires sudo access on the server and
*might* end up forwarding ports that you do not want *might* end up forwarding ports that you do not want
forwarded (e.g., port 22 for sshd, or port 53 for systemd.) forwarded (e.g., port 22 for sshd, or port 53 for systemd.)
--log-filter FILTER
Set remote server's log level. Default is `warn`. Supports
all of Rust's env_logger filter syntax, e.g.
`--log-filter=fwd::trace`.
"}); "});
} }
@ -34,7 +38,7 @@ enum Args {
Help, Help,
Version, Version,
Server, Server,
Client(String, bool), Client(String, bool, String),
Browse(String), Browse(String),
Clip(String), Clip(String),
Error, Error,
@ -43,9 +47,11 @@ enum Args {
fn parse_args(args: Vec<String>) -> Args { fn parse_args(args: Vec<String>) -> Args {
let mut server = None; let mut server = None;
let mut sudo = None; let mut sudo = None;
let mut log_filter = None;
let mut rest = Vec::new(); let mut rest = Vec::new();
for arg in args.into_iter().skip(1) { let mut arg_iter = args.into_iter().skip(1);
while let Some(arg) = arg_iter.next() {
if arg == "--help" || arg == "-?" || arg == "-h" { if arg == "--help" || arg == "-?" || arg == "-h" {
return Args::Help; return Args::Help;
} else if arg == "--version" { } else if arg == "--version" {
@ -54,6 +60,14 @@ fn parse_args(args: Vec<String>) -> Args {
server = Some(true) server = Some(true)
} else if arg == "--sudo" || arg == "-s" { } else if arg == "--sudo" || arg == "-s" {
sudo = Some(true) sudo = Some(true)
} else if arg.starts_with("--log-filter") {
if arg.contains('=') {
log_filter = Some(arg.split('=').nth(1).unwrap().to_owned());
} else if let Some(arg) = arg_iter.next() {
log_filter = Some(arg);
} else {
return Args::Error;
}
} else { } else {
rest.push(arg) rest.push(arg)
} }
@ -71,20 +85,24 @@ fn parse_args(args: Vec<String>) -> Args {
if rest.len() == 2 { if rest.len() == 2 {
Args::Browse(rest[1].to_string()) Args::Browse(rest[1].to_string())
} else if rest.len() == 1 { } else if rest.len() == 1 {
Args::Client(rest[0].to_string(), sudo.unwrap_or(false)) Args::Client(rest[0].to_string(), sudo.unwrap_or(false), log_filter.unwrap_or("warn".to_owned()))
} else { } else {
Args::Error Args::Error
} }
} else if rest[0] == "clip" { } else if rest[0] == "clip" {
if rest.len() == 1 { if rest.len() == 1 {
Args::Client(rest[0].to_string(), sudo.unwrap_or(false)) Args::Client(rest[0].to_string(), sudo.unwrap_or(false), log_filter.unwrap_or("warn".to_owned()))
} else if rest.len() == 2 { } else if rest.len() == 2 {
Args::Clip(rest[1].to_string()) Args::Clip(rest[1].to_string())
} else { } else {
Args::Error Args::Error
} }
} else if rest.len() == 1 { } else if rest.len() == 1 {
Args::Client(rest[0].to_string(), sudo.unwrap_or(false)) Args::Client(
rest[0].to_string(),
sudo.unwrap_or(false),
log_filter.unwrap_or("warn".to_owned()),
)
} else { } else {
Args::Error Args::Error
} }
@ -124,8 +142,8 @@ async fn main() {
Args::Clip(file) => { Args::Clip(file) => {
clip_file(file).await; clip_file(file).await;
} }
Args::Client(server, sudo) => { Args::Client(server, sudo, log_filter) => {
fwd::run_client(&server, sudo).await; fwd::run_client(&server, sudo, &log_filter).await;
} }
Args::Error => { Args::Error => {
usage(); usage();
@ -175,14 +193,29 @@ mod tests {
#[test] #[test]
fn client() { fn client() {
assert_arg_parse!(&["foo.com"], Args::Client(_, false)); assert_arg_parse!(&["foo.com"], Args::Client( _, false, _));
assert_arg_parse!(&["a"], Args::Client(_, false)); assert_arg_parse!(&["a"], Args::Client(_, false, _));
assert_arg_parse!(&["browse"], Args::Client(_, false)); assert_arg_parse!(&["browse"], Args::Client(_, false, _));
assert_arg_parse!(&["clip"], Args::Client(_, false)); assert_arg_parse!(&["clip"], Args::Client(_, false, _));
assert_arg_parse!(&["foo.com", "--sudo"], Args::Client(_, true)); assert_arg_parse!(&["foo.com", "--sudo"], Args::Client(_, true, _));
assert_arg_parse!(&["a", "-s"], Args::Client(_, true)); assert_arg_parse!(&["a", "-s"], Args::Client(_, true, _));
assert_arg_parse!(&["-s", "browse"], Args::Client(_, true)); assert_arg_parse!(&["-s", "browse"], Args::Client(_, true, _));
assert_arg_parse!(&["-s", "clip"], Args::Client(_, true)); assert_arg_parse!(&["-s", "clip"], Args::Client(_, true, _));
assert_client_parse(&["a"], "a", false, "warn");
assert_client_parse(&["a", "--log-filter", "info"], "a", false, "info");
assert_client_parse(&["a", "--log-filter=info"], "a", false, "info");
assert_client_parse(&["a", "--sudo", "--log-filter=info"], "a", true, "info");
}
fn assert_client_parse(x: &[&str], server: &str, sudo: bool, log_filter: &str) {
let args = parse_args(args(x));
assert_matches!(args, Args::Client(_, _, _));
if let Args::Client(s, sdo, lf) = args {
assert_eq!(s, server);
assert_eq!(sdo, sudo);
assert_eq!(lf, log_filter);
}
} }
#[test] #[test]

View file

@ -82,6 +82,10 @@ async fn server_main<
} }
pub async fn run_server() { pub async fn run_server() {
env_logger::Builder::from_env(
env_logger::Env::new().filter_or("FWD_LOG", "warn"),
)
.init();
let stdin = tokio::io::stdin(); let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout(); let stdout = tokio::io::stdout();
if let Err(e) = server_main(stdin, stdout).await { if let Err(e) = server_main(stdin, stdout).await {

View file

@ -1,5 +1,7 @@
use anyhow::Result; use anyhow::Result;
use log::{error, warn}; #[cfg_attr(not(target_os = "linux"), allow(unused))]
use log::error;
use log::warn;
use std::collections::HashMap; use std::collections::HashMap;
use crate::message::PortDesc; use crate::message::PortDesc;
@ -8,9 +10,10 @@ use crate::message::PortDesc;
mod procfs; mod procfs;
pub async fn get_entries() -> Result<Vec<PortDesc>> { pub async fn get_entries() -> Result<Vec<PortDesc>> {
#[allow(unused)] #[cfg_attr(not(target_os = "linux"), allow(unused_mut))]
let mut attempts = 0; let mut attempts = 0;
#[cfg_attr(not(target_os = "linux"), allow(unused_mut))]
let mut result: HashMap<u16, PortDesc> = HashMap::new(); let mut result: HashMap<u16, PortDesc> = HashMap::new();
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]