Commit Graph
10113 Commits
Author SHA1 Message Date
Lennart Poettering 2251f726b3 bootctl: add --print-efi-architecture switch
This is extremely useful for our own test cases, since acquiring the
right EFI architecture string is otherwise a bit nasty.
2026-04-20 22:21:46 +02:00
Lennart Poettering a4256a9b31 sd-path: expose XDG 'projects' user dir
As per:

https://blog.tenstral.net/2026/04/hello-projects-directory.html
2026-04-20 15:01:17 +01:00
Felix Pehla 89d705a892 man/fstab-generator: fix option list and make formatting consistent
Add "overlay", which is already mentioned further down below, to the
list of possible options.

Consistently use <literal> for possible values of systemd.volatile=,
rather than <param> or no special formatting.

Use yes/no rather than true/false as boolean since that is what's used
everywhere else and I'm already touching the lines anyway.
2026-04-20 09:39:35 +02:00
Nandakumar Raghavan fa6eb2e016 repart: add EncryptKDF= option for LUKS2 partitions
systemd-repart currently creates LUKS2 encrypted partitions using
libcryptsetup's default KDF (Argon2id), which requires ~1GB of memory
during key derivation. This is too much for memory-constrained
environments such as kdump with limited crashkernel memory, where
luksOpen fails due to insufficient memory.

Add an EncryptKDF= option to repart.d partition definitions that allows
selecting the KDF type. Supported values are:

- "argon2id" — Argon2id with libcryptsetup-benchmarked parameters
- "pbkdf2"  — PBKDF2 with libcryptsetup-benchmarked parameters
- "minimal" — PBKDF2 with SHA-512, 1000 iterations, no benchmarking,
  matching the existing cryptsetup_set_minimal_pbkdf() behaviour used
  for TPM2-sealed keys

When not specified, the libcryptsetup default (argon2id) is used,
preserving existing behaviour.

