diff --git a/Cargo.lock b/Cargo.lock index 2d93764..eed4ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,6 +819,7 @@ dependencies = [ "nix", "sd-notify", "serde", + "serde_json", "tokio", "toml", "tracing", diff --git a/Cargo.toml b/Cargo.toml index e734881..63470ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ tracing = "0.1.44" tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } # onc-rpc = "0.3.2" sd-notify = "0.4.5" -# serde_json = "1.0.148" +serde_json = "1.0.148" uucore = { version = "0.5.0", features = ["utmpx"] } zlink = { version = "0.2.0", features = ["introspection"] } diff --git a/src/bin/rwho.rs b/src/bin/rwho.rs index 5797f93..2cf4621 100644 --- a/src/bin/rwho.rs +++ b/src/bin/rwho.rs @@ -1,5 +1,6 @@ +use anyhow::Context; use clap::Parser; -use roowho2_lib::server::rwhod::RwhodClientProxy; +use roowho2_lib::{proto::WhodUserEntry, server::rwhod::RwhodClientProxy}; /// Check who is logged in on local machines. /// @@ -16,13 +17,17 @@ pub struct Args { #[arg(long, short)] all: bool, + /// Print the output with the old formatting + #[arg(long, short)] + old: bool, + /// Output in JSON format #[arg(long, short)] json: bool, } #[tokio::main] -async fn main() { +async fn main() -> anyhow::Result<()> { let args = Args::parse(); let mut conn = zlink::unix::connect("/run/roowho2/roowho2.varlink") @@ -32,7 +37,81 @@ async fn main() { let reply = conn .rwho(args.all) .await - .expect("Failed to send rwho request"); + .context("Failed to send rwho request")? + .map_err(|e| anyhow::anyhow!("Server returned an error for rwho request: {:?}", e))?; - println!("{:?}", reply); + if args.json { + println!("{}", serde_json::to_string_pretty(&reply).unwrap()); + } else if args.old { + old_format_user_entries(&reply) + .iter() + .for_each(|line| println!("{}", line)); + } else { + old_format_user_entries(&reply) + .iter() + .for_each(|line| println!("{}", line)); + } + + Ok(()) +} + +fn old_format_user_entries(entries: &[(String, WhodUserEntry)]) -> Vec { + let hostname_tty_width = entries + .iter() + .map(|(host, user)| host.len() + user.tty.len() + 1) + .max() + .unwrap_or(0); + + let idle_time_width = entries + .iter() + .map(|(_, user)| user.idle_time.num_hours()) + .max() + .map(|hours| { + if hours >= 10 { + 5 + } else if hours > 0 { + 4 + } else { + 3 + } + }) + .unwrap_or(0); + + let result = entries + .iter() + .map(|(hostname, user)| { + old_format_user_entry(hostname, hostname_tty_width, idle_time_width, user) + }) + .collect(); + + result +} + +fn old_format_user_entry( + hostname: &str, + hostname_tty_width: usize, + idle_time_width: usize, + user: &WhodUserEntry, +) -> String { + let idle_str = { + let hours = user.idle_time.num_hours().min(99); + let minutes = user.idle_time.num_minutes() % 60; + format!( + "{}:{:02}", + if hours == 0 { + "".to_string() + } else { + hours.to_string() + }, + minutes + ) + }; + + format!( + "{:<8.8} {:idle_time_width$}", + user.user_id, + format!("{hostname}:{}", user.tty), + user.login_time.format("%b %d %H:%M"), + idle_str, + ) } diff --git a/src/server/rwhod.rs b/src/server/rwhod.rs index 984d570..558e14e 100644 --- a/src/server/rwhod.rs +++ b/src/server/rwhod.rs @@ -236,7 +236,7 @@ pub trait RwhodClientProxy { async fn rwho( &mut self, all: bool, - ) -> zlink::Result, RwhodClientError>>; + ) -> zlink::Result, RwhodClientError>>; async fn ruptime(&mut self) -> zlink::Result, RwhodClientError>>; } @@ -257,7 +257,7 @@ pub enum RwhodClientRequest { #[derive(Debug, Serialize)] #[serde(untagged)] pub enum RwhodClientResponse { - Rwho(Vec), + Rwho(Vec<(String, WhodUserEntry)>), Ruptime(Vec), } @@ -298,7 +298,13 @@ impl zlink::Service for RwhodClientServer { let store = self.whod_status_store.read().await; let mut all_user_entries = Vec::new(); for status_update in store.values() { - all_user_entries.extend_from_slice(&status_update.users); + all_user_entries.extend_from_slice( + &status_update + .users + .iter() + .map(|user| (status_update.hostname.clone(), user.clone())) + .collect::>(), + ); } MethodReply::Single(Some(RwhodClientResponse::Rwho(all_user_entries))) }