Skip to content

utmpx: fall back to systemd-logind on Linux when traditional utmp is empty/unavailable#11579

Open
mattsu2020 wants to merge 5 commits into
uutils:mainfrom
mattsu2020:users
Open

utmpx: fall back to systemd-logind on Linux when traditional utmp is empty/unavailable#11579
mattsu2020 wants to merge 5 commits into
uutils:mainfrom
mattsu2020:users

Conversation

@mattsu2020

@mattsu2020 mattsu2020 commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • On Linux, continue to prefer the traditional utmp/utmpx file when it is usable.
  • When the default utmp file is missing or empty (e.g. recent Ubuntu where systemd no longer populates /var/run/utmp), try the systemd-logind backend instead.
  • Load libsystemd.so.0 dynamically with libloading, without adding a strong runtime dependency.
  • Fall back to the traditional utmp iterator when the library cannot be loaded, a required symbol is missing, or systemd-logind initialization fails.
  • Keep the existing traditional utmp/utmpx behavior on non-Linux platforms.
  • Remove the feat_systemd_logind opt-in feature and its propagation through users, who, pinky, and uptime.
  • Read the boot time (who -b / uptime -s) from the kernel's /proc/stat btime instead of the /var/lib/systemd/random-seed modification time, so both report the actual kernel boot time.

Motivation

On Ubuntu releases where systemd no longer populates /var/run/utmp, utilities that read only traditional utmp records can produce empty output even when users are logged in. Keeping systemd-logind support behind an opt-in feature would leave default and published Linux binaries affected unless downstream packagers explicitly enabled it.

Dynamic loading allows the Linux default to consult systemd-logind when the traditional utmp file is empty, while keeping the binary usable on systems without libsystemd.

Runtime behavior

On Linux, iter_all_records() / iter_all_records_from():

  1. Check whether the traditional utmp file is usable: it exists, is a regular file, is at least as large as one utmpx record, and can be opened. (On musl, the traditional path is treated as never usable, so systemd-logind is always attempted.)
  2. If the traditional file is usable, use the traditional utmp iterator.
  3. If it is missing or empty, try to load the runtime SONAME libsystemd.so.0 and resolve the required sd-login symbols.
  4. Read active sessions through systemd-logind.
  5. If loading the library, resolving a symbol, or systemd-logind initialization fails, initialize the traditional utmp iterator instead.

SystemdLoginApi retains the Library handle for as long as the copied function pointers can be used.

iter_all_records_from() continues to use traditional utmp for custom paths. For the default utmp path, it applies the same usable-file check and only tries systemd-logind when the file is missing or empty. When falling back from systemd-logind, it explicitly resets utmpxname() to the path that was passed in, so a previously selected custom database is not reused accidentally.

Dependency behavior

libloading is enabled through uucore's utmpx feature and is compiled only for Linux. The resulting binary has no NEEDED entry for libsystemd.so.0; libsystemd is opened only when login records are requested.

Verification

Verified in a Linux container:

cargo build --features feat_os_unix --bin coreutils
cargo test -p uucore --features utmpx systemd_failure_uses_explicit_fallback_path

The Linux fallback regression test passes.

Also verified the users-enabled multicall binary has no strong libsystemd dependency:

cargo build --no-default-features --features users --bin coreutils
readelf -d target/debug/coreutils | grep -i systemd
# no output

ldd target/debug/coreutils | grep -i systemd
# no output

Additional checks:

cargo check -p uucore --features utmpx --tests
cargo check -p uucore --no-default-features --target x86_64-unknown-linux-gnu
cargo check -p uucore --features utmpx --tests --target x86_64-unknown-linux-gnu
cargo clippy -p uucore --features utmpx --tests --target x86_64-unknown-linux-gnu -- -D warnings

Scope

This PR addresses login-record discovery on Linux systems without a populated utmp database. It does not implement stale utmp entry filtering or login PID liveness checks. DNS canonicalization for systemd-derived records used by who --lookup and pinky --lookup can be handled separately.

Related to #3219; it does not close that issue.

@oech3

oech3 commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Is this dynamically linked againsyt libsystemd.so? If so, this should be disabled by default.

@mattsu2020

mattsu2020 commented Apr 1, 2026

Copy link
Copy Markdown
Contributor Author

Is this dynamically linked againsyt libsystemd.so? If so, this should be disabled by default.

Change to fallback only

@mattsu2020 mattsu2020 changed the title feat: enable systemd-logind by default and add utmp fallback users: add utmp fallback Apr 1, 2026
@github-actions

github-actions Bot commented Apr 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Note: The gnu test tests/tail/tail-n0f is now being skipped but was previously passing.

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Note: The gnu test tests/pr/bounded-memory is now being skipped but was previously passing.
Note: The gnu test tests/rm/many-dir-entries-vs-OOM is now being skipped but was previously passing.
Congrats! The gnu test tests/tail/pipe-f is now passing!
Skip an intermittent issue tests/cut/bounded-memory (was skipped on 'main', now failing)