The KDF type is applied via sym_crypt_set_pbkdf_type() after
sym_crypt_format() and before any keyslots are added.
2026-04-18 11:03:52 +01:00
Tobias Stoeckmann 4d2847dcc2 man: Fix NOTES formatting
The NOTES section in os-release(5) contains an unusual formatting.
Switch function and ulink tags and remove a newline within ulink text to
keep the entry formatting in sync with others. Also, this preserves the
formatting within the text itself.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-04-17 21:12:16 +01:00
Zbigniew Jędrzejewski-Szmek b332dfb033 Rework ssh-issue to be verb-based and convert to the new verb+option parsing (#41668)
This is a bit involved because the old interface was documented, so
compat glue needed to be added.
But I think it's an improvement overall.
2026-04-17 19:01:01 +02:00
Lennart Poettering c83c21a054 man: drop redundant word from varlinkctl man page 2026-04-17 15:11:37 +01:00
Luca Boccassi 2e79f8aa41 systemctl: add --kernel-cmdline= argument
Allows appending kernel command line arguments, like
kexec-tool does. This is especially needed for the integration
tests, as mkosi adds a bunch of options that are needed for the
test suite to work, and it breaks without them.
2026-04-17 13:58:05 +01:00
Zbigniew Jędrzejewski-Szmek 252783981b ssh-issue: replace verb options by normal verbs
The interface of this program was rather strange. It took an option that
specified what to do, but that option behaved exactly like a verb. Let's
change the interface to the more modern style with verbs. Since the
inteface was documented in the man page, provide a compat shim to handle
the old options.

(In practice, I doubt anybody will notice the change. But since it was
documented, it's easier to provide the compat then to think too much
whether it is actually needed. I think we can drop it an year or so.)
2026-04-17 11:54:47 +02:00
Zbigniew Jędrzejewski-Szmek 18acfba6f6 Support for "positional options" and another batch of conversions (#41651)
To make review more sensible, I split out the reordering into separate
commits when there was a lot of reordering.
2026-04-16 22:33:06 +02:00
Zbigniew Jędrzejewski-Szmek a60f12202f cgls: fix/update/restore the handling of --xattr and --cgroup-id
This is a bit tricky. Previously, the --help string said
"-x --xattr=BOOL", which normally means that '-x BOOL' and '--xattr BOOL'
and '--xattr=BOOL' are all accepted and equivalent. But actually only
the third form was accepted. '-x' should have been and is now documented
as "Same as --xattr=true". The man page tried to explain this, but not
very strongly. So update the man page to have more emphasis and restore
the special behaviour for -x and -c. This is a on old program, so I
think in this case, maintaining compatiblity in behaviour is important.
2026-04-16 16:48:17 +02:00
Christian Brauner 3b796616cc man: document machinectl pause/resume and update poweroff for VMs
Add manpage entries for the new pause and resume verbs. Update the
poweroff description to cover VMs (ACPI powerdown via QMP) in addition
to containers (SIGRTMIN+4).

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-04-15 10:14:48 +02:00
Arnout Engelen 086707ba79 run: allow setting output format
This is useful when moving from `--pty` or `--pipe` to using
`--verbose`: you can use `--verbose-output=cat` to get similar output on
stdout while still having all of the advantages of `--verbose` over the
other options.
2026-04-13 13:38:04 +02:00
Daan De Meyer 4bbdc8a6a2 nspawn: Add --restrict-address-families= option
Add a new --restrict-address-families= command line option and
corresponding RestrictAddressFamilies= setting for .nspawn files to
restrict which socket address families may be used inside a container.

Many address families such as AF_VSOCK and AF_NETLINK are not
network-namespaced, so restricting access to them in containers
improves isolation. The option supports allowlist and denylist modes
(via ~ prefix), as well as "none" to block all families, matching the
semantics of RestrictAddressFamilies= in unit files.

The address family parsing logic is extracted into a shared
parse_address_families() helper in parse-helpers.c, which is now also
used by config_parse_address_families() in load-fragment.c.

This is currently opt-in. In a future version, the default will be
changed to restrict address families to AF_INET, AF_INET6 and AF_UNIX.
2026-04-13 11:14:11 +02:00
Lennart Poettering 5401a1270a logind: add io.systemd.Shutdown varlink interface (#41229)
The shutdown interface is currently only exposed via dbus. This PR
adds a comparable varlink implementation. It is inspired by the existing
dbus methods and implements PowerOff, Reboot, Halt, Kexec, SoftReboot
as varlink methods on a new io.systemd.Shutdown interface.

It is (intentional) simpler than the dbus for now, i.e. no Can* yet,
mostly
because I want to get feedback first (happy to do that in a followup). 

The only real difficulty here is what to do about
verify_shutdown_creds()
as this is needed by both dbus and varlink and its dbus only. I went for
an ugly but (hopefully) pragmatic choice (see the commit message for
details). But I can totally understand if a refactor instead is
preferred.
2026-04-10 12:29:32 +02:00
Daan De Meyer 158f2d50bf docs: Update MEMORY_PRESSURE.md => PRESSURE.md
Make the doc more generic and mention all pressure types, not just
memory.
2026-04-09 22:47:10 +02:00
Daan De Meyer 594659da06 core: Add I/O pressure support 2026-04-09 22:47:10 +02:00
Daan De Meyer 316d17fcbd core: Add support for CPU pressure notifications
Works the same way as memory pressure notifications. Code is refactored
to work on enum arrays to reduce duplication.
2026-04-09 22:47:10 +02:00
Daan De Meyer b3bb4fefde test-mempress: Support unprivileged operation 2026-04-09 22:47:10 +02:00
Daan De Meyer b5bd726f8c varlink: add sd_varlink_reply_and_upgrade and varlinkctl serve (#41474) 2026-04-09 14:19:47 +02:00
Michael Vogt 34f29079fd varlinkctl: add new serve verb to allow wrapping command in varlink
With the new protocol upgrade support in varlinkctl client we can
now do the equivalent for the server side. This commit adds a new
`serve` verb that will serve any command that speaks stdin/stdout
via varlink and its protocol upgrade feature. This is the
"inetd for varlink".

This is useful for various reasons:
1. Allows to e.g. provide a heavily sandboxed io.myorg.xz.Decompress
   varlink endpoint, c.f. xz CVE-2024-3094)
2. Allow sftp over varlink which is quite useful with the
   varlink-http-bridge (that has more flexible auth mechanism than
   plain sftp).
3. Makes testing the varlinkctl client protocol upgrade simpler.
4. Because we can.
2026-04-09 13:02:09 +02:00
Lennart Poettering 3fa1f48695 man: fix borked reference to v262 2026-04-09 10:12:17 +02:00
Daan De Meyer bb93fdaaf3 vmspawn: Support direct kernel boot without UEFI firmware
When --linux= specifies a non-PE kernel image, automatically disable
UEFI firmware loading (as if --firmware= was passed). If --firmware=
is explicitly set to a path in this case, fail with an error. Booting
a UKI with --firmware= is also rejected since UKIs require UEFI.

--firmware= (empty string) can also be used explicitly to disable
firmware loading for PE kernels.

Other changes:
- Extract OVMF pflash drive setup into cmdline_add_ovmf()
- Extract kernel image type detection into determine_kernel()
- Add smbios_supported() helper to centralize the SMBIOS availability
  check (always available on x86, elsewhere requires firmware)
- Gate SMM, OVMF drives, SMBIOS11 and credential SMBIOS paths
  on firmware/SMBIOS being available
- Beef up the credential logic to fall back to fw_cfg and kernel
  command line in case SMBIOS is not available
2026-04-08 23:24:56 +02:00
Luca Boccassi c0b3b855b7 coredumpctll: avoid unnecessary heap copy and decompression for field existence checks (#41520)
`print_list()` and `print_info()` used `RETRIEVE()` to `strndup()` the
entire
`COREDUMP` field into a heap-allocated string, only to check whether it
exists.
With `sd_journal_set_data_threshold(j, 0)` in `print_info()`, this
copies the
full coredump binary (potentially hundreds of MB) to heap just to print
"Storage: journal".

This PR:

1. Makes `sd_journal_get_data()` output parameters optional
(`NULL`-safe), so
   callers can do pure existence checks without receiving the data.
2. Short-circuits `maybe_decompress_payload()` after
`decompress_startswith()`
   succeeds when neither output pointer is requested, skipping full blob
   decompression for compressed journal entries.
3. Switches coredumpctl to pass `NULL, NULL` for the existence checks
instead
   of heap-copying via `RETRIEVE()`.
2026-04-08 21:34:47 +01:00
noxiouz 329233dcbf sd-journal: make sd_journal_get_data() output params optional
Allow callers to pass NULL for ret_data and/or ret_size when they only
need to check whether a field exists.  Initialize provided output
pointers to safe defaults and update the manual page accordingly.

Propagate the NULL-ness through to journal_file_data_payload() so that
downstream helpers can optimize for the existence-check case.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 16:33:20 +02:00
Frantisek Sumsal 3d5bd67a22 fstab-generator: support swap on network block devices
Teach swap units to support the _netdev option as well, which should
make swaps on iSCSI possible. This mirrors the logic we already have for
regular mounts in both the fstab-generator and the core
(mount.c/swap.c).

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 11:51:09 +01:00
Lennart Poettering a50e6e0a1e sysext: provide systemd-{sysext,confext}-sysroot.service services (#41161)
This should pretty much close #38985

The new services are used to activate system and configuration
extensions for the main system from the initrd, this allows to overcome
the limitation that sysext/confext cannot be used to update the
resources which are required in the earliest boot of the system (before
systemd-sysext/systemd-confext start).

To make it possible to disable sysext/confext merging logic,
`systemd.sysext=0`, `systemd.confext=0`, `rd.systemd.sysext=0`,
`rd.systemd.confext=0` kernel cmdline options are introduced.

Closes #38985
2026-04-08 11:30:20 +02:00
Michael Vogt d1d72563e0 logind: add io.systemd.Shutdown varlink Shutdown interface
The shutdown interface is currently only exposed via dbus. This commit
adds a comparable varlink implementation. It is inspired by the existing
dbus methods and implements PowerOff, Reboot, Halt, Kexec, SoftReboot.

It is (intentional) simpler than the dbus for now, i.e. strictly root
only. To match dbus we will need to the functionality
of verify_shutdown_creds() which is dbus-ish right now and would
need some refactor.

For the same reason it does not do the Can* methods - we will need
the verify_shutdown_creds() equivalent first.
2026-04-07 20:38:40 +02:00
Daan De Meyer e4f535fdca vmspawn: Always enable CXL on supported architectures
Drop the --cxl= option and unconditionally enable cxl=on the QEMU
machine type whenever the host architecture supports it (x86_64 and
aarch64). The flag was only added for testing parity with mkosi's CXL=
setting and there is no reason to leave it as an opt-in toggle: with no
pxb-cxl device or cxl-fmw window attached, enabling it on the machine
only reserves a small MMIO region and emits an empty CEDT, so the cost
is negligible while removing one knob users would otherwise have to
flip explicitly to exercise the CXL code paths in QEMU.
2026-04-07 12:47:31 +02:00
Jonas Rebmann 07745c222c repart: Do not refer to SizeMinBytes= as SizeMin=
No SizeMin= option exists for repart.d; it seems that SizeMinBytes= was
intended.

Update all references accordingly.

Signed-off-by: Jonas Rebmann <jre@pengutronix.de>
2026-04-07 11:15:54 +02:00
Vitaly Kuznetsov 2299a37e28 sysext: provide a cmdline kill switch for the sysext/confext merging logic
While it is possible to disable sysext/confext merging in the main system
with 'systemctl disable', sysext/confext are always merged in the initrd,
both by systemd-{sys,conf}ext-initrd.service and by
systemd-{sys,conf}ext-sysroot.service and especially the latter can be
unexpected. Provide kernel cmdline options systemd.{sys,conf}ext=0 and
rd.systemd.{sys,conf}ext=0 covering all options.
2026-04-07 09:51:03 +02:00
Vitaly Kuznetsov c70b17daad sysext: provide systemd-{sysext,confext}-sysroot.service services
The new services are used to activate system and configuration extensions
for the main system from the initrd, this allows to overcome the limitation
that sysext/confext cannot be used to update the resources which are required
in the earliest boot of the system (before systemd-sysext/systemd-confext
start).
2026-04-07 09:50:59 +02:00
Daan De Meyer e7fb7296f5 nspawn: rename --user= to --uid= and repurpose --user/--system for runtime scope
Rename nspawn's --user=NAME option to --uid=NAME for selecting the
container user. The -u short option is preserved. --user=NAME and
--user NAME are still accepted but emit a deprecation warning. A
pre-parsing step stitches the space-separated --user NAME form into
--user=NAME before getopt sees it, preserving backwards compatibility
despite --user now being an optional_argument.

Repurpose --user (without argument) and --system as standalone
switches for selecting the runtime scope (user vs system service
manager).

Replace all uses of the arg_privileged boolean with
arg_runtime_scope comparisons throughout nspawn. The default scope
is auto-detected from the effective UID.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 18:49:40 +02:00
Daan De Meyer 1e084aad7d shared: move machine registration to shared machine-register.{c,h}
Move register_machine() and unregister_machine() from
vmspawn-register.{c,h} into shared machine-register.{c,h} so both
nspawn and vmspawn can use the same implementation.

The unified register_machine() uses varlink first (for richer
features like SSH support and unit allocation) with a D-Bus
RegisterMachineWithNetwork fallback for older machined. The
interface adds a class parameter ("vm" or "container") and
local_ifindex for nspawn's network interface support.

The unified unregister_machine() similarly tries varlink first
(io.systemd.Machine.Unregister) before falling back to D-Bus.

Both register_machine() and unregister_machine() only log at debug
level internally, leaving error/notice logging to callers.

Add register_machine_with_fallback() which tries system and/or user
scope registration based on a RuntimeScope parameter
(_RUNTIME_SCOPE_INVALID for both), and
unregister_machine_with_fallback() as its counterpart. Both use
RET_GATHER() to collect errors from each scope.

Make --register= a tristate (yes/no/auto) defaulting to auto. When
set to auto, registration failures are logged at notice level and
ignored. When set to yes, failures are fatal.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 17:13:40 +02:00
Daan De Meyer fb4bfe651b vmspawn: Use ~ instead of ! as negation prefix for --firmware-features=
Switch the negation character for firmware feature exclusion from
"!" to "~" to be consistent with other systemd options that support
negation such as SystemCallFilter=.
2026-04-05 21:09:23 +02:00
Lennart Poettering 0874eea302 shutdown: enforce a minimum uptime to make boot loops less annoying
Fixes: #9453
2026-04-04 22:52:58 +02:00
Daan De Meyer 4ded480ea5 repart: Use --shrink with mkfs.btrfs to get a minimal filesystem
Avoids populating the filesystem twice similar to read-only
filesystems.
2026-04-03 19:12:46 +02:00
Daan De Meyer 6a9888c4c6 vmspawn: add --cxl= option and memory hotplug support
Add --cxl=BOOL option to enable CXL (Compute Express Link) support in
the virtual machine. CXL is a high-speed interconnect standard that
allows CPUs to access memory attached to devices such as accelerators
and memory expanders, enabling flexible memory pooling and expansion
beyond what is physically installed on the motherboard. When enabled,
adds cxl=on to the QEMU machine configuration. Only supported on x86_64
and aarch64 architectures.

This is added for testing purposes and for feature parity with mkosi's
CXL= setting.

Extend --ram= to accept an optional maximum size for memory hotplug,
using the syntax --ram=SIZE[:MAXSIZE] (e.g. --ram=2G:8G). When a
maximum is specified, the maxmem key is added to the QEMU memory
configuration section to enable memory hotplug up to the given limit.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 16:31:42 +02:00
Zbigniew Jędrzejewski-Szmek b98d532a6c core: do not GC units that have FDs stored (#41435)
If a unit has FileDescriptorStorePreserve=yes we'll keep its FDs around
in case it starts again. But if there are no reverse dependencies
referencing it, we'll also GC it and lose all the FDs, which defeats the
point of the setting (which is opt-in).

Do not GC units that have FDs stored to avoid this.

Follow-up for b9c1883a9c
2026-04-03 14:30:07 +02:00
Chris Down ef31b4c1c5 core: Prevent corrupting units from stale alias state on daemon-reload (#39703)
During daemon-reload (or daemon-reexec), when a unit becomes an alias to
another unit, deserialising the alias's stale serialised state can
corrupt the canonical unit's live runtime state.

Consider this scenario:

1. Before reload:
   - a.service is running
   - b.service was stopped earlier and is dead
   - Both exist as independent units

2. User creates an alias to migrate from b -> a:

   - `ln -s /run/systemd/system/a.service /etc/systemd/system/b.service`

3. daemon-reload triggers serialisation. State file contains both units:
- a.service -> state=running, cgroup=/system.slice/a.service, PID=1234,
...
   - b.service -> state=dead, cgroup=(empty), no PIDs, ...

4. During deserialisation:
   - Processes a.service: loads Unit A, deserialises -> state=RUNNING
- Processes b.service: manager_load_unit() detects symlink, returns Unit
A
   - unit_deserialize_state(Unit A, ...) overwrites with b's dead state

5. The result is that:
   - Unit A incorrectly shows state=dead despite PID 1234 still running
- If a.service has Upholds= dependents, catch-up logic sees a.service
should be running but is dead
   - systemd starts a.service again -> PID 5678
   - Two instances run: PID 1234 (left-over) and PID 5678 (new)

This bug is deterministic when serialisation orders a.service before
b.service.

The root cause is that manager_deserialize_one_unit() calls
manager_load_unit(name, &u) which resolves aliases via
unit_follow_merge(), returning the canonical Unit object. However, the
code doesn't distinguish between two cases when u->id differs from the
requested name from the state file. In the corruption case, we're
deserialising an alias entry and unit_deserialize_state() blindly
overwrites the canonical unit's fields with stale data from the old,
independent unit. The serialised b.service then overwrites Unit A's
correct live state.

This commit first scans the serialised unit names, then adds a check
after manager_load_unit():

    if (!streq(u->id, name) && set_contains(serialized_units, u->id))
        ...

This detects when the loaded unit's canonical ID (u->id) differs from
the serialised name, indicating the name is now an alias for a different
unit and the canonical unit also has its own serialised state entry.

If the canonical unit does not have its own serialised state entry, we
keep the state entry. That handles cases where the old name is really
just a rename, and thus the old name is the only serialised state for
the unit. In that case there is no bug, because there is no separate
canonical state entry for the stale alias entry to overwrite.

Skipping is safe because:

1. The canonical unit's own state entry will be correctly deserialised
regardless of order. This fix only prevents other stale alias entries
from corrupting it.
2. unit_merge() has already transferred the necessary data. When
b.service became an alias during unit loading, unit_merge() already
migrated dependencies and references to the canonical unit.
3. After merging, the alias doesn't have its own runtime state. The
serialised data represents b.service when it was independent, which is
now obsolete once the canonical unit also has its own serialised entry.
4. All fields are stale. unit_deserialize_state() would overwrite state,
timestamps, cgroup paths, pids, etc. There's no scenario where we want
this data applied on top of the canonical unit's own serialised state.

This fix also correctly handles unit precedence. For example, imagine
this scenario:

1. `b.service` is a valid, running unit defined in `/run`.
2. The sysadmin creates `ln -s .../a.service /etc/.../b.service`.
3. On reload, the new symlink in `/etc` overrides the unit in `/run`.

The new perspective from the manager side is that `b.service` is now an
alias for `a.service`.

In this case, systemd correctly abandons the old b.service unit, because
that's the intended general semantics of unit file precedence. We also
do that in other cases, like when a unit file in /etc/systemd/system/
masks a vendor-supplied unit file in /lib/systemd/system/, or when an
admin uses systemctl mask to explicitly disable a unit.

In all these scenarios, the configuration with the highest precedence
(in /etc/) is treated as the new source of truth. The old unit's
definition is discarded, and its running processes are (correctly)
abandoned. In that respect we are not doing anything new here.

Some may ask why we shouldn't just ignore the symlink if we think this
case will come up. I think there are multiple very strong reasons not to
do so:

1. It violates unit precedence. The unit design is built on a strict
precedence list. When an admin puts any file in /etc, they are
intentionally overriding everything else. If manager_load_unit were to
"ignore" this file based on runtime state, it would break this
fundamental precedent.
2. It makes daemon-reload stateful. daemon-reload is supposed to be a
simple, stateless operation, basically to read the files on disk and
apply the new configuration. But doing this would make daemon-reload
stateful, because we'd have to read the files on disk, but
cross-reference the current runtime state, and... maybe ignore some
files. This is complex and unpredictable.
3. It also completely ignores the user intent. The admin clearly has
tried to replace the old service with an alias. Ignoring their
instruction is the opposite of what they want.

Fixes: https://github.com/systemd/systemd/issues/38817
Fixes: https://github.com/systemd/systemd/issues/37482
2026-04-03 13:52:42 +08:00
Nikolas Kyx 1f6935145d manager: Add DefaultMemoryZSwapWriteback
Allow setting system-wide MemoryZSwapWriteback in system.conf

Resolves: #41320
2026-04-02 20:04:30 +01:00
Daan De Meyer a586648800 vmspawn: Add --console-transport= option to select serial vs virtio-serial
Add a --console-transport= option that selects between virtio-serial
(the default, appearing as /dev/hvc0) and a regular serial port
(appearing as /dev/ttyS0 or /dev/ttyAMA0 depending on architecture).

This is primarily useful for testing purposes, for example to test
sd-stub's automatic console= kernel command line parameter handling. It
allows verifying that the guest OS correctly handles serial console
configurations without virtio.

When serial transport is selected, -serial chardev:console is used on
the QEMU command line to connect the chardev to the platform's default
serial device. This cannot be done via the QEMU config file as on some
platforms (e.g. ARM) the serial device is a sysbus device that can only
be connected via serial_hd() which is populated by -serial.
2026-04-02 20:43:42 +02:00
Lennart Poettering 5f598576b8 network-generator: support BOOTIF= and rd.bootif=0 options (#41028)
The network generator currently supports many of the options described
by dracut.cmdline(7), but not everything.

This commit adds support for the BOOTIF= option (and the related
rd.bootif= option) used in PXE setups.

This is implemented by treating BOOTIF as a special name/placeholder
when used as an interface name, and expecting a MAC address to be set in
the BOOTIF= parameter. The resulting .network file then uses MACAddress=
in the [Match] section, instead of Name=.
2026-04-02 16:17:11 +02:00
Daan De Meyer 2371c9e798 vmspawn: add scsi-cd disk type for ISO/CD-ROM image support
Add DISK_TYPE_SCSI_CD to support attaching disk images as CD-ROM
drives, needed for testing El Torito ISO images built by
systemd-repart.

When --image-disk-type=scsi-cd is specified, the image is attached
with media=cdrom and readonly=on on the drive, using scsi-cd as the
device driver on the SCSI bus. This also works for --extra-drive=
with the scsi-cd: prefix.

The QEMU configuration matches the standard OVMF CD-ROM boot setup:
  -drive if=none,media=cdrom,format=raw,readonly=on
  -device virtio-scsi-pci
  -device scsi-cd

When direct kernel booting with scsi-cd, if the kernel command line
contains "rw", append "ro" to override it since CD-ROMs are
read-only.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 16:02:43 +02:00
Chris Down a77c7a8224 core: Prevent corrupting units from stale alias state on daemon-reload
During daemon-reload (or daemon-reexec), when a unit becomes an alias to
another unit, deserialising the alias's stale serialised state can
corrupt the canonical unit's live runtime state.

Consider this scenario:

1. Before reload:
   - a.service is running
   - b.service was stopped earlier and is dead
   - Both exist as independent units

2. User creates an alias to migrate from b -> a:

   - `ln -s /run/systemd/system/a.service /etc/systemd/system/b.service`

3. daemon-reload triggers serialisation. State file contains both units:
   - a.service -> state=running, cgroup=/system.slice/a.service,
     PID=1234, ...
   - b.service -> state=dead, cgroup=(empty), no PIDs, ...

4. During deserialisation:
   - Processes a.service: loads Unit A, deserialises -> state=RUNNING
   - Processes b.service: manager_load_unit() detects symlink, returns
     Unit A
   - unit_deserialize_state(Unit A, ...) overwrites with b's dead state

5. The result is that:
   - Unit A incorrectly shows state=dead despite PID 1234 still running
   - If a.service has Upholds= dependents, catch-up logic sees
     a.service should be running but is dead
   - systemd starts a.service again -> PID 5678
   - Two instances run: PID 1234 (left-over) and PID 5678 (new)

This bug is deterministic when serialisation orders a.service before
b.service.

The root cause is that manager_deserialize_one_unit() calls
manager_load_unit(name, &u) which resolves aliases via
unit_follow_merge(), returning the canonical Unit object. However, the
code doesn't distinguish between two cases when u->id differs from the
requested name from the state file. In the corruption case, we're
deserialising an alias entry and unit_deserialize_state() blindly
overwrites the canonical unit's fields with stale data from the old,
independent unit. The serialised b.service then overwrites Unit A's
correct live state.

This commit first scans the serialised unit names, then adds a check
after manager_load_unit():

    if (!streq(u->id, name) && set_contains(serialized_units, u->id))
        ...

This detects when the loaded unit's canonical ID (u->id) differs from
the serialised name, indicating the name is now an alias for a different
unit and the canonical unit also has its own serialised state entry.

If the canonical unit does not have its own serialised state entry, we
keep the state entry. That handles cases where the old name is really
just a rename, and thus the old name is the only serialised state for
the unit. In that case there is no bug, because there is no separate
canonical state entry for the stale alias entry to overwrite.

Skipping is safe because:

1. The canonical unit's own state entry will be correctly deserialised
   regardless of order. This fix only prevents other stale alias entries
   from corrupting it.
2. unit_merge() has already transferred the necessary data. When
   b.service became an alias during unit loading, unit_merge() already
   migrated dependencies and references to the canonical unit.
3. After merging, the alias doesn't have its own runtime state. The
   serialised data represents b.service when it was independent, which
   is now obsolete once the canonical unit also has its own serialised
   entry.
4. All fields are stale. unit_deserialize_state() would overwrite state,
   timestamps, cgroup paths, pids, etc. There's no scenario where we
   want this data applied on top of the canonical unit's own serialised
   state.

This fix also correctly handles unit precedence. For example, imagine
this scenario:

1. `b.service` is a valid, running unit defined in `/run`.
2. The sysadmin creates `ln -s .../a.service /etc/.../b.service`.
3. On reload, the new symlink in `/etc` overrides the unit in `/run`.

The new perspective from the manager side is that `b.service` is now an
alias for `a.service`.

In this case, systemd correctly abandons the old b.service unit, because
that's the intended general semantics of unit file precedence. We also
do that in other cases, like when a unit file in /etc/systemd/system/
masks a vendor-supplied unit file in /lib/systemd/system/, or when an
admin uses systemctl mask to explicitly disable a unit.

In all these scenarios, the configuration with the highest precedence
(in /etc/) is treated as the new source of truth. The old unit's
definition is discarded, and its running processes are (correctly)
abandoned. In that respect we are not doing anything new here.

Some may ask why we shouldn't just ignore the symlink if we think this
case will come up. I think there are multiple very strong reasons not to
do so:

1. It violates unit precedence. The unit design is built on a strict
   precedence list. When an admin puts any file in /etc, they are
   intentionally overriding everything else. If manager_load_unit were
   to "ignore" this file based on runtime state, it would break this
   fundamental precedent.
2. It makes daemon-reload stateful. daemon-reload is supposed to be a
   simple, stateless operation, basically to read the files on disk and
   apply the new configuration. But doing this would make daemon-reload
   stateful, because we'd have to read the files on disk, but
   cross-reference the current runtime state, and...  maybe ignore some
   files. This is complex and unpredictable.
3. It also completely ignores the user intent. The admin clearly has
   tried to replace the old service with an alias. Ignoring their
   instruction is the opposite of what they want.

Fixes: https://github.com/systemd/systemd/issues/38817
Fixes: https://github.com/systemd/systemd/issues/37482
2026-04-02 22:58:19 +09:00
Luca Boccassi 55327a1fd8 core: do not GC units that have FDs stored
If a unit has FileDescriptorStorePreserve=yes we'll keep its FDs
around in case it starts again. But if there are no reverse
dependencies referencing it, we'll also GC it and lose all the FDs,
which defeats the point of the setting (which is opt-in).

Do not GC units that have FDs stored to avoid this.

Follow-up for b9c1883a9c
2026-04-02 12:22:43 +01:00
Michael Vogt 042f633b12 varlinkctl: add support for --exec with --upgrade
Having support for `--exec` when using `--upgrade` is nice so this
commit adds it. It does it by extracting a shared helper called
`exec_with_listen_fds()` and then use that in the `verb_call()`
and `varlink_call_and_upgrade()` calls.
2026-03-31 18:25:09 +02:00
Michael Vogt 0fc3f85d35 varlinkctl: add protocol upgrade support
The varlink spec supports protocol upgrades and they are very
useful to e.g. transfer binary data directly via varlink. So
far varlinkctl/sd-varlink was not supporting this. This commit
adds support for it in varlinkctl by using the new code in
sd-varlink and the generalized socket-forward code.
2026-03-31 18:25:09 +02:00
Antonio Alvarez Feijoo d2983ba6e7 man: fix typos in some binary names 2026-03-31 09:47:27 +01:00
Nick Rosbrook cceddc41fd network-generator: support BOOTIF= and rd.bootif=0 options
The network generator currently supports many of the options described
by dracut.cmdline(7), but not everything.

This commit adds support for the BOOTIF= option (and the related
rd.bootif= option) used in PXE setups.

This is implemented by treating BOOTIF as a special name/placeholder
when used as an interface name, and expecting a MAC address to be set in
the BOOTIF= parameter. The resulting .network file then uses MACAddress=
in the [Match] section, instead of Name=.
2026-03-30 10:48:55 -04:00