Extract the transient unit setup logic from transient_unit_from_message()
in dbus-manager.c into a shared helper.
This prepares for reuse by the varlink StartTransient implementation.
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.
So far when a job completed we'd never transition into any new state,
we'd just do some final processing work (such as notifying clients) and
destroy it.
Let's change that, and briefly enter a final state: "finished". This is
useful so that code that notifies clients can generically send the
quadruplet of id, type, state, result for any change notification and
naturally can communicate job completion that way: by setting the state
field to "finished".
Prompted by the discussions in: #41583
Common page-code parsing extracted into a parse_page_code() helper.
While at it, return real error values (-EINVAL, etc.) rather than -1,
and rename retval to r throughout for consistency.
The body of main is moved to new run. The closing of logging file
descriptors is dropped. They will be closed automatically anyway. Not
sure what the original purpose of that code was. The code is also
modernized in various places… though more changes could be made. The
return convention of help() and similar functions is changed to usual
negative/0/1, where 0 means that the caller should quit.
set_inq_values would return positive error values too, which was
previously ignored. It's not entirely clear, but that doesn't seem
to have been on purpose.
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>
The commandline check is tightened. Previously the program would
crash if called without an argument.
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
The commandline check is tightened. Previously the program would
crash if called without an argument.
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
The WIN 5 (DMI product G1618-05) ships the same BMI0160
accelerometer with the same physical mounting as the Win Max 2
(G1619-04), so reuse its mount matrix. Verified on hardware:
without the matrix iio-sensor-proxy reports
AccelerometerOrientation=normal regardless of physical pose,
and applying the G1619-04 matrix makes orientation transitions
(normal / left-up / right-up / bottom-up) track the device
correctly.
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.
This is mostly equivalent to dhcp_packet_verify_headers(), but
- it optionally returns the UDP payload as iovec, and
- supports IP header with options,
- check packet length more strictly.
Then make dhcp_packet_append_ip_headers() just a wrapper of the new
function. Currently, the wrapper is inefficient, but will be removed in
a later commit.
Without this, the socket is available well before systemd-networkd.service
is able to start, because of its own After=network-pre.target ordering.
Then, if resolved handles queries before network-pre.target, it will
hang waiting for networkd to reply to hook queries.
This is currently happening in the wild with cloud-init.
In resolute with gcc 15.2.0:
472s ../src/nss-myhostname/nss-myhostname.c: In function ‘_nss_myhostname_gethostbyname4_r’:
472s ../src/nss-myhostname/nss-myhostname.c:132:44: error: ‘local_address_ipv4’ may be used uninitialized [-Werror=maybe-uninitialized]
472s 132 | *(uint32_t*) r_tuple->addr = local_address_ipv4;
472s | ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
472s ../src/nss-myhostname/nss-myhostname.c:42:18: note: ‘local_address_ipv4’ was declared here
472s 42 | uint32_t local_address_ipv4;
472s | ^~~~~~~~~~~~~~~~~~
The block-device hotplug work (#NNNNN) needs a number of cross-cutting
changes to the QMP plumbing that aren't hotplug-specific in
themselves: a refcounted DriveInfo so async stage callbacks can keep
slot refs, a counter-based naming scheme so multiple drives can share
the same backing path, pipelined remove-fd so QEMU's fdsets get
released at blockdev-del time, and a generic-completion callback that
doesn't tear the VM down on a runtime QMP error. None of these change
behaviour from a user's point of view, none of them depend on the
varlink hotplug methods landing, and several are wins on their own
(the fdset leak in particular is observable today with --extra-drive
under long-running VMs). Pulling them out of the hotplug PR keeps
that PR focused on the IDL + server-side method handlers, and lets
this preparatory work land on its own merits without waiting for the
larger feature review.
Cleanup pieces that fall out for free:
qmp-client: widen next_fdset_id to uint64_t
vmspawn: move VMSPAWN_PCIE_HOTPLUG_SPARES to vmspawn-qmp.h
vmspawn-varlink: use error < 0 in async QMP completion callbacks
vmspawn-varlink: simplify on_qmp_describe_complete result extraction
vmspawn-varlink: extract notify_event_subscribers from on_qmp_event
vmspawn-varlink: treat empty event subscription filter as catch-all
vmspawn-qmp: pass bridge to on_cont_complete via invoke userdata
Infrastructure the hotplug add path will sit on top of:
vmspawn-qmp: convert DriveInfo to a refcounted object
vmspawn-qmp: derive QMP node and device ids from a bridge counter
vmspawn-qmp: pipeline remove-fd after each blockdev-add
vmspawn-qmp: keep the event loop running on post-setup QMP failures
vmspawn-qmp: add the hotplug-capable block-device add machinery
vmspawn-qmp: add vmspawn_qmp_remove_block_device
The two final commits introduce vmspawn_qmp_add_block_device() and
vmspawn_qmp_remove_block_device() but leave them without varlink
callers — the io.systemd.VirtualMachineInstance method handlers that
forward into them land with the rest of the hotplug PR. Boot-time
drive setup is rewritten on top of vmspawn_qmp_add_block_device() so
the hotplug and boot paths share a single staged-add pipeline from
day one.
This changes the .result field to invalid initially, which arguably
makes more sense than "done", which was previously the default.
This is a correctnes fix, and afaics has no effect on the API, since we
do not expose this 1:1 as D-Bus property: it's only seen on D-Bus as
part of the job completion signal, at which part it is correctly
initialized.
Noticed while reviewing: https://github.com/systemd/systemd/pull/41583
The first paragraph of the description of the systemd-cat utility incorrectly referred to stdin when it obviously meant stderr: the other fd that it connects to the journal via a unix(7) domain socket, as clarified in the following paragraphs.
I've also replaced "process" with "command" as in that mode, systemd-cat executes a file and does not spawn a process.
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>
Hot-remove counterpart to vmspawn_qmp_add_block_device. Looks the
drive up in the bridge's block_devices registry by caller-supplied
id and dispatches device_del using the internal qmp_device_id; the
varlink link gets the immediate ack/error reply once QEMU completes
the request.
Concurrency: a second remove for the same id while the first is in
flight (between device_del dispatch and DEVICE_DELETED) would
otherwise reach QEMU and earn a confusing 'already in the process of
unplug' reply. Track the in-flight state with a new
BLOCK_DEVICE_REMOVE_PENDING bit on the existing rollback_mask, and
short-circuit duplicate calls with -EBUSY. The bit is cleared on
device_del failure (the drive is still attached, so retries make
sense) and naturally vanishes on success when the registry entry is
dropped.
DEVICE_DELETED handling: the actual blockdev-del + registry removal
+ pcie-port release is deferred to vmspawn_qmp_dispatch_device_deleted,
which fires from on_qmp_event in vmspawn-varlink.c when the guest
acks the eject. Hooking it from the existing QMP event dispatcher
keeps the cleanup local to vmspawn-qmp.{c,h}.
The function has no varlink callers in this PR — the
io.systemd.VirtualMachineInstance method handler that forwards into
it lands with the rest of the hotplug PR.
Signed-off-by: Christian Brauner <brauner@kernel.org>
This is the bulk of the runtime block-device hotplug feature. The
boot-time qmp_setup_regular_drive() path is rewritten on top of a new
vmspawn_qmp_add_block_device() that owns the DriveInfo, drives a four
QMP-command pipeline (add-fd → blockdev-add → remove-fd → device_add)
through staged ref-counted callbacks, and registers the drive in a
per-bridge block-device registry on success.
New on the bridge:
- block_devices: user-id → DriveInfo* registry (owned ref).
- hotplug_port_owner[VMSPAWN_PCIE_HOTPLUG_SPARES]: per-port owner
string for the spare pcie-root-ports, allocated/released through
vmspawn_qmp_bridge_{allocate,release_pcie_port_by_idx}().
- scsi_controller_port_idx / scsi_controller_created: track the
on-demand virtio-scsi-pci controller so the first SCSI hotplug
creates it (against an allocated spare port) and subsequent ones
just attach.
- next_block_counter: already in place from the previous commit, now
actually consumed by add_block_device().
New on DriveInfo:
- bridge (weak), id (varlink-visible — caller-supplied or auto-set
to qmp_device_id), disk_type (for List replies later), counter,
qmp_node_name / qmp_device_id (already added), fdset_path,
pcie_port_idx (the hotplug port reserved by this drive — stays
set across the add pipeline so drive_info_free releases it when
the registry ref drops at DEVICE_DELETED time),
rollback_mask (BlockDeviceAddStage bits of completed stages —
plus a FAILED sentinel that suppresses cascading errors), and
link (NULL ⇒ boot-time, sd_event_exit on fail; non-NULL ⇒
hotplug, varlink reply on fail).
Helpers:
- drive_info_unref() now releases any reserved hotplug port through
the bridge and unrefs link.
- drive_info_add_fail(): single failure entry point — fires the
teardown for completed stages, sets the FAILED bit, and sends
either the varlink error or sd_event_exit. Boot-time failures
(link == NULL) always exit the loop, so late-arriving ephemeral
continuation replies don't get silenced after cont.
- vmspawn_qmp_block_device_teardown(): post-hoc blockdev-del when
blockdev-add succeeded but a later stage failed.
- reply_qmp_error: varlink reply helper — disconnect errors map to
io.systemd.MachineInstance.NotConnected, everything else goes
through sd_varlink_error_errno.
- on_add_observe_stage / on_add_blockdev_stage /
on_add_device_add_complete: the staged callbacks that drive the
add pipeline, each holding one slot ref on the DriveInfo. The
ephemeral blockdev-create continuation reuses the latter two so
its post-cont replies go through drive_info_add_fail instead of
the generic on_qmp_complete (which would silently log under
setup_done).
- on_scsi_controller_complete: handles the SCSI controller setup
(releases the reserved port on failure, propagates the boot-time
fatal error policy).
- qmp_build_blockdev_add_inline(): single blockdev-add JSON that
creates the file+format pair as one node, used by the hotplug
path (the boot-time helpers stay separate so they can stack with
the ephemeral path's base/overlay format nodes).
EphemeralDriveCtx is trimmed down to a DriveInfo ref plus the two
ephemeral-local scratch node names (overlay-file, base-fmt). The
copies of disk_driver/serial/pcie_port/flags/qmp_node_name/
qmp_device_id go away — the continuation reads them straight off the
ref'd drive. qmp_setup_ephemeral_drive now sets drive->bridge /
drive->id / drive->counter up front (matching the hotplug path) and
folds the feature-dependent DISCARD_NO_UNREF into drive->flags so
qmp_build_blockdev_add_format picks it up.
vmspawn_qmp_bridge_free() unrefs the qmp client first — its pending
callbacks may still reach for the bridge's hotplug port table when
they drop their last DriveInfo ref — then tears down the hashmaps
and the port owner strings.
The boot-time qmp_setup_regular_drive() collapses to a thin wrapper
that asserts "caller hasn't pre-set drive->id", takes ownership and
calls vmspawn_qmp_add_block_device(); the dispatcher in
vmspawn_qmp_setup_drives() now hands ownership over with TAKE_PTR.
The previous qmp_setup_drive() dispatcher disappears (its body is
inlined into the loop). vmspawn_qmp_init() initialises
scsi_controller_port_idx to -1.
The cosmetic (*d) → DriveInfo *drive = *d; locals in
drives_need_scsi_controller (vmspawn-qmp.c) and assign_pcie_ports
(vmspawn.c) ride along — they live in code touched by this commit
and would otherwise produce churn.
vmspawn_qmp_remove_block_device() — the symmetric remove API — is
added in the next commit so this one stays focused on the add path.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
on_qmp_complete tears the event loop down on any QMP error, which is
the right behaviour while we're still building the VM (a missing
device means we'd boot a half-configured guest). Once the boot-time
setup is finished and the guest is running, killing the event loop on
a QMP error means a single failed runtime command (e.g. a hotplug
device_add that the guest rejects) takes the whole VM down.
Consult bridge->setup_done — already flipped at the end of boot
setup — and skip the event-loop exit once it's set; logging is
sufficient post-setup. The bridge is fetched from the qmp client
userdata, the same pointer the rest of the file uses.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
QEMU keeps a monitor-side fd alive until either an explicit remove-fd
arrives or the fdset's last duplicate is closed. Today vmspawn issues
add-fd but never the matching remove-fd, so each fdset stays around for
the lifetime of the VM even after the consuming blockdev is torn down.
Pipelining a remove-fd directly after the blockdev-add that consumed
the fd hands ownership entirely to the blockdev: the fdset
auto-disposes when raw_close runs at blockdev-del time. This is the
shape needed by hotplug, where blockdev-del must clean up everything
without further coordination.
Mechanically:
- qmp_fdset_add() takes a callback/userdata pair (so callers control
failure handling) and an optional out-param for the numeric fdset id.
All boot-time callers keep using on_qmp_complete with a label.
- A new qmp_fdset_remove() helper sends remove-fd with caller-supplied
callback/userdata.
- qmp_setup_ephemeral_drive captures both fdset ids and fires remove-fd
immediately after each base/overlay file blockdev-add.
- qmp_setup_regular_drive does the same for its single file blockdev-add.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Replace the caller-supplied DriveInfo.node_name with two QMP-internal
strings generated at setup time from a monotonic per-bridge counter:
qmp_node_name = "vmspawn-<N>-storage" (blockdev node-name)
qmp_device_id = "vmspawn-<N>-disk" (qdev id)
This is the naming scheme the upcoming runtime-hotplug add path needs:
unique across the lifetime of the VM, decoupled from any user-visible id,
and stable across the four QMP commands that make up an add (add-fd,
blockdev-add, remove-fd, device_add). The boot-time setups don't care
about uniqueness, but switching them now means the hotplug path can share
qmp_build_device_add() and EphemeralDriveCtx without a parallel naming
scheme.
vmspawn.c stops assigning node_name in prepare_primary_drive (which used
the literal "vmspawn") and prepare_extra_drives (which counted
"vmspawn_extra_%zu"); both are replaced by the bridge counter at the
point the drive is actually pushed into QEMU. Ephemeral helper-node
names follow the same vmspawn-<N>-{base-file,base-fmt,overlay-file}
convention; the blockdev-create job-id becomes
vmspawn-<N>-overlay-create.
EphemeralDriveCtx grows a qmp_device_id field (the qdev id is
independent of the format node-name now) and renames node_name →
qmp_node_name to match. No external behaviour change other than the new
internal names.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
In preparation for runtime block-device hotplug, where in-flight QMP
callbacks need to keep a slot reference on the DriveInfo while the bridge
also holds it in its block-device registry. Today each DriveInfo has
exactly one owner; switch the API from drive_info_free() /
drive_info_freep to drive_info_ref() / drive_info_unref() /
drive_info_unrefp so future code can take additional refs without the
caller losing track of ownership.
drive_info_new() initialises n_ref to 1 (one ref for the caller). The
existing drive_infos_done() and the prepare_*_drive() callers in
vmspawn.c are switched to the unref form. No behaviour change: each
DriveInfo still has exactly one ref at every point in this commit.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
The callback already has the bridge available — but it was reaching for
it via qmp_client_get_userdata() instead of through its own userdata
parameter. Pass the bridge directly from vmspawn_qmp_start() so the
callback can read its argument the way the rest of the file does. No
behaviour change.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
A client supplying "filter": [] previously matched no events at all,
because filter is checked with strv_contains() — an unintuitive corner
case. Treat an empty filter strv identically to a NULL filter (deliver
all events) by freeing the empty strv before it lands in the
subscription map. Brings the API closer to least-surprise.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>