Skip to content

Commit 3b8691d

Browse files
committed
top: implement user count
1 parent eea3f87 commit 3b8691d

File tree

3 files changed

+148
-5
lines changed

3 files changed

+148
-5
lines changed

Cargo.lock

+25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/top/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ chrono = { workspace = true }
2222
systemstat = { workspace = true }
2323
bytesize = { workspace = true }
2424

25+
[target.'cfg(target_os="linux")'.dependencies]
26+
libsystemd-sys = "0.9.3"
27+
28+
[target.'cfg(target_os="windows")'.dependencies]
29+
windows = { version = "0.59.0", features = ["Win32_System_RemoteDesktop"] }
30+
2531
[lib]
2632
path = "src/top.rs"
2733

src/uu/top/src/header.rs

+117-5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub(crate) fn header(arg: &ArgMatches) -> String {
5151
)
5252
}
5353

54+
#[cfg(not(target_os = "linux"))]
5455
fn todo() -> String {
5556
"TODO".into()
5657
}
@@ -85,9 +86,122 @@ fn uptime() -> String {
8586
res
8687
}
8788

88-
//TODO: Implement active user count
89+
#[inline]
90+
fn format_user(user: u64) -> String {
91+
match user {
92+
0 => "0 user".to_string(),
93+
1 => "1 user".to_string(),
94+
_ => format!("{} users", user),
95+
}
96+
}
97+
98+
#[cfg(target_os = "windows")]
8999
fn user() -> String {
90-
todo()
100+
use windows::{core::*, Win32::System::RemoteDesktop::*};
101+
102+
let mut num_user = 0;
103+
104+
unsafe {
105+
let mut session_info_ptr = std::ptr::null_mut();
106+
let mut session_count = 0;
107+
108+
WTSEnumerateSessionsW(
109+
Some(WTS_CURRENT_SERVER_HANDLE),
110+
0,
111+
1,
112+
&mut session_info_ptr,
113+
&mut session_count,
114+
)
115+
.unwrap();
116+
117+
let sessions = std::slice::from_raw_parts(session_info_ptr, session_count as usize);
118+
119+
for session in sessions {
120+
let mut buffer = PWSTR::null();
121+
let mut bytes_returned = 0;
122+
123+
WTSQuerySessionInformationW(
124+
Some(WTS_CURRENT_SERVER_HANDLE),
125+
session.SessionId,
126+
WTS_INFO_CLASS(5),
127+
&mut buffer,
128+
&mut bytes_returned,
129+
)
130+
.unwrap();
131+
132+
let username = PWSTR(buffer.0).to_string().unwrap_or_default();
133+
if !username.is_empty() {
134+
num_user += 1;
135+
}
136+
137+
WTSFreeMemory(buffer.0 as _);
138+
}
139+
140+
WTSFreeMemory(session_info_ptr as _);
141+
}
142+
143+
format_user(num_user)
144+
}
145+
146+
#[cfg(unix)]
147+
// see: https://gitlab.com/procps-ng/procps/-/blob/4740a0efa79cade867cfc7b32955fe0f75bf5173/library/uptime.c#L63-L115
148+
fn user() -> String {
149+
use libc::{endutxent, getutxent, setutxent};
150+
151+
unsafe {
152+
#[cfg(target_os = "linux")]
153+
{
154+
use libc::free;
155+
use libsystemd_sys::daemon::sd_booted;
156+
use libsystemd_sys::login::{sd_get_sessions, sd_session_get_class};
157+
use std::ffi::{c_char, c_void, CStr};
158+
use std::ptr;
159+
// systemd
160+
if sd_booted() > 0 {
161+
let mut sessions_list: *mut *mut c_char = ptr::null_mut();
162+
let mut num_user = 0;
163+
let sessions = sd_get_sessions(&mut sessions_list); // rust-systemd does not implement this
164+
165+
if sessions > 0 {
166+
for i in 0..sessions {
167+
let mut class: *mut c_char = ptr::null_mut();
168+
169+
if sd_session_get_class(
170+
*sessions_list.add(i as usize) as *const c_char,
171+
&mut class,
172+
) < 0
173+
{
174+
continue;
175+
}
176+
if CStr::from_ptr(class).to_str().unwrap().starts_with("user") {
177+
num_user += 1;
178+
}
179+
free(class as *mut c_void);
180+
}
181+
}
182+
183+
for i in 0..sessions {
184+
free(*sessions_list.add(i as usize) as *mut c_void);
185+
}
186+
free(sessions_list as *mut c_void);
187+
188+
return format_user(num_user);
189+
}
190+
}
191+
192+
// utmpx
193+
let mut num_user = 0;
194+
setutxent();
195+
let mut ut = getutxent();
196+
while !ut.is_null() {
197+
if ((*ut).ut_type == 7) && ((*ut).ut_user[0] != ('\0' as i8)) {
198+
num_user += 1;
199+
}
200+
ut = getutxent();
201+
}
202+
endutxent();
203+
format_user(num_user)
204+
}
91205
}
92206

93207
#[cfg(not(target_os = "windows"))]
@@ -214,9 +328,7 @@ fn memory(arg: &ArgMatches) -> String {
214328
"e" => (1_152_921_504_606_846_976, "EiB"),
215329
_ => (bytesize::MIB, "MiB"),
216330
},
217-
None => {
218-
(bytesize::MIB, "MiB")
219-
}
331+
None => (bytesize::MIB, "MiB"),
220332
};
221333

222334
format!(

0 commit comments

Comments
 (0)