Skip to content

Commit 1a09cbd

Browse files
Bluemangoosylvestre
authored andcommitted
top: implement user count for linux
1 parent 78e68b6 commit 1a09cbd

File tree

3 files changed

+99
-3
lines changed

3 files changed

+99
-3
lines changed

Cargo.lock

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

src/uu/top/Cargo.toml

+3
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

+72-3
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,81 @@ 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(target_os = "windows")]
8999
fn user() -> String {
90100
todo()
91101
}
92102

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

222291
format!(

0 commit comments

Comments
 (0)