Add testcase_btrfs_compression() to verify that btrfs partitions with
Compression= and CopyFiles= directives work correctly.
The test verifies the fix for issue #39584, where mkfs.btrfs would fail
with "ERROR: --compression must be used with --rootdir" when repart
tried to create compressed btrfs filesystems.
The test creates a partition definition with Format=btrfs,
Compression=zstd, and CopyFiles=, then validates:
1. systemd-repart output shows "Rootdir from:" and "Compress:",
confirming that the --rootdir code path is used
2. mkfs.btrfs is invoked with both --compress and --rootdir options
3. The file is successfully copied to the filesystem
4. Compression is actually applied (verified via compsize output
containing "zstd")
When a btrfs partition is configured with both Compression= and
CopyFiles=, we need to ensure files are copied during filesystem
creation using mkfs.btrfs --rootdir, rather than copying files
afterwards via loop device mounting.
This is required because mkfs.btrfs can only apply compression settings
when files are provided via --rootdir during filesystem creation. If we
format the filesystem first and then mount it to copy files, the
compression setting is meaningless.
Modify the partition_needs_populate() condition to force the --rootdir
code path when the format is btrfs and compression is requested.
This ensures that partition_populate_directory() runs and creates a
temporary directory with the files, which is then passed to
make_filesystem() as the root parameter, allowing mkfs.btrfs to create
the filesystem with compression applied.
Fixes: https://github.com/systemd/systemd/issues/39584
mkfs.btrfs requires that the --compress option be used together with
--rootdir, as compression only makes sense in that context (because
compression is not a persistent setting).
Right now, If --compress is specified without --rootdir, mkfs.btrfs
fails with:
ERROR: --compression must be used with --rootdir
This can occur when repart is configured with Compression= but the
partition populate logic doesn't use the --rootdir code path (eg. when
using loop device mounting to copy files after mkfs).
Add a defensive check to skip compression and emit a user-friendly
warning when compression is requested but no root directory is
provided. The warning message references the repart directive names
(Compression= and CopyFiles=) rather than low-level mkfs options to
help users understand the requirement.
This prevents crashes but doesn't enable compression, that requires
ensuring the --rootdir code path is used, which it currently is not and
will be addressed in the next patch.
Fixes: https://github.com/systemd/systemd/issues/39584
Before aa47d8ade1, when we failed to lock
the device node, we simply trigger change events for the device and its
partitions. But the commit killed the fallback logic. Let's restore that.
Fixes#39544.
We track session leaders by pidfd precisely to make restarts reliable,
as leader exiting before deserialization is somewhat expected.
Such case is already handled gracefully (we'd GC sessions without leader
before kicking off the new cycle), but let's also tweak the log message
a bit to reduce annoyance.
Closes#39556
In these two cases we need to sync the journal _after_ the unit finishes
as well, because we try to match messages from systemd itself, not
(only) from the unit, and the messages about units are dispatched
asynchronously.
That is, in the first case (silent-success.service) we want to make sure
that LogLevelMax= filters out messages _about_ units (from systemd) as
well, including messages like "Deactivated..." and "Finished...", which
are sent out only when/after the unit is stopped.
In the second case we try to match messages with the "systemd" syslog
tag, but these messages come from systemd (obviously) and are sent out
asynchronously, which means they might not reach the journal before we
call `journalctl --sync` from the test unit itself, like happened here:
[ 1754.150391] TEST-04-JOURNAL.sh[13331]: + systemctl start verbose-success.service
[ 1754.172256] bash[13692]: success
[ 1754.221210] TEST-04-JOURNAL.sh[13694]: ++ journalctl -b -q -u verbose-success.service -t systemd
[ 1754.221493] TEST-04-JOURNAL.sh[13331]: + [[ -n '' ]]
[ 1754.175709] systemd[1]: Starting verbose-success.service - Verbose successful service...
[ 1754.221697] TEST-04-JOURNAL.sh[122]: + echo 'Subtest /usr/lib/systemd/tests/testdata/units/TEST-04-JOURNAL.journal.sh failed'
[ 1754.221697] TEST-04-JOURNAL.sh[122]: Subtest /usr/lib/systemd/tests/testdata/units/TEST-04-JOURNAL.journal.sh failed
[ 1754.221697] TEST-04-JOURNAL.sh[122]: + return 1
[ 1754.205408] systemd[1]: verbose-success.service: Deactivated successfully.
[ 1754.205687] systemd[1]: Finished verbose-success.service - Verbose successful service.
By syncing the journal after the unit is stopped we have much bigger
chance that the systemd messages already reached the journal - the race
is technically still there, but the chance we'd hit it should be pretty
negligible.
Resolves: #39555
The current code causes assertion in strv_parse_nulstr() if
read_credential() results in an error different from ENXIO or ENOENT
(strace shows I'm getting EACCES):
# homectl create waldo --real-name=Waldo --disk-size=200M
Before:
Assertion 's || l <= 0' failed at src/basic/nulstr-util.c:32, function strv_parse_nulstr_full(). Aborting.
After:
Failed to acquire password: Permission denied
Follow-up-for: 8806bb4bc7
# valgrind --leak-check=full homectl create waldo --real-name=Waldo --disk-size=200M --setopt=FOO=bar
Before:
==25155== HEAP SUMMARY:
==25155== in use at exit: 12,879 bytes in 39 blocks
==25155== total heap usage: 90 allocs, 51 frees, 53,964 bytes allocated
==25155==
==25155== 8 bytes in 1 blocks are definitely lost in loss record 4 of 38
==25155== at 0x4845866: malloc (vg_replace_malloc.c:446)
==25155== by 0x547FC2E: strdup (strdup.c:42)
==25155== by 0x4B2647C: strv_env_replace_strdup_passthrough (env-util.c:435)
==25155== by 0x42D547: parse_argv (homectl.c:3909)
==25155== by 0x43999C: run (homectl.c:5606)
==25155== by 0x4399F5: main (homectl.c:5613)
==25155==
==25155== LEAK SUMMARY:
==25155== definitely lost: 8 bytes in 1 blocks
After:
==25224== HEAP SUMMARY:
==25224== in use at exit: 12,871 bytes in 38 blocks
==25224== total heap usage: 90 allocs, 52 frees, 53,964 bytes allocated
==25224==
==25224== LEAK SUMMARY:
==25224== definitely lost: 0 bytes in 0 blocks
Follow-up-for: aaf057c4bb
Add io.systemd.Resolve.DumpDNSConfiguration. This provides the same
information as io.systemd.Resolve.Monitor.SubscribeDNSConfiguration,
but just returns the configuration once without the subscription logic.
In order to use the same definitions for DNSConfiguration et al. between
both interfaces, move the definitions to io.systemd.Resolve, and include
them in io.systemd.Resolve.Monitor.
This will be used to implement --json for resolvectl status.
The io.systemd.Resolve.Monitor.DNSConfiguration type is being expanded,
but we do not need the extra information for determining online status.
Ignore these fields when dispatching JSON to avoid "Unrecognized object field"
messages adding noise to systemd-networkd-wait-online debug output.
Although the JSON output is mostly intended to be machine readable,
humans also consume the output through logs and scripts.
Add an addressString field to DNSServer to improve human-readability.
systemd.service(5)’s documentation of `ExecCondition=` uses “failed” with
respect to the unit active state.
In particular the unit won’t be considered failed when `ExecCondition=`’s
command exits with a status of 1 through 254 (inclusive). It will however, when
it exits with 255 or abnormally (e.g. timeout, killed by a signal, etc.).
The table “Defined $SERVICE_RESULT values” in systemd.exec(5) uses “failed”
however rather with respect to the condition.
Tests seem to have shown that, if the exit status of the `ExecCondition=`
command is one of 1 through 254 (inclusive), `$SERVICE_RESULT` will be
`exec-condition`, if it is 255, `$SERVICE_RESULT` will be `exit-code` (but
`$EXIT_CODE` and `$EXIT_STATUS` will be empty or unset), if it’s killed because
of `SIGKILL`, `$SERVICE_RESULT` will `signal` and if it times out,
`$SERVICE_RESULT` will be `timeout`.
This commit clarifies the table at least for the case of an exit status of 1
through 254 (inclusive).
The others (signal, timeout and 255 are probably also still ambiguous (e.g.
`signal` uses “A service process”, which could be considered as the actual
service process only).
Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
When VirtIO VSOCK device is not present, IOCTL_VM_SOCKETS_GET_LOCAL_CID
returns VMADDR_CID_LOCAL/1, and we issue a hint to connect to vsock%1.
This does not work. Filter out VMADDR_CID_LOCAL and VMADDR_CID_HOST,
those are not real addresses that can be used from the outside.
`i` only counts the number of matches with the current family,
while `n_addresses` counts the number of matches with the family INET or INET6.
If the address contains both INET and INET6, `assert(i == n_addresses)` will fail.
`systemctl show`'s `--timestamp` flag is supposed to reformat all
timestamp-based properties. However, the logic for detecting these
properties was incomplete and only checked if the name ended in
Timestamp.
Expand the check to explicitly include some non-"timestamp" named
properties that really are timestamps.
Fixes: https://github.com/systemd/systemd/issues/39282
Before calling io.systemd.MachineImage.List.
The systemd-nspawn process takes a lock in the run() function in
nspawn.c and holds it for the entire runtime of that function. If we
call `machinectl terminate` the machine gets unregistered _before_ we
release the lock, so the original `machinectl status` check would return
early, allowing for a race where we call io.systemd.MachineImage.List
over Varlink when systemd-nspawn still holds the lock because the
process is still running.:
```
[ 41.691826] TEST-13-NSPAWN.sh[1102]: + machinectl terminate long-running
[ 41.695009] systemd-nspawn[2171]: Trying to halt container by sending TERM to container PID 1. Send SIGTERM again to trigger immediate termination.
[ 41.698235] systemd-machined[1192]: Machine long-running terminated.
[ 41.709520] TEST-13-NSPAWN.sh[1102]: + systemctl kill --signal=KILL systemd-nspawn@long-running.service
[ 41.709169] systemd-nspawn[2171]: Failed to unregister machine: No machine 'long-running' known
[ 41.720869] TEST-13-NSPAWN.sh[2346]: + varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}'
[ 41.723359] TEST-13-NSPAWN.sh[2347]: + grep long-running
...
[ 41.735453] TEST-13-NSPAWN.sh[2352]: + varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running", "acquireMetadata": "yes"}'
[ 41.736222] TEST-13-NSPAWN.sh[2353]: + grep OSRelease
[ 41.739500] TEST-13-NSPAWN.sh[2352]: Method call io.systemd.MachineImage.List() failed: Device or resource busy
[ 41.740641] systemd[1]: Received SIGCHLD.
[ 41.740670] systemd[1]: Child 2171 (systemd-nspawn) died (code=killed, status=9/KILL)
[ 41.740725] systemd[1]: systemd-nspawn@long-running.service: Child 2171 belongs to systemd-nspawn@long-running.service.
[ 41.740748] systemd[1]: systemd-nspawn@long-running.service: Main process exited, code=killed, status=9/KILL
[ 41.740755] systemd[1]: systemd-nspawn@long-running.service: Will spawn child (service_enter_stop_post): systemd-nspawn
[ 41.740872] systemd[1]: systemd-nspawn@long-running.service: About to execute: systemd-nspawn --cleanup --machine=long-running
...
```
Let's mitigate this by waiting until the corresponding
systemd-nspawn@.service instance enters the 'inactive' state where the
lock should be properly released.
Resolves: https://github.com/systemd/systemd/issues/39547
It should exit on its own anyway and this will work even if the job has
already finished* (unlike kill).
[*] assuming job control is off, as it's the case when running the
test suite
Resolves: #39543
Before calling io.systemd.MachineImage.List.
The systemd-nspawn process takes a lock in the run() function in
nspawn.c and holds it for the entire runtime of that function. If we
call `machinectl terminate` the machine gets unregistered _before_ we
release the lock, so the original `machinectl status` check would return
early, allowing for a race where we call io.systemd.MachineImage.List
over Varlink when systemd-nspawn still holds the lock because the
process is still running.:
[ 41.691826] TEST-13-NSPAWN.sh[1102]: + machinectl terminate long-running
[ 41.695009] systemd-nspawn[2171]: Trying to halt container by sending TERM to container PID 1. Send SIGTERM again to trigger immediate termination.
[ 41.698235] systemd-machined[1192]: Machine long-running terminated.
[ 41.709520] TEST-13-NSPAWN.sh[1102]: + systemctl kill --signal=KILL systemd-nspawn@long-running.service
[ 41.709169] systemd-nspawn[2171]: Failed to unregister machine: No machine 'long-running' known
[ 41.720869] TEST-13-NSPAWN.sh[2346]: + varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}'
[ 41.723359] TEST-13-NSPAWN.sh[2347]: + grep long-running
...
[ 41.735453] TEST-13-NSPAWN.sh[2352]: + varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running", "acquireMetadata": "yes"}'
[ 41.736222] TEST-13-NSPAWN.sh[2353]: + grep OSRelease
[ 41.739500] TEST-13-NSPAWN.sh[2352]: Method call io.systemd.MachineImage.List() failed: Device or resource busy
[ 41.740641] systemd[1]: Received SIGCHLD.
[ 41.740670] systemd[1]: Child 2171 (systemd-nspawn) died (code=killed, status=9/KILL)
[ 41.740725] systemd[1]: systemd-nspawn@long-running.service: Child 2171 belongs to systemd-nspawn@long-running.service.
[ 41.740748] systemd[1]: systemd-nspawn@long-running.service: Main process exited, code=killed, status=9/KILL
[ 41.740755] systemd[1]: systemd-nspawn@long-running.service: Will spawn child (service_enter_stop_post): systemd-nspawn
[ 41.740872] systemd[1]: systemd-nspawn@long-running.service: About to execute: systemd-nspawn --cleanup --machine=long-running
...
Let's mitigate this by waiting until the corresponding
systemd-nspawn@.service instance enters the 'inactive' state where the
lock should be properly released.
Resolves: #39547
Commit 38748596f0 ("core: Make DelegateNamespaces= work for user
managers with CAP_SYS_ADMIN") refactored the logic for when an
unprivileged process should create a new user namespace for sandboxing.
This refactor inadvertently removed a check (`params->runtime_scope !=
RUNTIME_SCOPE_USER`) that differentiated between system services and user
services.
This causes a regression in rootless containers where systemd runs
unprivileged. When starting a system service (like `dbus-broker`) that
uses sandboxing features (eg. with `PrivateTmp=yes`), systemd now
incorrectly creates a new, minimal `PRIVATE_USERS_SELF` namespace.
This new namespace only maps UID/GID 0. When dbus-broker attempts to
drop privileges to the `dbus` user (GID 81), the `setresgid(81, 81, 81)`
call fails because GID 81 is not mapped.
Restore the check to ensure that the special unprivileged sandboxing
logic is only applied to user services, as was the original intent.
System services in a rootless context will now correctly run in the
container's main user namespace, where all necessary UIDs/GIDs are
mapped.
Fixes: https://github.com/systemd/systemd/issues/39563
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2391343