## Summary
`updatectl features` failed because it expected the old feature JSON
fields,
while `systemd-sysupdate` now returns `id`, `isEnabled`, and
`documentation`
as an array.
This change:
- Aligns `updatectl` with the current feature JSON schema.
- Unifies JSON output between `systemd-sysupdate`, D-Bus, and Varlink.
- Adds support for multiple `Documentation=` entries for Features and
Components.
- Exposes and parses `appstream` and `suggested` metadata.
- Propagates condition-evaluation errors through `condition_test_list()`
and
`feature_is_suggested()`.
- Reports suggestion-evaluation failures instead of silently treating
them as
`false`.
- Improves JSON dump error handling.
- Expands `TEST-72-SYSUPDATE` coverage and fixes its unknown-field
assertion.
- Documents the `DescribeFeature()` JSON field transition in `NEWS`.
- Keeps the existing Component name list for `sysupdated` compatibility.
The `name`/`enabled` to `id`/`isEnabled` transition is intentional and
documented as a v262 payload change.
Fixes#43603
Component definitions may contain multiple Documentation= URLs, but the
components command previously displayed only the first URL in text output.
Use TABLE_STRV_WRAPPED to display all component documentation URLs in the
table, making the full documentation list visible to users.
Add integration test coverage for multiple Documentation= assignments.
The feature JSON payload switched to `id`, `isEnabled`, and `appstream`,
and made `documentation` an array of URLs.
`updatectl` still parsed legacy table-derived names and a single URL.
This broke feature JSON parsing and made output inconsistent.
Features without documentation could also cause a null dereference.
Align `updatectl` with the current schema and preserve all
documentation URLs across sysupdate, D-Bus, and Varlink payloads.
Propagate condition evaluation errors through `condition_test_list()`
and `feature_is_suggested()`, with consistent JSON error handling and
tests.
The `suggested` field is now optional and always a boolean when
present; it is omitted rather than set to an error string when
SuggestOn*= conditions cannot be evaluated.
Add `suggested` metadata and document the field transition.
Follow-up for 3e34ccf6df.
Follow-up for 75a247d4de.
Follow-up for 40e727c93b.
Fixes#43603
--recursive=no is not silently ignored when all tasks are counted,
as claimed: cgtop rejects it with an error unless -P or -k is used,
since task counting is always recursive. Also document that --cpu
without an argument is equivalent to --cpu=time.
Make condition_test_list() return negative errno on condition
evaluation errors instead of coercing them to false. This allows
callers to distinguish between a condition that evaluated to false
and a condition that failed to evaluate.
Callers that only need a boolean result explicitly check for a
positive return value, preserving their current behavior.
Add comprehensive unit tests covering error propagation with trigger
and non-trigger conditions. Tests use uname_architecture() to get the
runtime architecture instead of native_architecture(), ensuring they
work correctly in environments where binaries run under architecture
emulation.
Originally https://github.com/systemd/systemd/pull/43595 was
targeted for v263 and the NEWS entry was for v263 too.
However it got merged now for v262 (for the right reasons) so
this commit updates the NEWS file.
Standard Linux POSIX ACL tag values are 0x00, 0x01, 0x02, 0x04, 0x08,
0x10 and 0x20. ELEMENTSOF(tag_map) evaluates to 33, which is one past
the last valid index 32.
The new unit is nice, but it might not be appropriate in all scenarios. It
requires a new kernel, which might be known to not be available, and also
changes sysctl kernel.core_pattern at runtime, which might not be desired.
Replace static enablement by a normal [Install] section and add the unit
to presets to enable it by default.
The awkward instructions in the man page are simplified and adjusted.
The json mode was returning the string `none` when unmerged and
an array for merged extensions. Return an empty array instead.
Instead of branching on the output format, always add an empty strv
for an unmerged hierarchy: the table code renders an empty strv as
the ersatz string ("-") in text mode and as an empty array in json
mode. This also changes the human-readable output from "none" to "-".
It also updated the TEST-50-DISSECT.sysext.sh test. Technically
this is a compatbility break but its also arguably a bugfix.
The PolicyPCR digest is not always the complete input to an offline
signature. PolicyAuthorize also includes the hashed policy reference.
This is now required for NVPCRs to work in the initrd.
Expose the complete data as "tbs" (to-be-signed) and use it when ukify
matches offline signatures. Keep "pol" as the compatibility fallback.
Fixes: https://github.com/systemd/systemd/issues/43635
Follow-up for f0f3687700
OCI pulls create .mstack machine images, which vmspawn can
discover via -M. Since vmspawn only supports directory and disk
images, this currently reaches assert_not_reached() and aborts.
Return EOPNOTSUPP with a readable error instead.
Fixes#41541
The color list is extended by searching, suggestions from llms, etc.
In particular, I included "-ish" variants of common colors: "greenish",
"reddish", etc. They are easy to identify and I think quite distinct from
the base versions.
For weather words, add more stuff. Some are not commonly used, but mostly
easy to spell. In the worst case, people learn more about dreary weather.
For worlds, add a bunch of solar system moons, asteroids, trans-Neptunian
objects, and mythological realms.
This raises the number of combinations from 68'880 to appox. 2'300'000,
making collisions and repeats less likely.
Split out of https://github.com/systemd/systemd/pull/42983
## Problem
`dns_packet_append_name()` fails with `-EEXIST` once a name that was
first appended at an offset beyond RFC 1035's 14-bit compression-pointer
range (0x4000) occurs again in the same packet: the fall-through that
re-emits the labels trips `hashmap_ensure_put()` on the existing map
entry, and the whole packet build fails — reported by the caller as, for
example, "Failed to build reply packet: File exists".
The map is meant to hold only offsets a pointer can actually reference.
Names past that range were being inserted anyway, so they were dead
weight that could never be pointed at, and re-encountering one turned
into an error.
## Fix
Keep names at offsets ≥ 0x4000 out of the compression map entirely.
"Mapped implies referenceable" becomes an invariant of the map rather
than something callers have to reason about, and the `-EEXIST` collision
cannot arise. A re-occurrence of such a name is emitted as labels, still
compressed from the first suffix that *is* mapped in range — which is
the only encoding RFC 1035 section 4.1.4 permits in that case, since a
pointer must address ≤ 0x3FFF.
## Impact
Any packet large enough to push a name past 16KiB hits this — it is not
mDNS-specific, though that is where it was found: a DNS-SD announcement
covering a few hundred published services crosses the threshold easily,
and every record after it fails to encode.
## Test
`test-dns-packet-append` gains coverage for the boundary: a name first
written past the pointer range and then repeated, the same for a full
resource record, the rollback path when an append fails mid-name, and
the map invariant itself (asserted over the whole map, so a future
change that inserts an unreferenceable offset is caught rather than
silently reintroducing the bug).
## Testing status
Builds clean with `-Dwerror=true`; `meson test --suite resolve` passes
(19 ok, 1 skipped) at both commits.
dns_packet_append_name() appends a name's labels and then the root byte
that terminates it. A failure on that last byte returned directly,
leaving both the labels on the packet and the compression entries this
call had added -- entries naming offsets the caller then truncates away.
Take the same rollback path as every other failure in the function.
Independent of the map invariant fixed before it: this is reachable
whether or not any offset exceeded the pointer range.
The existing case only covered a fully-uncompressed re-occurrence. Every
DNS-SD name shares an in-range suffix, so also pin that a name first
seen beyond the pointer range still compresses from its mapped suffix --
one label plus a two-byte pointer per occurrence, map unchanged, and the
pointer addressing the suffix's prior occurrence rather than any
in-range octet.
A padding helper puts the next append at a chosen offset, for the two
sides of the boundary: at 0x3FFF the name is still mapped and re-occurs
as a pointer whose wire bytes are checked independently of the parser;
one past it, it is not mapped.
dns_packet_append_name() added every name it appends to the packet's
compression map, including names written at offsets >= 0x4000 that an
RFC 1035 compression pointer (14 bits) can never reference. Such
entries are not just useless -- when the same name occurs again, the
lookup finds it, the pointer cannot be emitted, the code falls through
to appending the labels again, and the re-insertion into the map then
fails the whole packet build with hashmap_ensure_put() returning
-EEXIST.
This is easily triggered with mDNS: an unsolicited DNS-SD announcement
covering a zone with a few hundred services exceeds 16KiB, and
dns_scope_announce() then fails with "Failed to build reply packet:
File exists".
This lifts the failure only: dns_scope_announce() still builds the whole
zone into one packet and hands it to the socket as a single datagram,
fragmented as the link requires. Splitting announcements across
MTU-sized packets, as RFC 6762 section 17 asks, is done separately.
Only insert a name into the map if its offset is pointer-expressible,
making "mapped implies referenceable" a map invariant. Re-occurrences of
out-of-range names are emitted as labels again -- the only encoding RFC
1035 section 4.1.4 permits in that case -- still compressed from the
first suffix that is mapped in range.
The lookup restates that range as control flow rather than asserting it.
Under -Db_ndebug=true, which CI builds, assert() is
__builtin_unreachable(), so an out-of-range entry would put 0xC000 | n
truncated to 14 bits on the wire -- a pointer to an arbitrary earlier
offset -- where falling through emits the labels instead. This layer is
shared with unicast DNS, DoT and LLMNR.
The pointer's tag bits and its offset bound are now named once, in
dns-def.h next to the other wire-format constants rather than in
dns-packet.h, since the format is RFC 1035's and not this packet
abstraction's -- dhcp-message.c open-codes the same two bits to parse
compressed names out of DHCP options. Compile-time checks pin that the
two partition the 16-bit field and that the tag is its top two bits,
which is what lets the reader take it from the first octet alone.
The offset's other bound becomes control flow along with it: it was an
assert(), which under -Db_ndebug=true is no check at all, for the bound
whose violation puts a pointer to the wrong bytes on the wire. It is
also tightened to the start of the name being appended rather than the
end of the packet, which is the "prior occurrence" RFC 1035 section
4.1.4 allows a pointer to address and what dns_packet_read_name()
enforces on the way in.
Add regression tests, including one through dns_packet_append_rr() --
the path the reported failure came from, where the name append is nested
inside the rdlength backpatch and two further rollbacks -- and one for
the reader's label-type dispatch, which the renamed constants touch.
Fixes#43645.
`FailureAction=exit` propagates the exit status of the unit's main
process. When there is none, `emergency_action()` left `m->return_value`
at its initial 0, so the manager exited 0 for a unit that failed —
indistinguishable from an orderly shutdown to whoever supervises it.
machinectl clone --read-only ignored the requested read-only mode when
cloning a foreign-UID-owned directory because the unprivileged clone path
did not receive the read_only argument.
Pass the flag through and apply the immutable bit via the already-open
destination directory fd after copying. Keep this best-effort, matching
the ordinary directory clone fallback, so failure to set the attribute
does not turn a completed copy into a reported clone failure.
Add a regression test using foreign-0 rather than a fixed UID. Verify
the cloned image's ReadOnly property, copied contents and ownership, and
that the immutable directory rejects new entries. Run this subtest only
when file attributes and the existing rootless nspawn prerequisites are
available, leaving the other machined tests unchanged.
unprivileged_clone() returns success when mountfsd_make_directory() fails.
Consequently, callers can report a successful clone even though the
destination directory was never created.
Return the original error instead of zero so the failed clone is
reported to the caller.
Add a regression test that places a regular file without an image suffix
at the destination path. Such a file is ignored by image discovery but
prevents directory creation. Check that cloning reports failure without
changing the file, then succeeds after the conflicting file is removed.
The Varlink service enumerates image targets and needs a private mount
namespace while dissecting images. Keep its sandbox in sync with
systemd-sysupdated.service.
Follow-up to 31616d00efFixes#43604
Vacuuming unlinks and deallocates archived journal files. A reader
may have already selected an entry from such a file, so final
validation can fail after candidate selection succeeded.
If the selected file is now unlinked, remove it and retry candidate
selection. Preserve errors from linked files, including the EIO
reported after SIGBUS recovery from a truncated mapping.
network-generator writes generated command line configuration below
the alternate root passed with --root=, but network.* credentials
were copied to fixed host /run paths.
Build the credential target directories with arg_root before passing
them to pick_up_credentials(). This keeps normal boot behavior
unchanged while making credential output land in the requested root.
manager_recv() sizes its buffer with next_datagram_size_fd(), which peeks at
the datagram with MSG_PEEK|MSG_TRUNC. That peek makes the kernel verify the UDP
checksum - which is the whole point of using it over FIONREAD - and hence also
drop the datagram when the checksum is bad. The datagram that woke the event
source can thus be gone by the time we peek at it, and the peek returns EAGAIN.
manager_recv() returned that error, while the recvmsg_safe() below, which can
fail the same way, already treats it as "no packet". The callers on the mDNS,
LLMNR and stub listening sockets pass a negative return on to the event loop,
which disables the event source, so a single datagram with a bad checksum -
something anyone on the link can send - stops resolved from receiving on that
socket for good. on_dns_packet() completes the transaction with -EAGAIN instead
of retrying it.
So return 0 on a transient error here too, like the other users of the helper
on an event source do. Disconnect errors are left as they are: they show up
here from ICMP errors on the connected UDP socket, and on_dns_packet() handles
them.
Fixes#43442.
A `.swap` unit can end up **active but unloadable, with no device**, and
stopping such a unit aborts PID 1.
### What it looks like
```
$ systemctl show 'dev-disk-byx2dpartlabel-primary.swap' -p LoadState,ActiveState,What
LoadState=not-found
ActiveState=active
What= # <-- no device
$ systemctl stop 'dev-disk-byx2dpartlabel-primary.swap'
systemd[1]: About to execute: /usr/sbin/swapoff # <-- no argument
systemd[1]: Caught <ABRT>, from our own process.
systemd[1]: Freezing execution.
```
PID 1 stays alive but frozen, so the machine stops answering `systemctl`
and never reboots.
### How it happens
1. systemd creates one `.swap` unit per udev device link of every device
in `/proc/swaps`.
2. A device link gets repointed at another device while the swap stays
on. `/proc/swaps` doesn't change, so nothing re-examines the unit — it
stays active.
3. The next `daemon-reload` drops all units and rebuilds them.
Enumeration from `/proc/swaps` no longer produces this name, so only the
deserialization brings the unit back: by name, with no fragment on disk.
4. `swap_load()` only calls `swap_add_extras()` for units that loaded a
fragment or are backed by `/proc/swaps`, so this one gets neither a
`What=` nor a patched `ExecContext`.
5. `swap_coldplug()` restores its active state anyway.
6. Shutdown stops it. `unit_stop()` deliberately permits stopping units
whose load failed, so it reaches `exec_spawn()` and trips the
`PrivateVarTmp=` assertion in `exec_context_serialize()`.