Commit Graph
89014 Commits
Author SHA1 Message Date
Luca Boccassi d4bc62713e test: fix flaky testcase_15_wait_online_dns in TEST-75-RESOLVED
The test used `timeout 30 bash -c "journalctl -b -u $unit -f | grep -m1 ..."`
to wait for systemd-networkd-wait-online to log that no DNS server is
accessible. The expected message is actually emitted ~1s after the unit
starts, but `grep -m1` exiting doesn't tear down `journalctl -f`: journalctl
only notices the closed pipe on its next write, which may never happen for
an otherwise idle unit. The pipeline therefore hangs until the 30s timeout
fires, eventually causing the test to fail.

Replace the follow+pipe with a polling `journalctl --grep` loop, which
exits cleanly as soon as the message lands in the journal.

Logs from the failing run:

  [ 2650.871441] systemd-networkd-wait-online[2190]: dns0: No DNS configuration yet
  [ 2651.723180] systemd-networkd-wait-online[2190]: dns0: No DNS server is accessible.
  [ 2680.909048] systemd-networkd-wait-online[2190]: json-stream: Got POLLHUP from socket.
  [ 2680.909092] systemd-networkd-wait-online[2190]: DNS configuration monitor disconnected, reconnecting...
  [ 2680.914368] systemd-networkd-wait-online[2190]: Failed to connect to io.systemd.Resolve.Monitor: Connection refused
  [ 2681.966674] systemd-networkd-wait-online[2190]: dns0: No DNS server is accessible.
  [ 2681.969527] systemd-networkd-wait-online[2190]: Failed to connect to io.systemd.Resolve.Monitor: Connection refused
  [ 2682.077032] systemd[1]: Stopping wait-online-dns-0f9e4f6d-8b34-4cff-b2da-03612ca731e8.service - [systemd-run] /usr/lib/systemd/systemd-networkd-wait-online --timeout=0 --dns --interface=dns0...

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 14:36:00 +02:00
Zbigniew Jędrzejewski-Szmek 355f2bec79 mount: stop showing mount options for systemd-unmount
This only serves to confuse the user.
2026-05-07 13:11:50 +02:00
Zbigniew Jędrzejewski-Szmek 561f3a65da mount: convert to OPTION macros
Previously, we'd show a partial synopsis for systemd-mount
in --help for systemd-umount. I don't think it makes sense to do that.
So now the --help for systemd-umount is separate, with just its syntax
and a new blurb.

"transiently" is dropped from the description. Mount points generally
are transient, so no need to say that. (E.g. the man page for mount just
says "attach" and "detach".)

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:11:50 +02:00
Zbigniew Jędrzejewski-Szmek b49a4daa1b coredumpctl: convert to OPTION and VERB macros
The order of options is changed (to what was present in parse_argv).
I don't the order in --help was mostly random, as is the new one,
so I didn't try to preserve the old order. Some help strings are
reworded/adjusted.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:11:50 +02:00
Zbigniew Jędrzejewski-Szmek a42f1ebb94 report-cgroup-server: convert to OPTION macros
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:11:50 +02:00
Zbigniew Jędrzejewski-Szmek de29e61843 portablectl: actually allow set-limit with one arg
In the man page and in the actual code, the first arg is
optional. But the arg limit in the verbs table did not allow
only one arg to be specified.

Fixes: 61d0578b07
2026-05-07 13:11:50 +02:00
Paul Meyer 67e23e0e05 vmspawn: search XDG_DATA_DIRS for QEMU firmware
get_firmware_search_dirs() previously hardcoded /usr/share/qemu/firmware
as the only system-wide search path. That assumption breaks on
distributions that deliberately do not populate /usr/share, making
vmspawn fail: "Failed to find OVMF config: No such file or directory".
NixOS exposes those firmware locations through XDG_DATA_DIRS.

Extend the search list with XDG_DATA_HOME/XDG_DATA_DIRS. This is the
standard XDG mechanism and is already what QEMU itself uses for the same
descriptors, so behavior matches user expectations across tooling.

To avoid regressing setups where user has set XDG_DATA_DIRS to a custom
value that omits /usr/share, keep /usr/share/qemu/firmware as an
unconditional fallback.

