profile/systemd-osc-context: also escape $USER and $HOSTNAME
Claude pointed out that while rather unlikely, it is possible for the USER and
HOSTNAME environment variables to contain problematic characters that require
escaping. Now that that escaping no longer involves the fork+exec overhead of
calling sed, let's ensure those fields do get put through the escape routine.
Refactor __systemd_osc_context_escape so that it not only takes the value and
prints that, but also the format specifier used for emitting the field. That
way, we avoid an extra subshell (i.e. fork+pipe and all that overhead), and
combined with the previous commit, we now only spawn one subshell instead of
two per start= sequence.
That could be reduced to zero, if the "callers" were rewritten to something
like
but that will mean that the sequence is not emitted with a single write()
system call [which isn't really guaranteed currently either, just very likely],
so some background process could end up writing to the middle of the sequence,
thus losing that output and mangling the OSC3008 info. This in turn could be
overcome by building the whole sequence in a shell variable using 'printf -v'
and only printing at the end, but that would be a somewhat invasive change.
profile/systemd-osc-context: fold emitting cwd= field into __systemd_osc_context_common
Now that the %s specifier used to embed the common fields in the output appears
immediately before the cwd= field, we can just emit that cwd= field as part of
the common fields.
profile/systemd-osc-context: emit type= field first
Not because it is required by the specification, but it will make the following
patches simpler. Also, the fact that the spec explicitly does call out that
type= can appear at the end or in the middle suggests that people would
normally expect it to appear at the beginning.
profile/systemd-osc-context: don't do arithmetic expansion on systemd_exitstatus
There's really no point in having bash do arithmetic expansion on the
systemd_exitstatus variable, i.e. have it convert the string to a number, do no
actual arithmetic, then convert it back to a string to be used as the argument to
the printf, which will again convert it to a number for the %d specifier, and
finally emit it as a decimal. Also, it deviates for no obvious reason from how
it is passed to printf in the "probably died by signal" case just above.
profile/systemd-osc-context: do not zero-pad pid value
The %.20d specifier was introduced in 2d738a0aee ("profile/systemd-osc-context:
Enforce length limits"). But, for numeric conversions, the precision is not an
upper bound, but rather a lower bound on the output width, padding as necessary
with 0 on the left. So as-is, this does not in fact limit the output to at most
20 characters.
Of course, in practice, pids on linux are never greater than 2^22, and
certainly never larger than what would fit in a 20-digit decimal. On the other
hand, that more or less guarantees that the pid= field is always emitted with
12+ leadings zeroes, which is a bit silly. Moreover, a leading 0 can cause a
parser to treat it as octal.
Since $$ does expand to the PID in decimal, just print that as a string, with the
enforced 20 character limit.
profile/systemd-osc-context: do not use sed for escaping
Bash is perfectly capable of performing the simple substitutions needed for
escaping according to the OSC 3008 spec, so there is no need for the fork+exec
and other overhead of calling sed.
In fact, when writing a test for ensuring that this is a drop-in replacement, I
found out that the current sed method is flawed: If $PWD contains newline
characters, they are passed through unchanged, because sed obviously is
line-oriented.
I do not know which bash version started supporting
${foo//pattern/replacement}, but I ran the below on all Debian images from docker
hub going back to Debian 6 (EOL 2016), carrying BASH_VERSION =
4.1.5(1)-release, and they all succeeded. Since the logic otherwise relies on
PROMPT_COMMAND being an array variable, which happened in 5.1, this should be
all good.
do_test() {
local r0="$1"
local r1="$(using_sed "$r0")"
local r2="$(pure_bash "$r0")"
local s0="$(printf '%s' "$r0" | od -A x -t x1z -w40 | head -n1)"
local s1="$(printf '%s' "$r1" | od -A x -t x1z -w40 | head -n1)"
local s2="$(printf '%s' "$r2" | od -A x -t x1z -w40 | head -n1)"
if [ "$r1" != "$r2" ] || [ "$s1" != "$s2" ] ; then
echo "Input: $s0"
echo "sed: $s1"
echo "bash: $s2"
ret=1
fi
}
# The last cases show that the existing function doesn't actually work in
# the case of $PWD containing a newline character, because sed is
# line-oriented, so a newline character will never be replaced.
if [ "$1" = "all" ] ; then
do_test $'embedded \n newline'
do_test $'ending in newline\n'
fi
Nowadays, varlink is used to control systemd-udevd. Let's drop the
legacy socket.
Note, the existence of /run/udev/control socket is widely used in both
our code and external projects. Also, the dependency to
systemd-udevd-control.socket is widely used in many projects. Hence, we
need to create a symlink to the socket file and .socket unit file.
networkctl,networkd: add --no-reconfigure flag to networkctl reload
Add a new --no-reconfigure flag to 'networkctl reload' that reloads
.network and .netdev files from disk without reconfiguring any network
interfaces. This may be useful to avoid reconfiguring multiple interfaces
simultaneously when multiple .network files are updated, or when an updated
.network file is applied to multiple interfaces.
On the networkd side, manager_reload() gains a reconfigure_links parameter
that gates the per-link reconfiguration loop. A new io.systemd.Network.Reload
varlink method is added that exposes this as an optional reconfigureLinks
boolean (defaults to true). Both plain 'networkctl reload' and
'--no-reconfigure' now unconditionally call this method first. If an older
networkd returns MethodNotFound, plain reload falls back to
io.systemd.service.Reload for backward compatibility; '--no-reconfigure'
fails with a clear error in that case.
core/dbus: do not block the manager on GetId during bus (re-)connection
bus_init_api() issued a synchronous GetId call on every API bus
(re-)connection to decide whether saved subscription state could be
coldplugged onto the new connection.
If the D-Bus socket unit is listening while the message bus daemon
behind it is gone, connect() succeeds against the socket backlog but
nothing answers the authentication handshake. The synchronous call
then blocks PID 1 for BUS_AUTH_TIMEOUT (90 seconds by default), and
queued bus operations can trigger repeated reconnection attempts.
This was observed during shutdown as roughly 15 minutes of teardown
progressing only in 90-second intervals.
Query the instance ID asynchronously on every connection. Defer API
setup until the reply is processed, so saved subscriptions are
validated and coldplugged before new subscription requests can arrive.
If the query cannot be queued or its reply is invalid, discard the
unvalidated state and expose the API without blocking the manager.
Reset the live bus ID on every connection and serialize pending bus ID
and subscription state across reload and reexec. During daemon-reload,
preserve state that was already awaiting the asynchronous reply while
discarding the duplicate state produced by the reload itself.
Also remove the now-unused synchronous bus_get_instance_id() helper.
password-quality-util-passwdqc: restore password-quality-util.h include
suggest_passwords() references the N_SUGGESTIONS macro, which is defined in
password-quality-util.h. Commit ff33c8f87d ("Extend test-dlopen-so to also
cover cases when built without support") introduced the per-backend split
headers: for the pwquality backend it added the new
password-quality-util-pwquality.h include while keeping password-quality-util.h,
but for the passwdqc backend it replaced password-quality-util.h with
password-quality-util-passwdqc.h (which only pulls in shared-forward.h). As a
result N_SUGGESTIONS is no longer declared in the passwdqc translation unit and
the build fails when the passwdqc backend is enabled.
The passwdqc backend is not exercised by the default CI, so this went
unnoticed. Add the include back, matching the pwquality backend.
Let's make timer prop handling less special, and more like path/socket
handling. Let's move the checks for at least one OnXYZ= setting to a
common place at the end of parsing, instead of explicit checks for each
property.
Yu Watanabe [Sun, 22 Feb 2026 16:01:45 +0000 (01:01 +0900)]
udev: drop home-grown udev-ctrl socket
Nowadays, varlink is used to control systemd-udevd. Let's drop the
legacy socket.
Note, the existence of /run/udev/control socket is widely used in both
our code and external projects. Also, the dependency to
systemd-udevd-control.socket is widely used in many projects.
Hence, we need to create a symlink to the socket file and .socket unit
file.
Removed note clarifying that portable services are only for system services and not user services, and changed comparisons to "system services" with just "services". With newer systemd versions, `systemd-portabled` can be run as a user service.
dlopen-note: downgrade all dlopen notes in libsystemd.so and libsystemd-shared.so
Since all executables now manage their required dlopen notes directly
within their own source code with explicit priority levels, it is no
longer necessary to declare high-priority dlopen notes in the shared
libraries themselves.
Since 4c0d8d967300fde858f83ec4b361db19e3e257c8, most dlopen notes are
set at the beginning of the executables. Let's manage all dlopen notes
there, rather than setting them where dlopen is called.
Note that the only exceptions are the LIBBPF_NOTE for networkd and
nsresource. Since dlopen_bpf() is wrapped in an `#if` guard, the notes
are instead set within the corresponding functionality.
As a result, the DLOPEN_FOO() wrapper macros are no longer needed and
can be dropped completely.
The purpose of this header was to provide MIT-0 sources that can be copied
and pasted liberally. Including an LGPL-2.1+ header from it deafeats its
purpose. Make it self-standing again.
network: silence false warning about unitialized variable
[1177/3647] Compiling C object systemd-networkd.p/src_network_networkd-bridge-vlan.c.o
In function ‘bridge_vlan_append_set_info’,
inlined from ‘bridge_vlan_set_message’ at ../src/network/networkd-bridge-vlan.c:257:21:
../src/network/networkd-bridge-vlan.c:162:28: warning: ‘untagged’ may be used uninitialized [-Wmaybe-uninitialized]
162 | if (untagged == u)
| ^
../src/network/networkd-bridge-vlan.c: In function ‘bridge_vlan_set_message’:
../src/network/networkd-bridge-vlan.c:111:14: note: ‘untagged’ was declared here
111 | bool untagged, pvid_is_untagged;
| ^~~~~~~~
sysinstall: don't ask whether to erase a disk that contains no partitions
When the target disk carried no partitions, the installer still asked:
Please type 'keep' to install the OS in addition to what the disk
already contains, or 'erase' to erase all data on the disk:
The question is meaningless in that case: there's nothing on the disk
worth preserving, and both answers lead to the same result.
Modify fsystemd-repart to report in the dry-run reply of
io.systemd.Repart.Run() the number of partitions currently on the disk.
The field is only included if the existing partition table was actually
read, i.e. in the 'refuse' and 'allow' empty modes, and omitted
otherwise.
Make systemd-sysinstall skip the erase question if the reply positively
indicates that there are no partitions, proceeding as if 'keep' was
selected.
sysinstall: suppress 'no' prompt to begin installation
The user has to type 'yes', but it doesn't mean that the default of
'no' is ever useful. Suppress it, so the user doesn't have to press
backspace twice.
udev-util: bound leading whitespace skip in udev_replace_whitespace (#42757)
udev_replace_whitespace() is documented to read at most 'len' bytes from
'str':
- the strspn() skip of leading whitespace stops at a non-space byte or
NUL, not at 'len'
- ata_id passes the space padded, non-NUL-terminated ATA IDENTIFY
model/serial/fw fields
- an all-blank field reads off the end of the 512-byte hd_driveid stack
struct
Capped the skip to 'len'; existing outputs are unchanged. Added a
regression test.
discover-image: don't ignore symlinks to raw images
Since 5c6bb289990ba53898cbc62db6e732ecb9dc87ac image_discover() uses
chaseat() to chase the path to the image. This however breaks the raw
image check in image_make() as "path" is now not the symlink itself, but
the symlink target.
The endswith(path, ".raw") check is now performed on "/.../foo.squashfs"
instead of "/.../foo.raw", making it false and thus ignoring the image
symlink completely.
Address this by also checking if the pretty name is set - if so, and the
path is a regular file, the caller must've been image_find() or
image_discover() which already checked if the original path ends in .raw
and is a regular file.
repart: log allocation failure at debug level in Varlink service mode
When systemd-sysinstall probes whether an installation would fit by
calling io.systemd.Repart.Run() in dry-run mode, systemd-repart runs as
a child process sharing sysinstall's stderr. When the requested
partitions didn't fit, context_ponder() logged its failure at LOG_ERR
before vl_method_run() converted it into a structured Varlink error
(InsufficientFreeSpace or DiskTooSmall), which the client then reports
to the user in its own words. The internal message hence appeared
interleaved with the user-facing report:
Can't fit requested partitions into available free space (1.9G), refusing.
The selected disk is not large enough for an OS installation.
The size of the selected disk is 0B, but a minimal size of 15.7G is required.
Log at LOG_DEBUG when running as a Varlink service, and keep LOG_ERR
for CLI invocations, where this message is the primary error report
shown to the user.
repart: report the actual block device size in currentSizeBytes
The io.systemd.Repart interface documents the currentSizeBytes field as
the size of the selected block device, both in the Run() method's
dry-run reply and in the InsufficientFreeSpace and DiskTooSmall errors.
The implementation however filled it in from the "current size"
computed by determine_auto_size(), which means something else entirely:
the size of the image as it currently exists, i.e. the GPT metadata
overhead plus the sizes of all existing partitions — a metric designed
for growing image files with --size=auto. For a disk that is being
partitioned from scratch (i.e. carries no partition table yet) that
value is 0.
As a result, when systemd-sysinstall was pointed at a blank 2G disk
that is too small for the OS installation, it reported:
The selected disk is not large enough for an OS installation.
The size of the selected disk is 0B, but a minimal size of 15.7G is required.
Report context->total instead, i.e. the actual size of the block
device, which is also the value the DiskTooSmall check compares the
required size against. Do this in all three places that send
currentSizeBytes, matching the documented semantics of the field.
repart,dissect: explicitly support DDIs that are both signed *and* encrypted (#43009)
This is a pretty relevant usecase: preparing an image on some trusted
host, submitting it to some other host that authenticates it and
decrypts it, and consumes it only then.
machined: Allow user ids in open_shell for machine-dbus
Previously "machinectl shell --uid=1000 <container>" resulted in a
sucessful drop into a shell in the respective target machine. After
commit a9e9288288567beae57337ae903dd3b6c774001c this is no longer the
case.
udev-util: bound leading whitespace skip in udev_replace_whitespace
The function documents that at most 'len' bytes are read from 'str',
but the leading whitespace skip used strspn(), which is bounded only
by a non-whitespace byte or a NUL. ata_id passes the space padded,
non-NUL-terminated ATA IDENTIFY fields, so an all-blank model reads
past the buffer. Use strnspn() to cap the skip to 'len'.
Like strspn(), but reads at most 'n' bytes from the input. strspn()
is bounded only by a non-matching byte or a NUL, so it over-reads a
buffer that is all matching bytes and not NUL terminated within 'n'.
For various cases it is interesting to both sign and encrypted a file
system, for example to prepare it on one host and provide it to another.
Let's explicitly support preparing this in systemd-repart via setting
both Verity= and Encrypt=.
Pass relax_extension_release_check through the directory extraction
path instead of hardcoding false. Directory extensions now honor the
same --force relaxation as dissected images.
Before:
directory image extraction always used strict extension-release name
checks. --force relaxed other extension paths but still rejected a
renamed directory extension with matching metadata.
sysext: validate work directory metadata before removal
unmerge_hierarchy() joined the persisted work_dir value directly with
--root=. An empty value therefore resolved to the root itself and was passed
to rm_rf().
Require the decoded metadata to name a non-empty, safe, normalized relative
path before constructing the removal target. Add coverage using a disposable
root with deliberately emptied metadata.
shift-uid: close consumed directory fds on early return
recurse_fd() consumes each directory fd passed to it, but it has no
cleanup set up until after take_fdopendir() succeeds.
Errors and skipped procfs, sysfs, or read-only subtrees therefore leak the
incoming fd.
path_is_root_at() returns a negative errno when it cannot determine whether
a target is the root file system. rm_rf_at() treats those errors as a
negative answer and continued with destructive operations.
Propagate root-check errors before modifying the target, while preserving
REMOVE_MISSING_OK for an absent path.
vconsole: reject empty layout during keymap conversion with error
A leading-dash console keymap or leading-comma X11 layout can produce an
empty layout while converting between the two formats. find_converted_keymap()
then asserts on it, allowing malformed input to abort callers such as localed.
Return EINVAL for an empty derived layout instead.
resolved: preserve unchanged question on asymmetric redirect
dns_question_cname_redirect() returns no replacement when a question
already matches a CNAME target or a DNAME does not apply. If only one of
the UTF-8 and IDNA questions redirects, dns_query_cname_redirect() then
installs NULL for the unchanged side.
Keep a reference to the original question on whichever side does not
redirect, and update the existing asymmetric DNAME test to verify it.
MHDDaemonWrapper_free() unconditionally unrefs both event sources, but
setup_microhttpd_server() could return before initializing either pointer.
Zero-initialize the wrapper so cleanup after MHD startup failures safely
unrefs NULL.
journal-remote: remove disabled compression entry before freeing
compression_config_put() frees the COMPRESSION_NONE value when a later
algorithm is selected, but lefts the map entry pointing at it. Remove the
entry first so subsequent parsing, mangling, and teardown cannot access or
free stale memory.
network: cancel netlink calls for detached requests
Store the asynchronous netlink slot in the Request object and release it
when the request is detached. This cancels the pending callback instead of
keeping the request alive until its reply arrives.
The multipath route comparator looks up every nexthop in the first
route and hence compares each entry with itself. Compare both ordered
nexthop sets instead, including weights, as the hash function does.
Paul Meyer [Tue, 14 Jul 2026 11:26:56 +0000 (13:26 +0200)]
tpm2-util: keep the measurement log's torn-write marker intact
The userspace measurement log carries a sticky-bit marker while a writer
is between updating a measurement register and appending the matching
record, so that a writer dying in between leaves the log detectably
incomplete.
However, the next successful writer used to clear the marker again after
appending its own record, erasing the evidence that an earlier writer
had died and the log is still missing a record. Keep the marker set in
that case instead; the new record is appended and synced regardless. The
marker likewise stays set if the measurement itself fails, so that any
non-clean completion remains flagged: a spurious flag on a failed but
harmless measurement is preferable to erasing evidence of a real gap,
and PCR replay stays authoritative either way.
Finally, warn when systemd-pcrlock loads a marked log, so the resulting
PCR validation failures come with a hint at their cause.
Before, the field listing ignored the unit filter and listed unrelated
units, while the normal journal query applied the filter.
sd_journal_query_unique() cannot represent journalctl filters such as
unit, boot, time, cursor, or grep filters. Walking the journal line by
line would make field listing linear in the number of entries and
duplicate unique-value handling.
Keep the scope-option predicate next to add_filters(), and reject field
listings combined with options that actually limit the journal.
Display-only options such as --pager-end are left alone.
The target parameter length is added to two headers and aligned before
allocation. Check each operation and reject payloads that cannot be
represented by the device-mapper ioctl header.
gaih_addrtuple.addr has room for an IPv6 address, while IPv4 uses only
the first struct in_addr. Express the remaining range directly in terms
of that type so static analysis can prove the access remains within the
array.
* 88c420c621 Install new files for upstream build
* 5b7f67b86a Update changelog for 261.1-3 release
* 3f91c8567c NEWS: update about removal of sd-tpm dep
* e13ba94737 Drop obsolete TODO
* b0c5def366 systemd: downgrade systemd-tpm and mount to recommends
* e9eb859a3a systemd-tpm: depend on tpm-udev for rules and tmpfiles.d
* 42f74bb110 Drop obsolete comment from d/control
* fc40986137 debian/extra/network: use NamePolicy=keep mac on USB wifi devices
Kai Lüke [Wed, 15 Jul 2026 07:09:13 +0000 (16:09 +0900)]
oci-util: Don't fall back to default registry for explicit registries
When a reference specifies a registry like ghcr.io or quay.io for which
we don't have a registry config file, the fallback forced the default
registry which is wrong.
Only use the default as fallback when there is no explicit registry.
config_add_entry_loader_auto() returns NULL when the Windows loader
is missing. With reboot-for-bitlocker enabled, the caller still
assigned e->call and could dereference NULL.
This can happen when reboot-for-bitlocker is enabled but the ESP does
not contain EFI/Microsoft/Boot/bootmgfw.efi.
Before:
systemd-boot could dereference NULL while building the menu
Aritra Basu [Sun, 7 Jun 2026 06:04:11 +0000 (02:04 -0400)]
network: add IPv4ProxyARPAddress= and consolidate proxy ARP/NDP handling
This adds an IPv4 counterpart to `IPv6ProxyNDPAddress=` for adding
manual entries to the kernel's IPv4 neighbour proxy table (check via
`ip -4 neighbour show proxy dev <dev>`). systemd-networkd only exposed
`IPv4ProxyARP=` for per-interface `proxy_arp` sysctl (automatic proxy
ARP) with no way to manage manual entries from a .network file.
To avoid duplicating the IPv6 proxy NDP code path, both families are
now combined into a single new `networkd-neighbor-proxy` module. The
IPv6 behaviour is preserved: `IPv6ProxyNDPAddress=` still implies
`IPv6ProxyNDP=yes` unless `IPv6ProxyNDP=` is explicitly disabled and
entries are still dropped if the kernel has no IPv6 support.
The same rule is applied to `IPv4ProxyARPAddress=`. It implies
`IPv4ProxyARP=yes` when the sysctl is not explicitly set and has no
effect if `IPv4ProxyARP=` has been set to false.
This keeps the user model symmetric and predictable across both
families: a single per-address setting that turns on the matching
per-interface sysctl automatically, while still letting system
administrators opt out by setting the boolean explicitly to false.
Note that the IPv4 manual NTF_PROXY entries installed here would
actually function without `proxy_arp` (unlike IPv6, where `proxy_ndp`
gates the manual entries); the implication is kept for symmetry with
`IPv6ProxyNDPAddress=` and is now called out explicitly in the man
page, together with the fact that enabling `proxy_arp` also activates
interface-wide automatic proxy ARP for routed-toward addresses on
connected subnets.
Parser-time validation rejects addresses the kernel would refuse:
the ANY/null address for both families, IPv4 and IPv6 multicast
and the IPv4 limited broadcast 255.255.255.255.
shared/copy: simplify argument convention for reflink_range
reflink_range() would treat size==UINT64_MAX as "copy everything", but
only if the offsets were 0. There are only two callers: one always passes
a fixed size, the other translated size==UINT64_MAX to 0. Make things
more uniform by always treating size==UINT64_MAX the same as size==0.