- test-random-util is reworked to not use sqrt()
- pretty-print.c inlines ceil() so libm doesn't have
to be linked into libshared
- We add fno-math-errno to allow inlining of more math
functions by not requiring standard math functions to
set errno on invalid input.
exp10() has a symbol version > 2.34 on latest glibc. To allow
dropping our baseline required glibc runtime version to <= 2.34,
let's add our own version to prevent pulling in the newer symbol
from glibc.
Each dlopen_*() wrapper kept its dl handle as a file-scope
'static void *xxx_dl = NULL;' even though only the wrapper itself
ever referenced it. Move each one inside the corresponding function
so its scope matches its actual use, leaving the rest of each
translation unit free of the unused file-scope name.
In pcre2-util.c the assert(pcre2_dl) in pattern_matches_and_log()
becomes assert(sym_pcre2_match), which carries the same invariant
(pattern_compile_and_log() ran dlopen_pcre2()).
Add COREDUMP_CODE to the fields captured by systemd-coredump. This makes
it possible for system administrators to filter coredumps based on si_code,
which describes the reason why a given signal was sent.
For example, to find processes killed due to invalid permissions
(SEGV_ACCERR):
$ journalctl COREDUMP_SIGNAL=11 COREDUMP_CODE=2
I've decided to add the value of si_code to the 'Signal: ' line of
coredumpctl info:
Signal: 11 (SEGV) si_code: SEGV_ACCERR
Filesystems like xfs, btrfs, gfs2 and ocfs2 handle quotas internally
and do not need external quotacheck/quotaon services. When usrquota or
grpquota mount options are used in fstab for these filesystems,
generator_hook_up_quotacheck() falls through to the !fstype_needs_quota()
branch and emits a misleading warning that quotas are "not supported"
when they actually work fine — the kernel handles them internally.
Add fstype_has_internal_quota() to return early with a debug message,
and adopt a tri-state return convention so the caller skips quotaon
when quotacheck was not needed.
The buggy code path was introduced in #24824 and #24880.
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
Add signal_code_to_string() in signal-util.c and cover the si_code values
defined in libc's siginfo-consts.h. Fall back to the numeric value when no
symbolic name is known.
Co-developed-by: Codex (GPT-5) <noreply@openai.com>
Signed-off-by: Emanuele Rocca <emanuele.rocca@arm.com>
- **bpf-util: rename from bpf-dlopen, unify version-specific symbol
handling**
- **cryptsetup: dlopen libcryptsetup in tokens**
- **tree-wide: dlopen libpam in pam plugins**
- **test-bus-marshal: dlopen() glib and libdbus instead of linking
directly**
- **lock-util: Simplify timeout for lock_generic_with_timeout()**
- **color-util: simplify hsv_to_rgb, fix rgb_to_hsv negative-hue wrap**
- **tree-wide: Use our own macros instead of fabs()/fmax()/fmin()**
- **locale-util: dlopen() libintl instead of linking against it**
- **home: Use log2u64() over log2()**
- **meson: drop libdl, threads, and librt dependencies**
- **libc: Use dlsym() from a constructor instead of weak symbols**
- **libc: Make sure C23 versions of strtol(), sscanf() are not used**
Our baseline glibc is 2.34, which merged libdl, libpthread (the
dependency('threads') target), and librt into libc. Empty .so/.a stubs
remain for backward compatibility with old binaries, but new builds
resolve dl_*, pthread_*, mq_*, timer_*, etc. directly from libc.
On musl the same libraries are likewise empty stubs.
Drop the libdl, threads, and librt entries from every meson.build, and
remove the now-stale 'Libs.private: -lrt -pthread' from libudev.pc.in
since both flags resolve to empty link-time stubs on glibc 2.34+ and
musl.
Verified with readelf -d that libsystemd.so, libudev.so, and systemd no
longer carry DT_NEEDED entries for libdl/libpthread/librt.
dgettext() lives in libc on glibc and in libintl.so.8 on musl with
gettext. Resolve it via dlsym() so neither configuration produces a
hard link-time dependency on libintl: try libintl.so.8 first and fall
back to RTLD_DEFAULT (which finds dgettext in libc on glibc).
The _() macro now expands to a runtime check that returns the
untranslated string if dlopen_libintl() has not run successfully, so
callers don't have to gate every translatable message on a runtime
check. pam_systemd_home — currently the only consumer of _() — calls
dlopen_libintl() best-effort from each PAM entry point.
The meson find_library('intl') dance is replaced with a has_header()
check; the only thing we need at build time is the prototype.
The getxattr(2) man page only enumerates xattr-specific errors (ENODATA,
ENOTSUP, ERANGE, E2BIG, ...) in its own ERRORS section, but at the
bottom of that section notes that "the errors documented in stat(2) can
also be returned." stat(2) returns -ENOENT when a component of the path
does not exist, so any xattr lookup against a path can fail with -ENOENT
exactly the same way as -ENODATA — both mean "there is nothing here for
me to read." The previous definition of ERRNO_IS_NEG_XATTR_ABSENT()
reflected only the directly-enumerated errors and missed -ENOENT, so
callers that should semantically swallow "the xattr is absent" instead
bubbled -ENOENT up as a hard error.
The most visible consequence on real fleets has been systemd-journald
spamming dmesg with one line per dispatched log message whenever a
unit's cgroup directory cannot be found at the time
client_context_read_log_filter_patterns() is called — typically inside
containers whose journald observes clients whose unit cgroup is no
longer present in its view (cgroup-namespace boundary, unit teardown
race, transient sub-scope already collapsed back to its unit cgroup,
etc.). The same bug pattern lurks at every other cgroup-xattr callsite:
systemd-oomd reading user.oomd_avoid / user.oomd_omit / user.oomd_ooms
on cgroups it is concurrently killing; killall reading
user.survive_final_kill_signal during shutdown; cg_is_delegated() /
cg_has_coredump_receive() / cgroup_get_managed_oom_kill_last(); etc. For
these, "path is gone" is by construction the same answer as "xattr is
not set" — there is no way for the user to have attached an xattr to a
path that does not exist.
A quick survey of non-cgroup callers (src/portable/portable.c,
src/home/{homework-luks,user-record-util}.c,
src/random-seed/random-seed-tool.c, src/basic/os-util.c) confirms they
all operate on fds or on paths whose absence is already the desired
silent-skip outcome, so widening the macro to also fold in -ENOENT does
not change observable behavior at any other site.
Extend test-xattr-util's getxattr_at_malloc test with an explicit
non-existent-path lookup that asserts ERRNO_IS_NEG_XATTR_ABSENT() now
matches, alongside the pre-existing non-existent-xattr (-ENODATA) check.
This bothered me for a while, but I didn't think too much about it and just
copied the existing usage pattern. But it really doesn't make sense. We expect
the compiler to align the section properly. But if it didn't align it, applying
alignment after the fact would just cause our pointer to point to the middle
of the structure. That'd be even worse than a misaligned pointer.
Similarly, when doing pointer arithmetic, p++ should really result in a value
with the appropriate alignment. This is the basic principle of C pointer
addition. So we really shouldn't try to adjust the pointer ourselves. At most,
we can assert that it is indeed aligned in tests.
The BPF kprobe that fires on user namespace destruction is the only
thing
that triggers registry cleanup, so any time it doesn't run — ring buffer
overflow, kprobe missing, fdstore entry dropped outside our cleanup path
— a registry entry is left behind forever.
Stamp each registry entry with the kernel's unique namespace identifier
(NS_GET_ID, kernel ≥ 6.13) at allocation time. At manager startup, after
the existing fdstore→registry sweep, walk the registry and ask the
kernel
to look each namespace up by id via open_by_handle_at() on nsfs; if the
lookup returns -ESTALE the namespace is gone and we release the entry.
Old entries written before this change carry no identifier and are left
alone.
Add a namespace_open_by_id() helper for the lookup. The kernel restricts
open_by_handle_at() on nsfs to processes in the initial user namespace,
collapsing both permission denials and dead namespaces onto -ESTALE; the
helper refuses early with -EPERM outside the initial user namespace
so callers can tell the two apart.
The BPF kprobe that fires on user namespace destruction is the only thing
that triggers registry cleanup, so any time it doesn't run — ring buffer
overflow, kprobe missing, fdstore entry dropped outside our cleanup path
— a registry entry is left behind forever.
Stamp each registry entry with the kernel's unique namespace identifier
(NS_GET_ID, kernel ≥ 6.13) at allocation time. At manager startup, after
the existing fdstore→registry sweep, walk the registry and ask the kernel
to look each namespace up by id via open_by_handle_at() on nsfs; if the
lookup returns -ESTALE the namespace is gone and we release the entry.
Old entries written before this change carry no identifier and are left
alone.
Add a namespace_open_by_id() helper for the lookup. The kernel restricts
open_by_handle_at() on nsfs to processes in the initial user namespace,
collapsing both permission denials and dead namespaces onto -ESTALE; the
helper refuses early with -EHOSTDOWN outside the initial user namespace
so callers can tell the two apart.
This function is used in a quite a few places and it calls getenv and
does a bit of parsing, so let's try to speed this up.
Also, mark it _const_. With the caching, it now always returns the same
result, so we can tell the compiler to eliminate subsequent invocations.
It'd be nice if we could not repeat the information about the option
list a second time. But I don't see a nice way to do this, since
(by design) with the macro approach, the macros must be intertwined
with the parse_argv() code. But that code in turn refers to a bunch
of variables, so lifting out the function is not immediately possible.
So I think it's best to keep the existing approach where we provide
a list of options, without additional context, and skip them using
a custom routine.
For libc syscall wrappers (pidfd_open, fsopen, openat2, etc.) we previously
gated the calls behind build-time HAVE_* checks. Replace these with shim
functions in src/libc/ that fall back to the raw syscall at runtime when the
loaded glibc lacks the symbol. The infrastructure lives in src/libc/libc-shim.h:
DEFINE_SYSCALL_SHIM falls back to a direct syscall, DEFINE_LIBC_SHIM returns
ENOSYS (for posix_spawn-family helpers that have no corresponding syscall), and
DEFINE_LIBC_ERRNO_SHIM sets errno=ENOSYS and returns -1 (for read/write-style
helpers). The weak reference to the libc symbol is bound via __asm__(\"name\")
rename so the bare libc identifier never appears as a C token — this avoids
both #undef boilerplate against override-header redirects and the resulting
-Wredundant-decls warning. Drop the corresponding cc.has_function() loop from
meson.build.
For optional libraries (libcryptsetup, libdw, libarchive), drop the per-symbol
HAVE_* checks. Always declare the prototypes, suppressing the redundant-decl
warnings via DISABLE_WARNING_REDUNDANT_DECLS and NOLINT, and resolve the symbols
after the main dlopen via a new DLSYM_OPTIONAL() helper that only assigns on
success. libcryptsetup's crypt_set_keyring_to_link / crypt_token_set_external_path
and libarchive's *_is_set wrappers use fallback functions as their pointer
initializers (returning -ENOSYS and 0 respectively), so call sites can invoke
the symbol unconditionally and just check for -ENOSYS where the \"not supported\"
distinction matters.
The same shim treatment applies to pidfd_spawn / posix_spawnattr_setcgroup_np
(src/libc/spawn.c) and epoll_pwait2 (src/libc/epoll.c), with corresponding
override headers in src/include/override/spawn.h and
src/include/override/sys/epoll.h. posix_spawn_wrapper() in process-util.c and
epoll_wait_usec() in sd-event.c now detect ENOSYS in the return value instead
of checking the function pointer, falling back to plain posix_spawn() and
epoll_wait() respectively. coredump-config and coredump-submit get a
dlopen_dw_has_dwfl_set_sysroot() helper. The kexec arch gate now uses
defined(__NR_kexec_file_load) directly; pidfd.h uses __has_include_next() to
decide whether to pull in glibc's header.
This lets binaries built against newer glibc / libcryptsetup / libdw /
libarchive headers still load and run on older targets where these symbols are
absent.
This makes sure that whenever we want to show the OS name we can show
the fancy name. Thus this moves the escaping/validation of the fancy
name out of hostnamed into generic code, and then makes use of it in
sysinstall,firstboot,prompt-util.
We have a number of *_unref_and_replace macros. One could think that
they are like the various free_and_replace variants, but they actually
create a new ref to the passed object. The free_and_replace variants
take ownership of the argument. This inconsistency is surprising. Rename
all those functions to have "_new_ref" at the end to make the difference
clear.
The mount.storage helper open-codes the conventional 64K UID/GID
delegation block size as 0x10000 / 0x10000U in four places. Several
other places in the tree do the same (nspawn's arg_uid_range default,
homed's mount setup, …), but with no shared name.
Add USERNS_RANGE_SIZE in user-util.h alongside UID_NOBODY and friends,
and switch storagectl over to it. Other call sites can adopt it
incrementally.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
If all entries of a menu prompt start with the same prefix, let's
preselect the prefix to enhance user experience.
This is particularly relevant when prompting for a disk to install
things on, as typically they all start with the same prefix /dev/, and
if there's only a single target medium discoverable, then we can even
fill it out fully.
Let's cap the number of states/patterns per query to something
reasonable, i.e. max 256 states and 4K patterns per query.
---
Also, use the env assignments limit in a couple more places.
This maps from TOS, which can be used for setsockopt(IPPROTO_IP, IP_TOS),
to socket priority, which can be used for setsockopt(SOL_SOCKET, SO_PRIORITY).
With this, we can set priority like the following:
```
uint8_t tos = IPTOS_CLASS_CS6;
setsockopt_int(fd, IPPROTO_IP, IP_TOS, tos);
setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, tos_to_priority(tos));
```
Co-authored with Google Gemini.
Turns out we can utilize this limit at a couple more places, so let's
move the previously defined limit constant to env-util.h and use it to
guard a couple more D-Bus methods. Also, bump it a bit, given it's meant
to be a safety cap that can't be hit in valid scenarios.
Follow-up for 7671b43cb8.