mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
add support for systemd-logind (#8483)
* add support for systemd-logind
Add support for systemd-logind for systems where `/var/run/utmp` does not exist any more (e.g. Ubuntu 25.04).
Here is some context explaining the switch from utmp to systemd-logind:
https://www.thkukuk.de/blog/Y2038_glibc_utmp_64bit/
Fixes https://github.com/uutils/coreutils/issues/8376
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* fix duplicated boot record
When feature feat_systemd_logind is enabled and utmp is present, two boot records were printed.
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* improve error forwarding
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
* remove unnecessary negation in if/else block
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
* use getpwuid_r() instead of getpwuid()
getpwuid() is not thread-safe.
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* improve error handling
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* use Path instead of string
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* use closure to reduce the number of clones() calls
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* use MaybeUninit instead of mem::zeroed()
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* systemd-logind: add some unit-tests
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* use sysconf(_SC_GETPW_R_SIZE_MAX) instead of 1024
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* fix cspell
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* add auto-enablement of feat_systemd_logind in GNUmakefile
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
* Revert "add auto-enablement of feat_systemd_logind in GNUmakefile"
This breaks "Tests/BusyBox test suite" in CI.
This reverts commit 16626d4653.
---------
Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
@@ -5,7 +5,7 @@ name: CICD
|
||||
# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain fuzzers dedupe devel profdata
|
||||
# spell-checker:ignore (people) Peltoche rivy dtolnay Anson dawidd
|
||||
# spell-checker:ignore (shell/tools) binutils choco clippy dmake dpkg esac fakeroot fdesc fdescfs gmake grcov halium lcov libclang libfuse libssl limactl mkdir nextest nocross pacman popd printf pushd redoxer rsync rustc rustfmt rustup shopt sccache utmpdump xargs
|
||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest Swatinem tempfile testsuite toybox uutils
|
||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest Swatinem tempfile testsuite toybox uutils libsystemd
|
||||
|
||||
env:
|
||||
PROJECT_NAME: coreutils
|
||||
@@ -425,7 +425,7 @@ jobs:
|
||||
run: |
|
||||
## Install dependencies
|
||||
sudo apt-get update
|
||||
sudo apt-get install jq libselinux1-dev
|
||||
sudo apt-get install jq libselinux1-dev libsystemd-dev
|
||||
- name: "`make install`"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -714,9 +714,9 @@ jobs:
|
||||
esac
|
||||
case '${{ matrix.job.os }}' in
|
||||
ubuntu-*)
|
||||
# selinux headers needed to build tests
|
||||
# selinux and systemd headers needed to build tests
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install libselinux1-dev
|
||||
sudo apt-get -y install libselinux1-dev libsystemd-dev
|
||||
# pinky is a tool to show logged-in users from utmp, and gecos fields from /etc/passwd.
|
||||
# In GitHub Action *nix VMs, no accounts log in, even the "runner" account that runs the commands, and "system boot" entry is missing.
|
||||
# The account also has empty gecos fields.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Code Quality
|
||||
|
||||
# spell-checker:ignore (people) reactivecircus Swatinem dtolnay juliangruber pell taplo
|
||||
# spell-checker:ignore (misc) TERMUX noaudio pkill swiftshader esac sccache pcoreutils shopt subshell dequote
|
||||
# spell-checker:ignore (misc) TERMUX noaudio pkill swiftshader esac sccache pcoreutils shopt subshell dequote libsystemd
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -110,8 +110,8 @@ jobs:
|
||||
## Install/setup prerequisites
|
||||
case '${{ matrix.job.os }}' in
|
||||
ubuntu-*)
|
||||
# selinux headers needed to enable all features
|
||||
sudo apt-get -y install libselinux1-dev
|
||||
# selinux and systemd headers needed to enable all features
|
||||
sudo apt-get -y install libselinux1-dev libsystemd-dev
|
||||
;;
|
||||
esac
|
||||
- name: "`cargo clippy` lint testing"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: FreeBSD
|
||||
|
||||
# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc sccache nextest copyback
|
||||
# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc sccache nextest copyback logind
|
||||
|
||||
env:
|
||||
# * style job configuration
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y curl gmake sudo
|
||||
prepare: pkg install -y curl gmake sudo jq
|
||||
run: |
|
||||
## Prepare, build, and test
|
||||
# implementation modelled after ref: <https://github.com/rust-lang/rustup/pull/2783>
|
||||
@@ -194,7 +194,11 @@ jobs:
|
||||
export RUST_BACKTRACE=1
|
||||
export CARGO_TERM_COLOR=always
|
||||
if (test -z "\$FAULT"); then cargo nextest run --hide-progress-bar --profile ci --features '${{ matrix.job.features }}' || FAULT=1 ; fi
|
||||
if (test -z "\$FAULT"); then cargo nextest run --hide-progress-bar --profile ci --all-features -p uucore || FAULT=1 ; fi
|
||||
# There is no systemd-logind on FreeBSD, so test all features except feat_systemd_logind ( https://github.com/rust-lang/cargo/issues/3126#issuecomment-2523441905 )
|
||||
if (test -z "\$FAULT"); then
|
||||
UUCORE_FEATURES=\$(cargo metadata --format-version=1 --no-deps -p uucore | jq -r '.packages[] | select(.name == "uucore") | .features | keys | .[]' | grep -v "feat_systemd_logind" | paste -s -d "," -)
|
||||
cargo nextest run --hide-progress-bar --profile ci --features "\$UUCORE_FEATURES" -p uucore || FAULT=1
|
||||
fi
|
||||
# Test building with make
|
||||
if (test -z "\$FAULT"); then make PROFILE=ci || FAULT=1 ; fi
|
||||
# Clean to avoid to rsync back the files
|
||||
|
||||
+9
-1
@@ -1,7 +1,7 @@
|
||||
# coreutils (uutils)
|
||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||
|
||||
# spell-checker:ignore (libs) bigdecimal datetime serde bincode gethostid kqueue libselinux mangen memmap uuhelp startswith constness expl unnested
|
||||
# spell-checker:ignore (libs) bigdecimal datetime serde bincode gethostid kqueue libselinux mangen memmap uuhelp startswith constness expl unnested logind
|
||||
|
||||
[package]
|
||||
name = "coreutils"
|
||||
@@ -38,6 +38,14 @@ uudoc = ["zip", "dep:uuhelp_parser"]
|
||||
## Optional feature for stdbuf
|
||||
# "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it
|
||||
feat_external_libstdbuf = ["stdbuf/feat_external_libstdbuf"]
|
||||
# "feat_systemd_logind" == enable feat_systemd_logind support for utmpx replacement
|
||||
feat_systemd_logind = [
|
||||
"pinky/feat_systemd_logind",
|
||||
"uptime/feat_systemd_logind",
|
||||
"users/feat_systemd_logind",
|
||||
"uucore/feat_systemd_logind",
|
||||
"who/feat_systemd_logind",
|
||||
]
|
||||
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
|
||||
# NOTE:
|
||||
# * On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# spell-checker:ignore logind
|
||||
|
||||
[package]
|
||||
name = "uu_pinky"
|
||||
description = "pinky ~ (uutils) display user information"
|
||||
@@ -14,6 +16,9 @@ readme.workspace = true
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
feat_systemd_logind = ["uucore/feat_systemd_logind"]
|
||||
|
||||
[lib]
|
||||
path = "src/pinky.rs"
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use uucore::entries::{Locate, Passwd};
|
||||
use uucore::error::{FromIo, UResult};
|
||||
use uucore::libc::S_IWGRP;
|
||||
use uucore::translate;
|
||||
use uucore::utmpx::{self, Utmpx, time};
|
||||
use uucore::utmpx::{self, Utmpx, UtmpxRecord, time};
|
||||
|
||||
use std::io::BufReader;
|
||||
use std::io::prelude::*;
|
||||
@@ -137,7 +137,7 @@ fn idle_string(when: i64) -> String {
|
||||
})
|
||||
}
|
||||
|
||||
fn time_string(ut: &Utmpx) -> String {
|
||||
fn time_string(ut: &UtmpxRecord) -> String {
|
||||
// "%b %e %H:%M"
|
||||
let time_format: Vec<time::format_description::FormatItem> =
|
||||
time::format_description::parse("[month repr:short] [day padding:space] [hour]:[minute]")
|
||||
@@ -158,7 +158,7 @@ fn gecos_to_fullname(pw: &Passwd) -> Option<String> {
|
||||
}
|
||||
|
||||
impl Pinky {
|
||||
fn print_entry(&self, ut: &Utmpx) -> std::io::Result<()> {
|
||||
fn print_entry(&self, ut: &UtmpxRecord) -> std::io::Result<()> {
|
||||
let mut pts_path = PathBuf::from("/dev");
|
||||
pts_path.push(ut.tty_device().as_str());
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# spell-checker:ignore logind
|
||||
|
||||
[package]
|
||||
name = "uu_uptime"
|
||||
description = "uptime ~ (uutils) display dynamic system information"
|
||||
@@ -14,6 +16,9 @@ readme.workspace = true
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
feat_systemd_logind = ["uucore/feat_systemd_logind"]
|
||||
|
||||
[lib]
|
||||
path = "src/uptime.rs"
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# spell-checker:ignore logind
|
||||
|
||||
[package]
|
||||
name = "uu_users"
|
||||
description = "users ~ (uutils) display names of currently logged-in users"
|
||||
@@ -14,6 +16,9 @@ readme.workspace = true
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
feat_systemd_logind = ["uucore/feat_systemd_logind"]
|
||||
|
||||
[lib]
|
||||
path = "src/users.rs"
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let filename = maybe_file.unwrap_or(utmpx::DEFAULT_FILE.as_ref());
|
||||
|
||||
users = Utmpx::iter_all_records_from(filename)
|
||||
.filter(Utmpx::is_user_process)
|
||||
.filter(|ut| ut.is_user_process())
|
||||
.map(|ut| ut.user())
|
||||
.collect::<Vec<_>>();
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# spell-checker:ignore logind
|
||||
|
||||
|
||||
[package]
|
||||
name = "uu_who"
|
||||
description = "who ~ (uutils) display information about currently logged-in users"
|
||||
@@ -14,6 +17,9 @@ readme.workspace = true
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
feat_systemd_logind = ["uucore/feat_systemd_logind"]
|
||||
|
||||
[lib]
|
||||
path = "src/who.rs"
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use uucore::libc::{S_IWGRP, STDIN_FILENO, ttyname};
|
||||
use uucore::translate;
|
||||
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::utmpx::{self, Utmpx, time};
|
||||
use uucore::utmpx::{self, UtmpxRecord, time};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CStr;
|
||||
@@ -162,7 +162,7 @@ fn idle_string<'a>(when: i64, boottime: i64) -> Cow<'a, str> {
|
||||
})
|
||||
}
|
||||
|
||||
fn time_string(ut: &Utmpx) -> String {
|
||||
fn time_string(ut: &UtmpxRecord) -> String {
|
||||
// "%b %e %H:%M"
|
||||
let time_format: Vec<time::format_description::FormatItem> =
|
||||
time::format_description::parse("[month repr:short] [day padding:space] [hour]:[minute]")
|
||||
@@ -202,14 +202,14 @@ impl Who {
|
||||
utmpx::DEFAULT_FILE
|
||||
};
|
||||
if self.short_list {
|
||||
let users = Utmpx::iter_all_records_from(f)
|
||||
.filter(Utmpx::is_user_process)
|
||||
let users = utmpx::Utmpx::iter_all_records_from(f)
|
||||
.filter(|ut| ut.is_user_process())
|
||||
.map(|ut| ut.user())
|
||||
.collect::<Vec<_>>();
|
||||
println!("{}", users.join(" "));
|
||||
println!("{}", translate!("who-user-count", "count" => users.len()));
|
||||
} else {
|
||||
let records = Utmpx::iter_all_records_from(f);
|
||||
let records = utmpx::Utmpx::iter_all_records_from(f);
|
||||
|
||||
if self.include_heading {
|
||||
self.print_heading();
|
||||
@@ -248,7 +248,7 @@ impl Who {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_runlevel(&self, ut: &Utmpx) {
|
||||
fn print_runlevel(&self, ut: &UtmpxRecord) {
|
||||
let last = (ut.pid() / 256) as u8 as char;
|
||||
let curr = (ut.pid() % 256) as u8 as char;
|
||||
let runlevel_line = translate!("who-runlevel", "level" => curr);
|
||||
@@ -268,7 +268,7 @@ impl Who {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_clockchange(&self, ut: &Utmpx) {
|
||||
fn print_clockchange(&self, ut: &UtmpxRecord) {
|
||||
self.print_line(
|
||||
"",
|
||||
' ',
|
||||
@@ -282,7 +282,7 @@ impl Who {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_login(&self, ut: &Utmpx) {
|
||||
fn print_login(&self, ut: &UtmpxRecord) {
|
||||
let comment = translate!("who-login-id", "id" => ut.terminal_suffix());
|
||||
let pidstr = format!("{}", ut.pid());
|
||||
self.print_line(
|
||||
@@ -298,7 +298,7 @@ impl Who {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_deadprocs(&self, ut: &Utmpx) {
|
||||
fn print_deadprocs(&self, ut: &UtmpxRecord) {
|
||||
let comment = translate!("who-login-id", "id" => ut.terminal_suffix());
|
||||
let pidstr = format!("{}", ut.pid());
|
||||
let e = ut.exit_status();
|
||||
@@ -316,7 +316,7 @@ impl Who {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_initspawn(&self, ut: &Utmpx) {
|
||||
fn print_initspawn(&self, ut: &UtmpxRecord) {
|
||||
let comment = translate!("who-login-id", "id" => ut.terminal_suffix());
|
||||
let pidstr = format!("{}", ut.pid());
|
||||
self.print_line(
|
||||
@@ -332,7 +332,7 @@ impl Who {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_boottime(&self, ut: &Utmpx) {
|
||||
fn print_boottime(&self, ut: &UtmpxRecord) {
|
||||
self.print_line(
|
||||
"",
|
||||
' ',
|
||||
@@ -345,7 +345,7 @@ impl Who {
|
||||
);
|
||||
}
|
||||
|
||||
fn print_user(&self, ut: &Utmpx) -> UResult<()> {
|
||||
fn print_user(&self, ut: &UtmpxRecord) -> UResult<()> {
|
||||
let mut p = PathBuf::from("/dev");
|
||||
p.push(ut.tty_device().as_str());
|
||||
let mesg;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# spell-checker:ignore (features) bigdecimal zerocopy extendedbigdecimal tzdb zoneinfo
|
||||
# spell-checker:ignore (features) bigdecimal zerocopy extendedbigdecimal tzdb zoneinfo logind
|
||||
|
||||
[package]
|
||||
name = "uucore"
|
||||
@@ -113,6 +113,7 @@ fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
||||
fsext = ["libc", "windows-sys"]
|
||||
fsxattr = ["xattr"]
|
||||
lines = []
|
||||
feat_systemd_logind = ["utmpx", "libc"]
|
||||
format = [
|
||||
"bigdecimal",
|
||||
"extendedbigdecimal",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// file that was distributed with this source code.
|
||||
// features ~ feature-gated modules (core/bundler file)
|
||||
//
|
||||
// spell-checker:ignore (features) extendedbigdecimal
|
||||
// spell-checker:ignore (features) extendedbigdecimal logind
|
||||
|
||||
#[cfg(feature = "backup-control")]
|
||||
pub mod backup_control;
|
||||
@@ -74,6 +74,8 @@ pub mod fsxattr;
|
||||
pub mod selinux;
|
||||
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
|
||||
pub mod signals;
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
pub mod systemd_logind;
|
||||
#[cfg(all(
|
||||
unix,
|
||||
not(target_os = "android"),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
//
|
||||
// spell-checker:ignore logind
|
||||
|
||||
//! Aims to provide platform-independent methods to obtain login records
|
||||
//!
|
||||
//! **ONLY** support linux, macos and freebsd for the time being
|
||||
@@ -39,6 +41,9 @@ use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
use crate::features::systemd_logind;
|
||||
|
||||
pub use self::ut::*;
|
||||
|
||||
// See the FAQ at https://wiki.musl-libc.org/faq#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?
|
||||
@@ -278,18 +283,30 @@ impl Utmpx {
|
||||
/// This will use the default location, or the path [`Utmpx::iter_all_records_from`]
|
||||
/// was most recently called with.
|
||||
///
|
||||
/// On systems with systemd-logind feature enabled at compile time,
|
||||
/// this will use systemd-logind instead of traditional utmp files.
|
||||
///
|
||||
/// Only one instance of [`UtmpxIter`] may be active at a time. This
|
||||
/// function will block as long as one is still active. Beware!
|
||||
pub fn iter_all_records() -> UtmpxIter {
|
||||
let iter = UtmpxIter::new();
|
||||
unsafe {
|
||||
// This can technically fail, and it would be nice to detect that,
|
||||
// but it doesn't return anything so we'd have to do nasty things
|
||||
// with errno.
|
||||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||
setutxent();
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
{
|
||||
// Use systemd-logind instead of traditional utmp when feature is enabled
|
||||
UtmpxIter::new_systemd()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "feat_systemd_logind"))]
|
||||
{
|
||||
let iter = UtmpxIter::new();
|
||||
unsafe {
|
||||
// This can technically fail, and it would be nice to detect that,
|
||||
// but it doesn't return anything so we'd have to do nasty things
|
||||
// with errno.
|
||||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||
setutxent();
|
||||
}
|
||||
iter
|
||||
}
|
||||
iter
|
||||
}
|
||||
|
||||
/// Iterate through all the utmp records from a specific file.
|
||||
@@ -298,8 +315,20 @@ impl Utmpx {
|
||||
///
|
||||
/// This function affects subsequent calls to [`Utmpx::iter_all_records`].
|
||||
///
|
||||
/// On systems with systemd-logind feature enabled at compile time,
|
||||
/// if the path matches the default utmp file, this will use systemd-logind
|
||||
/// instead of traditional utmp files.
|
||||
///
|
||||
/// The same caveats as for [`Utmpx::iter_all_records`] apply.
|
||||
pub fn iter_all_records_from<P: AsRef<Path>>(path: P) -> UtmpxIter {
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
{
|
||||
// Use systemd-logind for default utmp file when feature is enabled
|
||||
if path.as_ref() == Path::new(DEFAULT_FILE) {
|
||||
return UtmpxIter::new_systemd();
|
||||
}
|
||||
}
|
||||
|
||||
let iter = UtmpxIter::new();
|
||||
let path = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
|
||||
unsafe {
|
||||
@@ -336,6 +365,8 @@ pub struct UtmpxIter {
|
||||
/// Ensure UtmpxIter is !Send. Technically redundant because MutexGuard
|
||||
/// is also !Send.
|
||||
phantom: PhantomData<std::rc::Rc<()>>,
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
systemd_iter: Option<systemd_logind::SystemdUtmpxIter>,
|
||||
}
|
||||
|
||||
impl UtmpxIter {
|
||||
@@ -345,13 +376,144 @@ impl UtmpxIter {
|
||||
Self {
|
||||
guard,
|
||||
phantom: PhantomData,
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
systemd_iter: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
fn new_systemd() -> Self {
|
||||
// PoisonErrors can safely be ignored
|
||||
let guard = LOCK.lock().unwrap_or_else(|err| err.into_inner());
|
||||
let systemd_iter = match systemd_logind::SystemdUtmpxIter::new() {
|
||||
Ok(iter) => iter,
|
||||
Err(_) => {
|
||||
// Like GNU coreutils: graceful degradation, not fallback to traditional utmp
|
||||
// Return empty iterator rather than falling back (GNU coreutils also returns 0 when /var/run/utmp is not present, so we don't need to propagate the error here)
|
||||
systemd_logind::SystemdUtmpxIter::empty()
|
||||
}
|
||||
};
|
||||
Self {
|
||||
guard,
|
||||
phantom: PhantomData,
|
||||
systemd_iter: Some(systemd_iter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type that can hold either traditional utmpx records or systemd records
|
||||
pub enum UtmpxRecord {
|
||||
Traditional(Box<Utmpx>),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
Systemd(systemd_logind::SystemdUtmpxCompat),
|
||||
}
|
||||
|
||||
impl UtmpxRecord {
|
||||
/// A.K.A. ut.ut_type
|
||||
pub fn record_type(&self) -> i16 {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.record_type(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.record_type(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_pid
|
||||
pub fn pid(&self) -> i32 {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.pid(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.pid(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_id
|
||||
pub fn terminal_suffix(&self) -> String {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.terminal_suffix(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.terminal_suffix(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_user
|
||||
pub fn user(&self) -> String {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.user(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.user(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_host
|
||||
pub fn host(&self) -> String {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.host(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.host(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_line
|
||||
pub fn tty_device(&self) -> String {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.tty_device(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.tty_device(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_tv
|
||||
pub fn login_time(&self) -> time::OffsetDateTime {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.login_time(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.login_time(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A.K.A. ut.ut_exit
|
||||
///
|
||||
/// Return (e_termination, e_exit)
|
||||
pub fn exit_status(&self) -> (i16, i16) {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.exit_status(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.exit_status(),
|
||||
}
|
||||
}
|
||||
|
||||
/// check if the record is a user process
|
||||
pub fn is_user_process(&self) -> bool {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.is_user_process(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.is_user_process(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonicalize host name using DNS
|
||||
pub fn canon_host(&self) -> IOResult<String> {
|
||||
match self {
|
||||
UtmpxRecord::Traditional(utmpx) => utmpx.canon_host(),
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
UtmpxRecord::Systemd(systemd) => systemd.canon_host(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for UtmpxIter {
|
||||
type Item = Utmpx;
|
||||
type Item = UtmpxRecord;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
{
|
||||
if let Some(ref mut systemd_iter) = self.systemd_iter {
|
||||
// We have a systemd iterator - use it exclusively (never fall back to traditional utmp)
|
||||
return systemd_iter.next().map(UtmpxRecord::Systemd);
|
||||
}
|
||||
}
|
||||
|
||||
// Traditional utmp path
|
||||
unsafe {
|
||||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||
let res = getutxent();
|
||||
@@ -362,9 +524,9 @@ impl Iterator for UtmpxIter {
|
||||
// call to getutxent(), so we have to read it now.
|
||||
// All the strings live inline in the struct as arrays, which
|
||||
// makes things easier.
|
||||
Some(Utmpx {
|
||||
Some(UtmpxRecord::Traditional(Box::new(Utmpx {
|
||||
inner: ptr::read(res as *const _),
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! library ~ (core/bundler file)
|
||||
// #![deny(missing_docs)] //TODO: enable this
|
||||
//
|
||||
// spell-checker:ignore sigaction SIGBUS SIGSEGV extendedbigdecimal myutil
|
||||
// spell-checker:ignore sigaction SIGBUS SIGSEGV extendedbigdecimal myutil logind
|
||||
|
||||
// * feature-gated external crates (re-shared as public internal modules)
|
||||
#[cfg(feature = "libc")]
|
||||
@@ -67,6 +67,8 @@ pub use crate::features::ranges;
|
||||
pub use crate::features::ringbuffer;
|
||||
#[cfg(feature = "sum")]
|
||||
pub use crate::features::sum;
|
||||
#[cfg(feature = "feat_systemd_logind")]
|
||||
pub use crate::features::systemd_logind;
|
||||
#[cfg(feature = "time")]
|
||||
pub use crate::features::time;
|
||||
#[cfg(feature = "update-control")]
|
||||
|
||||
Reference in New Issue
Block a user