When /proc is bind-mounted read-only (common in mock/Koji buildroots,
containers, and other sandboxed environments), opening
/proc/sys/fs/binfmt_misc returns ELOOP if it is an automount point
that cannot be triggered in the read-only context.
Currently binfmt_mounted_and_writable() only handles ENOENT, so ELOOP
propagates as an error. This causes test-binfmt-util to fail with
SIGABRT and disable_binfmt() to log a spurious warning at shutdown.
Treat ELOOP and EACCES the same as ENOENT: binfmt_misc is not usably
available, return false.
Note: PR #37006 (merged April 2025) addressed ELOOP in the xstatfsat()
path, but the open() call in binfmt_mounted_and_writable() remained
unhandled.
Fixes#38070
FOREACH_ARRAY declares 'i' as the iterator but the body passed 'd' (the
array base) to block_device_done(). Since mfree() leaves the field NULL
after the first call, element 0 is freed repeatedly while elements
1..N-1 leak their node, symlinks strv, model, vendor and subsystem.
The bug predates the sanitizer-instrumented callers. PR #41776's new
systemd-storage-block daemon runs blockdev_list() under ASan/LSan in
TEST-87-AUX-UTILS-VM and exposes it (15 allocs / 804 bytes leaked per
ListVolumes request). The fix also benefits repart and blockdev_list's
internal CLEANUP_ARRAY cleanup.
Follow-up for 9f6b2745ea
option_parser_next_arg() is renamed to option_parser_peek_next_arg()
to match option_parser_consume_next_arg().
A new helper is added option_parser_get_arg(…, n). It is a common pattern
to only need a single arg, and getting an array and extracting a single
item from it is too verbose.
Generic Varlink API for services that hand out file descriptors to
storage volumes. Three methods: Acquire() returns an fd for a named
volume (optionally creating it from a template), ListVolumes()
enumerates available volumes, ListTemplates() enumerates supported
creation templates. Volume types follow kernel inode-type naming:
blk (block device), reg (regular file), dir (directory).
Intent is that multiple providers can sit behind AF_UNIX sockets in a
well-known directory and be consumed uniformly by nspawn, vmspawn,
the service manager (BindVolume=) and similar tools.
Merge the two blocks adding tests, since there seems to be
no obvious reason to have two separate blocks, as they both
contain tests from the same libraries.
This commit adds a simple version of io.systemd.Unit.StartTransient
for varlink. It is similar to the dbus version, but there is a key
difference:
1. Instead of building the unit from key/value properties it
takes a structured json object "UnitContext" with a "Service" field
inside.
It is also only implementing a minimal set of what can be done with a
service.
2. No aux units (for now)
3. When called with --more the varlink socket can notify about
state changes depending on the notify{Job,Unit}Changes parameter
This aligns to the json objects/format from
https://github.com/systemd/systemd/pull/39391
and to show how the format can be shared it adds a new
(minimal) `ServiceContext` that is now part of
`io.systemd.Unit.List()`.
It seems that clang reorders the entries in the options array that
originate from different functions, but not within a function. Using
"named namespaces" exclusively should sidestep the issue.
(A bigger hammer would be to sort the array. We *can* do this, since the
options have the increasing .id field. But that'd require duplicating
the memory or making it writable. Let's avoid this until we know for
sure that it's needed.)
This allows multiple option parsers to be defined in a single
compilation unit. We put the OPTION_NAMESPACE("name") to split
up the options. The basic implementation is similar to groups,
except that groups only matter for help display, while namespaces
matter for both help display and actual option parsing. When parsing,
we locate the appropriate range between the beginning of options
and the next namespace marker or between two namespace markers and
only look at that range.
Some devices (e.g. android phones running pmOS) cannot have their OEM
partition table altered without breaking the firmware, so the distros's
partitions live inside a nested GPT carved into one of the OEM
partitions. Exposing these subpartitions requires wrapping the outer
partition in a loop device with partscan enabled, since the kernel does
not go into nested partition tables.
systemd already detects this case in udev-builtin-blkid
(ID_PART_GPT_AUTO_ROOT_DISK_NEEDS_LOOP) and acts on with
systemd-loop@.service, but this fails towards the end.
loop_device_make_internal has an optimization where if the input is
already a block device with a matching sector size, it skips creating
a loop and just hands back the original fd. That's fine for whole disks
but wrong for partitions, which don't support partscan, so this causes
dissect_image to fail with EPROTONOSUPPORT.
This patch changes the behavior to only take the shortcut when the input
is a whole disk, or when partscan was not requested.
Co-Authored-By: Clayton Craft <clayton@craftyguy.net>
In 4339197f5d the helper to format -o/--opt=
was split out, but the indentation was for --long-options was messed up.
We'd print:
Options:
-h --help Show this help
--version Show package version
--no-ask-password Do not prompt for password
...
But we want
-h --help Show this help
--version Show package version
--no-ask-password Do not prompt for password
...
The prefix argument was arguably ugly, even if it allowed one alloc to be
avoided. Let's get rid of it and let the handler prefix the string as
appropriate. This makes other callers nicer too.
Fixup for 4339197f5d.
Let's track the state of the option parser in an explicit 'state' field.
Benefits:
1. We can make sure that a parser that got into a failure state will
be invalidated for good (i.e. further operations are guaranteed to
fail too).
2. As a side effect this cleans up the option_parse() return parameter
handling: we'll now always initialize ret_option/ret_arg when
returning >= 0, as per coding style.
ASAN showed a use-after-free error for systemd-vmspawn's
machine_register call because the reply got accessed and freed again
through _cleanup. The same problem exists in two
verb_machine_control_one/unregister_machine.
Fix these call sites to not set up _cleanup.
This came up when running systemd-vmspawn with ASAN to fix another bug
and thus I had to fix this overrun here first: The dispatch tables were
missing the terminator, add it.
In 012d87c1fc/cc8f398202 it was made possible for fork_notify() to
return in the child but at that point all FDs were closed and the
_cleanup path from the return causes assertion failures due to invalid
FDs in notify_event_source/event, leading to a vmspawn failing to start
with a SIGABRT logged in coredump.
Instead of TAKE_PTR on a bunch of things which is fragile, rather avoid
the return and instead add an explicit callback handler and guarantee
to exit directly after it. A userdata argument is also added but not
used yet I think it's quite normal to have for a callback.
This commit adds a simple version of io.systemd.Unit.StartTransient
for varlink. It is similar to the dbus version, but there is a key
difference:
1. Instead of building the unit from key/value properties it
takes a json object in the "service" parameter. It is also
only implementing a minimal set of what can be done with a
service (for now)
2. No aux units (for now)
3. When called with --more the varlink socket can notify about
unit job and state changes controlled via a bool on the
varlink call inputs: notify{Job,Unit}Changes
We use the new io.systemd.Job interface when outputing the
io.systemd.Unit.StartTransient result as it makes the output
nice and mirrors the input.
Note that the property names follow the D-Bus naming to make a
future "systemctl show" transition from D-Bus -> varlink easier.
Because UnitContext is now also used for the inputs we need
to make a bunch of fields `SD_VARLINK_NULLABLE` so that the
input is even accepted. This does not affect the output, it
is still fully populated, just the schema. The ID of UnitContext
is still required.
Thanks to ikruglov and Lennart for their excellent feedback on
this.
This commit creates a new varlink-io.systemd.Job.c file and puts
the job related varlink types into this file. Those will be
used by the upcoming io.systemd.Unit.StartTransient.
Note that the property names follow the D-Bus naming to make a
future "systemctl show" transition from D-Bus -> varlink easier.
Thanks to @ikruglov for suggesting this preparation commit and
to Lennart for suggesting the D-Bus compatibility considerations.
It was requested to make the 'c', 'opt', and 'arg' params the same, i.e.
defined through the FOREACH_OPTION macro. But we can't do that easily,
because 'c' was defined in the for loop definition, and we can only
define variables of the same type in that way. Also, in some cases we
need only 'c', in other cases with need 'c' and 'arg, in some cases 'c'
and 'opt', and in other cases all three. We'd need to either
conditionalize or mark those variables with _unused_ to deal with
compiler warnings. But a different approach works quite nicely: add
state.opt and state.arg to show the current option and it's argument.
(The short names are picked on purpose to reduce verbosity since those
are used a lot.)
In format-table.h, TABLE_IN_ADDR is commented as "Takes a union in_addr_union
(or a struct in_addr)". However, if we pass struct in_addr to table_add_many(),
the function reads more than the size of the struct.
There are various definitions of the root disk, let's suppress them all if
the flag is set. So far only the outermost is suppressed, which is a bit
weird, given it's "further away" from the rootfs.
For some reason, the old code had duplicate {"version", 'v'} entry in
the options table and a matching case that could not be reached.
Commandline parsing is tightened to reject any positional arguments.
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
I see --help output formatting as contiuation of @keszybz's work on
options.[ch], hence let's start to add some really basic infrastructure
to unify the --help output more.
Let's introduce some helpers for generating uniform --help texts with
some minimal ANSI styling.
This shortens the help() functions generally, and allows us to change
the style at a single, central place.
This mostly just follows our current styling for --help, but it makes
two updates to it:
1. The command line summary at the very top of the --help text is now
prefixes with a grey ">" character to indicate it's a command line.
2. The human language introductionary description/abstract right after
that command line is set in italics, to emphasize it's not dry,
technical, structural information, but more human friendly prose.
The AcquireQMP() method was a placeholder that always returned
EOPNOTSUPP, reserving room for a future id-rewriting QMP multiplex
proxy. The broader direction is for systemd-vmspawn to remain the single
source of truth for VM control rather than exposing raw QMP to clients.
Since AcquireQMP was the only method on io.systemd.QemuMachineInstance
(and AlreadyAcquired was its only error), remove the whole interface
along with the stub, and update the controlAddress field comment in
io.systemd.Machine to stop referencing it.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
The fdset id is a monotonic counter; an unsigned int is more than wide
enough today, but uint64_t matches the type of other QMP-internal counters
(e.g. job ids) and avoids any worry about wraparound on long-running
hosts. Update the storage in struct QmpClient, the qmp_client_next_fdset_id()
return type, and the corresponding caller in qmp_fdset_add(), switching the
sprintf format from %u to PRIu64.
No behavioural change.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Move the ImageFormat / DiskType enums and their string tables out of
vmspawn's private settings header into a new src/shared/machine-util,
and add parse_disk_spec() — the colon-prefix loop that turns
"[FORMAT:][DISKTYPE:]PATH" into the two enums plus a normalized path.
No behavior change for vmspawn. A follow-up machinectl attach-disk
change accepts the same syntax and consumes the shared helper.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Add qmp_client_set_userdata()/qmp_client_get_userdata() accessors
mirroring the sd_varlink API, and wire up the VmspawnQmpBridge as
userdata on the QmpClient so that command callbacks can retrieve it.
Add a setup_done flag to VmspawnQmpBridge, set by on_cont_complete()
when the VM has booted and all boot-time device setup is finished.
This lets command callbacks differentiate boot-time errors (fatal —
exit event loop) from runtime errors (recoverable — log and continue).
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
QEMU's QMP greeting is an unsolicited, informational, server-initiated advertisement
— it doesn't gate commands. The server accepts (and pipelines) commands the instant
the socket is open. We were using it as a trigger to build qmp_capabilities and
blocking callers via qmp_client_ensure_running() until the reply came back.
Build qmp_capabilities inside qmp_client_connect_fd() instead and let the JsonStream
output queue preserve FIFO ordering between it and any user command a subsequent
invoke() enqueues. That satisfies QEMU's only ordering requirement (cap must precede
other commands) without any blocking in the send path.
Fallout:
- Collapse QmpClientState to {RUNNING, DISCONNECTED}. The three HANDSHAKE_* states
and the QMP_CLIENT_STATE_IS_HANDSHAKE() macro go away.
- Drop qmp_client_dispatch_handshake(); fold greeting-drop into dispatch_reply as
a one-line shape check.
- Drop qmp_client_ensure_running() and its qmp_client_send() call site. send()
now only refuses when state == DISCONNECTED.
- The qmp_capabilities reply lands on an ordinary slot whose callback logs a
protocol-level error and force-disconnects if cap negotiation failed, matching
the old EPROTO behaviour at the same observable boundary.
- qmp_client_phase() no longer special-cases the old handshake states; it maps
directly to READING / AWAITING_REPLY based on whether slots are in flight.
Test updates:
- qmp_client_first_invoke_with_fd → qmp_client_invoke_with_fd. The scenario it
was pinned to (push_fd+invoke staging order) has been structurally impossible
since the QmpClientArgs rework in 8ad4adcb6f; eager-cap removes it a second
way. The test now covers end-to-end fd-passing on the first invoke, accepting
either recv carrying the single SCM_RIGHTS fd (AF_UNIX absorbs non-scm skbs
forward into the next scm-bearing skb's recv, so the fd may surface with cap
or add-fd depending on kernel scheduling — QEMU's FIFO fd queue handles either).
- qmp_client_invoke_failure_closes_fds restructured around the new invariant:
invoke no longer blocks and no longer returns ENOTCONN for a dead peer, so
the fd-leak assertion moves to "still open while the JsonStream queue owns it,
closed on client teardown" and the nested block is flattened into an explicit
qmp_client_unref().
QmpSlot now stores a back-reference to its QmpClient (mirroring sd_bus_slot)
and is exposed as a public refcounted type via qmp_slot_ref/qmp_slot_unref.
qmp_client_invoke() gains an optional QmpSlot **ret_slot out-parameter
matching sd_bus_call_async(): passing non-NULL hands back a reference whose
unref cancels the pending call (the callback is deregistered; a late reply
is logged and discarded as unknown-id).
Internally slots come in two flavours, following sd_bus's model: floating
(owned by the client's pending set, used when ret_slot is NULL) and
non-floating (ref held by the caller, slot holds a ref on the client).
qmp_slot_disconnect() centralizes the teardown so the reply-dispatched,
explicit-cancel, and client-teardown paths all converge on the same
idempotent cleanup.
qmp_client_call()'s sync slot is now non-floating and observes completion
by watching slot->client go NULL instead of set_contains() on an id.
When running with `SYSTEMD_RELAX_ESP_CHECKS=1` the fsroot check is still
being ran; preventing (for example) `bootctl` from operating a on a tree
as it expects a filesystem to be mounted where it finds the ESP (or
XBOOTLDR).
Expand the enum with an additional option to skip the fsroot checks and
enable it by default when `SYSTEMD_RELAX_ESP_CHECKS=1`.
See these RFEs [1], [2] for rationale.
[1]: https://github.com/systemd/systemd/issues/29871
[2]: https://github.com/systemd/systemd/issues/41707
Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
Until now OpenSSL was linked into every binary and library that needed
cryptography, pulling libcrypto (and, for resolved, libssl) into the
address space of services that never touch them at runtime. This commit
moves all OpenSSL usage behind the same dlopen helper pattern that we
already use for other optional libraries (libpam, libseccomp, libxz, …)
so libcrypto/libssl are only loaded on demand.
The bulk of the work lives in src/shared/crypto-util.{c,h} (libcrypto)
and src/shared/ssl-util.{c,h} (libssl), which replace the previous
src/shared/openssl-util.{c,h}:
- crypto-util.{c,h} declares every libcrypto function we call via
DLSYM_PROTOTYPE() and resolves them inside dlopen_libcrypto().
- ssl-util.{c,h} holds the libssl-specific DLSYM_PROTOTYPEs,
dlopen_libssl(), and the SSL_freep cleanup helper, so translation
units that only need libcrypto do not pull in libssl declarations.
- Callers refer to the symbols through sym_* aliases rather than the
original names.
- Convenience macros that used to be provided by the OpenSSL headers
(OPENSSL_free, BN_num_bytes, the sk_TYPE_* helpers, …) are
reimplemented as sym_* wrappers so no code path needs to fall back
to the linker-resolved symbols.
- All _cleanup_ helpers are redefined in terms of the sym_* variants
(EVP_PKEY_freep, X509_freep, BIO_freep, …) so cleanup attributes
keep working without pulling in libcrypto symbols at link time.
- The public crypto-util.c entry points (openssl_pubkey_from_pem,
openssl_digest_many, openssl_hmac_many, openssl_cipher_many,
kdf_ss_derive, kdf_kb_hmac_derive, rsa_* / ecc_* helpers,
pubkey_fingerprint, digest_and_sign, pkcs7_new, x509_fingerprint,
openssl_extract_public_key, pkey_generate_volume_keys, the load_*
helpers, …) now call dlopen_libcrypto() at entry before touching any
sym_* pointer.
The call sites across the tree have been converted to call
dlopen_libcrypto()/dlopen_libssl() at the appropriate entry point
before their first sym_* use, and to use sym_* variants throughout:
- bootctl, sbsign, measure, pcrlock, pcrextend, tpm2-setup, repart,
cryptsetup, cryptenroll, homectl, homed, homework, keyutil,
sysupdate, creds, import, dissect-image, pe-binary, pkcs11-util,
pkcs7-util, tpm2-util, creds-util. resolved additionally dlopens
libssl for DoT.
The meson build files are updated to depend on libopenssl_cflags (a
new partial dependency that exposes include paths and compile flags
only, not the linker flags) instead of libopenssl for every target that
previously linked against OpenSSL. Nothing links against libcrypto or
libssl directly anymore.
A new src/sbsign/authenticode.c hosts the Authenticode ASN.1 type
definitions that used to live inline in sbsign.c. The OpenSSL
ASN1_SEQUENCE / ASN1_CHOICE / IMPLEMENT_ASN1_FUNCTIONS macros expand to
code that references libcrypto symbols directly, so to keep this
translation unit unlinked from libcrypto we redirect ASN1_item_* to
the sym_* variants via #define and wrap the ASN1_*_it() getters (which
appear as constant function pointers in static initializers) in small
trampoline functions that forward to the sym_* pointers at runtime.
test-dlopen-so gains assertions for dlopen_libcrypto and dlopen_libssl
so the dlopen contract is exercised in CI, and the openssl-specific
test was renamed from test-openssl.c to test-crypto-util.c to match
the new header naming.
Add a synchronous counterpart to qmp_client_invoke() that pumps the
client's own process()/wait() loop until the reply for the issued
command id arrives, mirroring sd_varlink_call()'s contract: *ret_result
and *ret_error_desc are borrowed pointers into c->current, valid until
the next qmp_client_call(), and a QMP error surfaces as -EIO when the
caller doesn't ask for the description.
Factor the command-build + slot-insert + enqueue sequence shared with
qmp_client_invoke() into qmp_client_send(). A NULL callback marks the
slot as synchronous: dispatch_reply still matches on id (so unknown
ids continue to be logged and discarded, preserving async-only
robustness), but skips the TAKE_PTR + callback invocation and leaves
c->current pinned for qmp_client_call() to read out.
Cover the three paths in test-qmp-client: successful reply, QMP error
with ret_error_desc, and QMP error returned as -EIO.
The options --private-key, --private-key-source, --certificate,
--certificate-source are almost identical in sbsign, but are described
slightly differently. Add OPTION_COMMON_ macros that are parametrized
to keep the purpose of the --private-key and --certificate options
in the description.
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>