Precedence is unchanged: XDG_CONFIG_HOME/qemu/firmware still wins
over /etc/qemu/firmware, which still wins over any shared-data dir.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
2026-05-07 10:33:01 +02:00
Alyssa Ross 106bb47d2d meson: improve precision of pefile checks
Previously, if boot was enabled but ukify wasn't, the check for the pefile
Python module would be omitted.  The check in want_ukify was incorrect — with
Meson it's only possible to check for Python dependencies available for the
build Python.  When the build Python is not the same as the Python that will be
used to run the installed ukify, this would incorrectly require ukify to be
available to the build Python.  The most common, but not the only case where
this would happen would be when cross compiling.  If bootloader and tests are
disabled, there's no need for the build Python to have pefile even when
installing ukify.  Therefore, check in the more specific case of ukify's tests
being enabled, in which case pefile is required at build time.
2026-05-07 09:43:56 +02:00
Alyssa Ross f91e073056 ci: install pefile with pip as well
Otherwise, ukify gets disabled, so its build wasn't being tested by this job.
2026-05-07 09:43:56 +02:00
Nita Vesa f0a171d1d8 hwdb: add Fn-key mappings for MSI GE76 Raider 10UG 2026-05-06 19:20:04 +01:00
Frantisek Sumsal a7e276a82f dbus: limit the number of env variables to something reasonable, vol. 3
Let's limit the number of environment variables when creating a
transient unit via StartTransientUnit as well, since validating the
environment variable names/assignments is expensive.

