From ff78299c389365ef1b60e545ca82f4248e0e5d4e Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Thu, 23 Jan 2025 18:58:13 +0800 Subject: [PATCH] top: implement user count --- Cargo.lock | 25 +++++++++ src/uu/top/Cargo.toml | 6 ++ src/uu/top/src/header.rs | 116 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3e7806..42c37a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "build-env" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1522ac6ee801a11bf9ef3f80403f4ede6eb41291fac3dde3de09989679305f25" + [[package]] name = "bumpalo" version = "3.16.0" @@ -420,6 +426,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsystemd-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed080163caa59cc29b34bce2209b737149a4bac148cd9a8b04e4c12822798119" +dependencies = [ + "build-env", + "libc", + "pkg-config", +] + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -564,6 +581,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "powerfmt" version = "0.2.0" @@ -1170,11 +1193,13 @@ dependencies = [ "chrono", "clap", "libc", + "libsystemd-sys", "nix", "prettytable-rs", "sysinfo", "systemstat", "uucore", + "windows 0.59.0", ] [[package]] diff --git a/src/uu/top/Cargo.toml b/src/uu/top/Cargo.toml index 8e4855e..a82e2bc 100644 --- a/src/uu/top/Cargo.toml +++ b/src/uu/top/Cargo.toml @@ -22,6 +22,12 @@ chrono = { workspace = true } systemstat = { workspace = true } bytesize = { workspace = true } +[target.'cfg(target_os="linux")'.dependencies] +libsystemd-sys = "0.9.3" + +[target.'cfg(target_os="windows")'.dependencies] +windows = { version = "0.59.0", features = ["Win32_System_RemoteDesktop"] } + [lib] path = "src/top.rs" diff --git a/src/uu/top/src/header.rs b/src/uu/top/src/header.rs index 73101bb..634d7ca 100644 --- a/src/uu/top/src/header.rs +++ b/src/uu/top/src/header.rs @@ -51,6 +51,7 @@ pub(crate) fn header(arg: &ArgMatches) -> String { ) } +#[cfg(not(target_os = "linux"))] fn todo() -> String { "TODO".into() } @@ -85,9 +86,116 @@ fn uptime() -> String { res } -//TODO: Implement active user count +#[inline] +fn format_user(user: u64) -> String { + match user { + 0 => "0 user".to_string(), + 1 => "1 user".to_string(), + _ => format!("{} users", user), + } +} + +#[cfg(target_os = "windows")] fn user() -> String { - todo() + use windows::{core::*, Win32::System::RemoteDesktop::*}; + + let mut num_user = 0; + + unsafe { + let mut session_info_ptr = std::ptr::null_mut(); + let mut session_count = 0; + + WTSEnumerateSessionsW( + Some(WTS_CURRENT_SERVER_HANDLE), + 0, + 1, + &mut session_info_ptr, + &mut session_count, + ) + .unwrap(); + + let sessions = std::slice::from_raw_parts(session_info_ptr, session_count as usize); + + for session in sessions { + let mut buffer = PWSTR::null(); + let mut bytes_returned = 0; + + WTSQuerySessionInformationW( + Some(WTS_CURRENT_SERVER_HANDLE), + session.SessionId, + WTS_INFO_CLASS(5), + &mut buffer, + &mut bytes_returned, + ) + .unwrap(); + + let username = PWSTR(buffer.0).to_string().unwrap_or_default(); + if !username.is_empty() { + num_user += 1; + } + + WTSFreeMemory(buffer.0 as _); + } + + WTSFreeMemory(session_info_ptr as _); + } + + format_user(num_user) +} + +#[cfg(unix)] +// see: https://gitlab.com/procps-ng/procps/-/blob/4740a0efa79cade867cfc7b32955fe0f75bf5173/library/uptime.c#L63-L115 +fn user() -> String { + use uucore::utmpx::Utmpx; + + #[cfg(target_os = "linux")] + unsafe { + use libc::free; + use libsystemd_sys::daemon::sd_booted; + use libsystemd_sys::login::{sd_get_sessions, sd_session_get_class}; + use std::ffi::{c_char, c_void, CStr}; + use std::ptr; + // systemd + if sd_booted() > 0 { + let mut sessions_list: *mut *mut c_char = ptr::null_mut(); + let mut num_user = 0; + let sessions = sd_get_sessions(&mut sessions_list); // rust-systemd does not implement this + + if sessions > 0 { + for i in 0..sessions { + let mut class: *mut c_char = ptr::null_mut(); + + if sd_session_get_class( + *sessions_list.add(i as usize) as *const c_char, + &mut class, + ) < 0 + { + continue; + } + if CStr::from_ptr(class).to_str().unwrap().starts_with("user") { + num_user += 1; + } + free(class as *mut c_void); + } + } + + for i in 0..sessions { + free(*sessions_list.add(i as usize) as *mut c_void); + } + free(sessions_list as *mut c_void); + + return format_user(num_user); + } + } + + // utmpx + let mut num_user = 0; + Utmpx::iter_all_records().for_each(|ut| { + if ut.record_type() == 7 && !ut.user().is_empty() { + num_user += 1; + } + }); + format_user(num_user) } #[cfg(not(target_os = "windows"))] @@ -214,9 +322,7 @@ fn memory(arg: &ArgMatches) -> String { "e" => (1_152_921_504_606_846_976, "EiB"), _ => (bytesize::MIB, "MiB"), }, - None => { - (bytesize::MIB, "MiB") - } + None => (bytesize::MIB, "MiB"), }; format!(