Skip to content

Commit 9086112

Browse files
committed
top: implement user count for linux
1 parent ebbdbdc commit 9086112

File tree

3 files changed

+95
-3
lines changed

3 files changed

+95
-3
lines changed

Cargo.lock

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/top/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ 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+
2528
[lib]
2629
path = "src/top.rs"
2730

src/uu/top/src/header.rs

Lines changed: 68 additions & 3 deletions
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,11 +86,77 @@ fn uptime() -> String {
8586
res
8687
}
8788

89+
fn format_user(user: u64) -> String {
90+
match user {
91+
0 => "0 user".to_string(),
92+
1 => "1 user".to_string(),
93+
_ => format!("{} users", user),
94+
}
95+
}
96+
8897
//TODO: Implement active user count
98+
#[cfg(not(target_os = "linux"))]
8999
fn user() -> String {
90100
todo()
91101
}
92102

103+
#[cfg(target_os = "linux")]
104+
// see: https://gitlab.com/procps-ng/procps/-/blob/4740a0efa79cade867cfc7b32955fe0f75bf5173/library/uptime.c#L63-L115
105+
fn user() -> String {
106+
use libc::{endutxent, free, getutxent, setutxent};
107+
use libsystemd_sys::daemon::sd_booted;
108+
use libsystemd_sys::login::{sd_get_sessions, sd_session_get_class};
109+
use std::ffi::{c_char, c_void, CStr};
110+
use std::ptr;
111+
112+
unsafe {
113+
// systemd
114+
if sd_booted() > 0 {
115+
let mut sessions_list: *mut *mut c_char = ptr::null_mut();
116+
let mut num_user = 0;
117+
let sessions = sd_get_sessions(&mut sessions_list); // rust-systemd does not implement this
118+
119+
if sessions > 0 {
120+
for i in 0..sessions {
121+
let mut class: *mut c_char = ptr::null_mut();
122+
123+
if sd_session_get_class(
124+
*sessions_list.add(i as usize) as *const c_char,
125+
&mut class,
126+
) < 0
127+
{
128+
continue;
129+
}
130+
if CStr::from_ptr(class).to_str().unwrap().starts_with("user") {
131+
num_user += 1;
132+
}
133+
free(class as *mut c_void);
134+
}
135+
}
136+
137+
for i in 0..sessions {
138+
free(*sessions_list.add(i as usize) as *mut c_void);
139+
}
140+
free(sessions_list as *mut c_void);
141+
142+
return format_user(num_user);
143+
}
144+
145+
// utmp
146+
let mut num_user = 0;
147+
setutxent();
148+
let mut ut = getutxent();
149+
while !ut.is_null() {
150+
if ((*ut).ut_type == 7) && ((*ut).ut_user[0] != ('\0' as i8)) {
151+
num_user += 1;
152+
}
153+
ut = getutxent();
154+
}
155+
endutxent();
156+
format_user(num_user)
157+
}
158+
}
159+
93160
#[cfg(not(target_os = "windows"))]
94161
fn load_average() -> String {
95162
let binding = systemstat().read().unwrap();
@@ -214,9 +281,7 @@ fn memory(arg: &ArgMatches) -> String {
214281
"e" => (1_152_921_504_606_846_976, "EiB"),
215282
_ => (bytesize::MIB, "MiB"),
216283
},
217-
None => {
218-
(bytesize::MIB, "MiB")
219-
}
284+
None => (bytesize::MIB, "MiB"),
220285
};
221286

222287
format!(

0 commit comments

Comments
 (0)