Follow-up for 49c1e1bcf2.
2026-05-06 16:34:32 +01:00
Christian Brauner 5c03431107 vmspawn: reject --bind-volume= duplicates at parse time (#41961)
bind_volume_parse() does not look at peers, so passing the same
PROVIDER:VOLUME twice on the command line silently produces two parsed
entries in arg_bind_volumes. vmspawn_bind_volume_acquire() then builds
two DriveInfo with identical d->id ("<provider>:<volume>"). At boot,
bridge_register_drive() puts d->id into the b->block_devices hashmap;
the second insert returns -EEXIST and the user sees a bare "File exists"
with no context for which volume is responsible.

Reject the collision at the parse site with a linear scan over the
existing array — n_items is small (one entry per --bind-volume on the
command line), and a clear error message naming the offending volume is
much more useful than the late EEXIST from the QMP setup loop.
2026-05-06 16:46:42 +02:00
Lennart Poettering a5004653ac update TODO 2026-05-06 16:20:02 +02:00
Christian Brauner cbadda5e60 vmspawn: reject --bind-volume= duplicates at parse time
bind_volume_parse() does not look at peers, so passing the same
PROVIDER:VOLUME twice on the command line silently produces two parsed
entries in arg_bind_volumes. vmspawn_bind_volume_acquire() then builds
two DriveInfo with identical d->id ("<provider>:<volume>"). At boot,
bridge_register_drive() puts d->id into the b->block_devices hashmap;
the second insert returns -EEXIST and the user sees a bare "File
exists" with no context for which volume is responsible.

Reject the collision at the parse site with a linear scan over the
existing array — n_items is small (one entry per --bind-volume on the
command line), and a clear error message naming the offending volume
is much more useful than the late EEXIST from the QMP setup loop.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 14:30:37 +02:00
Lennart Poettering e0ea854328 shared/pager: add support for more(1) pager in secure mode (#41503)
The more(1) pager (part of util-linux) now supports secure mode,
which can be enabled with the MORESECURE environment variable.

Adding support for more(1) in secure mode serves as an alternative
for systems that do not have less installed or for users who prefer
it.

Commit d2fce960f9cac740 introduced secure mode in more(1) and the
new feature is available in version 2.42 of util-linux.
2026-05-06 14:30:29 +02:00
Luca Boccassi a1aed3eae2 test: add reproducer for alias-corruption skip-desync regression
The existing alias-corruption subtest only intermittently caught the
regression where unit_deserialize_state_skip() stopped at the first
empty line and thus failed to consume embedded "job" subsections
(written by job_serialize() when u->job or u->nop_job is non-NULL).
This same skip routine is also used by the pre-scan that builds the set
of serialized unit names (added in a77c7a8224 to detect stale alias
state). When skip terminates early at a job's end marker, the scan
desyncs and every subsequent unit name is dropped from the set,
silently bypassing the alias-corruption protection for those units.

Whether the bug fired depended on:
  1. Whether any unit happened to carry a pending job at the moment of
     reload/reexec (mostly chance, depends on timers, transient units,
     load, etc.).
  2. Hashmap iteration order during serialization (per-boot randomized
     siphash seed) determining if a job-bearing unit was written before
     a sus-NN.service alias.

To try and make the regression deterministic, add a "with_pending_jobs" mode
that creates 50 Type=oneshot services and starts them with
systemctl --no-block. Each remains in "activating" state with u->job
set forever, guaranteeing the serialized stream contains many embedded
job subsections regardless of hashmap order, and that at least one of
them precedes the sus units. Without the fix, the desync drops the
sus-NN entries from the set, the alias-corruption check
set_contains(serialized_units, u->id) returns false for every alias,
and legit.service's MainPID is overwritten on every run.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 13:02:38 +01:00
Luca Boccassi 4394046ee6 core: when skipping state deserializing units, also skip job subsections
If a unit has active jobs, when it gets serialized there are job
subsections, each with their own empty line marker. The skipping
function ignores this and skips until the marker, but then leaves
the job in place, breaking deserialization.
Consume jobs subsections too.

This shows up now that there's TEST-07-PID1.alias-corruption,
which occasionally fails when the aliased unit happens to
still have a job when the reexec happens.

[  967.551630] TEST-07-PID1.sh[179]: + echo 'Testing with: systemctl daemon-reexec'
[  967.551630] TEST-07-PID1.sh[179]: Testing with: systemctl daemon-reexec
[  968.405274] TEST-07-PID1.sh[179]: + echo '--- Attempt 1/3 ---'
[  968.405274] TEST-07-PID1.sh[179]: --- Attempt 1/3 ---
[  968.698641] TEST-07-PID1.sh[179]: + echo 'Running daemon-reexec...'
[  968.698641] TEST-07-PID1.sh[179]: Running daemon-reexec...
[  969.130261] TEST-07-PID1.sh[179]: + echo 'legit.service PID remains 1282. Attempt 1 passed.'
[  969.130261] TEST-07-PID1.sh[179]: legit.service PID remains 1282. Attempt 1 passed.
[  970.870456] TEST-07-PID1.sh[179]: + echo '--- Attempt 2/3 ---'
[  970.870456] TEST-07-PID1.sh[179]: --- Attempt 2/3 ---
[  971.267205] TEST-07-PID1.sh[179]: + echo 'Running daemon-reexec...'
[  971.267205] TEST-07-PID1.sh[179]: Running daemon-reexec...
[  971.715743] TEST-07-PID1.sh[179]: + echo 'legit.service PID changed from 1282 to 1643!'
[  971.715743] TEST-07-PID1.sh[179]: legit.service PID changed from 1282 to 1643!

Follow-up for a77c7a8224
Follow-up for 0742986650
2026-05-06 12:04:51 +01:00
Luca Boccassi e6ee724ac8 test: bump timeout in TEST-07-PID1.socket-on-failure
The timeout is too short so under sanitizers the test sometimes fails:

[ 3502.317855] TEST-07-PID1.sh[16212]: + mkdir -p /tmp/TEST-07-PID1-socket-29942
[ 3502.317855] TEST-07-PID1.sh[16212]: + cat
[ 3502.317855] TEST-07-PID1.sh[16212]: + cat
[ 3502.317855] TEST-07-PID1.sh[16212]: + cat
[ 3502.317855] TEST-07-PID1.sh[16212]: + systemctl start TEST-07-PID1-socket-29942.socket
[ 3502.317855] TEST-07-PID1.sh[16212]: + systemctl is-active TEST-07-PID1-socket-29942.socket
[ 3502.319398] TEST-07-PID1.sh[16221]: active
[ 3502.319758] TEST-07-PID1.sh[16212]: + [[ -S /tmp/TEST-07-PID1-socket-29942/test ]]
[ 3502.319758] TEST-07-PID1.sh[16212]: + systemctl stop TEST-07-PID1-socket-29942.socket
[ 3502.319758] TEST-07-PID1.sh[16212]: + rm /tmp/TEST-07-PID1-socket-29942/test
[ 3502.319758] TEST-07-PID1.sh[16212]: + chattr +i /tmp/TEST-07-PID1-socket-29942
[ 3502.320499] TEST-07-PID1.sh[16227]: + systemctl start TEST-07-PID1-socket-29942.socket
[ 3502.320914] TEST-07-PID1.sh[16228]: Job failed. See "journalctl -xe" for details.
[ 3502.321560] TEST-07-PID1.sh[16212]: + systemctl is-failed TEST-07-PID1-socket-29942.socket
[ 3502.321856] TEST-07-PID1.sh[16230]: failed
[ 3502.322315] TEST-07-PID1.sh[16233]: ++ systemctl show TEST-07-PID1-socket-29942.socket -P SubState
[ 3502.322722] TEST-07-PID1.sh[16212]: + assert_eq failed failed
[ 3502.323111] TEST-07-PID1.sh[16235]: + set +ex
[ 3502.323512] TEST-07-PID1.sh[16212]: + [[ ! -e /tmp/TEST-07-PID1-socket-29942/test ]]
[ 3502.323512] TEST-07-PID1.sh[16212]: + timeout 10 bash -c 'until systemctl is-failed TEST-07-PID1-socket-OnFailure.service; do sleep .5; done'
[ 3502.323949] TEST-07-PID1.sh[16238]: activating
[ 3502.324376] TEST-07-PID1.sh[16241]: activating
[ 3502.324754] TEST-07-PID1.sh[16244]: activating
[ 3502.325164] TEST-07-PID1.sh[16247]: activating
[ 3502.325587] TEST-07-PID1.sh[16250]: activating
[ 3502.325938] TEST-07-PID1.sh[16253]: activating
[ 3502.326393] TEST-07-PID1.sh[16256]: activating
[ 3502.326791] TEST-07-PID1.sh[16259]: activating
[ 3502.327180] TEST-07-PID1.sh[16262]: activating
[ 3502.327558] TEST-07-PID1.sh[16265]: activating
[ 3502.327971] TEST-07-PID1.sh[16268]: activating
[ 3502.328409] TEST-07-PID1.sh[16212]: + at_exit
[ 3502.328409] TEST-07-PID1.sh[16212]: + systemctl stop TEST-07-PID1-socket-29942.socket
[ 3502.328409] TEST-07-PID1.sh[16212]: + rm -f /run/systemd/system/TEST-07-PID1-socket-29942.socket /run/systemd/system/TEST-07-PID1-socket-29942.service /run/systemd/system/TEST-07-PID1-socket-OnFailure.service
[ 3502.328945] TEST-07-PID1.sh[147]: + echo 'Subtest /usr/lib/systemd/tests/testdata/units/TEST-07-PID1.socket-on-failure.sh failed'

Bump from 10s to 60s to try and make it less flaky
2026-05-06 12:04:51 +01:00
Zbigniew Jędrzejewski-Szmek 0731b6c4d9 portablectl: convert to OPTION and VERB macros
s|attach/detach from the local system|in the local system|, because
"attach from" doesn't work.

The synopses for 'list' and 'set-limit' are fixed.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 13:03:36 +02:00
Zbigniew Jędrzejewski-Szmek 5a41d43e1b portablectl: reorder verb functions
The order in --help is changed to move have 'list', 'inspect' (query
operations), 'attach'/'detach'/'reattach' (main ops), and then the
other more specialized verbs.

The functions are then reordered to match this.
2026-05-06 12:54:12 +02:00
Zbigniew Jędrzejewski-Szmek f6363a0b1d Another batch of conversions to option and verb macros (#41881) 2026-05-06 12:39:36 +02:00
Christian Brauner 9fe1684f93 fs-util: Some followups for xopenat_full() (#41835) 2026-05-06 12:35:55 +02:00
Christian Brauner 4ddfd04a48 vmspawn,machinectl: integrate io.systemd.StorageProvider (#41910)
PR #41776 introduced the io.systemd.StorageProvider Varlink interface
and
two backends ('block' exposes host block devices, 'fs' exposes regular
files / dirs / subvolumes under /var/lib/storage), plus the
storagectl(1)
CLI to enumerate them. The only consumer so far was mount.storage. This
series wires up the first of the three integrations called out in
TODO.md:

  systemd-vmspawn --bind-volume=PROVIDER:VOLUME[:CONFIG][:K=V,...]
      Boot-time attach. Drives added this way are immutable at runtime.

  io.systemd.MachineInstance.AddStorage  / .RemoveStorage
      Two new generic methods on the per-machine control socket. vmspawn
      implements them (this series); systemd-nspawn will reuse the same
      methods later.

  machinectl bind-volume   MACHINE PROVIDER:VOLUME[:CONFIG][:K=V,...]
  machinectl unbind-volume MACHINE PROVIDER:VOLUME
Runtime hotplug front-end: machinectl Acquire()s the fd locally and
      pushes it across to the target machine's MachineInstance socket.

Volumes are identified by a user-visible name "<provider>:<volume>"
(e.g.
"block:/dev/sda"). The 3rd 'config' field is opaque to the shared layer
and interpreted per backend — vmspawn maps it to a DiskType from
disk_type_table[] (virtio-blk default, virtio-scsi, nvme, scsi-cd; same
vocabulary as --extra-drive); future nspawn will read it as a mount
path.
2026-05-06 12:20:26 +02:00
Christian Brauner 8a4451d2f0 docs,test: --bind-volume / bind-volume / unbind-volume
- Document the new --bind-volume= option in systemd-vmspawn(1) and
    the new bind-volume / unbind-volume verbs in machinectl(1).

  - Add an integration test
    (TEST-87-AUX-UTILS-VM.bind-volume.sh) covering boot-time attach
    via --bind-volume, runtime attach via 'machinectl bind-volume',
    runtime detach via 'machinectl unbind-volume', the StorageImmutable
    rejection of attempts to detach boot-time volumes, and the
    NoSuchStorage rejection of detach on unknown names.

  - Strike "hook-up in systemd-vmspawn" from TODO.md; the nspawn and
    service-manager hookups remain.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner c9f461a806 machinectl: add bind-volume / unbind-volume verbs
machinectl bind-volume MACHINE PROVIDER:VOLUME[:CONFIG][:K=V,...]
  machinectl unbind-volume MACHINE PROVIDER:VOLUME

For bind-volume, machinectl parses the SPEC with the shared
bind_volume_parse(), Acquires the storage volume from the named
provider on the machinectl side, locates the target machine's
io.systemd.MachineInstance control socket via
machine_get_control_address(), pushes the fd across, and calls
io.systemd.MachineInstance.AddStorage with name='<provider>:<volume>'
and the user-supplied config string.

For unbind-volume, machinectl just forwards the name string to
io.systemd.MachineInstance.RemoveStorage.

Volumes attached at machine startup (e.g. via systemd-vmspawn's
--bind-volume=) are rejected with StorageImmutable when the user
attempts to unbind them at runtime.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner e7eac392ef vmspawn: implement io.systemd.MachineInstance.AddStorage / RemoveStorage
Wire up the runtime hotplug Varlink methods on the per-VM control
socket:

  AddStorage  → take fd from the link, look up the DiskType from the
                'config' field, build a DriveInfo flagged
                QMP_DRIVE_REMOVABLE, dispatch to
                vmspawn_qmp_add_block_device(). Reply delivered async
                by on_add_device_add_complete() once the guest sees
                the device.

  RemoveStorage → forward the user-visible name to
                  vmspawn_qmp_remove_block_device(); the existing
                  device_del / DEVICE_DELETED / blockdev-del chain
                  replies on the link.

Add SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT to the server flags so
clients can push storage fds across via sd_varlink_push_fd().

Maps -EEXIST → StorageExists and -EOPNOTSUPP/-EINVAL →
ConfigNotSupported in the AddStorage handler so callers see the
specific MachineInstance errors.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner 413fd62dd7 vmspawn: add --bind-volume= command line option
systemd-vmspawn --bind-volume=PROVIDER:VOLUME[:CONFIG][:K=V,...]

For each --bind-volume passed at startup, vmspawn calls Acquire() on
the named StorageProvider and attaches the resulting fd to the VM as
an additional drive. The drive is identified by the user-visible name
'<provider>:<volume>' on the bridge — that is also the handle used
later when machinectl unbind-volume detaches drives at runtime
(though boot-time drives like these are NOT removable; that is the
StorageImmutable behaviour added earlier).

The colon grammar is parsed by the shared bind_volume_parse() helper.
The 3rd 'config' field selects the guest device type from the
disk_type_table[] vocabulary (virtio-blk, virtio-scsi, nvme, scsi-cd);
empty defaults to virtio-blk per the TASK grammar.

Wiring lives next to the existing --extra-drive setup: parse_argv()
appends a parsed BindVolume to arg_bind_volumes, and prepare_device_info()
hands the array to vmspawn_bind_volume_prepare_boot() which Acquires
each volume and pushes a DriveInfo onto the existing drives array.
PCIe port assignment (assign_pcie_ports()) and the QMP setup loop pick
them up automatically.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner a34ad7f7b0 vmspawn: add vmspawn-bind-volume glue
This is vmspawn's per-backend code for the StorageProvider integration.
Other backends (future systemd-nspawn, future service-manager
BindVolume=) consume the same shared parser and Acquire helper but
each provides its own attach/detach glue; this is vmspawn's.

  - disk_type_from_bind_volume_config() turns the opaque BindVolume
    'config' field (e.g. "scsi-cd") into a DiskType. Empty defaults to
    virtio-blk to match the --bind-volume CLI grammar.

  - vmspawn_bind_volume_acquire() takes a parsed BindVolume, calls
    storage_acquire_volume() for the fd, and builds a DriveInfo ready
    for vmspawn_qmp_setup_drives() (boot) or vmspawn_qmp_add_block_device()
    (hotplug). Rejects directory-typed volumes (vmspawn block devices
    need a regular file or a host block device).

  - vmspawn_bind_volume_attach_fd() is the runtime path: takes a fd
    that was already pushed across by an AddStorage caller plus the
    name+config it specified, builds the DriveInfo with
    QMP_DRIVE_REMOVABLE set and a varlink link, and dispatches to
    vmspawn_qmp_add_block_device(). Reply is delivered asynchronously
    by the existing on_add_device_add_complete() callback.

  - vmspawn_bind_volume_prepare_boot() is a thin loop the boot-time
    path uses to populate DriveInfos.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner 0e911e4164 vmspawn: track removability as a QmpDriveFlags bit and expose add_block_device
Drives attached at boot via the existing CLI options (--image,
--extra-drive) must not be detachable at runtime via the upcoming
RemoveStorage Varlink method, while drives added at runtime via
AddStorage must be. Track this distinction with a new QMP_DRIVE_REMOVABLE
property flag — placed alongside QMP_DRIVE_BLOCK_DEVICE, not in the
transient BlockDeviceStateFlags state-machine, since "may be removed"
is a permanent property of the drive.

vmspawn_qmp_remove_block_device() now early-rejects unknown ids with
io.systemd.MachineInstance.NoSuchStorage and immutable drives with
io.systemd.MachineInstance.StorageImmutable.

vmspawn_qmp_add_block_device() loses its 'static' qualifier and gets a
declaration in the header, so the runtime hotplug path
(vmspawn-bind-volume.c, next) can dispatch into it directly.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner aa02ad284e shared: add AddStorage / RemoveStorage to io.systemd.MachineInstance
Define two new methods on the generic 'MachineInstance' Varlink
interface that systemd-vmspawn (this series) and (future)
systemd-nspawn implement on their per-machine control sockets:

  AddStorage(fileDescriptorIndex, name, config?) -> ()
      Attach a storage volume — the caller passes an fd previously
      acquired from a StorageProvider, plus a unique name of the form
      '<provider>:<volume>' that identifies this binding for later
      removal, plus a backend-specific 'config' field (vmspawn: guest
      device type; future nspawn: mount path).

  RemoveStorage(name) -> ()
      Detach a previously-added storage volume.

Plus errors NoSuchStorage, StorageExists, StorageImmutable (the volume
was attached at boot and cannot be removed), BadConfig, and
ConfigNotSupported. Names follow the io.systemd.StorageProvider
vocabulary (NoSuchVolume, BadTemplate, TypeNotSupported, etc.) so the
two interfaces are visually consistent.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner a0faa6a798 storagectl: refactor mount.storage helper to use storage_acquire_volume()
Drop the inline socket-build + sd_varlink_callbo() + reply-dispatch
+ take_fd block from run_as_mount_helper() in favour of the shared
helper. Preserves the type-fallback retry (TypeNotSupported / WrongType
re-tries with requestAs="blk") and the per-error-id message mapping;
the helper just reports the io.systemd.StorageProvider.* error name
back to the caller.

Net effect: ~50 lines of dedup, no functional change.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:17 +02:00
Christian Brauner fad897b8bf shared: add storage_acquire_volume() helper
storagectl's mount.storage helper bundles "open StorageProvider socket
+ Acquire() + dispatch reply + take fd" inline. Future consumers
(systemd-vmspawn boot-time --bind-volume, machinectl bind-volume) need
the same dance.

Factor it into a single libshared helper that takes the Acquire()
parameters by value and returns the fd plus the actual type/read-only
flags. Library code, so no logging — varlink errors are surfaced via
sd_varlink_error_to_errno() and the StorageProvider error_id is
returned to the caller via reterr_error_id (caller decides how to
format messages).

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:16 +02:00
Christian Brauner 32a80416b6 shared: add BindVolume parser in machine-util
Add a universal parser for the colon-separated grammar
'PROVIDER:VOLUME[:CONFIG][:K=V,K=V,…]' that backs --bind-volume on
systemd-vmspawn (next), machinectl bind-volume, and the future nspawn
+ service-manager BindVolume= integrations.

The 'config' field is opaque to shared code and interpreted per
backend (vmspawn: a DiskType name, future nspawn: a mount path). The
trailing key=value list is parsed into the io.systemd.StorageProvider
.Acquire() parameters (template, create, read-only/ro, size/create-size
and request-as), with values validated against the existing
storage-util enums and validators. Provider/volume names are checked
with storage_provider_name_is_valid() and storage_volume_name_is_valid();
the combined "<provider>:<volume>" string is also validated as
string_is_safe so it is safe to use as a QEMU device id.

Add a test-machine-util unit test covering the happy paths plus a
handful of malformed inputs.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:16 +02:00
Christian Brauner efb0a0fa7a shared: move storage-util to libshared
The storage backend providers (block, fs) and storagectl currently each
extract storage-util.c into their target. Several upcoming consumers
(machine-util's BindVolume parser, vmspawn's hotplug glue, machinectl's
new bind-volume verbs) need the StorageProvider type/string-table
helpers and a future shared Acquire client helper.

Move storage-util.{c,h} to src/shared so libshared exports the symbols
once and every consumer (storage providers, storagectl, libshared
itself) picks them up by linking libshared. Drop the now-redundant
'extract'/'objects' wiring in src/storage/meson.build.

No code changes; this is purely a relocation.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-06 10:30:16 +02:00
Christian Brauner eaa0073027 user-util,storagectl: introduce USERNS_RANGE_SIZE macro
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>
2026-05-06 10:30:13 +02:00
Lennart Poettering 9d2df9daf6 Add a manual test to exercise homectl prompt logic (#41954) 2026-05-06 09:21:17 +02:00
Sergei Trofimovich fc68ee6118 sd-boot: efi-log: fix __stack_chk_guard type
In https://gcc.gnu.org/PR121911 `gcc` started enforcing the type of
`__stack_chk_guard` to `uintptr_t` and broke `systemd` build as:

```
../src/boot/efi-log.c:136:17: error: conflicting types for '__stack_chk_guard'; have 'intptr_t' {aka 'long int'}
  136 | _used_ intptr_t __stack_chk_guard = (intptr_t) 0x70f6967de78acae3;
      |                 ^~~~~~~~~~~~~~~~~
cc1: note: previous declaration of '__stack_chk_guard' with type 'long unsigned int'
../src/boot/efi-log.c:136:17: error: declaration of '__stack_chk_guard' shadows a global declaration [-Werror=shadow]
  136 | _used_ intptr_t __stack_chk_guard = (intptr_t) 0x70f6967de78acae3;
      |                 ^~~~~~~~~~~~~~~~~
```

Let's match the declaration to unsigned type as suggested by upstream in
https://gcc.gnu.org/PR121911#c6.
2026-05-06 09:18:25 +02:00
Zbigniew Jędrzejewski-Szmek 72fedd6037 Couple of coverity fixes (#41951) 2026-05-05 22:45:13 +02:00
José Miguel Sarasola 852fcf5134 hwdb: add SOUND_FORM_FACTOR for Edifier M60 and Fractal Scape Dongle 2026-05-05 21:32:03 +01:00
Zbigniew Jędrzejewski-Szmek c379621426 homectl: drop redunant sort
Claude points out that prompt_groups() already does the sort
in every loop, including the last.
2026-05-05 22:08:19 +02:00
Zbigniew Jędrzejewski-Szmek 392846b370 test-homectl-prompts: add manual test to exercise prompt functionality
The prompt for groups is nice. The prompt for a shell could use some
love. Looking at this is much easier if we can invoke the code outside
in isolation.

I wrote this when looking at https://github.com/systemd/systemd/pull/41947,
where I wanted to see how the homectl prompt works with the changes.
2026-05-05 22:06:41 +02:00
Zbigniew Jędrzejewski-Szmek 937129d1f6 Merge facts into metrics and add systemd-report generate/upload (#41688) 2026-05-05 20:55:36 +02:00
Zbigniew Jędrzejewski-Szmek 764d9d5ddb homectl: split out two prompt functions
homectl.c is too long…
2026-05-05 20:53:23 +02:00
Luca Boccassi f149de5ce4 test: make TEST-04-JOURNAL.journalctl-varlink more robust (#41953)
This test is sometimes flaky under sanitizers, and it does repeated
calls with the same parameters to run through different greps, and
the second one sometimes fails.
Store the result and grep it twice instead to try and reduce
flakiness.

```
[ 2089.891152] TEST-04-JOURNAL.sh[22392]: + systemd-run --unit=test-journalctl-varlink-2-18237.service --wait bash -c 'echo hello-from-varlink-test-2'
[ 2090.066050] TEST-04-JOURNAL.sh[22460]: + varlinkctl call --more /run/systemd/io.systemd.JournalAccess io.systemd.JournalAccess.GetEntries '{"units": ["test-journalctl-varlink-1-22690.service"]}'
[ 2090.067075] TEST-04-JOURNAL.sh[22461]: + grep -q hello-from-varlink-test-1
[ 2090.384551] TEST-04-JOURNAL.sh[22466]: + varlinkctl call --more /run/systemd/io.systemd.JournalAccess io.systemd.JournalAccess.GetEntries '{"units": ["test-journalctl-varlink-1-22690.service"]}'
[ 2090.385373] TEST-04-JOURNAL.sh[22467]: + grep hello-from-varlink-test-2
[ 2090.723461] TEST-04-JOURNAL.sh[22474]: + grep -q hello-from-varlink-test-1
[ 2090.724294] TEST-04-JOURNAL.sh[22473]: + varlinkctl call --more /run/systemd/io.systemd.JournalAccess io.systemd.JournalAccess.GetEntries '{"units": ["test-journalctl-varlink-1-22690.service", "test-journalctl-varlink-2-18237.service"]}'
[ 2091.135655] TEST-04-JOURNAL.sh[22480]: + varlinkctl call --more /run/systemd/io.systemd.JournalAccess io.systemd.JournalAccess.GetEntries '{"units": ["test-journalctl-varlink-1-22690.service", "test-journalctl-varlink-2-18237.service"]}'
[ 2091.136605] TEST-04-JOURNAL.sh[22481]: + grep -q hello-from-varlink-test-2
[ 2091.479930] TEST-04-JOURNAL.sh[22480]: Method call failed: io.systemd.JournalAccess.NoEntries
```
2026-05-05 19:24:41 +01:00
Zbigniew Jędrzejewski-Szmek a92b60ae17 homectl: fix error handling in shell_is_ok()
Fixes f233132a67.
2026-05-05 18:37:13 +02:00
Jonas Dreßler 4ffb60319b sysupdate: Ensure that end of the MatchPattern is matched correctly
An error snuck into the pattern parsing of the `MatchPattern` key in the
sysupdate transfer files. If there's two files "part1-v2.raw", and
"part1-v2.raw.tar" in the source folder, and MatchPattern="part1-@v.raw",
sysupdate will incorrectly choose "part1-v2.raw.tar" instead of
"part1-v2.raw".

While the pattern matching works perfectly fine, after the full pattern
is successfully matched to the string, we don't ensure that the string
actually ends when the pattern just did.

This means we can end up choosing a wrong file for the update, if the
filename/path happens to start with the same MatchPattern.

Fix it by ensuring the string ends after our match pattern ended.
2026-05-05 18:21:55 +02:00
Michael Vogt 628ab0040e report: fold io.systemd.Basic facts into metrics
We removed the concept of facts, so we need to update the existing
io.systemd.Basic facts provider to metrics. This commit does just
that. Its mostly mechanical.

This also means that facts.{c,h} and varlink-io.systemd.Facts.{c,h}
are gone now.
2026-05-05 18:13:28 +02:00
Michael Vogt ffd4e7ab01 report: when a report fails, print the json error details
When a report upload fails the backend often provides useful
details via the varlink error. Show them as part of the upload
error message. For now we just dump the json because we have
no structure that the backends should follow. We may want to
consider adding one (like check for an "error_message" key in
the json). But for now this is a nice step forward.
2026-05-05 18:13:28 +02:00
Zbigniew Jędrzejewski-Szmek 9c336ef6eb report: set description on varlink sockets
We make multiple connections and without this it's hard to
know socket we're talking to.
2026-05-05 18:13:28 +02:00
Zbigniew Jędrzejewski-Szmek a2186070b7 report: upload reports using a "varlink socket directory"
Two new verbs are added: "generate" and "upload". The first one just
creates a "report", i.e. puts the metrics into a structured JSON object
that in the future is intended to carry additional data like a
signature:

$ build/systemd-report generate io.systemd.Manager.UnitsTotal
{
	"mediaType" : "application/vnd.io.systemd.report",
	"timestamp" : "Tue 2026-04-28 22:30:09 UTC",
	"metrics" : [
		{
			"name" : "io.systemd.Manager.UnitsTotal",
			"value" : 520
		}
	]
}

The second verb can be used to upload or otherwise process the report.
It builds on the code added in 0a8560eed8.
In /run/systemd/metrics-upload/ we expect a set of sockets. We'll call
out to each one of them. This allows the data to be processed in custom
ways, incl. writing to storage or sending over the network.

Each socket must provide a single interface:
  io.systemd.Metrics.Upload {"report":$data}
2026-05-05 18:12:35 +02:00