@github-actions

github-actions Bot commented Apr 7, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/tail/tail-n0f. tests/tail/tail-n0f is passing on 'main'. Maybe you have to rebase?
Skip an intermittent issue tests/tail/inotify-dir-recreate (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/pr/bounded-memory (passes in this run but fails in the 'main' branch)
Note: The gnu test tests/cut/bounded-memory is now being skipped but was previously passing.
Note: The gnu test tests/rm/many-dir-entries-vs-OOM is now being skipped but was previously passing.
Note: The gnu test tests/seq/seq-epipe is now being skipped but was previously passing.
Note: The gnu test tests/env/env-signal-handler was skipped on 'main' but is now failing.

@github-actions

github-actions Bot commented Apr 17, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Note: The gnu test tests/tail/pipe-f is now being skipped but was previously passing.
Congrats! The gnu test tests/rm/many-dir-entries-vs-OOM is now passing!
Note: The gnu test tests/env/env-signal-handler was skipped on 'main' but is now failing.

@codspeed-hq

codspeed-hq Bot commented Jun 10, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 339 untouched benchmarks
⏩ 46 skipped benchmarks1


Comparing mattsu2020:users (0c44ff5) with main (4988134)

Open in CodSpeed

Footnotes

  1. 46 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@oech3

oech3 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Is there any way to achieve full feature without runtime strong libsystemd.so dependency?
I think libselinux.so is not serious, but this should be part of binaries published at here.
Just parsing systemd files manually?

@oech3

oech3 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Or can we believe soname compatibility of systemd and publish them?

@mattsu2020
mattsu2020 marked this pull request as draft June 27, 2026 05:04
@mattsu2020 mattsu2020 changed the title users: add utmp fallback users: dynamically load libsystemd with utmp fallback Jun 27, 2026
@mattsu2020

Copy link
Copy Markdown
Contributor Author

Is there any way to achieve full feature without runtime strong libsystemd.so dependency? I think libselinux.so is not serious, but this should be part of binaries published at here. Just parsing systemd files manually?

Since I changed it to dynamic load, I'll test to see if this works.

@oech3

oech3 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

And remove feat_systemd_logind if succeed.

@oech3

oech3 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

It seems pure Rust solution is possible: https://github.com/lucab/zbus_systemd (thought maintainer might not want to add such dep...)

@HackingRepo

Copy link
Copy Markdown
Contributor

nice, ok

@mattsu2020 mattsu2020 changed the title users: dynamically load libsystemd with utmp fallback utmpx: prefer systemd-logind on Linux with utmp fallback Jun 27, 2026
@mattsu2020 mattsu2020 changed the title utmpx: prefer systemd-logind on Linux with utmp fallback utmpx: fall back to systemd-logind on Linux when traditional utmp is empty/unavailable Jun 27, 2026
@mattsu2020
mattsu2020 marked this pull request as ready for review June 27, 2026 07:43
@mattsu2020

Copy link
Copy Markdown
Contributor Author

Code changes are mostly complete

@oech3

oech3 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

An additional reason that we want to have this PR: #13212

@Ecordonnier

Copy link
Copy Markdown
Collaborator

It seems pure Rust solution is possible: https://github.com/lucab/zbus_systemd (thought maintainer might not want to add such dep...)

See #8376 (comment) where I explained why I did not use zbus in the implementation of systemd-logind. It is a huge crate.

…empty

On Linux, keep preferring the traditional utmp/utmpx file when it is
usable. When the default utmp file is missing or empty (e.g. recent
Ubuntu where systemd no longer populates /var/run/utmp), try the
systemd-logind backend instead, and fall back to the traditional utmp
iterator when libsystemd cannot be loaded, a required symbol is
missing, or systemd-logind initialization fails.

libsystemd.so.0 is loaded dynamically via libloading (enabled through
uucore's `utmpx` feature, Linux only), so the resulting binary has no
NEEDED entry for libsystemd and remains usable on systems without it.

Additional changes:
- Remove the `feat_systemd_logind` opt-in feature and its propagation
  through `users`, `who`, `pinky`, and `uptime`.
- Read the boot time (`who -b` / `uptime -s`) from the kernel's
  /proc/stat `btime` instead of the /var/lib/systemd/random-seed mtime.
- Keep the existing traditional utmp/utmpx behavior on non-Linux.

Related to uutils#3219.
The feat_systemd_logind feature was removed, so the grep -v filter is a
no-op. Apply the same cleanup already done for the OpenBSD workflow.
Note why traditional_utmp_is_usable stats before opening (opening a
non-regular file such as a FIFO could block), and document the window
in the fallback-path test where LOCK is released while libc's global
utmp path still points at the custom file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants