%O/minimal-1.root-%a.raw:/usr/share/minimal_1.raw
%O/minimal-1.root-%a-verity.raw:/usr/share/minimal_1.verity
%O/minimal-1.root-%a-verity-sig.raw:/usr/share/minimal_1.verity.sig
- %O/minimal-base:/usr/share/testsuite-13-container-template
+ %O/minimal-base:/usr/share/TEST-13-NSPAWN-container-template
Packages=
acl
--- /dev/null
+[Unit]
+Description=Service that never leaves state ACTIVATING
+Requires=always-activating.socket
+After=always-activating.socket
+
+[Service]
+Type=notify
+ExecStart=bash -c 'sleep infinity'
--- /dev/null
+[Unit]
+Description=Socket that activates always-activating.service
+
+[Socket]
+ListenStream=/run/test-03-always-activating.sock
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=oneshot
+ExecStart=false
+Restart=on-failure
+RestartMode=direct
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=fails-on-restart-restartdirect.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=oneshot
+ExecStart=false
+Restart=on-failure
+RestartMode=normal
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=fails-on-restart.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep for a minute, then say hello.
+Wants=sleep.service hello.service
+After=sleep.service
+Before=hello.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Hello World
+
+[Service]
+ExecStart=/bin/echo "Hello World"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=sleep-infinity-simple.service
+After=sleep-infinity-simple.service
+PropagatesStopTo=sleep-infinity-simple.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+PropagatesStopTo=propagatestopto-and-pullin.target
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+PropagatesStopTo=sleep-infinity-simple.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep infinitely
+
+[Service]
+Type=simple
+ExecStart=/bin/sleep infinity
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep for 1 minute
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sleep 60
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh
+Restart=on-failure
+RestartMode=direct
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=succeeds-on-restart-restartdirect.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh
+Restart=on-failure
+RestartMode=normal
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+if [[ ! -f "/succeeds-on-restart.ko" ]]
+then
+ touch "/succeeds-on-restart.ko"
+ exit 1
+else
+ rm "/succeeds-on-restart.ko"
+ exit 0
+fi
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=succeeds-on-restart.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/bin/echo "I'm unstoppable!"
+ExecStop=/bin/systemctl start --no-block unstoppable.service
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-03-JOBS.units']
--- /dev/null
+[Unit]
+Description=Test service for delegated logs filtering
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload.sh
+Delegate=yes
+SyslogLevel=notice
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=ForeverPrintHola service
+
+[Service]
+Type=exec
+ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /tmp/i-lose-my-logs; sleep 1; done'
--- /dev/null
+[Unit]
+Description=Log filtering unit
+
+[Service]
+Type=oneshot
+ExecStart=sh -c 'echo "Logging from the service, and ~more~ foo bar" && sleep 2'
+SyslogLevel=notice
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Silent successful service
+
+[Service]
+LogLevelMax=notice
+ExecStart=/bin/true
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Verbose successful service
+
+[Service]
+Type=oneshot
+# Sleep so that the cgroup is still there when journald processes the log message which is required for
+# journald to add the expected fields to the log message.
+ExecStart=sleep 2
+ExecStart=echo success
+ExecStart=sleep 2
'storage' : 'persistent',
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-04-JOURNAL.units']
# Since we nuke the journal repeatedly during this test, let's redirect
# stdout/stderr to the console as well to make the test a bit more debug-able.
if ! get_bool "${INTERACTIVE_DEBUG:-}"; then
- dropin_dir="${workspace:?}/etc/systemd/system/testsuite-04.service.d/"
+ dropin_dir="${workspace:?}/etc/systemd/system/TEST-04-JOURNAL.service.d/"
mkdir -p "$dropin_dir"
printf '[Service]\nStandardOutput=journal+console\nStandardError=journal+console' >"$dropin_dir/99-stdout.conf"
fi
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/bin/echo Start Hola
+ExecReload=/bin/echo Reload Hola
+ExecStop=/bin/echo Stop Hola
+RemainAfterExit=yes
'firmware' : 'uefi',
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-06-SELINUX.units']
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 14566 Repro
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-07-PID1.units/%N.sh
+ExecStopPost=/bin/true
+KillMode=mixed
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+sleep infinity &
+echo $! >/leakedtestpid
+wait $!
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 16115 Repro with on-abnormal
+
+[Service]
+Type=simple
+Restart=on-abnormal
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 16115 Repro with on-failure
+
+[Service]
+Type=simple
+Restart=on-failure
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 22257 Repro with Restart=always
+
+[Service]
+Type=simple
+Restart=always
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=issue2467.socket
+ConditionPathExistsGlob=/tmp/nonexistent
+# Make sure we hit the socket trigger limit in the test and not the service start limit.
+StartLimitInterval=1000
+StartLimitBurst=1000
+
+[Service]
+ExecStart=true
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Socket]
+ListenStream=/run/test.ctl
+# We might not be fast enough to hit the default limit (20 triggers per 2 secs)
+# in certain environments, i.e. when running without KVM or when collecting
+# coverage. Let's help it a bit in such cases by lowering the limit to 10 seconds.
+TriggerLimitIntervalSec=10
--- /dev/null
+issue2730.mount
\ No newline at end of file
--- /dev/null
+[Mount]
+What=tmpfs
+Where=/issue2730
+Type=tmpfs
+
+[Install]
+WantedBy=local-fs.target
+Alias=issue2730-alias.mount
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=This unit should not remain active once the shell process exits
+
+[Service]
+Type=oneshot
+ExecStart=sh -c 'sleep infinity & exit 0'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=exec
+ExecStart=false
+Restart=always
--- /dev/null
+../issue2730.mount
\ No newline at end of file
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test if ExecXYZ= commands don't inherit listen FDs when PassFileDescriptorsToExec= is unset
+
+[Socket]
+# With Accept= set we don't need a corresponding service unit
+Accept=yes
+FileDescriptorName=foo
+ListenStream=127.0.0.1:1234
+ListenStream=[::1]:1234
+PassFileDescriptorsToExec=no
+ExecStartPre=\
+ test ExecStartPre -a \
+ -z ${LISTEN_FDS} -a \
+ -z ${LISTEN_FDNAMES} -a \
+ ! -e /dev/fd/3 -a \
+ ! -e /dev/fd/4
+ExecStartPost=\
+ test ExecStartPost -a \
+ -z ${LISTEN_FDS} -a \
+ -z ${LISTEN_FDNAMES} -a \
+ ! -e /dev/fd/3 -a \
+ ! -e /dev/fd/4
+ExecStopPre=\
+ test ExecStopPre -a \
+ -z ${LISTEN_FDS} -a \
+ -z ${LISTEN_FDNAMES} -a \
+ ! -e /dev/fd/3 -a \
+ ! -e /dev/fd/4
+ExecStopPost=\
+ test ExecStopPost -a \
+ -z ${LISTEN_FDS} -a \
+ -z ${LISTEN_FDNAMES} -a \
+ ! -e /dev/fd/3 -a \
+ ! -e /dev/fd/4
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test if ExecXYZ= commands inherit listen FDs when PassFileDescriptorsToExec= is set
+
+[Socket]
+# With Accept= set we don't need a corresponding service unit
+Accept=yes
+FileDescriptorName=foo
+ListenStream=127.0.0.1:1234
+ListenStream=[::1]:1234
+PassFileDescriptorsToExec=yes
+# ExecStartPre runs before we create sockets. Nothing to pass.
+ExecStartPre=\
+ test ExecStartPre -a \
+ -z ${LISTEN_FDS} -a \
+ -z ${LISTEN_FDNAMES} -a \
+ ! -e /dev/fd/3 -a \
+ ! -e /dev/fd/4
+ExecStartPost=\
+ test ExecStartPost -a \
+ ${LISTEN_FDS} = 2 -a \
+ ${LISTEN_FDNAMES} = foo:foo -a \
+ -S /dev/fd/3 -a \
+ -S /dev/fd/4
+ExecStopPre=\
+ test "ExecStopPre" -a \
+ ${LISTEN_FDS} = 2 -a \
+ ${LISTEN_FDNAMES} = foo:foo -a \
+ -S /dev/fd/3 -a \
+ -S /dev/fd/4
+ExecStopPost=\
+ test "ExecStopPost" -a \
+ ${LISTEN_FDS} = 2 -a \
+ ${LISTEN_FDNAMES} = foo:foo -a \
+ -S /dev/fd/3 -a \
+ -S /dev/fd/4
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-07-PID1.units']
# Setup a one shot service in initrd that creates a dummy bind mount under /run
# to check if the mount persists though the initrd transition. The "check" part
-# is in the respective testsuite-08.sh script.
+# is in the respective TEST-08-INITRD.sh script.
#
# See: https://github.com/systemd/systemd/issues/28452
run_qemu_hook() {
test_append_files() {
local workspace="${1:?}"
- local container="$workspace/usr/share/testsuite-13-container-template"
+ local container="$workspace/usr/share/TEST-13-NSPAWN-container-template"
# For virtual wlan interface.
instmods mac80211_hwsim
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# sleep interval (seconds)
+sleep_interval="${sleep_interval:-1}"
+# extend_timeout_interval second(s)
+extend_timeout_interval="${extend_timeout_interval:-1}"
+# number of sleep_intervals before READY=1
+start_intervals="${start_intervals:-10}"
+# number of sleep_intervals before exiting
+stop_intervals="${stop_intervals:-10}"
+# run intervals, number of sleep_intervals to run
+run_intervals="${run_intervals:-7}"
+
+# We convert to usec
+extend_timeout_interval=$((extend_timeout_interval * 1000000))
+
+# shellcheck disable=SC2064
+trap "{ touch /${SERVICE}.terminated; exit 1; }" SIGTERM SIGABRT
+
+rm -f "/${SERVICE}".*
+touch "/${SERVICE}.startfail"
+
+systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+while [[ $start_intervals -gt 0 ]]
+do
+ sleep "$sleep_interval"
+ start_intervals=$((start_intervals - 1))
+ systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+done
+
+systemd-notify --ready --status="Waiting for your request"
+
+touch "/${SERVICE}.runtimefail"
+rm "/${SERVICE}.startfail"
+
+systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+while [[ $run_intervals -gt 0 ]]
+do
+ sleep "$sleep_interval"
+ run_intervals=$((run_intervals - 1))
+ systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+done
+
+systemd-notify STOPPING=1
+
+touch "/${SERVICE}.stopfail"
+rm "/${SERVICE}.runtimefail"
+
+systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+while [[ $stop_intervals -gt 0 ]]
+do
+ sleep "$sleep_interval"
+ stop_intervals=$((stop_intervals - 1))
+ systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+done
+
+touch "/${SERVICE}.success"
+rm "/${SERVICE}.stopfail"
+
+exit 0
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
+
+[Service]
+# EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=10
+Environment=SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
+
+[Service]
+
+# EXTEND_TIMEOUT_USEC on startup and 7 seconds from start. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=10
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
+
+[Service]
+# EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=10
+RuntimeMaxSec=4
+Environment=SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
+# Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1
+# This file makes the test assess.sh quicker by notifing it that this test has finished.
+ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
+
+[Service]
+
+# Normal success - startup / runtime / shutdown all take 8 seconds which is within the EXTEND_TIMEOUT_USEC=4 seconds interval
+# runtime is 8+8+8 seconds. so we are relying on the EXTEND_TIMEOUT_USEC to exceed all stages, Start, Runtime and Stop.
+# success occurs after 24 seconds
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
+
+[Service]
+
+# EXTEND_TIMEOUT_USEC=4 second once during runtime, but sleep for 6 seconds.
+# Runtime is 6 seconds and < RuntimeMaxSec so still successful.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=8
+Environment=SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
+
+[Service]
+# EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds.
+# Therefore startup is 6 seconds and < TimeoutStartSec so still successful.
+Type=notify
+TimeoutStartSec=8
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
+
+[Service]
+# EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds.
+# Therefore stop time is 6 seconds and < TimeoutStopSec so still successful.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=8
+RuntimeMaxSec=4
+Environment=SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-16-EXTEND-TIMEOUT.units']
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Unit with BindsTo=
+BindsTo=TEST-23-UNIT-FILE-bound-by.service
+After=TEST-23-UNIT-FILE-bound-by.service
+
+[Service]
+ExecStart=sleep infinity
+# --kill-who= (no 'm') to check that the short form is accepted
+ExecStopPost=systemctl kill --kill-whom=main -sRTMIN+1 TEST-23-UNIT-FILE.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Unit with BoundBy=
+
+[Service]
+ExecStart=sleep 0.7
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Failing unit
+OnFailure=TEST-23-UNIT-FILE-uphold.service
+
+[Service]
+ExecStart=false
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file
+ExecStart=touch /tmp/hoge
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file
+ExecStart=test -e /tmp/hoge
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-5.service
+
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file-x
+ExecStart=test ! -e /tmp/shared-private-file-y
+ExecStart=touch /tmp/hoge
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStartPre=test -e /tmp/shared-private-file-x
+ExecStartPre=test -e /tmp/hoge
+ExecStart=bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file-x
+ExecStart=test -e /tmp/shared-private-file-y
+ExecStart=test -e /tmp/hoge
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+RuntimeMaxSec=300
+# Adding a new mounts at runtime works if the unit is in the active state,
+# so use Type=notify to make sure there's no race condition in the test
+Type=notify
+RemainAfterExit=yes
+MountAPIVFS=yes
+PrivateTmp=yes
+BindPaths=/run/TEST-23-UNIT-FILE-marker-fixed:/tmp/testfile-marker-fixed
+InaccessiblePaths=/run/inaccessible
+ExecStartPre=grep -q -F MARKER_FIXED /tmp/testfile-marker-fixed
+ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; test ! -f /run/inaccessible/testfile-marker-fixed'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+RuntimeMaxSec=5
+Type=notify
+RemainAfterExit=yes
+ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0'
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if [[ -f "$1" ]]; then
+ exit 0
+fi
+
+touch "$1"
+exit 2
--- /dev/null
+[Unit]
+Description=OpenFile= test server socket
+
+[Socket]
+ListenStream=/tmp/test.sock
+Accept=yes
--- /dev/null
+[Unit]
+Description=OpenFile= test server service
+
+[Service]
+ExecStart=echo "Socket"
+StandardInput=socket
+StandardOutput=socket
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Stop Propagation Receiver
+Wants=TEST-23-UNIT-FILE-prop-stop-two.service
+After=TEST-23-UNIT-FILE-prop-stop-two.service
+StopPropagatedFrom=TEST-23-UNIT-FILE-prop-stop-two.service
+
+[Service]
+ExecStart=sleep infinity
+ExecStopPost=systemctl kill --kill-whom=main -sUSR2 TEST-23-UNIT-FILE.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Stop Propagation Sender
+
+[Service]
+ExecStart=sleep 1.5
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Failed Dependency Unit
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=sh -c "if [ -f /tmp/TEST-23-UNIT-FILE-retry-fail ]; then exit 0; else exit 1; fi"
+Restart=no
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Upheld Unit
+Requires=TEST-23-UNIT-FILE-retry-fail.service
+After=TEST-23-UNIT-FILE-retry-fail.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=echo ok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Upholding Unit
+Upholds=TEST-23-UNIT-FILE-retry-upheld.service
+
+[Service]
+ExecStart=sleep infinity
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Shortlived Unit
+StopWhenUnneeded=yes
+
+# Bump up the start limit logic, so that we can be restarted frequently enough
+StartLimitBurst=15
+StartLimitIntervalSec=1h
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-short-lived.sh
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Dependent service for percent-j specifier
+
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/test-specifier-j-wants
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Wants with percent-j specifier
+Wants=TEST-23-UNIT-FILE-specifier-j-depends-%j.service
+After=TEST-23-UNIT-FILE-specifier-j-depends-%j.service
+
+[Service]
+Type=oneshot
+ExecStart=test -f /tmp/test-specifier-j-%j
+ExecStart=touch /tmp/tetsuite-23-specifier-j-done
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Succeeding unit
+OnSuccess=TEST-23-UNIT-FILE-fail.service
+
+[Service]
+ExecStart=true
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Unit that sets UpheldBy= through [Install]
+
+[Service]
+ExecStart=sleep infinity
+
+[Install]
+UpheldBy=TEST-23-UNIT-FILE-retry-uphold.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Upholding Unit
+Upholds=TEST-23-UNIT-FILE-short-lived.service
+
+[Service]
+ExecStart=sleep infinity
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-23-UNIT-FILE.units']
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Service]
+WatchdogSec=10min
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-30-ONCLOCKCHANGE.units']
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Honor First Shutdown feature
+After=multi-user.target
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-52-HONORFIRSTSHUTDOWN.units/%N.sh
+ExecStop=sh -c 'kill -KILL $MAINPID'
+FailureAction=reboot
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+echo "Honor first shutdown test script"
+sleep infinity;
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-52-HONORFIRSTSHUTDOWN.units']
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExistsGlob=/tmp/test63-glob*
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63-glob-foo'
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63-glob.path'
+ExecStart=systemd-notify --ready
+RemainAfterExit=yes
+Type=notify
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=bash -c 'sleep infinity'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExists=/tmp/hoge
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=test63-issue-24577-dep.service
+After=test63-issue-24577-dep.service
+
+[Service]
+Type=oneshot
+ExecStart=bash -c 'sleep infinity'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathChanged=/tmp/copyme
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStart=cp -v /tmp/copyme /tmp/copied
+# once cp exits, service goes into deactivating state and then runs ExecStop
+ExecStop=flock -e /tmp/noexit true
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExists=/tmp/test63
+# Make the unit friendly to slower machines
+TriggerLimitIntervalSec=10
+TriggerLimitBurst=10
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+ConditionPathExists=/tmp/nonexistent
+
+[Service]
+ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63'
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63.path'
+ExecStart=true
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-63-PATH.units']
--- /dev/null
+[Service]
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=10
+FileDescriptorStorePreserve=restart
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 0
--- /dev/null
+[Service]
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=10
+FileDescriptorStorePreserve=yes
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 1
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+PINNED="$1"
+COUNTER="/tmp/fdstore-invoked.$PINNED"
+FILE="/tmp/fdstore-data.$PINNED"
+
+# This script is called six times: thrice from a service unit where the fdstore
+# is pinned, and thrice where it isn't. The second iteration of each series is
+# a restart, the third a stop followed by a start
+
+if [ -e "$COUNTER" ] ; then
+ read -r N < "$COUNTER"
+else
+ N=0
+fi
+
+echo "Invocation #$N with PINNED=$PINNED."
+
+if [ "$N" -eq 0 ] ; then
+ # First iteration
+ test "${LISTEN_FDS:-0}" -eq 0
+ test ! -e "$FILE"
+ echo waldi > "$FILE"
+ systemd-notify --fd=3 --fdname="fd-$N-$PINNED" 3< "$FILE"
+elif [ "$N" -eq 1 ] || { [ "$N" -eq 2 ] && [ "$PINNED" -eq 1 ]; } ; then
+ # Second iteration, or iteration with pinning on
+ test "${LISTEN_FDS:-0}" -eq 1
+ # We reopen fd #3 here, so that the read offset is at zero each time (hence no <&3 here…)
+ read -r word < /proc/self/fd/3
+ test "$word" = "waldi"
+else
+ test "${LISTEN_FDS:-0}" -eq 0
+ test -e "$FILE"
+fi
+
+if [ "$N" -ge 2 ] ; then
+ rm "$COUNTER" "$FILE"
+else
+ echo $((N + 1)) > "$COUNTER"
+fi
+
+systemd-notify --ready --status="Ready"
+
+exec sleep infinity
--- /dev/null
+[Unit]
+After=fdstore-pin.service fdstore-nopin.service
+Wants=fdstore-pin.service fdstore-nopin.service
--- /dev/null
+[Service]
+Type=notify
+NotifyAccess=all
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/test.sh
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+sync_in() {
+ read -r x < /tmp/syncfifo2
+ test "$x" = "$1"
+}
+
+sync_out() {
+ echo "$1" > /tmp/syncfifo1
+}
+
+export SYSTEMD_LOG_LEVEL=debug
+
+echo "toplevel PID: $BASHPID"
+
+systemd-notify --status="Test starts"
+sync_out a
+sync_in b
+(
+ echo "subshell PID: $BASHPID"
+
+ # Make us main process
+ systemd-notify --pid="$BASHPID"
+
+ # Lock down access to just us
+ systemd-notify "NOTIFYACCESS=main"
+
+ # This should still work
+ systemd-notify --status="Sending READY=1 in an unprivileged process"
+
+ # Send as subprocess of the subshell, this should not work
+ systemd-notify --ready --pid=self --status "BOGUS1"
+
+ sync_out c
+ sync_in d
+
+ # Move main process back to toplevel
+ systemd-notify --pid=parent "MAINPID=$$"
+
+ # Should be dropped again
+ systemd-notify --status="BOGUS2" --pid=parent
+
+ # Apparently, bash will automatically invoke the last command in a subshell
+ # via a simple execve() rather than fork()ing first. But we want that the
+ # previous command uses the subshell's PID, hence let's insert a final,
+ # bogus redundant command as last command to run in the subshell, so that
+ # bash can't optimize things like that.
+ echo "bye"
+)
+
+echo "toplevel again: $BASHPID"
+
+systemd-notify --ready --status="OK"
+systemd-notify "NOTIFYACCESS=none"
+systemd-notify --status="BOGUS3"
+
+sync_out e
+
+exec sleep infinity
'name' : fs.name(meson.current_source_dir()),
},
]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-80-NOTIFYACCESS.units']
parser.add_argument('--meson-source-dir', required=True, type=Path)
parser.add_argument('--meson-build-dir', required=True, type=Path)
parser.add_argument('--test-name', required=True)
- parser.add_argument('--test-number', required=True)
parser.add_argument('--storage', required=True)
parser.add_argument('--firmware', required=True)
parser.add_argument('--slow', action=argparse.BooleanOptionalAction)
exit(77)
name = args.test_name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "")
- test_unit = f"testsuite-{args.test_number}.service"
+ test_unit = f"{args.test_name}.service"
dropin = textwrap.dedent(
"""\
'--kernel-command-line-extra',
' '.join([
'systemd.hostname=H',
- f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-{args.test_number}.units:/usr/lib/systemd/tests/testdata/units:",
+ f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/{args.test_name}.units:/usr/lib/systemd/tests/testdata/units:",
f"systemd.unit={test_unit}",
'systemd.mask=systemd-networkd-wait-online.service',
*(
# SPDX-License-Identifier: LGPL-2.1-or-later
-if install_tests
- foreach subdir : [
- 'auxv',
- 'journal-data',
- 'knot-data',
- 'test-journals',
- 'units',
- 'test-execute',
- 'test-fstab-generator',
- 'test-path',
- 'test-path-util',
- 'test-umount',
- 'test-network',
- 'test-network-generator-conversion',
- 'testsuite-03.units',
- 'testsuite-04.units',
- 'testsuite-06.units',
- 'testsuite-07.units',
- 'testsuite-16.units',
- 'testsuite-23.units',
- 'testsuite-30.units',
- 'testsuite-52.units',
- 'testsuite-63.units',
- 'testsuite-80.units',
- ]
- # install_subdir() before meson 1.3.0 does not handle symlinks correctly (it follows them
- # instead of copying the symlink) so we use rsync instead.
- if meson.version().version_compare('<1.3.0')
- if not rsync.found()
- error('rsync is required to install the integration test data')
- endif
-
- rsync_r = rsync.full_path() + ' -rlpt --exclude .gitattributes --exclude 25-default.link -- "@0@" "${DESTDIR:-}@1@"'
- meson.add_install_script(sh, '-c',
- rsync_r.format(meson.current_source_dir() / subdir, testdata_dir))
- else
- install_subdir(subdir,
- exclude_files : ['.gitattributes', '25-default.link'],
- install_dir : testdata_dir,
- follow_symlinks : false)
- endif
- endforeach
-
- # test-network/conf/25-default.link is a local symlink that becomes dangling when installed, so we
- # exclude it and create the correct symlink here.
- meson.add_install_script(sh, '-c', ln_s.format(networkdir / '99-default.link',
- testdata_dir / 'test-network/conf/25-default.link'))
-
- install_data(kbd_model_map,
- install_dir : testdata_dir + '/test-keymap-util')
-
- if conf.get('HAVE_ZSTD') == 1 and efi_arch != ''
- install_subdir('test-bcd',
- exclude_files : '.gitattributes',
- install_dir : testdata_dir)
- endif
- if conf.get('ENABLE_RESOLVE') == 1
- install_subdir('test-resolve',
- exclude_files : '.gitattributes',
- install_dir : testdata_dir)
- endif
-
- # The unit tests implemented as shell scripts expect to find testdata/
- # in the directory where they are stored.
- meson.add_install_script(sh, '-c', ln_s.format(testdata_dir,
- unittestsdir / 'testdata'))
-endif
-
-############################################################
-
if conf.get('ENABLE_SYSUSERS') == 1
test_sysusers_sh = configure_file(
input : 'test-sysusers.sh.in',
'firmware' : 'linux',
'slow' : false,
}
+testdata_subdirs = [
+ 'auxv',
+ 'journal-data',
+ 'knot-data',
+ 'test-journals',
+ 'units',
+ 'test-execute',
+ 'test-fstab-generator',
+ 'test-path',
+ 'test-path-util',
+ 'test-umount',
+ 'test-network',
+ 'test-network-generator-conversion',
+]
foreach dirname : [
'TEST-01-BASIC',
endforeach
foreach integration_test : integration_tests
- integration_test_number = integration_test['name'].split('-')[1]
-
integration_test_args = [
'--meson-source-dir', meson.project_source_root(),
'--meson-build-dir', meson.project_build_root(),
'--test-name', integration_test['name'],
- '--test-number', integration_test_number,
'--storage', integration_test['storage'],
'--firmware', integration_test['firmware'],
]
suite : 'integration-tests',
)
endforeach
+
+if install_tests
+ foreach subdir : testdata_subdirs
+ # install_subdir() before meson 1.3.0 does not handle symlinks correctly (it follows them
+ # instead of copying the symlink) so we use rsync instead.
+ if meson.version().version_compare('<1.3.0')
+ if not rsync.found()
+ error('rsync is required to install the integration test data')
+ endif
+
+ rsync_r = rsync.full_path() + ' -rlpt --exclude .gitattributes --exclude 25-default.link -- "@0@" "${DESTDIR:-}@1@"'
+ meson.add_install_script(sh, '-c',
+ rsync_r.format(meson.current_source_dir() / subdir, testdata_dir))
+ else
+ install_subdir(subdir,
+ exclude_files : ['.gitattributes', '25-default.link'],
+ install_dir : testdata_dir,
+ follow_symlinks : false)
+ endif
+ endforeach
+
+ # test-network/conf/25-default.link is a local symlink that becomes dangling when installed, so we
+ # exclude it and create the correct symlink here.
+ meson.add_install_script(sh, '-c', ln_s.format(networkdir / '99-default.link',
+ testdata_dir / 'test-network/conf/25-default.link'))
+
+ install_data(kbd_model_map,
+ install_dir : testdata_dir + '/test-keymap-util')
+
+ if conf.get('HAVE_ZSTD') == 1 and efi_arch != ''
+ install_subdir('test-bcd',
+ exclude_files : '.gitattributes',
+ install_dir : testdata_dir)
+ endif
+ if conf.get('ENABLE_RESOLVE') == 1
+ install_subdir('test-resolve',
+ exclude_files : '.gitattributes',
+ install_dir : testdata_dir)
+ endif
+
+ # The unit tests implemented as shell scripts expect to find testdata/
+ # in the directory where they are stored.
+ meson.add_install_script(sh, '-c', ln_s.format(testdata_dir,
+ unittestsdir / 'testdata'))
+endif
echo "ERROR: Test name '$TESTNAME' is not in the expected format: TEST-[0-9]+-*" >&2
exit 1
fi
-TESTID="${BASH_REMATCH[1]:?}"
-if [[ ! -f "$TEST_UNITS_DIR/testsuite-$TESTID.service" ]]; then
- echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/testsuite-$TESTID.service" >&2
+if [[ ! -f "$TEST_UNITS_DIR/$TESTNAME.service" ]]; then
+ echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/$TESTNAME.service" >&2
exit 1
fi
"loglevel=2"
"init=$PATH_TO_INIT"
"console=$CONSOLE"
- "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:"
+ "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/$1.units:/usr/lib/systemd/tests/testdata/units:"
"systemd.unit=testsuite.target"
- "systemd.wants=testsuite-$1.service"
+ "systemd.wants=$1.service"
"noresume"
"oops=panic"
${TEST_MATCH_SUBTEST:+"systemd.setenv=TEST_MATCH_SUBTEST=$TEST_MATCH_SUBTEST"}
"--register=no"
"--kill-signal=SIGKILL"
"--directory=${1:?}"
- "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:"
- "--machine=TEST-$TESTID"
+ "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/$2.units:/usr/lib/systemd/tests/testdata/units:"
+ "--machine=$2"
)
local kernel_params=(
"$PATH_TO_INIT"
"systemd.unit=testsuite.target"
- "systemd.wants=testsuite-$2.service"
+ "systemd.wants=$2.service"
${TEST_MATCH_SUBTEST:+"systemd.setenv=TEST_MATCH_SUBTEST=$TEST_MATCH_SUBTEST"}
${TEST_MATCH_TESTCASE:+"systemd.setenv=TEST_MATCH_TESTCASE=$TEST_MATCH_TESTCASE"}
)
dest_name="system.journal"
fi
- # Show messages from the testsuite-XX.service or messages with priority "warning" and higher
+ # Show messages from the TEST-XX-XXX.service or messages with priority "warning" and higher
echo " --- $source_dir ---"
"$JOURNALCTL" --all --no-pager --no-hostname -o short-monotonic -D "$source_dir" \
- _SYSTEMD_UNIT="testsuite-${TESTID:?}.service" + SYSLOG_IDENTIFIER="testsuite-$TESTID.sh" + \
+ _SYSTEMD_UNIT="${TESTNAME:?}.service" + SYSLOG_IDENTIFIER="$TESTNAME.sh" + \
PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
if get_bool "$save"; then
}
test_run() {
- local test_id="${1:?}"
+ local test_name="${1:?}"
mount_initdir
if ! get_bool "${TEST_NO_QEMU:=}"; then
- if run_qemu "$test_id"; then
+ if run_qemu "$test_name"; then
check_result_qemu || { echo "qemu test failed"; return 1; }
else
dwarn "can't run qemu, skipping"
fi
if ! get_bool "${TEST_NO_NSPAWN:=}"; then
mount_initdir
- if run_nspawn "${initdir:?}" "$test_id"; then
+ if run_nspawn "${initdir:?}" "$test_name"; then
check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; }
else
dwarn "can't run systemd-nspawn, skipping"
if get_bool "${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then
dir="$TESTDIR/unprivileged-nspawn-root"
- if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_id"; then
+ if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_name"; then
check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; }
else
dwarn "can't run systemd-nspawn, skipping"
case $1 in
--run)
echo "${testname} RUN: $TEST_DESCRIPTION"
- test_run "$TESTID"
+ test_run "$TESTNAME"
ret=$?
if [ $ret -eq 0 ]; then
echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
test_setup_cleanup </dev/null >>"$TESTLOG" 2>&1 || ret=$?
fi
if [ $ret -eq 0 ]; then
- test_run "$TESTID" </dev/null >>"$TESTLOG" 2>&1 || ret=$?
+ test_run "$TESTNAME" </dev/null >>"$TESTLOG" 2>&1 || ret=$?
fi
test_cleanup
if [ $ret -eq 0 ]; then
+++ /dev/null
-[Unit]
-Description=Service that never leaves state ACTIVATING
-Requires=always-activating.socket
-After=always-activating.socket
-
-[Service]
-Type=notify
-ExecStart=bash -c 'sleep infinity'
+++ /dev/null
-[Unit]
-Description=Socket that activates always-activating.service
-
-[Socket]
-ListenStream=/run/test-03-always-activating.sock
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=oneshot
-ExecStart=false
-Restart=on-failure
-RestartMode=direct
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=fails-on-restart-restartdirect.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=oneshot
-ExecStart=false
-Restart=on-failure
-RestartMode=normal
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=fails-on-restart.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Sleep for a minute, then say hello.
-Wants=sleep.service hello.service
-After=sleep.service
-Before=hello.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Hello World
-
-[Service]
-ExecStart=/bin/echo "Hello World"
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=sleep-infinity-simple.service
-After=sleep-infinity-simple.service
-PropagatesStopTo=sleep-infinity-simple.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-PropagatesStopTo=propagatestopto-and-pullin.target
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-PropagatesStopTo=sleep-infinity-simple.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Sleep infinitely
-
-[Service]
-Type=simple
-ExecStart=/bin/sleep infinity
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Sleep for 1 minute
-
-[Service]
-Type=oneshot
-ExecStart=/bin/sleep 60
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
-Restart=on-failure
-RestartMode=direct
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=succeeds-on-restart-restartdirect.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
-Restart=on-failure
-RestartMode=normal
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-if [[ ! -f "/succeeds-on-restart.ko" ]]
-then
- touch "/succeeds-on-restart.ko"
- exit 1
-else
- rm "/succeeds-on-restart.ko"
- exit 0
-fi
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=succeeds-on-restart.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=/bin/echo "I'm unstoppable!"
-ExecStop=/bin/systemctl start --no-block unstoppable.service
+++ /dev/null
-[Unit]
-Description=Test service for delegated logs filtering
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload.sh
-Delegate=yes
-SyslogLevel=notice
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=ForeverPrintHola service
-
-[Service]
-Type=exec
-ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /tmp/i-lose-my-logs; sleep 1; done'
+++ /dev/null
-[Unit]
-Description=Log filtering unit
-
-[Service]
-Type=oneshot
-ExecStart=sh -c 'echo "Logging from the service, and ~more~ foo bar" && sleep 2'
-SyslogLevel=notice
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Silent successful service
-
-[Service]
-LogLevelMax=notice
-ExecStart=/bin/true
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Verbose successful service
-
-[Service]
-Type=oneshot
-# Sleep so that the cgroup is still there when journald processes the log message which is required for
-# journald to add the expected fields to the log message.
-ExecStart=sleep 2
-ExecStart=echo success
-ExecStart=sleep 2
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=/bin/echo Start Hola
-ExecReload=/bin/echo Reload Hola
-ExecStop=/bin/echo Stop Hola
-RemainAfterExit=yes
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 14566 Repro
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-07.units/%N.sh
-ExecStopPost=/bin/true
-KillMode=mixed
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-sleep infinity &
-echo $! >/leakedtestpid
-wait $!
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 16115 Repro with on-abnormal
-
-[Service]
-Type=simple
-Restart=on-abnormal
-ExecCondition=/bin/false
-ExecStart=sleep 100
-RestartSec=1
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 16115 Repro with on-failure
-
-[Service]
-Type=simple
-Restart=on-failure
-ExecCondition=/bin/false
-ExecStart=sleep 100
-RestartSec=1
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 22257 Repro with Restart=always
-
-[Service]
-Type=simple
-Restart=always
-ExecCondition=/bin/false
-ExecStart=sleep 100
-RestartSec=1
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=issue2467.socket
-ConditionPathExistsGlob=/tmp/nonexistent
-# Make sure we hit the socket trigger limit in the test and not the service start limit.
-StartLimitInterval=1000
-StartLimitBurst=1000
-
-[Service]
-ExecStart=true
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Socket]
-ListenStream=/run/test.ctl
-# We might not be fast enough to hit the default limit (20 triggers per 2 secs)
-# in certain environments, i.e. when running without KVM or when collecting
-# coverage. Let's help it a bit in such cases by lowering the limit to 10 seconds.
-TriggerLimitIntervalSec=10
+++ /dev/null
-issue2730.mount
\ No newline at end of file
+++ /dev/null
-[Mount]
-What=tmpfs
-Where=/issue2730
-Type=tmpfs
-
-[Install]
-WantedBy=local-fs.target
-Alias=issue2730-alias.mount
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=This unit should not remain active once the shell process exits
-
-[Service]
-Type=oneshot
-ExecStart=sh -c 'sleep infinity & exit 0'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=exec
-ExecStart=false
-Restart=always
+++ /dev/null
-../issue2730.mount
\ No newline at end of file
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Test if ExecXYZ= commands don't inherit listen FDs when PassFileDescriptorsToExec= is unset
-
-[Socket]
-# With Accept= set we don't need a corresponding service unit
-Accept=yes
-FileDescriptorName=foo
-ListenStream=127.0.0.1:1234
-ListenStream=[::1]:1234
-PassFileDescriptorsToExec=no
-ExecStartPre=\
- test ExecStartPre -a \
- -z ${LISTEN_FDS} -a \
- -z ${LISTEN_FDNAMES} -a \
- ! -e /dev/fd/3 -a \
- ! -e /dev/fd/4
-ExecStartPost=\
- test ExecStartPost -a \
- -z ${LISTEN_FDS} -a \
- -z ${LISTEN_FDNAMES} -a \
- ! -e /dev/fd/3 -a \
- ! -e /dev/fd/4
-ExecStopPre=\
- test ExecStopPre -a \
- -z ${LISTEN_FDS} -a \
- -z ${LISTEN_FDNAMES} -a \
- ! -e /dev/fd/3 -a \
- ! -e /dev/fd/4
-ExecStopPost=\
- test ExecStopPost -a \
- -z ${LISTEN_FDS} -a \
- -z ${LISTEN_FDNAMES} -a \
- ! -e /dev/fd/3 -a \
- ! -e /dev/fd/4
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Test if ExecXYZ= commands inherit listen FDs when PassFileDescriptorsToExec= is set
-
-[Socket]
-# With Accept= set we don't need a corresponding service unit
-Accept=yes
-FileDescriptorName=foo
-ListenStream=127.0.0.1:1234
-ListenStream=[::1]:1234
-PassFileDescriptorsToExec=yes
-# ExecStartPre runs before we create sockets. Nothing to pass.
-ExecStartPre=\
- test ExecStartPre -a \
- -z ${LISTEN_FDS} -a \
- -z ${LISTEN_FDNAMES} -a \
- ! -e /dev/fd/3 -a \
- ! -e /dev/fd/4
-ExecStartPost=\
- test ExecStartPost -a \
- ${LISTEN_FDS} = 2 -a \
- ${LISTEN_FDNAMES} = foo:foo -a \
- -S /dev/fd/3 -a \
- -S /dev/fd/4
-ExecStopPre=\
- test "ExecStopPre" -a \
- ${LISTEN_FDS} = 2 -a \
- ${LISTEN_FDNAMES} = foo:foo -a \
- -S /dev/fd/3 -a \
- -S /dev/fd/4
-ExecStopPost=\
- test "ExecStopPost" -a \
- ${LISTEN_FDS} = 2 -a \
- ${LISTEN_FDNAMES} = foo:foo -a \
- -S /dev/fd/3 -a \
- -S /dev/fd/4
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# sleep interval (seconds)
-sleep_interval="${sleep_interval:-1}"
-# extend_timeout_interval second(s)
-extend_timeout_interval="${extend_timeout_interval:-1}"
-# number of sleep_intervals before READY=1
-start_intervals="${start_intervals:-10}"
-# number of sleep_intervals before exiting
-stop_intervals="${stop_intervals:-10}"
-# run intervals, number of sleep_intervals to run
-run_intervals="${run_intervals:-7}"
-
-# We convert to usec
-extend_timeout_interval=$((extend_timeout_interval * 1000000))
-
-# shellcheck disable=SC2064
-trap "{ touch /${SERVICE}.terminated; exit 1; }" SIGTERM SIGABRT
-
-rm -f "/${SERVICE}".*
-touch "/${SERVICE}.startfail"
-
-systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-while [[ $start_intervals -gt 0 ]]
-do
- sleep "$sleep_interval"
- start_intervals=$((start_intervals - 1))
- systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-done
-
-systemd-notify --ready --status="Waiting for your request"
-
-touch "/${SERVICE}.runtimefail"
-rm "/${SERVICE}.startfail"
-
-systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-while [[ $run_intervals -gt 0 ]]
-do
- sleep "$sleep_interval"
- run_intervals=$((run_intervals - 1))
- systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-done
-
-systemd-notify STOPPING=1
-
-touch "/${SERVICE}.stopfail"
-rm "/${SERVICE}.runtimefail"
-
-systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-while [[ $stop_intervals -gt 0 ]]
-do
- sleep "$sleep_interval"
- stop_intervals=$((stop_intervals - 1))
- systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-done
-
-touch "/${SERVICE}.success"
-rm "/${SERVICE}.stopfail"
-
-exit 0
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
-
-[Service]
-# EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=10
-Environment=SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC on startup and 7 seconds from start. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=10
-TimeoutStopSec=4
-RuntimeMaxSec=4
-Environment=SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
-
-[Service]
-# EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=10
-RuntimeMaxSec=4
-Environment=SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
-# Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1
-# This file makes the test assess.sh quicker by notifing it that this test has finished.
-ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
-
-[Service]
-
-# Normal success - startup / runtime / shutdown all take 8 seconds which is within the EXTEND_TIMEOUT_USEC=4 seconds interval
-# runtime is 8+8+8 seconds. so we are relying on the EXTEND_TIMEOUT_USEC to exceed all stages, Start, Runtime and Stop.
-# success occurs after 24 seconds
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=4
-Environment=SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC=4 second once during runtime, but sleep for 6 seconds.
-# Runtime is 6 seconds and < RuntimeMaxSec so still successful.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=8
-Environment=SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
-
-[Service]
-# EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds.
-# Therefore startup is 6 seconds and < TimeoutStartSec so still successful.
-Type=notify
-TimeoutStartSec=8
-TimeoutStopSec=4
-RuntimeMaxSec=4
-Environment=SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
-
-[Service]
-# EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds.
-# Therefore stop time is 6 seconds and < TimeoutStopSec so still successful.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=8
-RuntimeMaxSec=4
-Environment=SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Unit with BindsTo=
-BindsTo=testsuite-23-bound-by.service
-After=testsuite-23-bound-by.service
-
-[Service]
-ExecStart=sleep infinity
-# --kill-who= (no 'm') to check that the short form is accepted
-ExecStopPost=systemctl kill --kill-whom=main -sRTMIN+1 testsuite-23.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Unit with BoundBy=
-
-[Service]
-ExecStart=sleep 0.7
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Failing unit
-OnFailure=testsuite-23-uphold.service
-
-[Service]
-ExecStart=false
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file
-ExecStart=touch /tmp/hoge
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file
-ExecStart=test -e /tmp/hoge
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-5.service
-
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
-
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file-x
-ExecStart=test ! -e /tmp/shared-private-file-y
-ExecStart=touch /tmp/hoge
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStartPre=test -e /tmp/shared-private-file-x
-ExecStartPre=test -e /tmp/hoge
-ExecStart=bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file-x
-ExecStart=test -e /tmp/shared-private-file-y
-ExecStart=test -e /tmp/hoge
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-RuntimeMaxSec=300
-# Adding a new mounts at runtime works if the unit is in the active state,
-# so use Type=notify to make sure there's no race condition in the test
-Type=notify
-RemainAfterExit=yes
-MountAPIVFS=yes
-PrivateTmp=yes
-BindPaths=/run/testsuite-23-marker-fixed:/tmp/testfile-marker-fixed
-InaccessiblePaths=/run/inaccessible
-ExecStartPre=grep -q -F MARKER_FIXED /tmp/testfile-marker-fixed
-ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; test ! -f /run/inaccessible/testfile-marker-fixed'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-RuntimeMaxSec=5
-Type=notify
-RemainAfterExit=yes
-ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0'
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if [[ -f "$1" ]]; then
- exit 0
-fi
-
-touch "$1"
-exit 2
+++ /dev/null
-[Unit]
-Description=OpenFile= test server socket
-
-[Socket]
-ListenStream=/tmp/test.sock
-Accept=yes
+++ /dev/null
-[Unit]
-Description=OpenFile= test server service
-
-[Service]
-ExecStart=echo "Socket"
-StandardInput=socket
-StandardOutput=socket
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Stop Propagation Receiver
-Wants=testsuite-23-prop-stop-two.service
-After=testsuite-23-prop-stop-two.service
-StopPropagatedFrom=testsuite-23-prop-stop-two.service
-
-[Service]
-ExecStart=sleep infinity
-ExecStopPost=systemctl kill --kill-whom=main -sUSR2 testsuite-23.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Stop Propagation Sender
-
-[Service]
-ExecStart=sleep 1.5
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Failed Dependency Unit
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=sh -c "if [ -f /tmp/testsuite-23-retry-fail ]; then exit 0; else exit 1; fi"
-Restart=no
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Upheld Unit
-Requires=testsuite-23-retry-fail.service
-After=testsuite-23-retry-fail.service
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=echo ok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Upholding Unit
-Upholds=testsuite-23-retry-upheld.service
-
-[Service]
-ExecStart=sleep infinity
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Shortlived Unit
-StopWhenUnneeded=yes
-
-# Bump up the start limit logic, so that we can be restarted frequently enough
-StartLimitBurst=15
-StartLimitIntervalSec=1h
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/testsuite-23-short-lived.sh
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Dependent service for percent-j specifier
-
-[Service]
-Type=oneshot
-ExecStart=touch /tmp/test-specifier-j-wants
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Wants with percent-j specifier
-Wants=testsuite-23-specifier-j-depends-%j.service
-After=testsuite-23-specifier-j-depends-%j.service
-
-[Service]
-Type=oneshot
-ExecStart=test -f /tmp/test-specifier-j-%j
-ExecStart=touch /tmp/tetsuite-23-specifier-j-done
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Succeeding unit
-OnSuccess=testsuite-23-fail.service
-
-[Service]
-ExecStart=true
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Unit that sets UpheldBy= through [Install]
-
-[Service]
-ExecStart=sleep infinity
-
-[Install]
-UpheldBy=testsuite-23-retry-uphold.service
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Upholding Unit
-Upholds=testsuite-23-short-lived.service
-
-[Service]
-ExecStart=sleep infinity
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Service]
-WatchdogSec=10min
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Honor First Shutdown feature
-After=multi-user.target
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-52.units/%N.sh
-ExecStop=sh -c 'kill -KILL $MAINPID'
-FailureAction=reboot
-
-[Install]
-WantedBy=multi-user.target
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-echo "Honor first shutdown test script"
-sleep infinity;
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathExistsGlob=/tmp/test63-glob*
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63-glob-foo'
-ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63-glob.path'
-ExecStart=systemd-notify --ready
-RemainAfterExit=yes
-Type=notify
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=bash -c 'sleep infinity'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathExists=/tmp/hoge
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=test63-issue-24577-dep.service
-After=test63-issue-24577-dep.service
-
-[Service]
-Type=oneshot
-ExecStart=bash -c 'sleep infinity'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathChanged=/tmp/copyme
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-ExecStart=cp -v /tmp/copyme /tmp/copied
-# once cp exits, service goes into deactivating state and then runs ExecStop
-ExecStop=flock -e /tmp/noexit true
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathExists=/tmp/test63
-# Make the unit friendly to slower machines
-TriggerLimitIntervalSec=10
-TriggerLimitBurst=10
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-ConditionPathExists=/tmp/nonexistent
-
-[Service]
-ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63'
-ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63.path'
-ExecStart=true
+++ /dev/null
-[Service]
-Type=notify
-NotifyAccess=all
-FileDescriptorStoreMax=10
-FileDescriptorStorePreserve=restart
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 0
+++ /dev/null
-[Service]
-Type=notify
-NotifyAccess=all
-FileDescriptorStoreMax=10
-FileDescriptorStorePreserve=yes
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 1
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-PINNED="$1"
-COUNTER="/tmp/fdstore-invoked.$PINNED"
-FILE="/tmp/fdstore-data.$PINNED"
-
-# This script is called six times: thrice from a service unit where the fdstore
-# is pinned, and thrice where it isn't. The second iteration of each series is
-# a restart, the third a stop followed by a start
-
-if [ -e "$COUNTER" ] ; then
- read -r N < "$COUNTER"
-else
- N=0
-fi
-
-echo "Invocation #$N with PINNED=$PINNED."
-
-if [ "$N" -eq 0 ] ; then
- # First iteration
- test "${LISTEN_FDS:-0}" -eq 0
- test ! -e "$FILE"
- echo waldi > "$FILE"
- systemd-notify --fd=3 --fdname="fd-$N-$PINNED" 3< "$FILE"
-elif [ "$N" -eq 1 ] || { [ "$N" -eq 2 ] && [ "$PINNED" -eq 1 ]; } ; then
- # Second iteration, or iteration with pinning on
- test "${LISTEN_FDS:-0}" -eq 1
- # We reopen fd #3 here, so that the read offset is at zero each time (hence no <&3 here…)
- read -r word < /proc/self/fd/3
- test "$word" = "waldi"
-else
- test "${LISTEN_FDS:-0}" -eq 0
- test -e "$FILE"
-fi
-
-if [ "$N" -ge 2 ] ; then
- rm "$COUNTER" "$FILE"
-else
- echo $((N + 1)) > "$COUNTER"
-fi
-
-systemd-notify --ready --status="Ready"
-
-exec sleep infinity
+++ /dev/null
-[Unit]
-After=fdstore-pin.service fdstore-nopin.service
-Wants=fdstore-pin.service fdstore-nopin.service
+++ /dev/null
-[Service]
-Type=notify
-NotifyAccess=all
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/test.sh
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-sync_in() {
- read -r x < /tmp/syncfifo2
- test "$x" = "$1"
-}
-
-sync_out() {
- echo "$1" > /tmp/syncfifo1
-}
-
-export SYSTEMD_LOG_LEVEL=debug
-
-echo "toplevel PID: $BASHPID"
-
-systemd-notify --status="Test starts"
-sync_out a
-sync_in b
-(
- echo "subshell PID: $BASHPID"
-
- # Make us main process
- systemd-notify --pid="$BASHPID"
-
- # Lock down access to just us
- systemd-notify "NOTIFYACCESS=main"
-
- # This should still work
- systemd-notify --status="Sending READY=1 in an unprivileged process"
-
- # Send as subprocess of the subshell, this should not work
- systemd-notify --ready --pid=self --status "BOGUS1"
-
- sync_out c
- sync_in d
-
- # Move main process back to toplevel
- systemd-notify --pid=parent "MAINPID=$$"
-
- # Should be dropped again
- systemd-notify --status="BOGUS2" --pid=parent
-
- # Apparently, bash will automatically invoke the last command in a subshell
- # via a simple execve() rather than fork()ing first. But we want that the
- # previous command uses the subshell's PID, hence let's insert a final,
- # bogus redundant command as last command to run in the subshell, so that
- # bash can't optimize things like that.
- echo "bye"
-)
-
-echo "toplevel again: $BASHPID"
-
-systemd-notify --ready --status="OK"
-systemd-notify "NOTIFYACCESS=none"
-systemd-notify --status="BOGUS3"
-
-sync_out e
-
-exec sleep infinity
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-01-BASIC
+After=multi-user.target
+Wants=systemd-resolved.service systemd-networkd.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check if the colored --version output behaves correctly
+SYSTEMD_COLORS=256 systemctl --version
+
+# Check if we properly differentiate between a full systemd setup and a "light"
+# version of it that's done during daemon-reexec
+#
+# See: https://github.com/systemd/systemd/issues/27106
+if systemd-detect-virt -q --container; then
+ # We initialize /run/systemd/container only during a full setup
+ test -e /run/systemd/container
+ cp -afv /run/systemd/container /tmp/container
+ rm -fv /run/systemd/container
+ systemctl daemon-reexec
+ test ! -e /run/systemd/container
+ cp -afv /tmp/container /run/systemd/container
+else
+ # We bring the loopback netdev up only during a full setup, so it should
+ # not get brought back up during reexec if we disable it beforehand
+ [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]]
+ ip link set lo down
+ [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
+ systemctl daemon-reexec
+ [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
+ ip link set lo up
+
+ # We also disable coredumps only during a full setup
+ sysctl -w kernel.core_pattern=dont-overwrite-me
+ systemctl daemon-reexec
+ diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern)
+fi
+
+# Collect failed units & do one daemon-reload to a basic sanity check
+systemctl --state=failed --no-legend --no-pager | tee /failed
+test ! -s /failed
+systemctl daemon-reload
+
+# Check that the early setup is actually skipped on reexec.
+# If the early setup is done more than once, then several timestamps,
+# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT
+# of systemd-analyze blame. See issue #27187.
+systemd-analyze blame
+
+# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
+# See issue #27163.
+# shellcheck disable=SC2034
+if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then
+ for _ in {0..10}; do
+ systemctl daemon-reexec &
+ pid_reexec=$!
+ # shellcheck disable=SC2034
+ for _ in {0..10}; do
+ SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
+ done
+ wait "$pid_reexec"
+ done
+fi
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-02-UNITTESTS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if ! systemd-detect-virt -qc && [[ "${TEST_CMDLINE_NEWLINE:-}" != bar ]]; then
+ cat /proc/cmdline
+ echo >&2 "Expected TEST_CMDLINE_NEWLINE=bar from the kernel command line"
+ exit 1
+fi
+
+# If we're running with TEST_PREFER_NSPAWN=1 limit the set of tests we run
+# in QEMU to only those that can't run in a container to avoid running
+# the same tests again in a, most likely, very slow environment
+if ! systemd-detect-virt -qc && [[ "${TEST_PREFER_NSPAWN:-0}" -ne 0 ]]; then
+ TESTS_GLOB="test-loop-block"
+else
+ TESTS_GLOB=${TESTS_GLOB:-test-*}
+fi
+
+NPROC=$(nproc)
+MAX_QUEUE_SIZE=${NPROC:-2}
+
+# Reset state
+rm -fv /failed /skipped /testok
+touch /lock
+
+if ! systemd-detect-virt -qc; then
+ # Make sure ping works for unprivileged users (for test-bpf-firewall)
+ sysctl net.ipv4.ping_group_range="0 2147483647"
+fi
+
+# Check & report test results
+# Arguments:
+# $1: test path
+# $2: test exit code
+run_test() {
+ if [[ $# -ne 1 ]]; then
+ echo >&2 "run_test: missing arguments"
+ exit 1
+ fi
+
+ local test="$1"
+ local name="${test##*/}"
+
+ echo "Executing test $name as unit $name.service"
+
+ systemd-run --quiet --property Delegate=1 --unit="$name" --wait "$test" && ret=0 || ret=$?
+
+ exec {LOCK_FD}> /lock
+ flock --exclusive ${LOCK_FD}
+
+ if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then
+ echo "$name failed with $ret"
+ echo "$name" >>/failed-tests
+ {
+ echo "--- $name begin ---"
+ journalctl --unit="$name" --no-hostname -o short-monotonic
+ echo "--- $name end ---"
+ } >>/failed
+ elif [[ $ret == 77 || $ret == 127 ]]; then
+ echo "$name skipped"
+ echo "$name" >>/skipped-tests
+ {
+ echo "--- $name begin ---"
+ journalctl --unit="$name" --no-hostname -o short-monotonic
+ echo "--- $name end ---"
+ } >>/skipped
+ else
+ echo "$name OK"
+ echo "$name" >>/testok
+ fi
+
+ exec {LOCK_FD}<&-
+}
+
+export -f run_test
+
+find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}" -print0 |
+ xargs -0 -I {} --max-procs="$MAX_QUEUE_SIZE" bash -ec "run_test {}"
+
+# Test logs are sometimes lost, as the system shuts down immediately after
+journalctl --sync
+
+test ! -s /failed
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-03-JOBS
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Simple test for that daemon-reexec works in container.
+# See: https://github.com/systemd/systemd/pull/23883
+systemctl daemon-reexec
+
+# Test merging of a --job-mode=ignore-dependencies job into a previously
+# installed job.
+
+systemctl start --no-block hello-after-sleep.target
+
+systemctl list-jobs >/root/list-jobs.txt
+until grep 'sleep\.service.*running' /root/list-jobs.txt; do
+ systemctl list-jobs >/root/list-jobs.txt
+done
+
+grep 'hello\.service.*waiting' /root/list-jobs.txt
+
+# This is supposed to finish quickly, not wait for sleep to finish.
+START_SEC=$(date -u '+%s')
+systemctl start --job-mode=ignore-dependencies hello
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+
+test "$ELAPSED" -lt 3
+
+# sleep should still be running, hello not.
+systemctl list-jobs >/root/list-jobs.txt
+grep 'sleep\.service.*running' /root/list-jobs.txt
+grep 'hello\.service' /root/list-jobs.txt && exit 1
+systemctl stop sleep.service hello-after-sleep.target
+
+# Some basic testing that --show-transaction does something useful
+(! systemctl is-active systemd-importd)
+systemctl -T start systemd-importd
+systemctl is-active systemd-importd
+systemctl --show-transaction stop systemd-importd
+(! systemctl is-active systemd-importd)
+
+# Test for a crash when enqueuing a JOB_NOP when other job already exists
+systemctl start --no-block hello-after-sleep.target
+# hello.service should still be waiting, so these try-restarts will collapse
+# into NOPs.
+systemctl try-restart --job-mode=fail hello.service
+systemctl try-restart hello.service
+systemctl stop hello.service sleep.service hello-after-sleep.target
+
+# TODO: add more job queueing/merging tests here.
+
+# Test that restart propagates to activating units
+systemctl -T --no-block start always-activating.service
+systemctl list-jobs | grep 'always-activating.service'
+ACTIVATING_ID_PRE=$(systemctl show -P InvocationID always-activating.service)
+systemctl -T start always-activating.socket # Wait for the socket to come up
+systemctl -T restart always-activating.socket
+ACTIVATING_ID_POST=$(systemctl show -P InvocationID always-activating.service)
+[ "$ACTIVATING_ID_PRE" != "$ACTIVATING_ID_POST" ] || exit 1
+
+# Test for irreversible jobs
+systemctl start unstoppable.service
+
+# This is expected to fail with 'job cancelled'
+systemctl stop unstoppable.service && exit 1
+# But this should succeed
+systemctl stop --job-mode=replace-irreversibly unstoppable.service
+
+# We're going to shutdown soon. Let's see if it succeeds when
+# there's an active service that tries to be unstoppable.
+# Shutdown of the container/VM will hang if not.
+systemctl start unstoppable.service
+
+# Test waiting for a started units to terminate again
+cat <<EOF >/run/systemd/system/wait2.service
+[Unit]
+Description=Wait for 2 seconds
+[Service]
+ExecStart=sh -ec 'sleep 2'
+EOF
+cat <<EOF >/run/systemd/system/wait5fail.service
+[Unit]
+Description=Wait for 5 seconds and fail
+[Service]
+ExecStart=sh -ec 'sleep 5; false'
+EOF
+
+# wait2 succeeds
+START_SEC=$(date -u '+%s')
+systemctl start --wait wait2.service
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1
+
+# wait5fail fails, so systemctl should fail
+START_SEC=$(date -u '+%s')
+(! systemctl start --wait wait2.service wait5fail.service)
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
+
+# Test time-limited scopes
+START_SEC=$(date -u '+%s')
+set +e
+systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
+RESULT=$?
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
+[[ "$RESULT" -ne 0 ]] || exit 1
+
+# Test transactions with cycles
+# Provides coverage for issues like https://github.com/systemd/systemd/issues/26872
+for i in {0..19}; do
+ cat >"/run/systemd/system/transaction-cycle$i.service" <<EOF
+[Unit]
+After=transaction-cycle$(((i + 1) % 20)).service
+Requires=transaction-cycle$(((i + 1) % 20)).service
+
+[Service]
+ExecStart=true
+EOF
+done
+systemctl daemon-reload
+for i in {0..19}; do
+ systemctl start "transaction-cycle$i.service"
+done
+
+# Test PropagatesStopTo= when restart (issue #26839)
+systemctl start propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+
+systemctl restart propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl start propagatestopto-only.target
+systemctl --quiet is-active propagatestopto-only.target
+systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl restart propagatestopto-only.target
+assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl start propagatesstopto-indirect.target propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-indirect.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+
+systemctl restart propagatestopto-indirect.target
+assert_rc 3 systemctl --quiet is-active propagatestopto-and-pullin.target
+assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
+
+# Test restart mode direct
+systemctl start succeeds-on-restart-restartdirect.target
+assert_rc 0 systemctl --quiet is-active succeeds-on-restart-restartdirect.target
+
+systemctl start fails-on-restart-restartdirect.target || :
+assert_rc 3 systemctl --quiet is-active fails-on-restart-restartdirect.target
+
+systemctl start succeeds-on-restart.target || :
+assert_rc 3 systemctl --quiet is-active succeeds-on-restart.target
+
+systemctl start fails-on-restart.target || :
+assert_rc 3 systemctl --quiet is-active fails-on-restart.target
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+ . "$(dirname "$0")"/util.sh
+
+add_logs_filtering_override() {
+ local unit="${1:?}"
+ local override_name="${2:?}"
+ local log_filter="${3:-}"
+
+ mkdir -p "/run/systemd/system/$unit.d/"
+ echo -ne "[Service]\nLogFilterPatterns=$log_filter" >"/run/systemd/system/$unit.d/$override_name.conf"
+ systemctl daemon-reload
+}
+
+run_service_and_fetch_logs() {
+ local unit="${1:?}"
+ local start
+
+ start="$(date '+%Y-%m-%d %T.%6N')"
+ systemctl start "$unit"
+ journalctl --sync
+ journalctl -q -u "$unit" -S "$start" -p notice
+}
+
+if cgroupfs_supports_user_xattrs; then
+ # Accept all log messages
+ add_logs_filtering_override "logs-filtering.service" "00-reset" ""
+ [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ add_logs_filtering_override "logs-filtering.service" "01-allow-all" ".*"
+ [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Discard all log messages
+ add_logs_filtering_override "logs-filtering.service" "02-discard-all" "~.*"
+ [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Accept all test messages
+ add_logs_filtering_override "logs-filtering.service" "03-reset" ""
+ [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Discard all test messages
+ add_logs_filtering_override "logs-filtering.service" "04-discard-gg" "~.*gg.*"
+ [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Deny filter takes precedence
+ add_logs_filtering_override "logs-filtering.service" "05-allow-all-but-too-late" ".*"
+ [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Use tilde in a deny pattern
+ add_logs_filtering_override "logs-filtering.service" "06-reset" ""
+ add_logs_filtering_override "logs-filtering.service" "07-prevent-tilde" "~~more~"
+ [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Only allow a pattern that won't be matched
+ add_logs_filtering_override "logs-filtering.service" "08-reset" ""
+ add_logs_filtering_override "logs-filtering.service" "09-allow-only-non-existing" "non-existing string"
+ [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ # Allow a pattern starting with a tilde
+ add_logs_filtering_override "logs-filtering.service" "10-allow-with-escape-char" "\\\\x7emore~"
+ [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ add_logs_filtering_override "logs-filtering.service" "11-reset" ""
+ add_logs_filtering_override "logs-filtering.service" "12-allow-with-spaces" "foo bar"
+ [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+ add_logs_filtering_override "delegated-cgroup-filtering.service" "00-allow-all" ".*"
+ [[ -n $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
+
+ add_logs_filtering_override "delegated-cgroup-filtering.service" "01-discard-hello" "~hello"
+ [[ -z $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
+
+ rm -rf /run/systemd/system/{logs-filtering,delegated-cgroup-filtering}.service.d
+fi
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# https://bugzilla.redhat.com/show_bug.cgi?id=2183546
+mkdir /run/systemd/system/systemd-journald.service.d
+MACHINE_ID="$(</etc/machine-id)"
+
+# Reset the start-limit counters, as we're going to restart journald a couple of times
+systemctl reset-failed systemd-journald.service
+
+for c in NONE XZ LZ4 ZSTD; do
+ cat >/run/systemd/system/systemd-journald.service.d/compress.conf <<EOF
+[Service]
+Environment=SYSTEMD_JOURNAL_COMPRESS=${c}
+EOF
+ systemctl daemon-reload
+ systemctl restart systemd-journald.service
+ journalctl --rotate
+
+ ID="$(systemd-id128 new)"
+ systemd-cat -t "$ID" /bin/bash -c "for ((i=0;i<100;i++)); do echo -n hoge with ${c}; done; echo"
+ journalctl --sync
+ timeout 10 bash -c "until SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /var/log/journal/$MACHINE_ID/system.journal 2>&1 | grep -q -F 'compress=${c}'; do sleep .5; done"
+
+ # $SYSTEMD_JOURNAL_COMPRESS= also works for journal-remote
+ if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
+ for cc in NONE XZ LZ4 ZSTD; do
+ rm -f /tmp/foo.journal
+ SYSTEMD_JOURNAL_COMPRESS="${cc}" /usr/lib/systemd/systemd-journal-remote --split-mode=none -o /tmp/foo.journal --getter="journalctl -b -o export -t $ID"
+ SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /tmp/foo.journal 2>&1 | grep -q -F "compress=${cc}"
+ journalctl -t "$ID" -o cat --file /tmp/foo.journal | grep -q -F "hoge with ${c}"
+ done
+ fi
+done
+
+rm /run/systemd/system/systemd-journald.service.d/compress.conf
+systemctl daemon-reload
+systemctl restart systemd-journald.service
+systemctl reset-failed systemd-journald.service
+journalctl --rotate
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt -cq; then
+ echo "This test requires a VM, skipping the test" | tee --append /skipped
+ exit 0
+fi
+
+if [[ ! -x /usr/lib/systemd/systemd-bsod ]]; then
+ echo "systemd-bsod is not installed, skipping the test" | tee --append /skipped
+ exit 0
+fi
+
+# shellcheck disable=SC2317
+at_exit() {
+ local EC=$?
+
+ if [[ $EC -ne 0 ]] && [[ -e /tmp/console.dump ]]; then
+ cat /tmp/console.dump
+ fi
+
+ if mountpoint -q /var/log/journal; then
+ # In order to preserve the journal from the just run test we need to do a little dance, as
+ # --relinquish-var is not a "true" opposite of --flush, meaning that it won't move the existing
+ # journal(s) from /var/log/ to /run/log/. To do that, let's rotate the journal first, so all
+ # important bits are in the archived journal(s)...
+ journalctl --rotate
+ # ...then instruct sd-journald to write further entries to the runtime journal...
+ journalctl --relinquish-var
+ # ...make sure there are no outstanding writes to the persistent journal that might block us from
+ # unmounting the tmpfs...
+ journalctl --sync
+ # ...move the archived journals to the runtime storage...
+ mv -v "/var/log/journal/$(</etc/machine-id)"/system@*.journal "/run/log/journal/$(</etc/machine-id)/"
+ # ...get rid of the tmpfs on /var/log/journal/...
+ umount /var/log/journal
+ # ...and finally flush everything to the "real" persistent journal, so we can collect it after the
+ # test finishes.
+ journalctl --flush
+ fi
+
+ return 0
+}
+
+vcs_dump_and_check() {
+ local expected_message="${1:?}"
+
+ # It might take a while before the systemd-bsod stuff appears on the VCS,
+ # so try it a couple of times
+ for _ in {0..9}; do
+ setterm --term linux --dump --file /tmp/console.dump
+ if grep -aq "Press any key to exit" /tmp/console.dump &&
+ grep -aq "$expected_message" /tmp/console.dump &&
+ grep -aq "The current boot has failed" /tmp/console.dump; then
+
+ return 0
+ fi
+
+ sleep .5
+ done
+
+ return 1
+}
+
+# Since systemd-bsod always fetches only the first emergency message from the
+# current boot, let's temporarily overmount /var/log/journal with a tmpfs,
+# as we're going to wipe it multiple times, but we need to keep the original
+# journal intact for the other tests to work correctly.
+trap at_exit EXIT
+mount -t tmpfs tmpfs /var/log/journal
+systemctl restart systemd-journald
+
+systemctl stop systemd-bsod
+
+# Since we just wiped the journal, there should be no emergency messages and
+# systemd-bsod should be just a no-op
+timeout 10s /usr/lib/systemd/systemd-bsod
+setterm --term linux --dump --file /tmp/console.dump
+(! grep "The current boot has failed" /tmp/console.dump)
+
+# systemd-bsod should pick up emergency messages only with UID=0, so let's check
+# that as well
+systemd-run --user --machine testuser@ --wait --pipe systemd-cat -p emerg echo "User emergency message"
+systemd-cat -p emerg echo "Root emergency message"
+journalctl --sync
+# Set $SYSTEMD_COLORS so systemd-bsod also prints out the QR code
+SYSTEMD_COLORS=256 /usr/lib/systemd/systemd-bsod &
+PID=$!
+vcs_dump_and_check "Root emergency message"
+grep -aq "Scan the QR code" /tmp/console.dump
+# TODO: check if systemd-bsod exits on a key press (didn't figure this one out yet)
+kill $PID
+timeout 10 bash -c "while kill -0 $PID; do sleep .5; done"
+
+# Wipe the journal
+journalctl --vacuum-size=1 --rotate
+(! journalctl -q -b -p emerg --grep .)
+
+# Check the systemd-bsod.service as well
+# Note: the systemd-bsod.service unit has ConditionVirtualization=no, so let's
+# temporarily override it just for the test
+mkdir /run/systemd/system/systemd-bsod.service.d
+printf '[Unit]\nConditionVirtualization=\n' >/run/systemd/system/systemd-bsod.service.d/99-override.conf
+systemctl daemon-reload
+systemctl start systemd-bsod
+systemd-cat -p emerg echo "Service emergency message"
+vcs_dump_and_check "Service emergency message"
+systemctl status systemd-bsod
+systemctl stop systemd-bsod
+
+# Wipe the journal
+journalctl --vacuum-size=1 --rotate
+(! journalctl -q -b -p emerg --grep .)
+
+# Same as above, but make sure the service responds to signals even when there are
+# no "emerg" messages, see systemd/systemd#30084
+(! systemctl is-active systemd-bsod)
+systemctl start systemd-bsod
+timeout 5s bash -xec 'until systemctl is-active systemd-bsod; do sleep .5; done'
+timeout 5s systemctl stop systemd-bsod
+timeout 5s bash -xec 'while systemctl is-active systemd-bsod; do sleep .5; done'
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl enable --now systemd-journald@cat-test.socket
+
+systemd-cat --namespace cat-test env CAT_TEST_RESULT=1
+
+timeout 30 bash -c "until systemctl -q is-active systemd-journald@cat-test.service; do sleep .5; done"
+
+journalctl --namespace cat-test --grep "JOURNAL_STREAM="
+journalctl --namespace cat-test --grep "CAT_TEST_RESULT=1"
+
+systemctl disable --now systemd-journald@cat-test.socket
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+JOURNAL_DIR="$(mktemp -d)"
+REMOTE_OUT="$(mktemp -d)"
+# tar on C8S doesn't support the --zstd option
+unzstd --stdout "/usr/lib/systemd/tests/testdata/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
+while read -r file; do
+ filename="${file##*/}"
+ unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
+done < <(find /usr/lib/systemd/tests/testdata/test-journals/corrupted/ -name "*.zst")
+# First, try each of them sequentially. Skip this part when running with plain
+# QEMU, as it is excruciatingly slow
+# Note: we care only about exit code 124 (timeout) and special bash exit codes
+# >124 (like signals)
+if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
+ while read -r file; do
+ timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]]
+ timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]]
+ timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]]
+ timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]]
+ timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]]
+ if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
+ timeout 10 /usr/lib/systemd/systemd-journal-remote \
+ --getter="journalctl --file=$file --output=export" \
+ --split-mode=none \
+ --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
+ timeout 10 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
+ rm -f "$REMOTE_OUT"/*
+ fi
+ done < <(find "$JOURNAL_DIR" -type f)
+fi
+# And now all at once
+timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]]
+if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
+ timeout 30 /usr/lib/systemd/systemd-journal-remote \
+ --getter="journalctl --directory=$JOURNAL_DIR --output=export" \
+ --split-mode=none \
+ --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
+ timeout 30 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
+ rm -f "$REMOTE_OUT"/*
+fi
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Forward Secure Sealing
+
+if ! journalctl --version | grep -qF +GCRYPT; then
+ echo "Built without gcrypt, skipping the FSS tests"
+ exit 0
+fi
+
+journalctl --force --setup-keys --interval=2 |& tee /tmp/fss
+FSS_VKEY="$(sed -rn '/([a-f0-9]{6}\-){3}[a-f0-9]{6}\/[a-f0-9]+\-[a-f0-9]+/p' /tmp/fss)"
+[[ -n "$FSS_VKEY" ]]
+
+# Generate some buzz in the journal and wait until the FSS key is changed
+# at least once
+systemd-cat cat /etc/os-release
+sleep 4
+# Seal the journal
+journalctl --rotate
+# Verification should fail without a valid FSS key
+(! journalctl --verify)
+(! journalctl --verify --verify-key="")
+(! journalctl --verify --verify-key="000000-000000-000000-000000/00000000-00000")
+# FIXME: ignore --verify result until #27532 is resolved
+journalctl --verify --verify-key="$FSS_VKEY" || :
+
+# Sealing + systemd-journal-remote
+/usr/lib/systemd/systemd-journal-remote --getter="journalctl -n 5 -o export" \
+ --split-mode=none \
+ --seal=yes \
+ --output=/tmp/sealed.journal
+(! journalctl --file=/tmp/sealed.journal --verify)
+(! journalctl --file=/tmp/sealed.journal --verify --verify-key="")
+(! journalctl --file=/tmp/sealed.journal --verify --verify-key="000000-000000-000000-000000/00000000-00000")
+# FIXME: ignore --verify result until #27532 is resolved
+journalctl --file=/tmp/sealed.journal --verify --verify-key="$FSS_VKEY" || :
+rm -f /tmp/sealed.journal
+
+# Return back to a journal without FSS
+rm -fv "/var/log/journal/$(</etc/machine-id)/fss"
+journalctl --rotate --vacuum-size=1
+# FIXME: ignore --verify result until #27532 is resolved
+journalctl --verify || :
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# test-journal-append corrupts the journal file by flipping a bit at a given offset and
+# following it by a write to check if we handle appending messages to corrupted journals
+# gracefully
+
+TEST_JOURNAL_APPEND=/usr/lib/systemd/tests/unit-tests/manual/test-journal-append
+
+[[ -x "$TEST_JOURNAL_APPEND" ]]
+
+# Corrupt the first ~1024 bytes, this should be pretty quick
+"$TEST_JOURNAL_APPEND" --sequential --start-offset=0 --iterations=350 --iteration-step=3
+
+# Skip most of the test when running without acceleration, as it's excruciatingly slow
+# (this shouldn't be an issue, as it should run in nspawn as well)
+if ! [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ # Corrupt the beginning of every 1K block between 1K - 32K
+ for ((i = 1024; i <= (32 * 1024); i += 1024)); do
+ "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+ done
+
+ # Corrupt the beginning of every 16K block between 32K - 128K
+ for ((i = (32 * 1024); i <= (256 * 1024); i += (16 * 1024))); do
+ "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+ done
+
+ # Corrupt the beginning of every 128K block between 128K - 1M
+ for ((i = (128 * 1024); i <= (1 * 1024 * 1024); i += (128 * 1024))); do
+ "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+ done
+
+ # And finally the beginning of every 1M block between 1M and 8M
+ for ((i = (1 * 1024 * 1024); i < (8 * 1024 * 1024); i += (1 * 1024 * 1024))); do
+ "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+ done
+
+ if [[ "$(nproc)" -ge 2 ]]; then
+ # Try to corrupt random bytes throughout the journal
+ "$TEST_JOURNAL_APPEND" --iterations=25
+ fi
+else
+ "$TEST_JOURNAL_APPEND" --iterations=10
+fi
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+journalctl --rotate --vacuum-files=1
+# Nuke all archived journals, so we start with a clean slate
+rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
+rm -f "/var/log/journal/$(</etc/machine-id)"/user-*@*.journal
+journalctl --header | grep path
+
+# Make sure the user instance is active when we rotate journals
+loginctl enable-linger testuser
+systemd-run --unit user-sleep.service --user -M testuser@ sleep infinity
+
+for _ in {0..9}; do
+ journalctl --rotate
+ journalctl --sync
+ SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
+ (! journalctl -n0 -q |& grep corrupted)
+done
+
+systemctl stop --user -M testuser@ user-sleep.service
+loginctl disable-linger testuser
+
+journalctl --sync
+journalctl --rotate --vacuum-files=1
+# Nuke all archived journals, so we start with a clean slate
+rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
+rm -f "/var/log/journal/$(</etc/machine-id)/"user-*@*.journal
+journalctl --header | grep path
+
+for _ in {0..9}; do
+ journalctl --rotate --vacuum-files=1
+ journalctl --sync
+ SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
+ (! journalctl -n0 -q |& grep corrupted)
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
+ echo "Built without systemd-journal-gatewayd support, skipping the test"
+ exit 0
+fi
+
+LOG_FILE="$(mktemp)"
+
+at_exit() {
+ if [[ $? -ne 0 ]]; then
+ # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
+ # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
+ # journal; things get very confusing otherwise.
+ systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
+ fi
+
+ rm -f "$LOG_FILE"
+}
+
+trap at_exit EXIT
+
+TEST_MESSAGE="-= This is a test message $RANDOM =-"
+TEST_TAG="$(systemd-id128 new)"
+
+BEFORE_TIMESTAMP="$(date +%s)"
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+sleep 1
+journalctl --sync
+TEST_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
+BOOT_CURSOR="$(journalctl -q -b -n 0 --show-cursor | awk '{ print $3; }')"
+AFTER_TIMESTAMP="$(date +%s)"
+
+/usr/lib/systemd/systemd-journal-gatewayd --version
+/usr/lib/systemd/systemd-journal-gatewayd --help
+
+# Default configuration (HTTP, socket activated)
+systemctl start systemd-journal-gatewayd.socket
+
+# /browse
+# We should get redirected to /browse by default
+curl -LSfs http://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+(! curl -LSfs http://localhost:19531/foo/bar/baz)
+(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
+
+# /entries
+# Accept: text/plain should be the default
+curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: entries=$BOOT_CURSOR:5:10" \
+ http://localhost:19531/entries >"$LOG_FILE"
+jq -se "length == 10" "$LOG_FILE"
+# Check if the specified cursor refers to an existing entry and return just that entry
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: entries=$TEST_CURSOR" \
+ http://localhost:19531/entries?discrete >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+# Check entry is present (resp. absent) when filtering by timestamp
+curl -LSfs \
+ --header "Range: realtime=$BEFORE_TIMESTAMP:" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+ --header "Range: realtime=:$AFTER_TIMESTAMP" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=:$BEFORE_TIMESTAMP" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$AFTER_TIMESTAMP:" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+# Check positive and negative skip when filtering by timestamp
+echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+TEST2_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
+echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+sleep 1
+END_TIMESTAMP="$(date +%s)"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$END_TIMESTAMP::-1:1" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+
+# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
+# line is either empty or begins with data:
+curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
+awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
+# Same thing as journalctl --output=export
+mkdir /tmp/remote-journal
+curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
+/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
+journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
+rm -rf /tmp/remote-journal/*
+# Let's do the same thing again, but let systemd-journal-remote spawn curl itself
+/usr/lib/systemd/systemd-journal-remote --url=http://localhost:19531/entries \
+ --output=/tmp/remote-journal/system.journal \
+ --split-mode=none
+journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
+rm -rf /tmp/remote-journal
+
+# /machine
+curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+
+# /fields
+curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
+grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs http://localhost:19531/fields/_TRANSPORT
+(! curl -LSfs http://localhost:19531/fields)
+(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
+
+systemctl stop systemd-journal-gatewayd.{socket,service}
+
+if ! command -v openssl >/dev/null; then
+ echo "openssl command not available, skipping the HTTPS tests"
+ exit 0
+fi
+
+# Generate a self-signed certificate for systemd-journal-gatewayd
+#
+# Note: older OpenSSL requires a config file with some extra options, unfortunately
+cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+ -config /tmp/openssl.conf \
+ -keyout /tmp/key.pem -out /tmp/cert.pem
+# Start HTTPS version of gatewayd via the systemd-socket-activate tool to give it some coverage as well
+systemd-socket-activate --listen=19531 -- \
+ /usr/lib/systemd/systemd-journal-gatewayd \
+ --cert=/tmp/cert.pem \
+ --key=/tmp/key.pem \
+ --file="/var/log/journal/*/*.journal" &
+GATEWAYD_PID=$!
+sleep 1
+
+# Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
+curl -LSfsk https://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+curl -LSfsk https://localhost:19531/fields/_TRANSPORT
+
+kill "$GATEWAYD_PID"
+
+# Test a couple of error scenarios
+GATEWAYD_FILE="$(mktemp /tmp/test-gatewayd-XXX.journal)"
+
+/usr/lib/systemd/systemd-journal-remote --output="$GATEWAYD_FILE" --getter="journalctl -n5 -o export"
+systemd-run --unit="test-gatewayd.service" --socket-property="ListenStream=19531" \
+ /usr/lib/systemd/systemd-journal-gatewayd --file="$GATEWAYD_FILE"
+
+# Call an unsupported endpoint together with some garbage data - gatewayd should not send garbage in return
+# See: https://github.com/systemd/systemd/issues/9858
+OUT="$(mktemp)"
+for _ in {0..4}; do
+ (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
+ (! grep '[^[:print:]]' "$OUT")
+done
+(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
+(! grep '[^[:print:]]' "$OUT")
+rm -rf "$OUT"
+
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+# Nuke the file behind the /browse endpoint
+mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
+(! curl --fail-with-body -L http://localhost:19531/browse)
+mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+
+# Nuke the journal file
+mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
+(! curl --fail-with-body -L http://localhost:19531/fields/_PID)
+mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
+curl -LSfs http://localhost:19531/fields/_PID
+
+systemctl stop test-gatewayd.{socket,service}
+rm -f "$GATEWAYD_FILE"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+if [[ ! -x /usr/lib/systemd/systemd-journal-remote || ! -x /usr/lib/systemd/systemd-journal-upload ]]; then
+ echo "Built without systemd-journal-remote/upload support, skipping the test"
+ exit 0
+fi
+
+if ! command -v openssl >/dev/null; then
+ echo "openssl command not available, skipping the tests"
+ exit 0
+fi
+
+at_exit() {
+ set +e
+
+ systemctl stop systemd-journal-upload
+ systemctl stop systemd-journal-remote.{socket,service}
+ # Remove any remote journals on exit, so we don't try to export them together
+ # with the local journals, causing a mess
+ rm -rf /var/log/journal/remote
+}
+
+trap at_exit EXIT
+
+TEST_MESSAGE="-= This is a test message $RANDOM =-"
+TEST_TAG="$(systemd-id128 new)"
+
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+
+/usr/lib/systemd/systemd-journal-remote --version
+/usr/lib/systemd/systemd-journal-remote --help
+/usr/lib/systemd/systemd-journal-upload --version
+/usr/lib/systemd/systemd-journal-upload --help
+
+# Generate a self-signed certificate for systemd-journal-remote
+#
+# Note: older OpenSSL requires a config file with some extra options, unfortunately
+# Note2: /run here is used on purpose, since the systemd-journal-remote service uses PrivateTmp=yes
+mkdir -p /run/systemd/journal-remote-tls
+cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+ -config /tmp/openssl.conf \
+ -keyout /run/systemd/journal-remote-tls/key.pem \
+ -out /run/systemd/journal-remote-tls/cert.pem
+chown -R systemd-journal-remote /run/systemd/journal-remote-tls
+
+# Configure journal-upload to upload journals to journal-remote without client certificates
+mkdir -p /run/systemd/journal-{remote,upload}.conf.d
+cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
+[Remote]
+SplitMode=host
+ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
+ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
+TrustedCertificateFile=-
+EOF
+cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
+[Upload]
+URL=https://localhost:19532
+ServerKeyFile=-
+ServerCertificateFile=-
+TrustedCertificateFile=-
+EOF
+systemd-analyze cat-config systemd/journal-remote.conf
+systemd-analyze cat-config systemd/journal-upload.conf
+
+systemctl restart systemd-journal-remote.socket
+systemctl restart systemd-journal-upload
+timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
+systemctl status systemd-journal-{remote,upload}
+
+# It may take a bit until the whole journal is transferred
+timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
+
+systemctl stop systemd-journal-upload
+systemctl stop systemd-journal-remote.{socket,service}
+rm -rf /var/log/journal/remote/*
+
+# Now let's do the same, but with a full PKI setup
+#
+# journal-upload keeps the cursor of the last uploaded message, so let's send a fresh one
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+
+mkdir /run/systemd/remote-pki
+cat >/run/systemd/remote-pki/ca.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = Test CA
+
+[ v3_ca ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:true
+EOF
+cat >/run/systemd/remote-pki/client.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = Test Client
+EOF
+cat >/run/systemd/remote-pki/server.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+# Generate a dummy CA
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+ -extensions v3_ca \
+ -config /run/systemd/remote-pki/ca.conf \
+ -keyout /run/systemd/remote-pki/ca.key \
+ -out /run/systemd/remote-pki/ca.crt
+openssl x509 -in /run/systemd/remote-pki/ca.crt -noout -text
+echo 01 >/run/systemd/remote-pki/ca.srl
+# Generate a client key and signing request
+openssl req -nodes -newkey rsa:2048 -sha256 \
+ -config /run/systemd/remote-pki/client.conf \
+ -keyout /run/systemd/remote-pki/client.key \
+ -out /run/systemd/remote-pki/client.csr
+# Sign the request with the CA key
+openssl x509 -req -days 7 \
+ -in /run/systemd/remote-pki/client.csr \
+ -CA /run/systemd/remote-pki/ca.crt \
+ -CAkey /run/systemd/remote-pki/ca.key \
+ -out /run/systemd/remote-pki/client.crt
+# And do the same for the server
+openssl req -nodes -newkey rsa:2048 -sha256 \
+ -config /run/systemd/remote-pki/server.conf \
+ -keyout /run/systemd/remote-pki/server.key \
+ -out /run/systemd/remote-pki/server.csr
+openssl x509 -req -days 7 \
+ -in /run/systemd/remote-pki/server.csr \
+ -CA /run/systemd/remote-pki/ca.crt \
+ -CAkey /run/systemd/remote-pki/ca.key \
+ -out /run/systemd/remote-pki/server.crt
+chown -R systemd-journal-remote:systemd-journal /run/systemd/remote-pki
+chmod -R g+rwX /run/systemd/remote-pki
+
+# Reconfigure journal-upload/journal remote with the new keys
+cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
+[Remote]
+SplitMode=host
+ServerKeyFile=/run/systemd/remote-pki/server.key
+ServerCertificateFile=/run/systemd/remote-pki/server.crt
+TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
+EOF
+cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
+[Upload]
+URL=https://localhost:19532
+ServerKeyFile=/run/systemd/remote-pki/client.key
+ServerCertificateFile=/run/systemd/remote-pki/client.crt
+TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
+EOF
+systemd-analyze cat-config systemd/journal-remote.conf
+systemd-analyze cat-config systemd/journal-upload.conf
+
+systemctl restart systemd-journal-remote.socket
+systemctl restart systemd-journal-upload
+timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
+systemctl status systemd-journal-{remote,upload}
+
+# It may take a bit until the whole journal is transferred
+timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
+
+systemctl stop systemd-journal-upload
+systemctl stop systemd-journal-remote.{socket,service}
+
+# Let's test if journal-remote refuses connection from journal-upload with invalid client certs
+#
+# We should end up with something like this:
+# systemd-journal-remote[726]: Client is not authorized
+# systemd-journal-upload[738]: Upload to https://localhost:19532/upload failed with code 401:
+# systemd[1]: systemd-journal-upload.service: Main process exited, code=exited, status=1/FAILURE
+# systemd[1]: systemd-journal-upload.service: Failed with result 'exit-code'.
+#
+cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
+[Upload]
+URL=https://localhost:19532
+ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
+ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
+TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
+EOF
+systemd-analyze cat-config systemd/journal-upload.conf
+mkdir -p /run/systemd/system/systemd-journal-upload.service.d
+cat >/run/systemd/system/systemd-journal-upload.service.d/99-test.conf <<EOF
+[Service]
+Restart=no
+EOF
+systemctl daemon-reload
+chgrp -R systemd-journal /run/systemd/journal-remote-tls
+chmod -R g+rwX /run/systemd/journal-remote-tls
+
+systemctl restart systemd-journal-upload
+timeout 10 bash -xec 'while [[ "$(systemctl show -P ActiveState systemd-journal-upload)" != failed ]]; do sleep 1; done'
+(! systemctl status systemd-journal-upload)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Rotation/flush test, see https://github.com/systemd/systemd/issues/19895
+journalctl --relinquish-var
+[[ "$(systemd-detect-virt -v)" == "qemu" ]] && ITERATIONS=10 || ITERATIONS=50
+for ((i = 0; i < ITERATIONS; i++)); do
+ dd if=/dev/urandom bs=1M count=1 | base64 | systemd-cat
+done
+journalctl --rotate
+# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --rotate' via varlinkctl
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Rotate '{}'
+journalctl --flush
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.FlushToVar '{}'
+journalctl --sync
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
+journalctl --rotate --vacuum-size=8M
+
+# Reset the ratelimit buckets for the subsequent tests below.
+systemctl restart systemd-journald
+
+# Test stdout stream
+write_and_match() {
+ local input="${1:?}"
+ local expected="${2?}"
+ local id
+ shift 2
+
+ id="$(systemd-id128 new)"
+ echo -ne "$input" | systemd-cat -t "$id" "$@"
+ journalctl --sync
+ diff <(echo -ne "$expected") <(journalctl -b -o cat -t "$id")
+}
+# Skip empty lines
+write_and_match "\n\n\n" "" --level-prefix false
+write_and_match "<5>\n<6>\n<7>\n" "" --level-prefix true
+# Remove trailing spaces
+write_and_match "Trailing spaces \t \n" "Trailing spaces\n" --level-prefix false
+write_and_match "<5>Trailing spaces \t \n" "Trailing spaces\n" --level-prefix true
+# Don't remove leading spaces
+write_and_match " \t Leading spaces\n" " \t Leading spaces\n" --level-prefix false
+write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix true
+
+# --output-fields restricts output
+ID="$(systemd-id128 new)"
+echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false
+# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
+journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output
+[[ $(wc -l </tmp/output) -eq 9 ]]
+grep -q '^__CURSOR=' /tmp/output
+grep -q '^MESSAGE=foo$' /tmp/output
+grep -q '^PRIORITY=6$' /tmp/output
+(! grep '^FOO=' /tmp/output)
+(! grep '^SYSLOG_FACILITY=' /tmp/output)
+
+# --truncate shows only first line, skip under asan due to logger
+ID="$(systemd-id128 new)"
+echo -e 'HEAD\nTAIL\nTAIL' | systemd-cat -t "$ID"
+journalctl --sync
+journalctl -b -t "$ID" | grep -q HEAD
+journalctl -b -t "$ID" | grep -q TAIL
+journalctl -b -t "$ID" --truncate-newline | grep -q HEAD
+journalctl -b -t "$ID" --truncate-newline | grep -q -v TAIL
+
+# '-b all' negates earlier use of -b (-b and -m are otherwise exclusive)
+journalctl -b -1 -b all -m >/dev/null
+
+# -b always behaves like -b0
+journalctl -q -b-1 -b0 | head -1 >/tmp/expected
+journalctl -q -b-1 -b | head -1 >/tmp/output
+diff /tmp/expected /tmp/output
+# ... even when another option follows (both of these should fail due to -m)
+{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/tmp/expected
+{ journalctl -ball -b -m 2>&1 || :; } | head -1 >/tmp/output
+diff /tmp/expected /tmp/output
+
+# https://github.com/systemd/systemd/issues/13708
+ID=$(systemd-id128 new)
+systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' &
+PID=$!
+wait $PID
+journalctl --sync
+# We can drop this grep when https://github.com/systemd/systemd/issues/13937
+# has a fix.
+journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/tmp/output
+[[ $(wc -l </tmp/output) -eq 2 ]]
+grep -q "^_PID=$PID" /tmp/output
+grep -vq "^_PID=$PID" /tmp/output
+
+# https://github.com/systemd/systemd/issues/15654
+ID=$(systemd-id128 new)
+printf "This will\nusually fail\nand be truncated\n" >/tmp/expected
+systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;'
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/tmp/output
+diff /tmp/expected /tmp/output
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]]
+
+# test that LogLevelMax can also suppress logging about services, not only by services
+systemctl start silent-success
+journalctl --sync
+[[ -z "$(journalctl -b -q -u silent-success.service)" ]]
+
+# Test syslog identifiers exclusion
+systemctl start verbose-success.service
+journalctl --sync
+[[ -n "$(journalctl -b -q -u verbose-success.service -t systemd)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -t echo)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -T systemd)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -T echo)" ]]
+[[ -z "$(journalctl -b -q -u verbose-success.service -T echo -T '(echo)' -T sleep -T '(sleep)' -T systemd -T '(systemd)' -T systemd-executor)" ]]
+
+# Exercise the matching machinery
+SYSTEMD_LOG_LEVEL=debug journalctl -b -n 1 /dev/null /dev/zero /dev/null /dev/null /dev/null
+journalctl -b -n 1 /bin/true /bin/false
+journalctl -b -n 1 /bin/true + /bin/false
+journalctl -b -n 1 -r --unit "systemd*"
+
+systemd-run --user -M "testuser@.host" /bin/echo hello
+journalctl --sync
+journalctl -b -n 1 -r --user-unit "*"
+
+(! journalctl -b /dev/lets-hope-this-doesnt-exist)
+(! journalctl -b /dev/null /dev/zero /dev/this-also-shouldnt-exist)
+(! journalctl -b --unit "this-unit-should-not-exist*")
+
+# Facilities & priorities
+journalctl --facility help
+journalctl --facility help | grep -F 'kern'
+journalctl --facility help | grep -F 'mail'
+journalctl --facility kern -n 1
+journalctl --facility syslog --priority 0..3 -n 1
+journalctl --facility syslog --priority 3..0 -n 1
+journalctl --facility user --priority 0..0 -n 1
+journalctl --facility daemon --priority warning -n 1
+journalctl --facility daemon --priority warning..info -n 1
+journalctl --facility daemon --priority notice..crit -n 1
+journalctl --facility daemon --priority 5..crit -n 1
+
+# Assorted combinations
+journalctl -o help
+journalctl -o help | grep -F 'short'
+journalctl -o help | grep -F 'export'
+journalctl -q -n all -a | grep . >/dev/null
+journalctl -q --no-full | grep . >/dev/null
+journalctl -q --user --system | grep . >/dev/null
+journalctl --namespace "*" | grep . >/dev/null
+journalctl --namespace "" | grep . >/dev/null
+journalctl -q --namespace "+foo-bar-baz-$RANDOM" | grep . >/dev/null
+(! journalctl -q --namespace "foo-bar-baz-$RANDOM" | grep .)
+journalctl --root / | grep . >/dev/null
+journalctl --cursor "t=0;t=-1;t=0;t=0x0" | grep . >/dev/null
+journalctl --header | grep system.journal
+journalctl --field _EXE | grep . >/dev/null
+journalctl --no-hostname --utc --catalog | grep . >/dev/null
+# Exercise executable_is_script() and the related code, e.g. `journalctl -b /path/to/a/script.sh` should turn
+# into ((_EXE=/bin/bash AND _COMM=script.sh) AND _BOOT_ID=c002e3683ba14fa8b6c1e12878386514)
+journalctl -b "$(readlink -f "$0")" | grep . >/dev/null
+journalctl -b "$(systemd-id128 boot-id)" | grep . >/dev/null
+journalctl --since yesterday --reverse | grep . >/dev/null
+journalctl --machine .host | grep . >/dev/null
+# Log something that journald will forward to wall
+echo "Oh no!" | systemd-cat -t "emerg$RANDOM" -p emerg --stderr-priority emerg
+
+TAG="$(systemd-id128 new)"
+echo "Foo Bar Baz" | systemd-cat -t "$TAG"
+journalctl --sync
+# Relevant excerpt from journalctl(1):
+# If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case sensitive.
+# This can be overridden with the --case-sensitive option
+journalctl -e -t "$TAG" --grep "Foo Bar Baz"
+journalctl -e -t "$TAG" --grep "foo bar baz"
+(! journalctl -e -t "$TAG" --grep "foo Bar baz")
+journalctl -e -t "$TAG" --case-sensitive=false --grep "foo Bar baz"
+
+(! journalctl --facility hopefully-an-unknown-facility)
+(! journalctl --priority hello-world)
+(! journalctl --priority 0..128)
+(! journalctl --priority 0..systemd)
+
+# Other options
+journalctl --disk-usage
+journalctl --dmesg -n 1
+journalctl --fields
+journalctl --list-boots
+journalctl --update-catalog
+journalctl --list-catalog
+
+# Add new tests before here, the journald restarts below
+# may make tests flappy.
+
+# Don't lose streams on restart
+systemctl start forever-print-hola
+sleep 3
+systemctl restart systemd-journald
+sleep 3
+systemctl stop forever-print-hola
+[[ ! -f "/tmp/i-lose-my-logs" ]]
+
+# https://github.com/systemd/systemd/issues/4408
+rm -f /tmp/i-lose-my-logs
+systemctl start forever-print-hola
+sleep 3
+systemctl kill --signal=SIGKILL systemd-journald
+sleep 3
+[[ ! -f "/tmp/i-lose-my-logs" ]]
+systemctl stop forever-print-hola
+
+set +o pipefail
+# https://github.com/systemd/systemd/issues/15528
+journalctl --follow --file=/var/log/journal/*/* | head -n1 | grep .
+# https://github.com/systemd/systemd/issues/24565
+journalctl --follow --merge | head -n1 | grep .
+set -o pipefail
+
+# https://github.com/systemd/systemd/issues/26746
+rm -f /tmp/issue-26746-log /tmp/issue-26746-cursor
+ID="$(systemd-id128 new)"
+journalctl -t "$ID" --follow --cursor-file=/tmp/issue-26746-cursor | tee /tmp/issue-26746-log &
+systemd-cat -t "$ID" /bin/sh -c 'echo hogehoge'
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until [[ -f /tmp/issue-26746-log && "$(cat /tmp/issue-26746-log)" =~ hogehoge ]]; do sleep .5; done'
+pkill -TERM journalctl
+timeout 10 bash -c 'until test -f /tmp/issue-26746-cursor; do sleep .5; done'
+CURSOR_FROM_FILE="$(cat /tmp/issue-26746-cursor)"
+CURSOR_FROM_JOURNAL="$(journalctl -t "$ID" --output=export MESSAGE=hogehoge | sed -n -e '/__CURSOR=/ { s/__CURSOR=//; p }')"
+test "$CURSOR_FROM_FILE" = "$CURSOR_FROM_JOURNAL"
+
+# Check that the seqnum field at least superficially works
+systemd-cat echo "ya"
+journalctl --sync
+SEQNUM1=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
+systemd-cat echo "yo"
+journalctl --sync
+SEQNUM2=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
+test "$SEQNUM2" -gt "$SEQNUM1"
+
+# Test for journals without RTC
+# See: https://github.com/systemd/systemd/issues/662
+JOURNAL_DIR="$(mktemp -d)"
+while read -r file; do
+ filename="${file##*/}"
+ unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
+done < <(find /usr/lib/systemd/tests/testdata/test-journals/no-rtc -name "*.zst")
+
+journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1
+diff -u /tmp/lb1 - <<'EOF'
+[{"index":-3,"boot_id":"5ea5fc4f82a14186b5332a788ef9435e","first_entry":1666569600994371,"last_entry":1666584266223608},{"index":-2,"boot_id":"bea6864f21ad4c9594c04a99d89948b0","first_entry":1666569601005945,"last_entry":1666584347230411},{"index":-1,"boot_id":"4c708e1fd0744336be16f3931aa861fb","first_entry":1666569601017222,"last_entry":1666584354649355},{"index":0,"boot_id":"35e8501129134edd9df5267c49f744a4","first_entry":1666569601009823,"last_entry":1666584438086856}]
+EOF
+rm -rf "$JOURNAL_DIR" /tmp/lb1
+
+# Check that using --after-cursor/--cursor-file= together with journal filters doesn't
+# skip over entries matched by the filter
+# See: https://github.com/systemd/systemd/issues/30288
+UNIT_NAME="test-cursor-$RANDOM.service"
+CURSOR_FILE="$(mktemp)"
+# Generate some messages we can match against
+journalctl --cursor-file="$CURSOR_FILE" -n1
+systemd-run --unit="$UNIT_NAME" --wait --service-type=exec bash -ec "sleep 2; set -x; echo hello; echo world; set +x; sleep 2"
+journalctl --sync
+# --after-cursor= + --unit=
+# The format of the "Starting ..." message depends on StatusUnitFormat=, so match only the beginning
+# which should be enough in this case
+[[ "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" _PID=1 )" =~ ^Starting\ ]]
+# There should be no such messages before the cursor
+[[ -z "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" --reverse)" ]]
+# --cursor-file= + a journal filter
+diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UNIT_NAME") - <<EOF
++ echo hello
+hello
++ echo world
+world
++ set +x
+EOF
+rm -f "$CURSOR_FILE"
+
+# Check that --until works with --after-cursor and --lines/-n
+# See: https://github.com/systemd/systemd/issues/31776
+CURSOR_FILE="$(mktemp)"
+journalctl -q -n 0 --cursor-file="$CURSOR_FILE"
+TIMESTAMP="$(journalctl -q -n 1 --cursor="$(<"$CURSOR_FILE")" --output=short-unix | cut -d ' ' -f 1 | cut -d '.' -f 1)"
+[[ -z "$(journalctl -q -n 10 --after-cursor="$(<"$CURSOR_FILE")" --until "@$((TIMESTAMP - 3))")" ]]
+rm -f "$CURSOR_FILE"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-04-JOURNAL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+pre=test05
+cat >/run/systemd/system/"$pre"alpha.slice <<EOF
+[Slice]
+MemoryMax=40M
+MemoryHigh=40M
+TasksMax=400
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
+[Slice]
+MemoryMax=10M
+MemoryHigh=10M
+TasksMax=100
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
+[Slice]
+MemoryMax=20M
+MemoryHigh=20M
+TasksMax=200
+EOF
+
+systemctl daemon-reload
+
+srv=probe.service
+slc0="$pre"alpha.slice
+slc="$pre"alpha-beta-gamma.slice
+
+systemd-run --unit "$srv" --slice "$slc" \
+ -p MemoryMax=5M \
+ -p MemoryHigh=5M \
+ -p TasksMax=50 \
+ sleep inf
+
+# Compare with inequality because test can run in a constrained container
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+systemd-run --unit "$srv" --slice "$slc" \
+ sleep inf
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
+
+systemctl set-property "$slc0" \
+ MemoryMax=5M \
+ MemoryHigh=5M \
+ TasksMax=50
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+rm -f /run/systemd/system/"$pre"* || :
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+P=/run/systemd/system.conf.d
+mkdir $P
+
+cat >$P/rlimits.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+systemctl daemon-reload
+
+[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
+[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
+
+[[ "$(systemctl show -P LimitNOFILESoft TEST-05-RLIMITS.service)" = "10000" ]]
+[[ "$(systemctl show -P LimitNOFILE TEST-05-RLIMITS.service)" = "16384" ]]
+
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-05-RLIMITS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-06-SELINUX
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+. /etc/os-release
+if ! [[ "$ID" =~ centos|fedora ]]; then
+ echo "Skipping because only CentOS and Fedora support SELinux tests" >>/skipped
+ exit 77
+fi
+
+# Note: ATTOW the following checks should work with both Fedora and upstream reference policy
+# (with or without MCS/MLS)
+
+sestatus
+
+# We should end up in permissive mode
+[[ "$(getenforce)" == "Permissive" ]]
+
+# Check PID 1's context
+PID1_CONTEXT="$(ps -h -o label 1)"
+[[ "$PID1_CONTEXT" =~ ^system_u:system_r:init_t(:s0)?$ ]]
+# The same label should be attached to all PID 1's journal messages
+journalctl -q -b -p info -n 5 --grep . _SELINUX_CONTEXT="$PID1_CONTEXT"
+
+# Check context on a couple of arbitrarily-selected files/directories
+[[ "$(stat --printf %C /run/systemd/journal/)" =~ ^system_u:object_r:(syslogd_runtime_t|syslogd_var_run_t)(:s0)?$ ]]
+[[ "$(stat --printf %C /run/systemd/notify)" =~ ^system_u:object_r:(init_runtime_t|init_var_run_t)(:s0)?$ ]]
+[[ "$(stat --printf %C /run/systemd/sessions/)" =~ ^system_u:object_r:(systemd_sessions_runtime_t|systemd_logind_sessions_t)(:s0)?$ ]]
+
+# Check if our SELinux-related functionality works
+#
+# Since the SELinux policies vary wildly, use a context from some existing file
+# as our test context
+CONTEXT="$(stat -c %C /proc/sys/kernel/core_pattern)"
+
+[[ "$(systemd-run --wait --pipe -p SELinuxContext="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
+(! systemd-run --wait --pipe -p SELinuxContext="foo:bar:baz" cat /proc/self/attr/current)
+(! systemd-run --wait --pipe -p ConditionSecurity='selinux' false)
+systemd-run --wait --pipe -p ConditionSecurity='!selinux' false
+
+NSPAWN_ARGS=(systemd-nspawn -q --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id)
+[[ "$("${NSPAWN_ARGS[@]}" cat /proc/self/attr/current | tr -d '\0')" != "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" --selinux-context="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" stat --printf %C /run)" != "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" stat --printf %C /run)" == "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" --tmpfs=/tmp stat --printf %C /tmp)" == "$CONTEXT" ]]
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+if ! grep -q pidfd_open /proc/kallsyms; then
+ echo "pidfds not available, skipping the test..."
+ exit 0
+fi
+
+systemd-run --unit test-aux-scope.service \
+ --service-type notify -p Slice=aux.slice -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
+ /usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
+kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
+
+timeout 30s bash -xec 'until systemctl is-active test-aux-scope.scope; do sleep 1; done'
+
+systemctl status test-aux-scope.service
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
+
+systemctl status test-aux-scope.scope
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
+
+test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
+test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
+test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
+test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
+
+systemctl stop test-aux-scope.scope
+systemctl stop test-aux-scope.service
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Make sure the unit's exec context matches its configuration
+# See: https://github.com/systemd/systemd/pull/29552
+
+# Even though hidepid= was introduced in kernel 3.3, we support only
+# the post 5.8 implementation that allows us to apply the option per-instance,
+# instead of the whole namespace. To distinguish between these two implementations
+# lets check if we can mount procfs with a named value (e.g. hidepid=off), since
+# support for this was introduced in the same commit as the per-instance stuff
+proc_supports_option() {
+ local option="${1:?}"
+ local proc_tmp ec
+
+ proc_tmp="$(mktemp -d)"
+ mount -t proc -o "$option" proc "$proc_tmp" && ec=0 || ec=$?
+ mountpoint -q "$proc_tmp" && umount -q "$proc_tmp"
+ rm -rf "$proc_tmp"
+
+ return $ec
+}
+
+# In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d
+# dropin in /etc. This dropin has, unfortunately, higher priority than
+# the transient stuff from systemd-run. Let's just skip the following tests
+# in that case instead of complicating the test setup even more */
+if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
+ if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
+ boot_path="$(bootctl --print-boot-path)"
+ esp_path="$(bootctl --print-esp-path)"
+
+ # If the mount points are handled by automount units, make sure we trigger
+ # them before proceeding further
+ ls -l "$boot_path" "$esp_path"
+ fi
+
+ systemd-run --wait --pipe -p ProtectSystem=yes \
+ bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
+ systemd-run --wait --pipe -p ProtectSystem=full \
+ bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
+ systemd-run --wait --pipe -p ProtectSystem=strict \
+ bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
+ systemd-run --wait --pipe -p ProtectSystem=no \
+ bash -xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc"
+
+ MARK="$(mktemp /root/.exec-context.XXX)"
+ systemd-run --wait --pipe -p ProtectHome=yes \
+ bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK"
+ systemd-run --wait --pipe -p ProtectHome=read-only \
+ bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK"
+ systemd-run --wait --pipe -p ProtectHome=tmpfs \
+ bash -xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK"
+ systemd-run --wait --pipe -p ProtectHome=no \
+ bash -xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK"
+ rm -f "$MARK"
+fi
+
+if proc_supports_option "hidepid=off"; then
+ systemd-run --wait --pipe -p ProtectProc=noaccess -p User=testuser \
+ bash -xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm'
+ systemd-run --wait --pipe -p ProtectProc=invisible -p User=testuser \
+ bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
+ systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser \
+ bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
+ systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser -p AmbientCapabilities=CAP_SYS_PTRACE \
+ bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
+ systemd-run --wait --pipe -p ProtectProc=default -p User=testuser \
+ bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
+fi
+
+if proc_supports_option "subset=pid"; then
+ systemd-run --wait --pipe -p ProcSubset=pid -p User=testuser \
+ bash -xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo"
+ systemd-run --wait --pipe -p ProcSubset=all -p User=testuser \
+ bash -xec "test -r /proc/1/comm; test -r /proc/cpuinfo"
+fi
+
+if ! systemd-detect-virt -cq; then
+ systemd-run --wait --pipe -p ProtectKernelLogs=yes -p User=testuser \
+ bash -xec "test ! -r /dev/kmsg"
+ systemd-run --wait --pipe -p ProtectKernelLogs=no -p User=testuser \
+ bash -xec "test -r /dev/kmsg"
+fi
+
+systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
+ bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
+systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
+ bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
+# Make sure we properly serialize/deserialize paths with spaces
+# See: https://github.com/systemd/systemd/issues/30747
+touch "/tmp/test file with spaces"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \
+ bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \
+ bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'"
+
+# Check if we correctly serialize, deserialize, and set directives that
+# have more complex internal handling
+if ! systemd-detect-virt -cq; then
+ # Funny detail: this originally used the underlying rootfs device, but that,
+ # for some reason, caused "divide error" in kernel, followed by a kernel panic
+ TEMPFILE="$(mktemp)"
+ LODEV="$(losetup --show -f "$TEMPFILE")"
+ ROOT_DEV_MAJ_MIN="$(lsblk -nro MAJ:MIN "$LODEV")"
+ EXPECTED_IO_MAX="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000"
+ EXPECTED_IO_LATENCY="$ROOT_DEV_MAJ_MIN target=69000"
+ SERVICE_NAME="test-io-directives-$RANDOM.service"
+ CGROUP_PATH="/sys/fs/cgroup/system.slice/$SERVICE_NAME"
+
+ # IO*=
+ ARGUMENTS=(
+ # Throw in a couple of invalid entries just to test things out
+ -p IOReadBandwidthMax="/foo/bar 1M"
+ -p IOReadBandwidthMax="/foo/baz 1M"
+ -p IOReadBandwidthMax="$LODEV 1M"
+ -p IOReadBandwidthMax="$LODEV 1K"
+ -p IOWriteBandwidthMax="$LODEV 1G"
+ -p IOWriteBandwidthMax="$LODEV 1T"
+ -p IOReadIOPSMax="$LODEV 2G"
+ -p IOWriteIOPSMax="$LODEV 4K"
+ -p IODeviceLatencyTargetSec="$LODEV 666ms"
+ -p IODeviceLatencyTargetSec="/foo/bar 69ms"
+ -p IODeviceLatencyTargetSec="$LODEV 69ms"
+ -p IOReadBandwidthMax="/foo/bar 1M"
+ -p IOReadBandwidthMax="/foo/baz 1M"
+ # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks
+ -p IODeviceWeight="$LODEV 999"
+ -p IODeviceWeight="/foo/bar 999"
+ )
+
+ systemctl set-property system.slice IOAccounting=yes
+ # io.latency not available by default on Debian stable
+ if [[ -e /sys/fs/cgroup/system.slice/io.latency ]]; then
+ systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
+ bash -xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency"
+ fi
+
+ # CPUScheduling=
+ ARGUMENTS=(
+ -p CPUSchedulingPolicy=rr # ID: 2
+ -p CPUSchedulingPolicy=fifo # ID: 1
+ -p CPUSchedulingPriority=5 # Actual prio: 94 (99 - prio)
+ -p CPUSchedulingPriority=10 # Actual prio: 89 (99 - prio)
+ )
+
+ systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
+ bash -xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched'
+
+ # Device*=
+ ARGUMENTS=(
+ -p DevicePolicy=closed
+ -p DevicePolicy=strict
+ -p DeviceAllow="char-mem rm" # Allow read & mknod for /dev/{null,zero,...}
+ -p DeviceAllow="$LODEV rw"
+ -p DeviceAllow="$LODEV w" # Allow write for the loop
+ # Everything else should be disallowed per the strict policy
+ )
+
+ systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
+ bash -xec "test -r /dev/null; test ! -w /dev/null; test ! -r $LODEV; test -w $LODEV; test ! -r /dev/tty; test ! -w /dev/tty"
+
+ if ! systemctl --version | grep -qF -- "-BPF_FRAMEWORK"; then
+ # SocketBind*=
+ ARGUMENTS=(
+ -p SocketBindAllow=
+ -p SocketBindAllow=1234
+ -p SocketBindAllow=ipv4:udp:any
+ -p SocketBindAllow=ipv6:6666
+ # Everything but the last assignment is superfluous, but it still exercises
+ # the parsing machinery
+ -p SocketBindDeny=
+ -p SocketBindDeny=1111
+ -p SocketBindDeny=ipv4:1111
+ -p SocketBindDeny=ipv4:any
+ -p SocketBindDeny=ipv4:tcp:any
+ -p SocketBindDeny=ipv4:udp:10000-11000
+ -p SocketBindDeny=ipv6:1111
+ -p SocketBindDeny=any
+ )
+
+ # We should fail with EPERM when trying to bind to a socket not on the allow list
+ # (nc exits with 2 in that case)
+ systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
+ systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -l ::1 9999; exit 42'
+ systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
+ systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
+ systemd-run --wait -p SuccessExitStatus="1 2" --pipe -p SocketBindDeny=any \
+ bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
+ # Consequently, we should succeed when binding to a socket on the allow list
+ # and keep listening on it until we're killed by `timeout` (EC 124)
+ systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1'
+ systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1'
+ systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -6 -l ::1 1234; exit 1'
+ systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+ bash -xec 'timeout 1s nc -6 -l ::1 6666; exit 1'
+ fi
+
+ losetup -d "$LODEV"
+ rm -f "$TEMPFILE"
+fi
+
+# {Cache,Configuration,Logs,Runtime,State}Directory=
+ARGUMENTS=(
+ -p CacheDirectory="foo/bar/baz also\ with\ spaces"
+ -p CacheDirectory="foo"
+ -p CacheDirectory="context"
+ -p CacheDirectoryMode="0123"
+ -p CacheDirectoryMode="0666"
+ -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon"
+ -p ConfigurationDirectoryMode="0400"
+ -p LogsDirectory="context/foo"
+ -p LogsDirectory=""
+ -p LogsDirectory="context/a/very/nested/logs/dir"
+ -p RuntimeDirectory="context/with\ spaces"
+ # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which
+ # requires an additional level of escaping for the colon character
+ -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces"
+ -p RuntimeDirectoryPreserve=yes
+ -p StateDirectory="context"
+ -p StateDirectory="./././././././context context context"
+ -p StateDirectoryMode="0000"
+)
+
+rm -rf /run/context
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+ bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]];
+ [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+ bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]];
+ [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+ bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
+ [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+ bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]];
+ [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]];
+ [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+ bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
+test -d "/run/context/with spaces"
+test -s "/run/a symlink with : col:ons and spaces"
+rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
+
+# Limit*=
+#
+# Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan)
+# won't be able to mmap the shadow maps
+ARGUMENTS=(
+ -p LimitCPU=15
+ -p LimitCPU=10:15 # ulimit -t
+ -p LimitFSIZE=96G # ulimit -f
+ -p LimitDATA=8T:infinity
+ -p LimitDATA=infinity # ulimit -d
+ -p LimitSTACK=8M # ulimit -s
+ -p LimitCORE=infinity
+ -p LimitCORE=17M # ulimit -c
+ -p LimitRSS=27G # ulimit -m
+ -p LimitNOFILE=7:127 # ulimit -n
+ -p LimitAS=infinity # ulimit -v
+ -p LimitNPROC=1
+ -p LimitNPROC=64:infinity # ulimit -u
+ -p LimitMEMLOCK=37M # ulimit -l
+ -p LimitLOCKS=19:1021 # ulimit -x
+ -p LimitSIGPENDING=21 # ulimit -i
+ -p LimitMSGQUEUE=666 # ulimit -q
+ -p LimitNICE=4 # ulimit -e
+ -p LimitRTPRIO=8 # ulimit -r
+ -p LimitRTTIME=666666 # ulimit -R
+)
+# Do all the checks in one giant inline shell blob to avoid the overhead of spawning
+# a new service for each check
+#
+# Note: ulimit shows storage-related values in 1024-byte increments*
+# Note2: ulimit -R requires bash >= 5.1
+#
+# * in POSIX mode -c a -f options show values in 512-byte increments; let's hope
+# we never run in the POSIX mode
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+ bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024));
+ : CPU; [[ $(ulimit -St) -eq 10 ]]; [[ $(ulimit -Ht) -eq 15 ]];
+ : FSIZE; [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]];
+ : DATA; [[ $(ulimit -Sd) == unlimited ]]; [[ $(ulimit -Hd) == unlimited ]];
+ : STACK; [[ $(ulimit -Ss) -eq $((8 * MB)) ]]; [[ $(ulimit -Hs) -eq $((8 * MB)) ]];
+ : CORE; [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]];
+ : RSS; [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]];
+ : NOFILE; [[ $(ulimit -Sn) -eq 7 ]]; [[ $(ulimit -Hn) -eq 127 ]];
+ : AS; [[ $(ulimit -Sv) == unlimited ]]; [[ $(ulimit -Hv) == unlimited ]];
+ : NPROC; [[ $(ulimit -Su) -eq 64 ]]; [[ $(ulimit -Hu) == unlimited ]];
+ : MEMLOCK; [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]];
+ : LOCKS; [[ $(ulimit -Sx) -eq 19 ]]; [[ $(ulimit -Hx) -eq 1021 ]];
+ : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]]; [[ $(ulimit -Hi) -eq 21 ]];
+ : MSGQUEUE; [[ $(ulimit -Sq) -eq 666 ]]; [[ $(ulimit -Hq) -eq 666 ]];
+ : NICE; [[ $(ulimit -Se) -eq 4 ]]; [[ $(ulimit -He) -eq 4 ]];
+ : RTPRIO; [[ $(ulimit -Sr) -eq 8 ]]; [[ $(ulimit -Hr) -eq 8 ]];
+ ulimit -R || exit 0;
+ : RTTIME; [[ $(ulimit -SR) -eq 666666 ]]; [[ $(ulimit -HR) -eq 666666 ]];'
+
+# RestrictFileSystems=
+#
+# Note: running instrumented binaries requires at least /proc to be accessible, so let's
+# skip the test when we're running under sanitizers
+#
+# Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files,
+# since gcov can't access the restricted filesystem (as expected)
+if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then
+ ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)"
+ systemd-run --wait --pipe -p RestrictFileSystems="" ls /
+ systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls /
+ (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc)
+ (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /)
+ systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc
+ systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc
+ systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup
+
+ systemd-run --wait --pipe -p RestrictFileSystems="~" ls /
+ systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /
+ systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /
+ (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /)
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc)
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc)
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc)
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys)
+ systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc)
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev)
+ (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys)
+fi
+
+# Make sure we properly (de)serialize various string arrays, including whitespaces
+# See: https://github.com/systemd/systemd/issues/31214
+systemd-run --wait --pipe -p Environment="FOO='bar4 '" \
+ bash -xec '[[ $FOO == "bar4 " ]]'
+systemd-run --wait --pipe -p Environment="FOO='bar4 ' BAR='\n\n'" \
+ bash -xec "[[ \$FOO == 'bar4 ' && \$BAR == $'\n\n' ]]"
+systemd-run --wait --pipe -p Environment='FOO="bar4 \\ "' -p Environment="BAR='\n\t'" \
+ bash -xec "[[ \$FOO == 'bar4 \\ ' && \$BAR == $'\n\t' ]]"
+TEST_ENV_FILE="/tmp/test-env-file-$RANDOM- "
+cat >"$TEST_ENV_FILE" <<EOF
+FOO="env file "
+BAR="
+ "
+EOF
+systemd-run --wait --pipe cat "$TEST_ENV_FILE"
+systemd-run --wait --pipe -p ReadOnlyPaths="'$TEST_ENV_FILE'" \
+ bash -xec '[[ ! -w "$TEST_ENV_FILE" ]]'
+systemd-run --wait --pipe -p PrivateTmp=yes -p BindReadOnlyPaths="'$TEST_ENV_FILE':'/tmp/bar- '" \
+ bash -xec '[[ -e "/tmp/bar- " && ! -w "/tmp/bar- " ]]'
+systemd-run --wait --pipe -p EnvironmentFile="$TEST_ENV_FILE" \
+ bash -xec "[[ \$FOO == 'env file ' && \$BAR == $'\n ' ]]"
+rm -f "$TEST_ENV_FILE"
+# manager_serialize()/manager_deserialize() uses similar machinery
+systemctl unset-environment FOO_WITH_SPACES
+systemctl set-environment FOO_WITH_SPACES="foo " FOO_WITH_TABS="foo\t\t\t"
+systemctl show-environment
+systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo '"
+systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
+systemctl daemon-reexec
+systemctl show-environment
+systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo '"
+systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
+
+# Ensure that clean-up codepaths work correctly if activation ultimately fails
+touch /run/not-a-directory
+mkdir /tmp/root
+touch /tmp/root/foo
+chmod +x /tmp/root/foo
+(! systemd-run --wait --pipe false)
+(! systemd-run --wait --pipe --unit "test-dynamicuser-fail" -p DynamicUser=yes -p WorkingDirectory=/nonexistent true)
+(! systemd-run --wait --pipe -p RuntimeDirectory=not-a-directory true)
+(! systemd-run --wait --pipe -p RootDirectory=/tmp/root this-shouldnt-exist)
+(! systemd-run --wait --pipe -p RootDirectory=/tmp/root /foo)
+(! systemd-run --wait --pipe --service-type=oneshot -p ExecStartPre=-/foo/bar/baz -p ExecStart=-/foo/bar/baz -p RootDirectory=/tmp/root -- "- foo")
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+setup_base_unit() {
+ local unit_path="${1:?}"
+ local log_file="${2:?}"
+ local unit_name="${unit_path##*/}"
+
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ systemctl --job-mode=replace --no-block start "$unit_name"
+ # Wait until the unit leaves the "inactive" state
+ timeout 5s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" == inactive ]]; do sleep .1; done"
+ # Sleep for 1 second from the unit start to get well "into" the first (or second) ExecStart= directive
+ sleep 1
+}
+
+check_output() {
+ local unit_name="${1:?}"
+ local log_file="${2:?}"
+ local expected="${3?}"
+ local unit_name="${unit_path##*/}"
+
+ # Wait until the unit becomes inactive before checking the log
+ timeout 10s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" != inactive ]]; do sleep .5; done"
+
+ diff "$log_file" <(echo -ne "$expected")
+}
+
+testcase_no_change() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-no-change-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Simple sanity test without any reordering shenanignans, to check if the base unit works as expected.
+ check_output "$unit_path" "$log_file" "foo\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_swapped() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-swapped-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Swap the two ExecStart= lines.
+ #
+ # Since we should be in the first "sleep" of the base unit, after replacing the unit with the following
+ # one we should continue running from the respective "ExecStart=sleep 3" line, which is now the last
+ # one, resulting no output in the final log file.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=sleep 3
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" ""
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_before() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-added-before-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Add one new ExecStart= before the existing ones.
+ #
+ # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
+ # bar" one will have no effect and we should end up with the same output as in the previous case.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" "foo\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_after() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-added-after-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Add an ExecStart= line after the existing ones.
+ #
+ # Same case as above, except the newly added ExecStart= should get executed, as it was added after the
+ # "sleep 3" statement.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_interleaved() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-interleaved-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Combination of the two previous cases.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo baz >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_removal() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-removal-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Remove the currently executed ExecStart= line.
+ #
+ # In this case we completely drop the currently executed "sleep 3" statement, so after reload systemd
+ # should complain that the currently executed command vanished and simply finish executing the unit,
+ # resulting in an empty log.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=bash -c "echo baz >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" ""
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_issue_6533() {
+ local unit_path unit_name log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-issue-6533-XXX.service)"
+ unit_name="${unit_path##*/}"
+ log_file="$(mktemp)"
+
+ cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+EOF
+ systemctl daemon-reload
+
+ systemctl --job-mode=replace --no-block start "$unit_name"
+ sleep 2
+
+ # Make sure we try to execute the next command only for oneshot services, as for other types we allow
+ # only one ExecStart= directive.
+ #
+ # See: https://github.com/systemd/systemd/issues/6533
+ cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" ""
+ (! journalctl -b --grep "Freezing execution" _PID=1)
+}
+
+mkdir -p /run/systemd/system/
+run_testcases
+systemctl daemon-reload
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that timestamps of a Type=notify service are consistent
+
+systemd-run --service-type notify --property NotifyAccess=all --unit notify.service --wait sh -c 'systemd-notify --ready; exit 1' || :
+
+start=$(systemctl show --property=ExecMainStartTimestampMonotonic --value notify.service)
+handoff=$(systemctl show --property=ExecMainHandoffTimestampMonotonic --value notify.service)
+active=$(systemctl show --property=ActiveEnterTimestampMonotonic --value notify.service)
+exit=$(systemctl show --property=ExecMainExitTimestampMonotonic --value notify.service)
+
+[[ $start -le $handoff ]]
+[[ $handoff -le $active ]]
+[[ $active -le $exit ]]
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test that KillMode=mixed does not leave left over processes with ExecStopPost=
+# Issue: https://github.com/systemd/systemd/issues/14566
+
+if [[ -n "${ASAN_OPTIONS:-}" ]]; then
+ # Temporarily skip this test when running with sanitizers due to a deadlock
+ # See: https://bugzilla.redhat.com/show_bug.cgi?id=2098125
+ echo "Sanitizers detected, skipping the test..."
+ exit 0
+fi
+
+systemctl start issue14566-repro
+sleep 4
+systemctl status issue14566-repro
+
+leaked_pid=$(cat /leakedtestpid)
+
+systemctl stop issue14566-repro
+sleep 4
+
+# Leaked PID will still be around if we're buggy.
+# I personally prefer to see 42.
+ps -p "$leaked_pid" && exit 42
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test ExecCondition= does not restart on abnormal or failure
+# Issue: https://github.com/systemd/systemd/issues/16115
+
+systemctl start issue16115-repro-1
+systemctl start issue16115-repro-2
+systemctl start issue16115-repro-3
+sleep 5 # wait a bit in case there are restarts so we can count them below
+
+[[ "$(systemctl show issue16115-repro-1 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show issue16115-repro-2 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show issue16115-repro-3 -P NRestarts)" == "0" ]]
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Segmentation fault in timer_enter_waiting while masking a unit
+# Issue: https://github.com/systemd/systemd/issues/1981
+
+at_exit() {
+ set +e
+
+ systemctl stop my.timer my.service
+ rm -f /run/systemd/system/my.{service,timer}
+ systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/systemd/system
+
+cat >/run/systemd/system/my.service <<\EOF
+[Service]
+Type=oneshot
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer'
+ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"'
+ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"'
+ExecStart=echo Timer runs me
+EOF
+
+cat >/run/systemd/system/my.timer <<EOF
+[Timer]
+OnBootSec=10s
+OnUnitInactiveSec=1h
+EOF
+
+systemctl unmask my.timer
+systemctl start my.timer
+
+mkdir -p /run/systemd/system/my.timer.d/
+cat >/run/systemd/system/my.timer.d/override.conf <<EOF
+[Timer]
+OnBootSec=10s
+OnUnitInactiveSec=1h
+EOF
+
+systemctl daemon-reload
+systemctl mask my.timer
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Don't start services every few ms if condition fails
+# Issue: https://github.com/systemd/systemd/issues/2467
+
+rm -f /tmp/nonexistent
+systemctl start issue2467.socket
+nc -i20 -w20 -U /run/test.ctl || :
+
+# TriggerLimitIntervalSec= by default is set to 2s. A "sleep 10" should give
+# systemd enough time even on slower machines, to reach the trigger limit.
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until [[ "$(systemctl show issue2467.socket -P ActiveState)" == failed ]]; do sleep .5; done'
+[[ "$(systemctl show issue2467.socket -P Result)" == trigger-limit-hit ]]
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check if the unit doesn't remain in active state after the main PID exits
+# Issue: https://github.com/systemd/systemd/issues/27953
+
+systemctl start issue27953.service
+timeout 10 sh -c 'while systemctl is-active issue27953.service; do sleep .5; done'
+[[ "$(systemctl show -P ExitType issue27953.service)" == main ]]
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that socket FDs are not double closed on error: https://github.com/systemd/systemd/issues/30412
+
+mkdir -p /run/systemd/system
+
+rm -f /tmp/badbin
+touch /tmp/badbin
+chmod 744 /tmp/badbin
+
+cat >/run/systemd/system/badbin_assert.service <<EOF
+[Service]
+ExecStart=/tmp/badbin
+Restart=no
+EOF
+
+cat >/run/systemd/system/badbin_assert.socket <<EOF
+[Socket]
+ListenStream=@badbin_assert.socket
+FlushPending=yes
+EOF
+
+systemctl daemon-reload
+systemctl start badbin_assert.socket
+
+socat - ABSTRACT-CONNECT:badbin_assert.socket
+
+timeout 10 sh -c 'while systemctl is-active badbin_assert.service; do sleep .5; done'
+[[ "$(systemctl show -P ExecMainStatus badbin_assert.service)" == 203 ]]
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Service doesn't enter the "failed" state
+# Issue: https://github.com/systemd/systemd/issues/3166
+
+systemctl --no-block start issue3166-fail-on-restart.service
+active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
+while [[ "$active_state" == "activating" || "$active_state" =~ ^(in)?active$ ]]; do
+ sleep .5
+ active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
+done
+systemctl is-failed issue3166-fail-on-restart.service || exit 1
+[[ "$(systemctl show --value --property NRestarts issue3166-fail-on-restart.service)" -le 3 ]] || exit 1
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# SocketGroup lost on daemon-reload with unit moving away temporarily
+# Issue: https://github.com/systemd/systemd/issues/3171
+
+echo "g adm - - -" | systemd-sysusers -
+
+U=/run/systemd/system/issue-3171.socket
+cat >$U <<EOF
+[Unit]
+Description=Test 12 socket
+[Socket]
+Accept=yes
+ListenStream=/run/issue-3171.socket
+SocketGroup=adm
+SocketMode=0660
+EOF
+
+cat >/run/systemd/system/issue-3171@.service <<EOF
+[Unit]
+Description=Test service
+[Service]
+StandardInput=socket
+ExecStart=sh -x -c cat
+EOF
+
+systemctl start issue-3171.socket
+systemctl is-active issue-3171.socket
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+echo A | nc -w1 -U /run/issue-3171.socket
+
+mv $U ${U}.disabled
+systemctl daemon-reload
+systemctl is-active issue-3171.socket
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+echo B | nc -w1 -U /run/issue-3171.socket && exit 1
+
+mv ${U}.disabled $U
+systemctl daemon-reload
+systemctl is-active issue-3171.socket
+echo C | nc -w1 -U /run/issue-3171.socket && exit 1
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+
+systemctl restart issue-3171.socket
+systemctl is-active issue-3171.socket
+echo D | nc -w1 -U /run/issue-3171.socket
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test changing the main PID
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# The main service PID should be the parent bash process
+MAINPID="${PPID:?}"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Start a test process inside of our own cgroup
+sleep infinity &
+INTERNALPID=$!
+disown
+
+# Start a test process outside of our own cgroup
+systemd-run -p DynamicUser=1 --unit=test-sleep.service /bin/sleep infinity
+EXTERNALPID="$(systemctl show -P MainPID test-sleep.service)"
+
+# Update our own main PID to the external test PID, this should work
+systemd-notify MAINPID="$EXTERNALPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$EXTERNALPID"
+
+# Update our own main PID to the internal test PID, this should work, too
+systemd-notify MAINPID=$INTERNALPID
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$INTERNALPID"
+
+# Update it back to our own PID, this should also work
+systemd-notify MAINPID="$MAINPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Try to set it to PID 1, which it should ignore, because that's the manager
+systemd-notify MAINPID=1
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Try to set it to PID 0, which is invalid and should be ignored
+systemd-notify MAINPID=0
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Try to set it to a valid but non-existing PID, which should be ignored. (Note
+# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
+# which means we can be pretty sure it doesn't exist by coincidence)
+systemd-notify MAINPID=1073741824
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
+systemd-notify --uid=1000 MAINPID="$EXTERNALPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
+systemd-notify --uid=1000 MAINPID="$INTERNALPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$INTERNALPID"
+
+# Update it back to our own PID, this should also work
+systemd-notify --uid=1000 MAINPID="$MAINPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+cat >/tmp/test-mainpid.sh <<\EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+# Create a number of children, and make one the main one
+sleep infinity &
+disown
+
+sleep infinity &
+MAINPID=$!
+disown
+
+sleep infinity &
+disown
+
+echo $MAINPID >/run/mainpidsh/pid
+EOF
+chmod +x /tmp/test-mainpid.sh
+
+systemd-run --unit=test-mainpidsh.service \
+ -p StandardOutput=tty \
+ -p StandardError=tty \
+ -p Type=forking \
+ -p RuntimeDirectory=mainpidsh \
+ -p PIDFile=/run/mainpidsh/pid \
+ /tmp/test-mainpid.sh
+test "$(systemctl show -P MainPID test-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)"
+
+cat >/tmp/test-mainpid2.sh <<\EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+# Create a number of children, and make one the main one
+sleep infinity &
+disown
+
+sleep infinity &
+MAINPID=$!
+disown
+
+sleep infinity &
+disown
+
+echo $MAINPID >/run/mainpidsh2/pid
+chown 1001:1001 /run/mainpidsh2/pid
+EOF
+chmod +x /tmp/test-mainpid2.sh
+
+systemd-run --unit=test-mainpidsh2.service \
+ -p StandardOutput=tty \
+ -p StandardError=tty \
+ -p Type=forking \
+ -p RuntimeDirectory=mainpidsh2 \
+ -p PIDFile=/run/mainpidsh2/pid \
+ /tmp/test-mainpid2.sh
+test "$(systemctl show -P MainPID test-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)"
+
+cat >/dev/shm/test-mainpid3.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+sleep infinity &
+disown
+
+sleep infinity &
+disown
+
+sleep infinity &
+disown
+
+# Let's try to play games, and link up a privileged PID file
+ln -s ../mainpidsh/pid /run/mainpidsh3/pid
+
+# Quick assertion that the link isn't dead
+test -f /run/mainpidsh3/pid
+EOF
+chmod 755 /dev/shm/test-mainpid3.sh
+
+# This has to fail, as we shouldn't accept the dangerous PID file, and then
+# inotify-wait on it to be corrected which we never do.
+(! systemd-run \
+ --unit=test-mainpidsh3.service \
+ -p StandardOutput=tty \
+ -p StandardError=tty \
+ -p Type=forking \
+ -p RuntimeDirectory=mainpidsh3 \
+ -p PIDFile=/run/mainpidsh3/pid \
+ -p DynamicUser=1 \
+ `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
+ -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+ -p TimeoutStartSec=2s \
+ /dev/shm/test-mainpid3.sh)
+
+# Test that this failed due to timeout, and not some other error
+test "$(systemctl show -P Result test-mainpidsh3.service)" = timeout
+
+# Test that scope units work
+systemd-run --scope --unit test-true.scope /bin/true
+test "$(systemctl show -P Result test-true.scope)" = success
+
+# Test that user scope units work as well
+
+systemctl start user@4711.service
+runas testuser systemd-run --scope --user --unit test-true.scope /bin/true
+test "$(systemctl show -P Result test-true.scope)" = success
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Don't send invalid characters over dbus if a mount contains them
+
+at_exit() {
+ mountpoint -q /proc/1/mountinfo && umount /proc/1/mountinfo
+ [[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
+ rm -f /run/systemd/system/foo-*.mount
+ systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+# Check invalid characters directly in /proc/mountinfo
+#
+# This is a bit tricky (and hacky), since we have to temporarily replace
+# PID 1's /proc/mountinfo, but we have to keep the original mounts intact,
+# otherwise systemd would unmount them on reload
+TMP_MOUNTINFO="$(mktemp)"
+
+cp /proc/1/mountinfo "$TMP_MOUNTINFO"
+# Add a mount entry with a "Unicode non-character" in it
+LANG="C.UTF-8" printf '69 1 252:2 / /foo/mountinfo rw,relatime shared:1 - cifs //foo\ufffebar rw,seclabel\n' >>"$TMP_MOUNTINFO"
+mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo
+systemctl daemon-reload
+# On affected versions this would throw an error:
+# Failed to get properties: Bad message
+systemctl status foo-mountinfo.mount
+
+umount /proc/1/mountinfo
+systemctl daemon-reload
+rm -f "$TMP_MOUNTINFO"
+
+# Check invalid characters in a mount unit
+#
+# systemd already handles this and refuses to load the invalid string, e.g.:
+# foo-fstab.mount:9: String is not UTF-8 clean, ignoring assignment: What=//localhost/foo���bar
+#
+# a) Unit generated from /etc/fstab
+[[ -e /etc/fstab ]] && cp -f /etc/fstab /tmp/fstab.bak
+
+LANG="C.UTF-8" printf '//localhost/foo\ufffebar /foo/fstab cifs defaults 0 0\n' >/etc/fstab
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-fstab.mount)" == bad ]]
+
+# b) Unit generated from /etc/fstab (but the invalid character is in options)
+LANG="C.UTF-8" printf '//localhost/foobar /foo/fstab/opt cifs nosuid,a\ufffeb,noexec 0 0\n' >/etc/fstab
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]]
+rm -f /etc/fstab
+
+[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
+systemctl daemon-reload
+
+# c) Mount unit
+mkdir -p /run/systemd/system
+LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foo\ufffebar\nWhere=/foo/unit\nType=cifs\nOptions=noexec\n' >/run/systemd/system/foo-unit.mount
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-unit.mount)" == bad ]]
+rm -f /run/systemd/system/foo-unit.mount
+
+# d) Mount unit (but the invalid character is in Options=)
+mkdir -p /run/systemd/system
+LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foobar\nWhere=/foo/unit/opt\nType=cifs\nOptions=noexec,a\ufffeb,nosuid\n' >/run/systemd/system/foo-unit-opt.mount
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-unit-opt.mount)" == bad ]]
+rm -f /run/systemd/system/foo-unit-opt.mount
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-analyze log-level debug
+
+cat >/run/systemd/system/floodme@.service <<EOF
+[Service]
+ExecStart=true
+EOF
+
+cat >/run/systemd/system/floodme.socket <<EOF
+[Socket]
+ListenStream=/tmp/floodme
+PollLimitIntervalSec=10s
+Accept=yes
+PollLimitBurst=3
+EOF
+
+systemctl daemon-reload
+systemctl start floodme.socket
+
+START=$(date +%s%N)
+
+# Trigger this 100 times in a flood
+for _ in {1..100}; do
+ logger -u /tmp/floodme foo &
+done
+
+# Let some time pass
+sleep 5
+
+END=$(date +%s%N)
+
+PASSED=$((END-START))
+
+# Calculate (round up) how many trigger events could have happened in the passed time
+MAXCOUNT=$(((PASSED+10000000000)*3/10000000000))
+
+# We started 100 connection attempts, but only 3 should have gone through, as per limit
+test "$(systemctl show -P NAccepted floodme.socket)" -le "$MAXCOUNT"
+
+systemctl stop floodme.socket floodme@*.service
+
+rm /run/systemd/system/floodme@.service /run/systemd/system/floodme.socket /tmp/floodme
+
+systemctl daemon-reload
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+cat >/run/systemd/system/nonexistent-execstart-exit-status.service <<EOF
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=-/foo/bar/not-exist
+EOF
+
+systemctl start nonexistent-execstart-exit-status.service
+systemctl is-active nonexistent-execstart-exit-status.service
+assert_eq "$(systemctl show nonexistent-execstart-exit-status.service -P Result)" "success"
+(( $(systemctl show nonexistent-execstart-exit-status.service -P ExecMainStatus) > 0 ))
+
+systemctl stop nonexistent-execstart-exit-status.service
+rm /run/systemd/system/nonexistent-execstart-exit-status.service
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# For issue https://github.com/systemd/systemd/issues/29526
+systemd-run -p PrivateNetwork=yes --wait /bin/true
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-07-PID1
+
+[Service]
+Type=oneshot
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+NotifyAccess=all
+# Issue: https://github.com/systemd/systemd/issues/2691
+ExecStop=sh -c 'kill -SEGV $$$$'
+RemainAfterExit=yes
+TimeoutStopSec=270s
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# Issue: https://github.com/systemd/systemd/issues/2730
+# See TEST-07-PID1/test.sh for the first "half" of the test
+mountpoint /issue2730
+
+run_subtests
+
+touch /testok
+systemctl --no-block exit 123
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test PassFileDescriptorsToExec= option in socket units
+
+for u in pass-fds-to-exec-{no,yes}.socket; do
+ systemctl start "$u"
+ systemctl stop "$u"
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Make sure that we never mistake a process starting but failing quickly for a process failing to start, with Type=exec.
+# See https://github.com/systemd/systemd/pull/30799
+
+seq 25 | xargs -n 1 -P 0 systemd-run -p Type=exec /bin/false
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-08-INITRD
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt -qc; then
+ echo >&2 "This test can't run in a container"
+ exit 1
+fi
+
+# This test requires systemd to run in the initrd as well, which is not the case
+# for mkinitrd-based initrd (Ubuntu/Debian)
+if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
+ echo "systemd didn't run in the initrd, skipping the test"
+ touch /skipped
+ exit 77
+fi
+
+# We should've created a mount under /run in initrd (see the other half of the test)
+# that should've survived the transition from initrd to the real system
+test -d /run/initrd-mount-target
+mountpoint /run/initrd-mount-target
+[[ -e /run/initrd-mount-target/hello-world ]]
+
+# Copy the prepared shutdown initrd to its intended location. Check the respective
+# test.sh file for details
+mkdir -p /run/initramfs
+cp -r /shutdown-initrd/* /run/initramfs/
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+get_first_boot_id() {
+ journalctl -b "${1:?}" -o json -n +1 | jq -r '._BOOT_ID'
+}
+
+get_last_boot_id() {
+ journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID'
+}
+
+get_first_timestamp() {
+ journalctl -b "${1:?}" -o json -n +1 | jq -r '.__REALTIME_TIMESTAMP'
+}
+
+get_last_timestamp() {
+ journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP'
+}
+
+# Issue: #29275, second part
+# Now let's check if the boot entries are in the correct/expected order
+index=0
+SYSTEMD_LOG_LEVEL=debug journalctl --list-boots
+journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' |
+ while read -r offset boot_id first_ts last_ts; do
+ : "Boot #$((++index)) ($offset) with ID $boot_id"
+
+ # Try the "regular" (non-json) variants first, as they provide a helpful
+ # error message if something is not right
+ SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index"
+ SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset"
+ SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id"
+
+ # Check the boot ID of the first entry
+ entry_boot_id="$(get_first_boot_id "$index")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_first_boot_id "$offset")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_first_boot_id "$boot_id")"
+ assert_eq "$entry_boot_id" "$boot_id"
+
+ # Check the timestamp of the first entry
+ entry_ts="$(get_first_timestamp "$index")"
+ assert_eq "$entry_ts" "$first_ts"
+ entry_ts="$(get_first_timestamp "$offset")"
+ assert_eq "$entry_ts" "$first_ts"
+ entry_ts="$(get_first_timestamp "$boot_id")"
+ assert_eq "$entry_ts" "$first_ts"
+
+ # Check the boot ID of the last entry
+ entry_boot_id="$(get_last_boot_id "$index")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_last_boot_id "$offset")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_last_boot_id "$boot_id")"
+ assert_eq "$entry_boot_id" "$boot_id"
+
+ # Check the timestamp of the last entry
+ if [[ "$offset" != "0" ]]; then
+ entry_ts="$(get_last_timestamp "$index")"
+ assert_eq "$entry_ts" "$last_ts"
+ entry_ts="$(get_last_timestamp "$offset")"
+ assert_eq "$entry_ts" "$last_ts"
+ entry_ts="$(get_last_timestamp "$boot_id")"
+ assert_eq "$entry_ts" "$last_ts"
+ fi
+ done
+
+verify_seqnum() {
+ if [[ "$REBOOT_COUNT" -ne "$NUM_REBOOT" ]]; then
+ return 0
+ fi
+
+ journalctl --flush
+ journalctl --sync
+
+ ls -lR /var/log/journal/
+ ls -lR /run/log/journal/
+
+ journalctl --system --header
+
+ (! journalctl --system -q -o short-monotonic -u systemd-journald.service --grep 'Journal file uses a different sequence number ID, rotating')
+
+ set +x
+ previous_seqnum=0
+ previous_seqnum_id=
+ previous_boot_id=
+ journalctl --system -q -o json | jq -r '[.__SEQNUM, .__SEQNUM_ID, ._BOOT_ID] | @tsv' |
+ while read -r seqnum seqnum_id boot_id; do
+
+ if [[ -n "$previous_seqnum_id" ]]; then
+ if ! test "$seqnum" -gt "$previous_seqnum"; then
+ echo "seqnum=$seqnum is not greater than previous_seqnum=$previous_seqnum"
+ echo "seqnum_id=$seqnum_id, previous_seqnum_id=$previous_seqnum_id"
+ echo "boot_id=$boot_id, previous_boot_id=$previous_boot_id"
+ return 1
+ fi
+
+ assert_eq "$seqnum_id" "$previous_seqnum_id"
+ fi
+
+ previous_seqnum="$seqnum"
+ previous_seqnum_id="$seqnum_id"
+ previous_boot_id="$boot_id"
+ done
+ set -x
+
+ return 0
+}
+
+verify_seqnum
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-09-REBOOT
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export NUM_REBOOT=4
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-cat echo "Reboot count: $REBOOT_COUNT"
+systemd-cat journalctl --list-boots
+
+run_subtests
+
+if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then
+ systemctl_final reboot
+elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then
+ assert_not_reached
+fi
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export PAGER=
+
+at_exit() {
+ set +e
+ umount -l -R /var/lib/confexts
+ rm -f /var/tmp/importtest /var/tmp/importtest2 /var/tmp/importtest.tar.gz /var/tmp/importtest2.tar.gz
+}
+
+trap at_exit EXIT
+
+systemctl service-log-level systemd-importd debug
+
+# Mount tmpfs over /var/lib/confexts to not pollute the image
+mkdir -p /var/lib/confexts
+mount -t tmpfs tmpfs /var/lib/confexts -o mode=755
+
+importctl
+importctl --no-pager --help
+importctl --version
+importctl list-transfers
+importctl list-transfers --no-legend --no-ask-password
+importctl list-transfers -j
+importctl list-images
+importctl list-images --no-legend --no-ask-password
+importctl list-images -j
+
+(! importctl cancel-transfer 4711)
+
+dd if=/dev/urandom of=/var/tmp/importtest bs=4096 count=10
+
+importctl import-raw --class=confext /var/tmp/importtest
+cmp /var/tmp/importtest /var/lib/confexts/importtest.raw
+importctl export-raw --class=confext importtest /var/tmp/importtest2
+cmp /var/tmp/importtest /var/tmp/importtest2
+
+(! importctl pull-raw --class=confext file:///var/tmp/importtest)
+importctl pull-raw --verify=no --class=confext file:///var/tmp/importtest importtest3
+cmp /var/tmp/importtest /var/lib/confexts/importtest3.raw
+
+tar czf /var/tmp/importtest.tar.gz -C /var/tmp importtest
+
+importctl import-tar --class=confext /var/tmp/importtest.tar.gz importtest4
+cmp /var/tmp/importtest /var/lib/confexts/importtest4/importtest
+
+importctl export-tar --class=confext importtest4 /var/tmp/importtest2.tar.gz
+importctl import-tar --class=confext /var/tmp/importtest2.tar.gz importtest5
+cmp /var/tmp/importtest /var/lib/confexts/importtest5/importtest
+
+importctl import-fs --class=confext /var/lib/confexts/importtest5 importtest6
+cmp /var/tmp/importtest /var/lib/confexts/importtest6/importtest
+
+(! importctl pull-tar --class=confext file:///var/tmp/importtest.tar.gz importtest7)
+importctl pull-tar --class=confext --verify=no file:///var/tmp/importtest.tar.gz importtest7
+cmp /var/tmp/importtest /var/lib/confexts/importtest7/importtest
+
+importctl list-images
+importctl list-images -j
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export PAGER=
+
+at_exit() {
+ set +e
+
+ machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running
+ mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
+ [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT"
+ rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+systemctl service-log-level systemd-machined debug
+systemctl service-log-level systemd-importd debug
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+# Create a couple of containers we can refer to in tests
+for i in {0..4}; do
+ create_dummy_container "/var/lib/machines/container$i"
+ machinectl start "container$i"
+done
+# Create one "long running" container with some basic signal handling
+create_dummy_container /var/lib/machines/long-running
+cat >/var/lib/machines/long-running/sbin/init <<\EOF
+#!/usr/bin/bash -x
+
+PID=0
+
+trap "touch /poweroff" RTMIN+4
+trap "touch /reboot" INT
+trap "touch /trap" TRAP
+trap 'kill $PID' EXIT
+
+# We need to wait for the sleep process asynchronously in order to allow
+# bash to process signals
+sleep infinity &
+PID=$!
+while :; do
+ wait || :
+done
+EOF
+machinectl start long-running
+
+machinectl
+machinectl --no-pager --help
+machinectl --version
+machinectl list
+machinectl list --no-legend --no-ask-password
+
+machinectl status long-running long-running long-running
+machinectl status --full long-running
+machinectl status --quiet --lines=1 long-running
+machinectl status --lines=0 --max-addresses=0 long-running
+machinectl status --machine=testuser@.host long-running
+machinectl status --output=help long-running
+while read -r output; do
+ machinectl status --output="$output" long-running
+done < <(machinectl --output=help)
+
+machinectl show
+machinectl show --all
+machinectl show --all --machine=root@
+machinectl show --all --machine=testuser@
+[[ "$(machinectl show --property=PoolPath --value)" == "/var/lib/machines" ]]
+machinectl show long-running
+machinectl show long-running long-running long-running --all
+[[ "$(machinectl show --property=RootDirectory --value long-running)" == "/var/lib/machines/long-running" ]]
+
+machinectl enable long-running
+test -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
+machinectl enable long-running long-running long-running container1
+machinectl disable long-running
+test ! -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
+machinectl disable long-running long-running long-running container1
+
+[[ "$(machinectl shell testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "" ]]
+[[ "$(machinectl shell --setenv=FOO=bar testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "bar" ]]
+
+[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
+# Equivalent to machinectl kill --signal=SIGRTMIN+4 --kill-whom=leader
+rm -f /var/lib/machines/long-running/poweroff
+machinectl poweroff long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sleep .5; done"
+# Equivalent to machinectl kill --signal=SIGINT --kill-whom=leader
+rm -f /var/lib/machines/long-running/reboot
+machinectl reboot long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done"
+# Skip machinectl terminate for now, as it doesn't play well with our "init"
+rm -f /var/lib/machines/long-running/trap
+machinectl kill --signal=SIGTRAP --kill-whom=leader long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done"
+# Multiple machines at once
+machinectl poweroff long-running long-running long-running
+machinectl reboot long-running long-running long-running
+machinectl kill --signal=SIGTRAP --kill-whom=leader long-running long-running long-running
+# All used signals should've been caught by a handler
+[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
+
+cp /etc/machine-id /tmp/foo
+machinectl copy-to long-running /tmp/foo /root/foo
+test -f /var/lib/machines/long-running/root/foo
+machinectl copy-from long-running /root/foo /tmp/bar
+diff /tmp/foo /tmp/bar
+rm -f /tmp/{foo,bar}
+
+# machinectl bind is covered by testcase_check_machinectl_bind() in nspawn tests
+
+machinectl list-images
+machinectl list-images --no-legend
+machinectl image-status
+machinectl image-status container1
+machinectl image-status container1 container1 container{0..4}
+machinectl show-image
+machinectl show-image container1
+machinectl show-image container1 container1 container{0..4}
+
+machinectl clone container1 clone1
+machinectl show-image clone1
+machinectl rename clone1 clone2
+(! machinectl show-image clone1)
+machinectl show-image clone2
+if lsattr -d /var/lib/machines >/dev/null; then
+ [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
+ machinectl read-only clone2 yes
+ [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]]
+ machinectl read-only clone2 no
+ [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
+fi
+machinectl remove clone2
+for i in {0..4}; do
+ machinectl clone container1 "clone$i"
+done
+machinectl remove clone{0..4}
+for i in {0..4}; do
+ machinectl clone container1 ".hidden$i"
+done
+machinectl list-images --all
+test -d /var/lib/machines/.hidden1
+machinectl clean
+test ! -d /var/lib/machines/.hidden1
+
+# Prepare a simple raw container
+mkdir -p /tmp/mnt
+dd if=/dev/zero of=/var/tmp/container.raw bs=1M count=256
+mkfs.ext4 /var/tmp/container.raw
+mount -o loop /var/tmp/container.raw /tmp/mnt
+cp -r /var/lib/machines/container1/* /tmp/mnt
+umount /tmp/mnt
+# Try to import it, run it, export it, and re-import it
+machinectl import-raw /var/tmp/container.raw container-raw
+[[ "$(machinectl show-image --property=Type --value container-raw)" == "raw" ]]
+machinectl start container-raw
+machinectl export-raw container-raw /var/tmp/container-export.raw
+machinectl import-raw /var/tmp/container-export.raw container-raw-reimport
+[[ "$(machinectl show-image --property=Type --value container-raw-reimport)" == "raw" ]]
+rm -f /var/tmp/container{,-export}.raw
+
+# Prepare a simple tar.gz container
+tar -pczf /var/tmp/container.tar.gz -C /var/lib/machines/container1 .
+# Try to import it, run it, export it, and re-import it
+machinectl import-tar /var/tmp/container.tar.gz container-tar
+[[ "$(machinectl show-image --property=Type --value container-tar)" =~ directory|subvolume ]]
+machinectl start container-tar
+machinectl export-tar container-tar /var/tmp/container-export.tar.gz
+machinectl import-tar /var/tmp/container-export.tar.gz container-tar-reimport
+[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" =~ directory|subvolume ]]
+rm -f /var/tmp/container{,-export}.tar.gz
+
+# Try to import a container directory & run it
+cp -r /var/lib/machines/container1 /var/tmp/container.dir
+machinectl import-fs /var/tmp/container.dir container-dir
+[[ "$(machinectl show-image --property=Type --value container-dir)" =~ directory|subvolume ]]
+machinectl start container-dir
+rm -fr /var/tmp/container.dir
+
+timeout 10 bash -c "until machinectl clean --all; do sleep .5; done"
+
+NSPAWN_FRAGMENT="machinectl-test-$RANDOM.nspawn"
+cat >"/var/lib/machines/$NSPAWN_FRAGMENT" <<EOF
+[Exec]
+Boot=true
+EOF
+machinectl cat "$NSPAWN_FRAGMENT"
+EDITOR=true script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
+test -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
+diff "/var/lib/machines/$NSPAWN_FRAGMENT" "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
+
+cat >/tmp/fragment.nspawn <<EOF
+[Exec]
+Boot=false
+EOF
+machinectl cat /tmp/fragment.nspawn
+EDITOR="cp /tmp/fragment.nspawn" script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
+diff /tmp/fragment.nspawn "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
+
+for opt in format lines machine max-addresses output setenv verify; do
+ (! machinectl status "--$opt=" long-running)
+ (! machinectl status "--$opt=-1" long-running)
+ (! machinectl status "--$opt=''" long-running)
+done
+(! machinectl show "")
+(! machinectl enable)
+(! machinectl enable "")
+(! machinectl disable)
+(! machinectl disable "")
+(! machinectl read-only container1 "")
+(! machinectl read-only container1 foo)
+(! machinectl read-only container1 -- -1)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+export SYSTEMD_LOG_TARGET=journal
+
+# shellcheck disable=SC2317
+at_exit() {
+ set +e
+
+ mountpoint -q /var/lib/machines && umount /var/lib/machines
+ [[ -n "${DEV:-}" ]] && rm -f "$DEV"
+ [[ -n "${NETNS:-}" ]] && umount "$NETNS" && rm -f "$NETNS"
+ [[ -n "${TMPDIR:-}" ]] && rm -fr "$TMPDIR"
+ rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+# Setup a couple of dirs/devices for the OCI containers
+DEV="$(mktemp -u /dev/oci-dev-XXX)"
+mknod -m 666 "$DEV" b 42 42
+NETNS="$(mktemp /var/tmp/netns.XXX)"
+mount --bind /proc/self/ns/net "$NETNS"
+TMPDIR="$(mktemp -d)"
+touch "$TMPDIR/hello"
+OCI="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.oci-bundle.XXX)"
+create_dummy_container "$OCI/rootfs"
+mkdir -p "$OCI/rootfs/opt/var"
+mkdir -p "$OCI/rootfs/opt/readonly"
+
+# Let's start with a simple config
+cat >"$OCI/config.json" <<EOF
+{
+ "ociVersion" : "1.0.0",
+ "root" : {
+ "path" : "rootfs"
+ },
+ "mounts" : [
+ {
+ "destination" : "/root",
+ "type" : "tmpfs",
+ "source" : "tmpfs"
+ }
+ ]
+}
+EOF
+systemd-nspawn --oci-bundle="$OCI" bash -xec 'mountpoint /root'
+
+# And now for something a bit more involved
+# Notes:
+# - the hooks are parsed & processed, but never executed
+# - set sysctl's are parsed but never used?
+# - same goes for arg_sysctl in nspawn.c
+cat >"$OCI/config.json" <<EOF
+{
+ "ociVersion" : "1.0.0",
+ "hostname" : "my-oci-container",
+ "root" : {
+ "path" : "rootfs",
+ "readonly" : false
+ },
+ "mounts" : [
+ {
+ "destination" : "/root",
+ "type" : "tmpfs",
+ "source" : "tmpfs"
+ },
+ ${COVERAGE_BUILD_DIR:+"{ \"destination\" : \"$COVERAGE_BUILD_DIR\" },"}
+ {
+ "destination" : "/var",
+ "type" : "none",
+ "source" : "$TMPDIR",
+ "options" : ["rbind", "rw"]
+ }
+ ],
+ "process" : {
+ "terminal" : false,
+ "consoleSize" : {
+ "height" : 25,
+ "width" : 80
+ },
+ "user" : {
+ "uid" : 0,
+ "gid" : 0,
+ "additionalGids" : [5, 6]
+ },
+ "env" : [
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "FOO=bar"
+ ],
+ "cwd" : "/root",
+ "args" : [
+ "bash",
+ "-xe",
+ "/entrypoint.sh"
+ ],
+ "noNewPrivileges" : true,
+ "oomScoreAdj" : 20,
+ "capabilities" : {
+ "bounding" : [
+ "CAP_AUDIT_WRITE",
+ "CAP_KILL",
+ "CAP_NET_BIND_SERVICE"
+ ],
+ "permitted" : [
+ "CAP_AUDIT_WRITE",
+ "CAP_KILL",
+ "CAP_NET_BIND_SERVICE"
+ ],
+ "inheritable" : [
+ "CAP_AUDIT_WRITE",
+ "CAP_KILL",
+ "CAP_NET_BIND_SERVICE"
+ ],
+ "effective" : [
+ "CAP_AUDIT_WRITE",
+ "CAP_KILL"
+ ],
+ "ambient" : [
+ "CAP_NET_BIND_SERVICE"
+ ]
+ },
+ "rlimits" : [
+ {
+ "type" : "RLIMIT_NOFILE",
+ "soft" : 1024,
+ "hard" : 1024
+ },
+ {
+ "type" : "RLIMIT_RTPRIO",
+ "soft" : 5,
+ "hard" : 10
+ }
+ ]
+ },
+ "linux" : {
+ "namespaces" : [
+ {
+ "type" : "mount"
+ },
+ {
+ "type" : "network",
+ "path" : "$NETNS"
+ },
+ {
+ "type" : "pid"
+ },
+ {
+ "type" : "uts"
+ }
+ ],
+ "uidMappings" : [
+ {
+ "containerID" : 0,
+ "hostID" : 1000,
+ "size" : 100
+ }
+ ],
+ "gidMappings" : [
+ {
+ "containerID" : 0,
+ "hostID" : 1000,
+ "size" : 100
+ }
+ ],
+ "devices" : [
+ {
+ "type" : "c",
+ "path" : "/dev/zero",
+ "major" : 1,
+ "minor" : 5,
+ "fileMode" : 444
+ },
+ {
+ "type" : "b",
+ "path" : "$DEV",
+ "major" : 4,
+ "minor" : 2,
+ "fileMode" : 666,
+ "uid" : 0,
+ "gid" : 0
+ }
+ ],
+ "resources" : {
+ "devices" : [
+ {
+ "allow" : false,
+ "access" : "m"
+ },
+ {
+ "allow" : true,
+ "type" : "b",
+ "major" : 4,
+ "minor" : 2,
+ "access" : "rwm"
+ }
+ ],
+ "memory" : {
+ "limit" : 134217728,
+ "reservation" : 33554432,
+ "swap" : 268435456
+ },
+ "cpu" : {
+ "shares" : 1024,
+ "quota" : 1000000,
+ "period" : 500000,
+ "cpus" : "0-7"
+ },
+ "blockIO" : {
+ "weight" : 10,
+ "weightDevice" : [
+ {
+ "major" : 4,
+ "minor" : 2,
+ "weight" : 500
+ }
+ ],
+ "throttleReadBpsDevice" : [
+ {
+ "major" : 4,
+ "minor" : 2,
+ "rate" : 500
+ }
+ ],
+ "throttleWriteBpsDevice" : [
+ {
+ "major" : 4,
+ "minor" : 2,
+ "rate" : 500
+ }
+ ],
+ "throttleReadIOPSDevice" : [
+ {
+ "major" : 4,
+ "minor" : 2,
+ "rate" : 500
+ }
+ ],
+ "throttleWriteIOPSDevice" : [
+ {
+ "major" : 4,
+ "minor" : 2,
+ "rate" : 500
+ }
+ ]
+ },
+ "pids" : {
+ "limit" : 1024
+ }
+ },
+ "sysctl" : {
+ "kernel.domainname" : "foo.bar",
+ "vm.swappiness" : "60"
+ },
+ "seccomp" : {
+ "defaultAction" : "SCMP_ACT_ALLOW",
+ "architectures" : [
+ "SCMP_ARCH_ARM",
+ "SCMP_ARCH_X86_64"
+ ],
+ "syscalls" : [
+ {
+ "names" : [
+ "lchown",
+ "chmod"
+ ],
+ "action" : "SCMP_ACT_ERRNO",
+ "args" : [
+ {
+ "index" : 0,
+ "value" : 1,
+ "op" : "SCMP_CMP_NE"
+ },
+ {
+ "index" : 1,
+ "value" : 2,
+ "valueTwo" : 3,
+ "op" : "SCMP_CMP_MASKED_EQ"
+ }
+ ]
+ }
+ ]
+ },
+ "rootfsPropagation" : "shared",
+ "maskedPaths" : [
+ "/proc/kcore",
+ "/root/nonexistent"
+ ],
+ "readonlyPaths" : [
+ "/proc/sys",
+ "/opt/readonly"
+ ]
+ },
+ "hooks" : {
+ "prestart" : [
+ {
+ "path" : "/bin/sh",
+ "args" : [
+ "-xec",
+ "echo \$PRESTART_FOO >/prestart"
+ ],
+ "env" : [
+ "PRESTART_FOO=prestart_bar",
+ "ALSO_FOO=also_bar"
+ ],
+ "timeout" : 666
+ },
+ {
+ "path" : "/bin/touch",
+ "args" : [
+ "/tmp/also-prestart"
+ ]
+ }
+ ],
+ "poststart" : [
+ {
+ "path" : "/bin/sh",
+ "args" : [
+ "touch",
+ "/poststart"
+ ]
+ }
+ ],
+ "poststop" : [
+ {
+ "path" : "/bin/sh",
+ "args" : [
+ "touch",
+ "/poststop"
+ ]
+ }
+ ]
+ },
+ "annotations" : {
+ "hello.world" : "1",
+ "foo" : "bar"
+ }
+}
+EOF
+# Create a simple "entrypoint" script that validates that the container
+# is created correctly according to the OCI config
+cat >"$OCI/rootfs/entrypoint.sh" <<EOF
+#!/usr/bin/bash -e
+
+# Mounts
+mountpoint /root
+mountpoint /var
+test -e /var/hello
+
+# Process
+[[ "\$PWD" == /root ]]
+[[ "\$FOO" == bar ]]
+
+# Process - rlimits
+[[ "\$(ulimit -S -n)" -eq 1024 ]]
+[[ "\$(ulimit -H -n)" -eq 1024 ]]
+[[ "\$(ulimit -S -r)" -eq 5 ]]
+[[ "\$(ulimit -H -r)" -eq 10 ]]
+[[ "\$(hostname)" == my-oci-container ]]
+
+# Linux - devices
+test -c /dev/zero
+test -b "$DEV"
+[[ "\$(stat -c '%t:%T' "$DEV")" == 4:2 ]]
+
+# Linux - maskedPaths
+test -e /proc/kcore
+cat /proc/kcore && exit 1
+test ! -e /root/nonexistent
+
+# Linux - readonlyPaths
+touch /opt/readonly/foo && exit 1
+
+exit 0
+EOF
+timeout 30 systemd-nspawn --oci-bundle="$OCI"
+
+# Test a couple of invalid configs
+INVALID_SNIPPETS=(
+ # Invalid object
+ '"foo" : { }'
+ '"process" : { "foo" : [ ] }'
+ # Non-absolute mount
+ '"mounts" : [ { "destination" : "foo", "type" : "tmpfs", "source" : "tmpfs" } ]'
+ # Invalid rlimit
+ '"process" : { "rlimits" : [ { "type" : "RLIMIT_FOO", "soft" : 0, "hard" : 0 } ] }'
+ # rlimit without RLIMIT_ prefix
+ '"process" : { "rlimits" : [ { "type" : "CORE", "soft" : 0, "hard" : 0 } ] }'
+ # Invalid env assignment
+ '"process" : { "env" : [ "foo" ] }'
+ '"process" : { "env" : [ "foo=bar", 1 ] }'
+ # Invalid process args
+ '"process" : { "args" : [ ] }'
+ '"process" : { "args" : [ "" ] }'
+ '"process" : { "args" : [ "foo", 1 ] }'
+ # Invalid capabilities
+ '"process" : { "capabilities" : { "bounding" : [ 1 ] } }'
+ '"process" : { "capabilities" : { "bounding" : [ "FOO_BAR" ] } }'
+ # Unsupported option (without JSON_PERMISSIVE)
+ '"linux" : { "resources" : { "cpu" : { "realtimeRuntime" : 1 } } }'
+ # Invalid namespace
+ '"linux" : { "namespaces" : [ { "type" : "foo" } ] }'
+ # Namespace path for a non-network namespace
+ '"linux" : { "namespaces" : [ { "type" : "user", "path" : "/foo/bar" } ] }'
+ # Duplicate namespace
+ '"linux" : { "namespaces" : [ { "type" : "ipc" }, { "type" : "ipc" } ] }'
+ # Invalid device type
+ '"linux" : { "devices" : [ { "type" : "foo", "path" : "/dev/foo" } ] }'
+ # Invalid cgroups path
+ '"linux" : { "cgroupsPath" : "/foo/bar/baz" }'
+ '"linux" : { "cgroupsPath" : "foo/bar/baz" }'
+ # Invalid sysctl assignments
+ '"linux" : { "sysctl" : { "vm.swappiness" : 60 } }'
+ '"linux" : { "sysctl" : { "foo..bar" : "baz" } }'
+ # Invalid seccomp assignments
+ '"linux" : { "seccomp" : { } }'
+ '"linux" : { "seccomp" : { "defaultAction" : 1 } }'
+ '"linux" : { "seccomp" : { "defaultAction" : "foo" } }'
+ '"linux" : { "seccomp" : { "defaultAction" : "SCMP_ACT_ALLOW", "syscalls" : [ { "action" : "SCMP_ACT_ERRNO", "names" : [ ] } ] } }'
+ # Invalid masked paths
+ '"linux" : { "maskedPaths" : [ "/foo", 1 ] }'
+ '"linux" : { "maskedPaths" : [ "/foo", "bar" ] }'
+ # Invalid read-only paths
+ '"linux" : { "readonlyPaths" : [ "/foo", 1 ] }'
+ '"linux" : { "readonlyPaths" : [ "/foo", "bar" ] }'
+ # Invalid hooks
+ '"hooks" : { "prestart" : [ { "path" : "/bin/sh", "timeout" : 0 } ] }'
+ # Invalid annotations
+ '"annotations" : { "" : "bar" }'
+ '"annotations" : { "foo" : 1 }'
+)
+
+for snippet in "${INVALID_SNIPPETS[@]}"; do
+ : "Snippet: $snippet"
+ cat >"$OCI/config.json" <<EOF
+{
+ "ociVersion" : "1.0.0",
+ "root" : {
+ "path" : "rootfs"
+ },
+ $snippet
+}
+EOF
+ (! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
+done
+
+# Invalid OCI bundle version
+cat >"$OCI/config.json" <<EOF
+{
+ "ociVersion" : "6.6.6",
+ "root" : {
+ "path" : "rootfs"
+ }
+}
+EOF
+(! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+#
+# Notes on coverage: when collecting coverage we need the $BUILD_DIR present
+# and writable in the container as well. To do this in the least intrusive way,
+# two things are going on in the background (only when built with -Db_coverage=true):
+# 1) the systemd-nspawn@.service is copied to /etc/systemd/system/ with
+# --bind=$BUILD_DIR appended to the ExecStart= line
+# 2) each create_dummy_container() call also creates an .nspawn file in /run/systemd/nspawn/
+# with the last fragment from the path used as a name
+#
+# The first change is quite self-contained and applies only to containers run
+# with machinectl. The second one might cause some unexpected side-effects, namely:
+# - nspawn config (setting) files don't support dropins, so tests that test
+# the config files might need some tweaking (as seen below with
+# the $COVERAGE_BUILD_DIR shenanigans) since they overwrite the .nspawn file
+# - also a note - if /etc/systemd/nspawn/cont-name.nspawn exists, it takes
+# precedence and /run/systemd/nspawn/cont-name.nspawn won't be read even
+# if it exists
+# - also a note 2 - --bind= overrides any Bind= from a config file
+# - in some cases we don't create a test container using create_dummy_container(),
+# so in that case an explicit call to coverage_create_nspawn_dropin() is needed
+#
+# However, even after jumping through all these hooks, there still might (and is)
+# some "incorrectly" missing coverage, especially in the window between spawning
+# the inner child process and bind-mounting the coverage $BUILD_DIR
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+
+export SYSTEMD_LOG_LEVEL=debug
+export SYSTEMD_LOG_TARGET=journal
+
+at_exit() {
+ set +e
+
+ mountpoint -q /var/lib/machines && umount --recursive /var/lib/machines
+ rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+# check cgroup-v2
+IS_CGROUPSV2_SUPPORTED=no
+mkdir -p /tmp/cgroup2
+if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
+ IS_CGROUPSV2_SUPPORTED=yes
+ umount /tmp/cgroup2
+fi
+rmdir /tmp/cgroup2
+
+# check cgroup namespaces
+IS_CGNS_SUPPORTED=no
+if [[ -f /proc/1/ns/cgroup ]]; then
+ IS_CGNS_SUPPORTED=yes
+fi
+
+IS_USERNS_SUPPORTED=no
+# On some systems (e.g. CentOS 7) the default limit for user namespaces
+# is set to 0, which causes the following unshare syscall to fail, even
+# with enabled user namespaces support. By setting this value explicitly
+# we can ensure the user namespaces support to be detected correctly.
+sysctl -w user.max_user_namespaces=10000
+if unshare -U bash -c :; then
+ IS_USERNS_SUPPORTED=yes
+fi
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+testcase_sanity() {
+ local template root image uuid tmpdir
+
+ tmpdir="$(mktemp -d)"
+ template="$(mktemp -d /tmp/nspawn-template.XXX)"
+ create_dummy_container "$template"
+ # Create a simple image from the just created container template
+ image="$(mktemp /var/lib/machines/TEST-13-NSPAWN.image-XXX.img)"
+ dd if=/dev/zero of="$image" bs=1M count=256
+ mkfs.ext4 "$image"
+ mkdir -p /mnt
+ mount -o loop "$image" /mnt
+ cp -r "$template"/* /mnt/
+ umount /mnt
+
+ systemd-nspawn --help --no-pager
+ systemd-nspawn --version
+
+ # --template=
+ root="$(mktemp -u -d /var/lib/machines/TEST-13-NSPAWN.sanity.XXX)"
+ coverage_create_nspawn_dropin "$root"
+ (! systemd-nspawn --directory="$root" bash -xec 'echo hello')
+ # Initialize $root from $template (the $root directory must not exist, hence
+ # the `mktemp -u` above)
+ systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
+ systemd-nspawn --directory="$root" bash -xec 'echo hello; touch /initialized'
+ test -e "$root/initialized"
+ # Check if the $root doesn't get re-initialized once it's not empty
+ systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
+ test -e "$root/initialized"
+
+ systemd-nspawn --directory="$root" --ephemeral bash -xec 'touch /ephemeral'
+ test ! -e "$root/ephemeral"
+ (! systemd-nspawn --directory="$root" \
+ --read-only \
+ bash -xec 'touch /nope')
+ test ! -e "$root/nope"
+ systemd-nspawn --image="$image" bash -xec 'echo hello'
+
+ # --volatile=
+ touch "$root/usr/has-usr"
+ # volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
+ systemd-nspawn --directory="$root"\
+ --volatile \
+ bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
+ test ! -e "$root/nope"
+ test ! -e "$root/usr/read-only"
+ systemd-nspawn --directory="$root"\
+ --volatile=yes \
+ bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
+ test ! -e "$root/nope"
+ test ! -e "$root/usr/read-only"
+ # volatile=state: rootfs is read-only, /var/ is tmpfs
+ systemd-nspawn --directory="$root" \
+ --volatile=state \
+ bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
+ test ! -e "$root/read-only"
+ test ! -e "$root/var/nope"
+ # volatile=state: tmpfs overlay is mounted over rootfs
+ systemd-nspawn --directory="$root" \
+ --volatile=overlay \
+ bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
+ test ! -e "$root/nope"
+ test ! -e "$root/var/also-nope"
+ test ! -e "$root/usr/nope-too"
+
+ # --machine=, --hostname=
+ systemd-nspawn --directory="$root" \
+ --machine="foo-bar.baz" \
+ bash -xec '[[ $(hostname) == foo-bar.baz ]]'
+ systemd-nspawn --directory="$root" \
+ --hostname="hello.world.tld" \
+ bash -xec '[[ $(hostname) == hello.world.tld ]]'
+ systemd-nspawn --directory="$root" \
+ --machine="foo-bar.baz" \
+ --hostname="hello.world.tld" \
+ bash -xec '[[ $(hostname) == hello.world.tld ]]'
+
+ # --uuid=
+ rm -f "$root/etc/machine-id"
+ uuid="deadbeef-dead-dead-beef-000000000000"
+ systemd-nspawn --directory="$root" \
+ --uuid="$uuid" \
+ bash -xec "[[ \$container_uuid == $uuid ]]"
+
+ # --as-pid2
+ systemd-nspawn --directory="$root" bash -xec '[[ $$ -eq 1 ]]'
+ systemd-nspawn --directory="$root" --as-pid2 bash -xec '[[ $$ -eq 2 ]]'
+
+ # --user=
+ # "Fake" getent passwd's bare minimum, so we don't have to pull it in
+ # with all the DSO shenanigans
+ cat >"$root/bin/getent" <<\EOF
+#!/bin/bash
+
+if [[ $# -eq 0 ]]; then
+ :
+elif [[ $1 == passwd ]]; then
+ echo "testuser:x:1000:1000:testuser:/:/bin/sh"
+elif [[ $1 == initgroups ]]; then
+ echo "testuser"
+fi
+EOF
+ chmod +x "$root/bin/getent"
+ systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]'
+ systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]'
+
+ # --settings= + .nspawn files
+ mkdir -p /run/systemd/nspawn/
+ uuid="deadbeef-dead-dead-beef-000000000000"
+ echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn
+ systemd-nspawn --directory="$root" \
+ --machine=foo-bar \
+ --settings=yes \
+ bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
+ systemd-nspawn --directory="$root" \
+ --machine=foo-bar \
+ --uuid="$uuid" \
+ --settings=yes \
+ bash -xec "[[ \$container_uuid == $uuid ]]"
+ systemd-nspawn --directory="$root" \
+ --machine=foo-bar \
+ --uuid="$uuid" \
+ --settings=override \
+ bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
+ systemd-nspawn --directory="$root" \
+ --machine=foo-bar \
+ --uuid="$uuid" \
+ --settings=trusted \
+ bash -xec "[[ \$container_uuid == $uuid ]]"
+
+ # Mounts
+ mkdir "$tmpdir"/{1,2,3}
+ touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three"
+ touch "$tmpdir/foo"
+ # --bind=
+ systemd-nspawn --directory="$root" \
+ ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
+ --bind="$tmpdir:/foo" \
+ --bind="$tmpdir:/also-foo:noidmap,norbind" \
+ bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar'
+ test -e "$tmpdir/bar"
+ # --bind-ro=
+ systemd-nspawn --directory="$root" \
+ --bind-ro="$tmpdir:/foo" \
+ --bind-ro="$tmpdir:/bar:noidmap,norbind" \
+ bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true'
+ # --inaccessible=
+ systemd-nspawn --directory="$root" \
+ --inaccessible=/var \
+ bash -xec 'touch /var/foo && exit 1; true'
+ # --tmpfs=
+ systemd-nspawn --directory="$root" \
+ --tmpfs=/var:rw,nosuid,noexec \
+ bash -xec 'touch /var/nope'
+ test ! -e "$root/var/nope"
+ # --overlay=
+ systemd-nspawn --directory="$root" \
+ --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
+ bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo'
+ test -e "$tmpdir/3/foo"
+ # --overlay-ro=
+ systemd-nspawn --directory="$root" \
+ --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
+ bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true'
+ test ! -e "$tmpdir/3/nope"
+ rm -fr "$tmpdir"
+
+ # --port (sanity only)
+ systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true
+ systemd-nspawn --network-veth --directory="$root" --port=80:8080 true
+ systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true
+ systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true
+ systemd-nspawn --network-veth --directory="$root" --port=udp:80 true
+ systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true
+ (! systemd-nspawn --network-veth --directory="$root" --port= true)
+ (! systemd-nspawn --network-veth --directory="$root" --port=-1 true)
+ (! systemd-nspawn --network-veth --directory="$root" --port=: true)
+ (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true)
+ (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true)
+ (! systemd-nspawn --network-veth --directory="$root" --port=8080: true)
+ # Exercise adding/removing ports from an interface
+ systemd-nspawn --directory="$root" \
+ --network-veth \
+ --port=6667 \
+ --port=80:8080 \
+ --port=udp:53 \
+ --port=tcp:22:2222 \
+ bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24'
+
+ # --load-credential=, --set-credential=
+ echo "foo bar" >/tmp/cred.path
+ systemd-nspawn --directory="$root" \
+ --load-credential=cred.path:/tmp/cred.path \
+ --set-credential="cred.set:hello world" \
+ bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
+ # Combine with --user to ensure creds are still readable
+ systemd-nspawn --directory="$root" \
+ --user=testuser \
+ --no-new-privileges=yes \
+ --load-credential=cred.path:/tmp/cred.path \
+ --set-credential="cred.set:hello world" \
+ bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
+ rm -f /tmp/cred.path
+
+ # Assorted tests
+ systemd-nspawn --directory="$root" --suppress-sync=yes bash -xec 'echo hello'
+ systemd-nspawn --capability=help
+ systemd-nspawn --resolv-conf=help
+ systemd-nspawn --timezone=help
+
+ # Handling of invalid arguments
+ opts=(
+ bind
+ bind-ro
+ bind-user
+ chdir
+ console
+ inaccessible
+ kill-signal
+ link-journal
+ load-credential
+ network-{interface,macvlan,ipvlan,veth-extra,bridge,zone}
+ no-new-privileges
+ oom-score-adjust
+ overlay
+ overlay-ro
+ personality
+ pivot-root
+ port
+ private-users
+ private-users-ownership
+ register
+ resolv-conf
+ rlimit
+ root-hash
+ root-hash-sig
+ set-credential
+ settings
+ suppress-sync
+ timezone
+ tmpfs
+ uuid
+ )
+ for opt in "${opts[@]}"; do
+ (! systemd-nspawn "--$opt")
+ [[ "$opt" == network-zone ]] && continue
+ (! systemd-nspawn "--$opt=''")
+ (! systemd-nspawn "--$opt=%\$š")
+ done
+ (! systemd-nspawn --volatile="")
+ (! systemd-nspawn --volatile=-1)
+ (! systemd-nspawn --rlimit==)
+}
+
+nspawn_settings_cleanup() {
+ for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
+ ip link del "$dev" || :
+ done
+
+ return 0
+}
+
+testcase_nspawn_settings() {
+ local root container dev private_users wlan_names
+
+ mkdir -p /run/systemd/nspawn
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.nspawn-settings.XXX)"
+ container="$(basename "$root")"
+ create_dummy_container "$root"
+ rm -f "/etc/systemd/nspawn/$container.nspawn"
+ mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
+
+ # add virtual wlan interfaces
+ if modprobe mac80211_hwsim radios=2; then
+ wlan_names="wlan0 wlan1:wl-renamed1"
+ fi
+
+ for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
+ ip link add "$dev" type dummy
+ done
+ udevadm settle
+ ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
+ ip link
+ trap nspawn_settings_cleanup RETURN
+
+ # Let's start with one huge config to test as much as we can at once
+ cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
+[Exec]
+Boot=no
+Ephemeral=no
+ProcessTwo=no
+Parameters=bash /entrypoint.sh "foo bar" 'bar baz'
+Environment=FOO=bar
+Environment=BAZ="hello world"
+User=root
+WorkingDirectory=/tmp
+Capability=CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN
+DropCapability=CAP_AUDIT_CONTROL CAP_AUDIT_WRITE
+AmbientCapability=CAP_BPF CAP_CHOWN
+NoNewPrivileges=no
+MachineID=f28f129b51874b1280a89421ec4b4ad4
+PrivateUsers=no
+NotifyReady=no
+SystemCallFilter=@basic-io @chown
+SystemCallFilter=~ @clock
+LimitNOFILE=1024:2048
+LimitRTPRIO=8:16
+OOMScoreAdjust=32
+CPUAffinity=0,0-5,1-5
+Hostname=nspawn-settings
+ResolvConf=copy-host
+Timezone=delete
+LinkJournal=no
+SuppressSync=no
+
+[Files]
+ReadOnly=no
+Volatile=no
+TemporaryFileSystem=/tmp
+TemporaryFileSystem=/opt/tmp
+Inaccessible=/opt/inaccessible
+Inaccessible=/opt/also-inaccessible
+PrivateUsersOwnership=auto
+Overlay=+/var::/var
+${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
+
+[Network]
+Private=yes
+VirtualEthernet=yes
+VirtualEthernetExtra=my-fancy-veth1
+VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
+Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names:-}
+MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
+IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
+Zone=sd-zone0
+Port=80
+Port=81:8181
+Port=tcp:60
+Port=udp:60:61
+EOF
+ cat >"$root/entrypoint.sh" <<\EOF
+#!/bin/bash
+set -ex
+
+env
+
+[[ "$1" == "foo bar" ]]
+[[ "$2" == "bar baz" ]]
+
+[[ "$USER" == root ]]
+[[ "$FOO" == bar ]]
+[[ "$BAZ" == "hello world" ]]
+[[ "$PWD" == /tmp ]]
+[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]]
+[[ "$(ulimit -S -n)" -eq 1024 ]]
+[[ "$(ulimit -H -n)" -eq 2048 ]]
+[[ "$(ulimit -S -r)" -eq 8 ]]
+[[ "$(ulimit -H -r)" -eq 16 ]]
+[[ "$(</proc/self/oom_score_adj)" -eq 32 ]]
+[[ "$(hostname)" == nspawn-settings ]]
+[[ -e /etc/resolv.conf ]]
+[[ ! -e /etc/localtime ]]
+
+mountpoint /tmp
+touch /tmp/foo
+mountpoint /opt/tmp
+touch /opt/tmp/foo
+touch /opt/inaccessible/foo && exit 1
+touch /opt/also-inaccessible/foo && exit 1
+mountpoint /var
+
+ip link
+ip link | grep host-only && exit 1
+ip link | grep host0@
+ip link | grep my-fancy-veth1@
+ip link | grep my-fancy-veth2@
+ip link | grep sd-shared1
+ip link | grep sd-renamed2
+ip link | grep sd-shared3
+ip link | grep sd-altname3
+ip link | grep sd-altname-tooooooooooooo-long
+ip link | grep mv-sd-macvlan1@
+ip link | grep my-macvlan2@
+ip link | grep iv-sd-ipvlan1@
+ip link | grep my-ipvlan2@
+EOF
+ if [[ -n "${wlan_names:-}" ]]; then
+ cat >>"$root/entrypoint.sh" <<\EOF
+ip link | grep wlan0
+ip link | grep wl-renamed1
+EOF
+ fi
+
+ timeout 30 systemd-nspawn --directory="$root"
+
+ # And now for stuff that needs to run separately
+ #
+ # Note on the condition below: since our container tree is owned by root,
+ # both "yes" and "identity" private users settings will behave the same
+ # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already
+ # exists there, so skip setting it in such case
+ for private_users in "131072:65536" yes identity pick; do
+ cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
+[Exec]
+Hostname=private-users
+PrivateUsers=$private_users
+
+[Files]
+PrivateUsersOwnership=auto
+BindUser=
+$([[ "$private_users" =~ (yes|identity) ]] || echo "BindUser=testuser")
+${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
+EOF
+ cat "/run/systemd/nspawn/$container.nspawn"
+ chown -R root:root "$root"
+ systemd-nspawn --directory="$root" bash -xec '[[ "$(hostname)" == private-users ]]'
+ done
+
+ rm -fr "$root" "/run/systemd/nspawn/$container.nspawn"
+}
+
+bind_user_cleanup() {
+ userdel --force --remove nspawn-bind-user-1
+ userdel --force --remove nspawn-bind-user-2
+}
+
+testcase_bind_user() {
+ local root
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-user.XXX)"
+ create_dummy_container "$root"
+ useradd --create-home --user-group nspawn-bind-user-1
+ useradd --create-home --user-group nspawn-bind-user-2
+ trap bind_user_cleanup RETURN
+ touch /home/nspawn-bind-user-1/foo
+ touch /home/nspawn-bind-user-2/bar
+ # Add a couple of POSIX ACLs to test the patch-uid stuff
+ mkdir -p "$root/opt"
+ setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' "$root/opt"
+ setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' "$root/opt"
+
+ systemd-nspawn --directory="$root" \
+ --private-users=pick \
+ --bind-user=nspawn-bind-user-1 \
+ bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo'
+
+ systemd-nspawn --directory="$root" \
+ --private-users=pick \
+ --private-users-ownership=chown \
+ --bind-user=nspawn-bind-user-1 \
+ --bind-user=nspawn-bind-user-2 \
+ bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar'
+ chown -R root:root "$root"
+
+ # User/group name collision
+ echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >"$root/etc/passwd"
+ (! systemd-nspawn --directory="$root" \
+ --private-users=pick \
+ --bind-user=nspawn-bind-user-1 \
+ --bind-user=nspawn-bind-user-2 \
+ true)
+ rm -f "$root/etc/passwd"
+
+ echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group"
+ (! systemd-nspawn --directory="$root" \
+ --private-users=pick \
+ --bind-user=nspawn-bind-user-1 \
+ --bind-user=nspawn-bind-user-2 \
+ true)
+ rm -f "$root/etc/group"
+
+ rm -fr "$root"
+}
+
+testcase_bind_tmp_path() {
+ # https://github.com/systemd/systemd/issues/4789
+ local root
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-tmp-path.XXX)"
+ create_dummy_container "$root"
+ : >/tmp/bind
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --bind=/tmp/bind \
+ bash -c 'test -e /tmp/bind'
+
+ rm -fr "$root" /tmp/bind
+}
+
+testcase_norbind() {
+ # https://github.com/systemd/systemd/issues/13170
+ local root
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.norbind-path.XXX)"
+ mkdir -p /tmp/binddir/subdir
+ echo -n "outer" >/tmp/binddir/subdir/file
+ mount -t tmpfs tmpfs /tmp/binddir/subdir
+ echo -n "inner" >/tmp/binddir/subdir/file
+ create_dummy_container "$root"
+
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --bind=/tmp/binddir:/mnt:norbind \
+ bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi'
+
+ umount /tmp/binddir/subdir
+ rm -fr "$root" /tmp/binddir/
+}
+
+rootidmap_cleanup() {
+ local dir="${1:?}"
+
+ mountpoint -q "$dir/bind" && umount "$dir/bind"
+ rm -fr "$dir"
+}
+
+testcase_rootidmap() {
+ local root cmd permissions
+ local owner=1000
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.rootidmap-path.XXX)"
+ # Create ext4 image, as ext4 supports idmapped-mounts.
+ mkdir -p /tmp/rootidmap/bind
+ dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048
+ mkfs.ext4 /tmp/rootidmap/ext4.img
+ mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind
+ trap "rootidmap_cleanup /tmp/rootidmap/" RETURN
+
+ touch /tmp/rootidmap/bind/file
+ chown -R "$owner:$owner" /tmp/rootidmap/bind
+
+ create_dummy_container "$root"
+ cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file'
+ if ! SYSTEMD_LOG_TARGET=console \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --bind=/tmp/rootidmap/bind:/mnt:rootidmap \
+ bash -c "$cmd" |& tee nspawn.out; then
+ if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
+ echo "idmapped mounts are not supported, skipping the test..."
+ return 0
+ fi
+
+ return 1
+ fi
+
+ permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file)
+ if [[ $permissions != "$owner:$owner" ]]; then
+ echo "*** wrong permissions: $permissions"
+ [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
+ fi
+}
+
+owneridmap_cleanup() {
+ local dir="${1:?}"
+
+ mountpoint -q "$dir/bind" && umount "$dir/bind"
+ rm -fr "$dir"
+}
+
+testcase_owneridmap() {
+ local root cmd permissions
+ local owner=1000
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.owneridmap-path.XXX)"
+ # Create ext4 image, as ext4 supports idmapped-mounts.
+ mkdir -p /tmp/owneridmap/bind
+ dd if=/dev/zero of=/tmp/owneridmap/ext4.img bs=4k count=2048
+ mkfs.ext4 /tmp/owneridmap/ext4.img
+ mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind
+ trap "owneridmap_cleanup /tmp/owneridmap/" RETURN
+
+ touch /tmp/owneridmap/bind/file
+ chown -R "$owner:$owner" /tmp/owneridmap/bind
+
+ # Allow users to read and execute / in order to execute binaries
+ chmod o+rx "$root"
+
+ create_dummy_container "$root"
+
+ # --user=
+ # "Fake" getent passwd's bare minimum, so we don't have to pull it in
+ # with all the DSO shenanigans
+ cat >"$root/bin/getent" <<\EOF
+#!/bin/bash
+
+if [[ $# -eq 0 ]]; then
+ :
+elif [[ $1 == passwd ]]; then
+ echo "testuser:x:1010:1010:testuser:/:/bin/sh"
+elif [[ $1 == initgroups ]]; then
+ echo "testuser"
+fi
+EOF
+ chmod +x "$root/bin/getent"
+
+ mkdir -p "$root/home/testuser"
+ chown 1010:1010 "$root/home/testuser"
+
+ cmd='PERMISSIONS=$(stat -c "%u:%g" /home/testuser/file); if [[ $PERMISSIONS != "1010:1010" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /home/testuser/other_file'
+ if ! SYSTEMD_LOG_TARGET=console \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ -U \
+ --user=testuser \
+ --bind=/tmp/owneridmap/bind:/home/testuser:owneridmap \
+ ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
+ /usr/bin/bash -c "$cmd" |& tee nspawn.out; then
+ if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
+ echo "idmapped mounts are not supported, skipping the test..."
+ return 0
+ fi
+
+ return 1
+ fi
+
+ permissions=$(stat -c "%u:%g" /tmp/owneridmap/bind/other_file)
+ if [[ $permissions != "$owner:$owner" ]]; then
+ echo "*** wrong permissions: $permissions"
+ [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
+ fi
+}
+
+testcase_notification_socket() {
+ # https://github.com/systemd/systemd/issues/4944
+ local root
+ local cmd='echo a | nc -U -u -w 1 /run/host/notify'
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_notification_socket.XXX)"
+ create_dummy_container "$root"
+
+ systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd"
+ systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd"
+
+ rm -fr "$root"
+}
+
+testcase_os_release() {
+ local root entrypoint os_release_source
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.os-release.XXX)"
+ create_dummy_container "$root"
+ entrypoint="$root/entrypoint.sh"
+ cat >"$entrypoint" <<\EOF
+#!/usr/bin/bash -ex
+
+. /tmp/os-release
+[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1
+[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1
+[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1
+[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1
+
+cd /tmp
+(cd /run/host && md5sum os-release) | md5sum -c
+EOF
+ chmod +x "$entrypoint"
+
+ os_release_source="/etc/os-release"
+ if [[ ! -r "$os_release_source" ]]; then
+ os_release_source="/usr/lib/os-release"
+ elif [[ -L "$os_release_source" ]]; then
+ # Ensure that /etc always wins if available
+ cp --remove-destination -fv /usr/lib/os-release /etc/os-release
+ echo MARKER=1 >>/etc/os-release
+ fi
+
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --bind="$os_release_source:/tmp/os-release" \
+ "${entrypoint##"$root"}"
+
+ if grep -q MARKER /etc/os-release; then
+ ln -svrf /usr/lib/os-release /etc/os-release
+ fi
+
+ rm -fr "$root"
+}
+
+testcase_machinectl_bind() {
+ local service_path service_name root container_name ec
+ local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;'
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.machinectl-bind.XXX)"
+ create_dummy_container "$root"
+ container_name="$(basename "$root")"
+
+ service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)"
+ service_name="${service_path##*/}"
+ cat >"$service_path" <<EOF
+[Service]
+Type=notify
+ExecStart=systemd-nspawn --directory="$root" --notify-ready=no /usr/bin/bash -xec "$cmd"
+EOF
+
+ systemctl daemon-reload
+ systemctl start "$service_name"
+ touch /tmp/marker
+ machinectl bind --mkdir "$container_name" /tmp/marker
+
+ timeout 10 bash -c "while [[ '\$(systemctl show -P SubState $service_name)' == running ]]; do sleep .2; done"
+ ec="$(systemctl show -P ExecMainStatus "$service_name")"
+ systemctl stop "$service_name"
+
+ rm -fr "$root" "$service_path"
+
+ return "$ec"
+}
+
+testcase_selinux() {
+ # Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976
+ if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then
+ echo >&2 "SELinux is not enabled, skipping SELinux-related tests"
+ return 0
+ fi
+
+ local root
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.selinux.XXX)"
+ create_dummy_container "$root"
+ chcon -R -t container_t "$root"
+
+ systemd-nspawn --register=no \
+ --boot \
+ --directory="$root" \
+ --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \
+ --selinux-context=system_u:system_r:container_t:s0:c0,c1
+
+ rm -fr "$root"
+}
+
+testcase_ephemeral_config() {
+ # https://github.com/systemd/systemd/issues/13297
+ local root container_name
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.ephemeral-config.XXX)"
+ create_dummy_container "$root"
+ container_name="$(basename "$root")"
+
+ mkdir -p /run/systemd/nspawn/
+ rm -f "/etc/systemd/nspawn/$container_name.nspawn"
+ cat >"/run/systemd/nspawn/$container_name.nspawn" <<EOF
+[Files]
+${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
+BindReadOnly=/tmp/ephemeral-config
+EOF
+ touch /tmp/ephemeral-config
+
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --ephemeral \
+ bash -x -c "test -f /tmp/ephemeral-config"
+
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --ephemeral \
+ --machine=foobar \
+ bash -x -c "! test -f /tmp/ephemeral-config"
+
+ rm -fr "$root" "/run/systemd/nspawn/$container_name.nspawn"
+}
+
+matrix_run_one() {
+ local cgroupsv2="${1:?}"
+ local use_cgns="${2:?}"
+ local api_vfs_writable="${3:?}"
+ local root
+
+ if [[ "$cgroupsv2" == "yes" && "$IS_CGROUPSV2_SUPPORTED" == "no" ]]; then
+ echo >&2 "Unified cgroup hierarchy is not supported, skipping..."
+ return 0
+ fi
+
+ if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]]; then
+ echo >&2 "CGroup namespaces are not supported, skipping..."
+ return 0
+ fi
+
+ root="$(mktemp -d "/var/lib/machines/TEST-13-NSPAWN.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")"
+ create_dummy_container "$root"
+
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --boot
+
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --private-network \
+ --boot
+
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --private-users=pick \
+ --boot; then
+ [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1
+ else
+ [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1
+ fi
+
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --private-network \
+ --private-users=pick \
+ --boot; then
+ [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1
+ else
+ [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1
+ fi
+
+ local netns_opt="--network-namespace-path=/proc/self/ns/net"
+ local net_opt
+ local net_opts=(
+ "--network-bridge=lo"
+ "--network-interface=lo"
+ "--network-ipvlan=lo"
+ "--network-macvlan=lo"
+ "--network-veth"
+ "--network-veth-extra=lo"
+ "--network-zone=zone"
+ )
+
+ # --network-namespace-path and network-related options cannot be used together
+ for net_opt in "${net_opts[@]}"; do
+ echo "$netns_opt in combination with $net_opt should fail"
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --boot \
+ "$netns_opt" \
+ "$net_opt"; then
+ echo >&2 "unexpected pass"
+ return 1
+ fi
+ done
+
+ # allow combination of --network-namespace-path and --private-network
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --boot \
+ --private-network \
+ "$netns_opt"
+
+ # test --network-namespace-path works with a network namespace created by "ip netns"
+ ip netns add nspawn_test
+ netns_opt="--network-namespace-path=/run/netns/nspawn_test"
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --network-namespace-path=/run/netns/nspawn_test \
+ ip a | grep -v -E '^1: lo.*UP'
+ ip netns del nspawn_test
+
+ rm -fr "$root"
+
+ return 0
+}
+
+testcase_check_os_release() {
+ # https://github.com/systemd/systemd/issues/29185
+ local base common_opts root
+
+ base="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release_base.XXX)"
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release.XXX)"
+ create_dummy_container "$base"
+ cp -d "$base"/{bin,sbin,lib,lib64} "$root/"
+ common_opts=(
+ --boot
+ --register=no
+ --directory="$root"
+ --bind-ro="$base/usr:/usr"
+ )
+
+ # Empty /etc/ & /usr/
+ (! systemd-nspawn "${common_opts[@]}")
+ (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
+ (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}")
+ SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
+
+ # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink
+ ln -svrf "$root/etc/os-release" "$root/usr/os-release"
+ (! systemd-nspawn "${common_opts[@]}")
+ (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
+ SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
+
+ rm -fr "$root" "$base"
+}
+
+run_testcases
+
+for api_vfs_writable in yes no network; do
+ matrix_run_one no no $api_vfs_writable
+ matrix_run_one yes no $api_vfs_writable
+ matrix_run_one no yes $api_vfs_writable
+ matrix_run_one yes yes $api_vfs_writable
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ set +e
+
+ machinectl kill --signal=KILL nss-mymachines-{noip,singleip,manyips}
+ mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
+ rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+# Create a bunch of containers that:
+# 1) Have no IP addresses assigned
+create_dummy_container /var/lib/machines/nss-mymachines-noip
+cat >/var/lib/machines/nss-mymachines-noip/sbin/init <<\EOF
+#!/usr/bin/bash -ex
+
+ip addr show dev ve-noip
+touch /initialized
+sleep infinity &
+# Run the sleep command asynchronously, so bash is able to process signals
+while :; do
+ wait || :
+done
+EOF
+# 2) Have one IP address assigned (IPv4 only)
+create_dummy_container /var/lib/machines/nss-mymachines-singleip
+cat >/var/lib/machines/nss-mymachines-singleip/sbin/init <<\EOF
+#!/usr/bin/bash -ex
+
+ip addr add 10.1.0.2/24 dev ve-singleip
+ip addr show dev ve-singleip
+touch /initialized
+sleep infinity &
+while :; do
+ wait || :
+done
+EOF
+# 3) Have bunch of IP addresses assigned (both IPv4 and IPv6)
+create_dummy_container /var/lib/machines/nss-mymachines-manyips
+cat >/var/lib/machines/nss-mymachines-manyips/sbin/init <<\EOF
+#!/usr/bin/bash -ex
+
+ip addr add 10.2.0.2/24 dev ve-manyips
+for i in {100..120}; do
+ ip addr add 10.2.0.$i/24 dev ve-manyips
+done
+ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad
+ip addr show dev ve-manyips
+touch /initialized
+sleep infinity
+while :; do
+ wait || :
+done
+EOF
+# Create the respective .nspawn config files
+mkdir -p /run/systemd/nspawn
+for container in noip singleip manyips; do
+ cat >"/run/systemd/nspawn/nss-mymachines-$container.nspawn" <<EOF
+[Exec]
+Boot=yes
+
+[Network]
+VirtualEthernetExtra=ve-$container
+EOF
+done
+
+# Start the containers and wait until all of them are initialized
+machinectl start nss-mymachines-{noip,singleip,manyips}
+for container in nss-mymachines-{noip,singleip,manyips}; do
+ timeout 30 bash -xec "while [[ ! -e /var/lib/machines/$container/initialized ]]; do sleep .5; done"
+done
+
+# We need to configure the dummy interfaces on the "outside" as well for `getent {ahosts4,ahosts6}` to work
+# properly. This is caused by getaddrinfo() calling _check_pf() that iterates through all interfaces and
+# notes if any of them has an IPv4/IPv6 - this is then used together with AF_INET/AF_INET6 to determine if we
+# can ever return a valid answer, and if we configured the container interfaces only in the container, we
+# would have no valid IPv4/IPv6 on the "outside" (as we don't set up any other netdev) which would make
+# getaddrinfo() return EAI_NONAME without ever asking nss-mymachines.
+ip addr add 10.1.0.1/24 dev ve-singleip
+ip addr add 10.2.0.1/24 dev ve-manyips
+ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad
+
+getent hosts -s mymachines
+getent ahosts -s mymachines
+
+# And finally check if we can resolve the containers via nss-mymachines
+for database in hosts ahosts{,v4,v6}; do
+ (! getent "$database" -s mymachines nss-mymachines-noip)
+done
+
+run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-singleip
+run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-singleip
+run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-singleip
+run_and_grep "^::ffff:10\.1\.0\.2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-singleip
+
+run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-manyips
+run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
+run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
+for i in {100..120}; do
+ run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
+ run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-manyips
+done
+run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-manyips
+(! run_and_grep "^fd00:" getent ahostsv4 -s mymachines nss-mymachines-manyips)
+(! run_and_grep "^10\.2:" getent ahostsv6 -s mymachines nss-mymachines-manyips)
+
+# Multiple machines at once
+run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
+
+for database in hosts ahosts ahostsv4 ahostsv6; do
+ (! getent "$database" -s mymachines foo-bar-baz)
+done
+
+# getgrid(), getgrnam(), getpwuid(), and getpwnam() are explicitly handled by nss-mymachines, so probe them
+# as well
+(! getent group -s mymachines foo 11)
+(! getent passwd -s mymachines foo 11)
+
+machinectl stop nss-mymachines-{noip,singleip,manyips}
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-13-NSPAWN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FSTYPE="$(stat --file-system --format "%T" /)"
+
+if [[ "$FSTYPE" == "fuseblk" ]]; then
+ echo "Root filesystem is virtiofs, skipping"
+ exit 77
+fi
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-15-DROPIN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+clear_unit() {
+ local unit_name="${1:?}"
+ local base suffix
+
+ systemctl stop "$unit_name" 2>/dev/null || :
+ rm -f /{etc,run,usr/lib}/systemd/system/"$unit_name"
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".d
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".{wants,requires}
+ if [[ $unit_name == *@* ]]; then
+ base="${unit_name%@*}"
+ suffix="${unit_name##*.}"
+ systemctl stop "$base@"*."$suffix" 2>/dev/null || :
+ rm -f /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix"
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".d
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".{wants,requires}
+ fi
+}
+
+clear_units() {
+ for u in "$@"; do
+ clear_unit "$u"
+ done
+ systemctl daemon-reload
+}
+
+create_service() {
+ local service_name="${1:?}"
+ clear_units "${service_name}".service
+
+ cat >/etc/systemd/system/"$service_name".service <<EOF
+[Unit]
+Description=$service_name unit
+
+[Service]
+ExecStart=sleep 100000
+EOF
+ mkdir -p /{etc,run,usr/lib}/systemd/system/"$service_name".service.{d,wants,requires}
+}
+
+create_services() {
+ for u in "$@"; do
+ create_service "$u"
+ done
+}
+
+check_ok() {
+ x="$(systemctl show --value -p "${2:?}" "${1:?}")"
+ case "$x" in
+ *${3:?}*) return 0 ;;
+ *) return 1 ;;
+ esac
+}
+
+check_ko() {
+ ! check_ok "$@"
+}
+
+testcase_basic_dropins() {
+ echo "Testing basic dropins..."
+
+ echo "*** test a wants b wants c"
+ create_services test15-a test15-b test15-c
+ ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
+ ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/
+ check_ok test15-a Wants test15-b.service
+ check_ok test15-b Wants test15-c.service
+
+ echo "*** test a wants,requires b"
+ create_services test15-a test15-b test15-c
+ ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
+ ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+ check_ok test15-a Wants test15-b.service
+ check_ok test15-a Requires test15-b.service
+
+ echo "*** test a wants nonexistent"
+ create_service test15-a
+ ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/
+ check_ok test15-a Wants nonexistent.service
+ systemctl start test15-a
+ systemctl stop test15-a
+
+ echo "*** test a requires nonexistent"
+ ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/
+ systemctl daemon-reload
+ check_ok test15-a Requires nonexistent.service
+
+ # 'b' is already loaded when 'c' pulls it in via a dropin.
+ echo "*** test a,c require b"
+ create_services test15-a test15-b test15-c
+ ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+ ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
+ systemctl start test15-a
+ check_ok test15-c Requires test15-b.service
+ systemctl stop test15-a test15-b
+
+ # 'b' is already loaded when 'c' pulls it in via an alias dropin.
+ echo "*** test a wants alias"
+ create_services test15-a test15-b test15-c
+ ln -sf test15-c.service /etc/systemd/system/test15-c1.service
+ ln -sf ../test15-c.service /etc/systemd/system/test15-a.service.wants/
+ ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/
+ systemctl start test15-a
+ check_ok test15-a Wants test15-c.service
+ check_ok test15-b Wants test15-c.service
+ systemctl stop test15-a test15-c
+
+ echo "*** test service.d/ top level drop-in"
+ create_services test15-a test15-b
+ check_ko test15-a ExecCondition "/bin/echo a"
+ check_ko test15-b ExecCondition "/bin/echo b"
+ mkdir -p /run/systemd/system/service.d
+ cat >/run/systemd/system/service.d/override.conf <<EOF
+[Service]
+ExecCondition=/bin/echo %n
+EOF
+ systemctl daemon-reload
+ check_ok test15-a ExecCondition "/bin/echo test15-a"
+ check_ok test15-b ExecCondition "/bin/echo test15-b"
+ rm -rf /run/systemd/system/service.d
+
+ clear_units test15-{a,b,c,c1}.service
+}
+
+testcase_linked_units() {
+ echo "Testing linked units..."
+ echo "*** test linked unit (same basename)"
+
+ create_service test15-a
+ mv /etc/systemd/system/test15-a.service /
+ ln -s /test15-a.service /etc/systemd/system/
+ ln -s test15-a.service /etc/systemd/system/test15-b.service
+
+ check_ok test15-a Names test15-a.service
+ check_ok test15-a Names test15-b.service
+
+ echo "*** test linked unit (cross basename)"
+
+ mv /test15-a.service /test15-a@.scope
+ ln -fs /test15-a@.scope /etc/systemd/system/test15-a.service
+ systemctl daemon-reload
+
+ check_ok test15-a Names test15-a.service
+ check_ok test15-a Names test15-b.service
+ check_ko test15-a Names test15-a@ # test15-a@.scope is the symlink target.
+ # Make sure it is completely ignored.
+
+ rm /test15-a@.scope
+ clear_units test15-{a,b}.service
+}
+
+testcase_template_alias() {
+ echo "Testing instance alias..."
+ echo "*** forward"
+
+ create_service test15-a@
+ ln -s test15-a@inst.service /etc/systemd/system/test15-b@inst.service # alias
+
+ check_ok test15-a@inst Names test15-a@inst.service
+ check_ok test15-a@inst Names test15-b@inst.service
+
+ check_ok test15-a@other Names test15-a@other.service
+ check_ko test15-a@other Names test15-b@other.service
+
+ echo "*** reverse"
+
+ systemctl daemon-reload
+
+ check_ok test15-b@inst Names test15-a@inst.service
+ check_ok test15-b@inst Names test15-b@inst.service
+
+ check_ko test15-b@other Names test15-a@other.service
+ check_ok test15-b@other Names test15-b@other.service
+
+ clear_units test15-{a,b}@.service
+}
+
+testcase_hierarchical_service_dropins() {
+ echo "Testing hierarchical service dropins..."
+ echo "*** test service.d/ top level drop-in"
+ create_services a-b-c
+ check_ko a-b-c ExecCondition "echo service.d"
+ check_ko a-b-c ExecCondition "echo a-.service.d"
+ check_ko a-b-c ExecCondition "echo a-b-.service.d"
+ check_ko a-b-c ExecCondition "echo a-b-c.service.d"
+
+ for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
+ mkdir -p "/run/systemd/system/$dropin"
+ cat >"/run/systemd/system/$dropin/override.conf" <<EOF
+[Service]
+ExecCondition=echo $dropin
+EOF
+ systemctl daemon-reload
+ check_ok a-b-c ExecCondition "echo $dropin"
+
+ # Check that we can start a transient service in presence of the drop-ins
+ systemd-run -u a-b-c2.service -p Description='sleepy' sleep infinity
+
+ # The transient setting replaces the default
+ check_ok a-b-c2.service Description "sleepy"
+
+ # The override takes precedence for ExecCondition
+ # (except the last iteration when it only applies to the other service)
+ if [ "$dropin" != "a-b-c.service.d" ]; then
+ check_ok a-b-c2.service ExecCondition "echo $dropin"
+ fi
+
+ # Check that things are the same after a reload
+ systemctl daemon-reload
+ check_ok a-b-c2.service Description "sleepy"
+ if [ "$dropin" != "a-b-c.service.d" ]; then
+ check_ok a-b-c2.service ExecCondition "echo $dropin"
+ fi
+
+ systemctl stop a-b-c2.service
+ done
+ for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
+ rm -rf "/run/systemd/system/$dropin"
+ done
+
+ clear_units a-b-c.service
+}
+
+testcase_hierarchical_slice_dropins() {
+ echo "Testing hierarchical slice dropins..."
+ echo "*** test slice.d/ top level drop-in"
+ # Slice units don't even need a fragment, so we test the defaults here
+ check_ok a-b-c.slice Description "Slice /a/b/c"
+ check_ok a-b-c.slice MemoryMax "infinity"
+
+ # Test drop-ins
+ for dropin in slice.d a-.slice.d a-b-.slice.d a-b-c.slice.d; do
+ mkdir -p "/run/systemd/system/$dropin"
+ cat >"/run/systemd/system/$dropin/override.conf" <<EOF
+[Slice]
+MemoryMax=1000000000
+EOF
+ systemctl daemon-reload
+ check_ok a-b-c.slice MemoryMax "1000000000"
+
+ busctl call \
+ org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager \
+ StartTransientUnit 'ssa(sv)a(sa(sv))' \
+ 'a-b-c.slice' 'replace' \
+ 2 \
+ 'Description' s 'slice too' \
+ 'MemoryMax' t 1000000002 \
+ 0
+
+ # The override takes precedence for MemoryMax
+ check_ok a-b-c.slice MemoryMax "1000000000"
+ # The transient setting replaces the default
+ check_ok a-b-c.slice Description "slice too"
+
+ # Check that things are the same after a reload
+ systemctl daemon-reload
+ check_ok a-b-c.slice MemoryMax "1000000000"
+ check_ok a-b-c.slice Description "slice too"
+
+ busctl call \
+ org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager \
+ StopUnit 'ss' \
+ 'a-b-c.slice' 'replace'
+
+ rm -f "/run/systemd/system/$dropin/override.conf"
+ done
+
+ # Test unit with a fragment
+ cat >/run/systemd/system/a-b-c.slice <<EOF
+[Slice]
+MemoryMax=1000000001
+EOF
+ systemctl daemon-reload
+ check_ok a-b-c.slice MemoryMax "1000000001"
+
+ clear_units a-b-c.slice
+}
+
+testcase_transient_service_dropins() {
+ echo "Testing dropins for a transient service..."
+ echo "*** test transient service drop-ins"
+
+ mkdir -p /etc/systemd/system/service.d
+ mkdir -p /etc/systemd/system/a-.service.d
+ mkdir -p /etc/systemd/system/a-b-.service.d
+ mkdir -p /etc/systemd/system/a-b-c.service.d
+
+ echo -e '[Service]\nStandardInputText=aaa' >/etc/systemd/system/service.d/drop1.conf
+ echo -e '[Service]\nStandardInputText=bbb' >/etc/systemd/system/a-.service.d/drop2.conf
+ echo -e '[Service]\nStandardInputText=ccc' >/etc/systemd/system/a-b-.service.d/drop3.conf
+ echo -e '[Service]\nStandardInputText=ddd' >/etc/systemd/system/a-b-c.service.d/drop4.conf
+
+ # There's no fragment yet, so this fails
+ systemctl cat a-b-c.service && exit 1
+
+ # xxx → eHh4Cg==
+ systemd-run -u a-b-c.service -p StandardInputData=eHh4Cg== sleep infinity
+
+ data=$(systemctl show -P StandardInputData a-b-c.service)
+ # xxx\naaa\n\bbb\nccc\nddd\n → eHh4…
+ test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
+
+ # Do a reload and check again
+ systemctl daemon-reload
+ data=$(systemctl show -P StandardInputData a-b-c.service)
+ test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
+
+ clear_units a-b-c.service
+ rm /etc/systemd/system/service.d/drop1.conf \
+ /etc/systemd/system/a-.service.d/drop2.conf \
+ /etc/systemd/system/a-b-.service.d/drop3.conf
+}
+
+testcase_transient_slice_dropins() {
+ echo "Testing dropins for a transient slice..."
+ echo "*** test transient slice drop-ins"
+
+ # FIXME: implement reloading of individual units.
+ #
+ # The settings here are loaded twice. For most settings it doesn't matter,
+ # but Documentation is not deduplicated, so we current get repeated entried
+ # which is a bug.
+
+ mkdir -p /etc/systemd/system/slice.d
+ mkdir -p /etc/systemd/system/a-.slice.d
+ mkdir -p /etc/systemd/system/a-b-.slice.d
+ mkdir -p /etc/systemd/system/a-b-c.slice.d
+
+ echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf
+ echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf
+ echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf
+ echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf
+
+ # Invoke daemon-reload to make sure that the call below doesn't fail
+ systemctl daemon-reload
+
+ # No fragment is required, so this works
+ systemctl cat a-b-c.slice
+
+ busctl call \
+ org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager \
+ StartTransientUnit 'ssa(sv)a(sa(sv))' \
+ 'a-b-c.slice' 'replace' \
+ 1 \
+ 'Documentation' as 1 'man:drop5' \
+ 0
+
+ data=$(systemctl show -P Documentation a-b-c.slice)
+ test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
+
+ # Do a reload and check again
+ systemctl daemon-reload
+ data=$(systemctl show -P Documentation a-b-c.slice)
+ test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
+
+ clear_units a-b-c.slice
+ rm /etc/systemd/system/slice.d/drop1.conf \
+ /etc/systemd/system/a-.slice.d/drop2.conf \
+ /etc/systemd/system/a-b-.slice.d/drop3.conf
+}
+
+testcase_template_dropins() {
+ echo "Testing template dropins..."
+
+ create_services foo bar@ yup@
+
+ # Declare some deps to check if the body was loaded
+ cat >>/etc/systemd/system/bar@.service <<EOF
+[Unit]
+After=bar-template-after.device
+EOF
+
+ cat >>/etc/systemd/system/yup@.service <<EOF
+[Unit]
+After=yup-template-after.device
+EOF
+
+ ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service
+ check_ok foo Wants bar@1.service
+
+ echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***"
+ ln -s bar@.service /etc/systemd/system/bar-alias@.service
+ ln -s bar@1.service /etc/systemd/system/bar-alias@1.service
+ ln -s yup@.service /etc/systemd/system/bar-alias@2.service
+ ln -s yup@3.service /etc/systemd/system/bar-alias@3.service
+
+ # create some dropin deps
+ mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/
+ mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/
+ mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/
+
+ ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/
+ ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/
+ ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/
+ ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/
+ ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/
+
+ ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/
+ ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/
+ ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/
+ ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/
+ ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/
+
+ ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/
+ ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/
+ ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/
+ ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/
+ ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/
+
+ systemctl daemon-reload
+
+ echo '*** bar@0 is aliased by bar-alias@0 ***'
+ systemctl show -p Names,Requires bar@0
+ systemctl show -p Names,Requires bar-alias@0
+ check_ok bar@0 Names bar@0
+ check_ok bar@0 Names bar-alias@0
+
+ check_ok bar@0 After bar-template-after.device
+
+ check_ok bar@0 Requires bar-0-requires.device
+ check_ok bar@0 Requires bar-alias-0-requires.device
+ check_ok bar@0 Requires bar-template-requires.device
+ check_ok bar@0 Requires bar-alias-template-requires.device
+ check_ko bar@0 Requires yup-template-requires.device
+
+ check_ok bar-alias@0 After bar-template-after.device
+
+ check_ok bar-alias@0 Requires bar-0-requires.device
+ check_ok bar-alias@0 Requires bar-alias-0-requires.device
+ check_ok bar-alias@0 Requires bar-template-requires.device
+ check_ok bar-alias@0 Requires bar-alias-template-requires.device
+ check_ko bar-alias@0 Requires yup-template-requires.device
+ check_ko bar-alias@0 Requires yup-0-requires.device
+
+ echo '*** bar@1 is aliased by bar-alias@1 ***'
+ systemctl show -p Names,Requires bar@1
+ systemctl show -p Names,Requires bar-alias@1
+ check_ok bar@1 Names bar@1
+ check_ok bar@1 Names bar-alias@1
+
+ check_ok bar@1 After bar-template-after.device
+
+ check_ok bar@1 Requires bar-1-requires.device
+ check_ok bar@1 Requires bar-alias-1-requires.device
+ check_ok bar@1 Requires bar-template-requires.device
+ # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418
+ check_ok bar@1 Requires bar-alias-template-requires.device
+ check_ko bar@1 Requires yup-template-requires.device
+ check_ko bar@1 Requires yup-1-requires.device
+
+ check_ok bar-alias@1 After bar-template-after.device
+
+ check_ok bar-alias@1 Requires bar-1-requires.device
+ check_ok bar-alias@1 Requires bar-alias-1-requires.device
+ check_ok bar-alias@1 Requires bar-template-requires.device
+ check_ok bar-alias@1 Requires bar-alias-template-requires.device
+ check_ko bar-alias@1 Requires yup-template-requires.device
+ check_ko bar-alias@1 Requires yup-1-requires.device
+
+ echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***'
+ systemctl show -p Names,Requires bar@2
+ systemctl show -p Names,Requires bar-alias@2
+ check_ok bar@2 Names bar@2
+ check_ko bar@2 Names bar-alias@2
+
+ check_ok bar@2 After bar-template-after.device
+
+ check_ok bar@2 Requires bar-2-requires.device
+ check_ko bar@2 Requires bar-alias-2-requires.device
+ check_ok bar@2 Requires bar-template-requires.device
+ check_ko bar@2 Requires bar-alias-template-requires.device
+ check_ko bar@2 Requires yup-template-requires.device
+ check_ko bar@2 Requires yup-2-requires.device
+
+ check_ko bar-alias@2 After bar-template-after.device
+
+ check_ko bar-alias@2 Requires bar-2-requires.device
+ check_ok bar-alias@2 Requires bar-alias-2-requires.device
+ check_ko bar-alias@2 Requires bar-template-requires.device
+ check_ok bar-alias@2 Requires bar-alias-template-requires.device
+ check_ok bar-alias@2 Requires yup-template-requires.device
+ check_ok bar-alias@2 Requires yup-2-requires.device
+
+ echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***'
+ systemctl show -p Names,Requires bar@3
+ systemctl show -p Names,Requires bar-alias@3
+ check_ok bar@3 Names bar@3
+ check_ko bar@3 Names bar-alias@3
+
+ check_ok bar@3 After bar-template-after.device
+
+ check_ok bar@3 Requires bar-3-requires.device
+ check_ko bar@3 Requires bar-alias-3-requires.device
+ check_ok bar@3 Requires bar-template-requires.device
+ check_ko bar@3 Requires bar-alias-template-requires.device
+ check_ko bar@3 Requires yup-template-requires.device
+ check_ko bar@3 Requires yup-3-requires.device
+
+ check_ko bar-alias@3 After bar-template-after.device
+
+ check_ko bar-alias@3 Requires bar-3-requires.device
+ check_ok bar-alias@3 Requires bar-alias-3-requires.device
+ check_ko bar-alias@3 Requires bar-template-requires.device
+ check_ok bar-alias@3 Requires bar-alias-template-requires.device
+ check_ok bar-alias@3 Requires yup-template-requires.device
+ check_ok bar-alias@3 Requires yup-3-requires.device
+
+ clear_units foo.service {bar,yup,bar-alias}@{,1,2,3}.service
+}
+
+testcase_alias_dropins() {
+ echo "Testing alias dropins..."
+
+ echo "*** test a wants b1 alias of b"
+ create_services test15-a test15-b
+ ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+ ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/
+ check_ok test15-a Wants test15-b.service
+ systemctl start test15-a
+ systemctl --quiet is-active test15-b
+ systemctl stop test15-a test15-b
+ rm /etc/systemd/system/test15-b1.service
+ clear_units test15-{a,b}.service
+
+ # Check that dependencies don't vary.
+ echo "*** test 2"
+ create_services test15-a test15-x test15-y
+ mkdir -p /etc/systemd/system/test15-a1.service.wants/
+ ln -sf test15-a.service /etc/systemd/system/test15-a1.service
+ ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/
+ ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/
+ check_ok test15-a1 Wants test15-x.service # see [1]
+ check_ok test15-a1 Wants test15-y.service
+ systemctl start test15-a
+ check_ok test15-a1 Wants test15-x.service # see [2]
+ check_ok test15-a1 Wants test15-y.service
+ systemctl stop test15-a test15-x test15-y
+ rm /etc/systemd/system/test15-a1.service
+
+ clear_units test15-{a,x,y}.service
+}
+
+testcase_masked_dropins() {
+ echo "Testing masked dropins..."
+
+ create_services test15-a test15-b
+
+ # 'b' is masked for both deps
+ echo "*** test a wants,requires b is masked"
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
+ check_ko test15-a Wants test15-b.service
+ check_ko test15-a Requires test15-b.service
+
+ # 'a' wants 'b' and 'b' is masked at a lower level
+ echo "*** test a wants b, mask override"
+ ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service
+ ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+ check_ok test15-a Wants test15-b.service
+
+ # 'a' wants 'b' and 'b' is masked at a higher level
+ echo "*** test a wants b, mask"
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+ ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+ check_ko test15-a Wants test15-b.service
+
+ # 'a' is masked but has an override config file
+ echo "*** test a is masked but has an override"
+ create_services test15-a test15-b
+ ln -sf /dev/null /etc/systemd/system/test15-a.service
+ cat >/usr/lib/systemd/system/test15-a.service.d/override.conf <<EOF
+[Unit]
+After=test15-b.service
+EOF
+ check_ok test15-a UnitFileState masked
+
+ # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep
+ echo "*** test a wants b, b1, and one is masked"
+ create_services test15-a test15-b
+ ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+ ln -sf ../test15-b1.service /usr/lib/systemd/system/test15-a.service.wants/test15-b1.service
+ systemctl cat test15-a
+ systemctl show -p Wants,Requires test15-a
+ systemctl cat test15-b1
+ systemctl show -p Wants,Requires test15-b1
+ check_ok test15-a Wants test15-b.service
+ check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
+ rm /etc/systemd/system/test15-b1.service
+
+ # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep
+ echo "*** test a wants b, alias dep is masked"
+ create_services test15-a test15-b
+ ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b1.service
+ ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+ check_ok test15-a Wants test15-b.service
+ check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
+ rm /etc/systemd/system/test15-b1.service
+
+ # 'a' has Wants=b.service but also has a masking
+ # dropin 'b': 'b' should still be pulled in.
+ echo "*** test a wants b both ways"
+ create_services test15-a test15-b
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+ cat >/usr/lib/systemd/system/test15-a.service.d/wants-b.conf <<EOF
+[Unit]
+Wants=test15-b.service
+EOF
+ check_ok test15-a Wants test15-b.service
+
+ # mask a dropin that points to an nonexistent unit.
+ echo "*** test a wants nonexistent is masked"
+ create_services test15-a
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/nonexistent.service
+ ln -sf ../nonexistent.service /usr/lib/systemd/system/test15-a.service.requires/
+ check_ko test15-a Requires nonexistent.service
+
+ # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
+ # masked at a higher level.
+ echo "*** test a wants b is masked"
+ create_services test15-a test15-b test15-c
+ ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+ ln -sf ../test15-b.service /run/systemd/system/test15-c.service.requires/
+ ln -sf /dev/null /etc/systemd/system/test15-c.service.requires/test15-b.service
+ systemctl start test15-a
+ check_ko test15-c Requires test15-b.service
+ systemctl stop test15-a test15-b
+
+ # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
+ # masked at a lower level.
+ echo "*** test a requires b is masked"
+ create_services test15-a test15-b test15-c
+ ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+ ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
+ ln -sf /dev/null /run/systemd/system/test15-c.service.requires/test15-b.service
+ systemctl start test15-a
+ check_ok test15-c Requires test15-b.service
+ systemctl stop test15-a test15-b
+
+ # 'a' requires 2 aliases of 'b' and one of them is a mask.
+ echo "*** test a requires alias of b, other alias masked"
+ create_services test15-a test15-b
+ ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+ ln -sf test15-b.service /etc/systemd/system/test15-b2.service
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b1.service
+ ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
+ ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
+ check_ok test15-a Requires test15-b
+
+ # Same as above but now 'b' is masked.
+ echo "*** test a requires alias of b, b dep masked"
+ create_services test15-a test15-b
+ ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+ ln -sf test15-b.service /etc/systemd/system/test15-b2.service
+ ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
+ ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
+ ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
+ check_ok test15-a Requires test15-b
+
+ clear_units test15-{a,b}.service
+}
+
+testcase_invalid_dropins() {
+ echo "Testing invalid dropins..."
+ # Assertion failed on earlier versions, command exits unsuccessfully on later versions
+ systemctl cat nonexistent@.service || true
+ create_services a
+ systemctl daemon-reload
+ # Assertion failed on earlier versions, command exits unsuccessfully on later versions
+ systemctl cat a@.service || true
+ systemctl stop a
+ clear_units a.service
+ return 0
+}
+
+testcase_symlink_dropin_directory() {
+ # For issue #21920.
+ echo "Testing symlink drop-in directory..."
+ create_services test15-a
+ rmdir /{etc,run,usr/lib}/systemd/system/test15-a.service.d
+ mkdir -p /tmp/TEST-15-DROPIN-test15-a-dropin-directory
+ ln -s /tmp/TEST-15-DROPIN-test15-a-dropin-directory /etc/systemd/system/test15-a.service.d
+ cat >/tmp/TEST-15-DROPIN-test15-a-dropin-directory/override.conf <<EOF
+[Unit]
+Description=hogehoge
+EOF
+ ln -s /tmp/TEST-15-DROPIN-test15-a-dropin-directory-nonexistent /run/systemd/system/test15-a.service.d
+ touch /tmp/TEST-15-DROPIN-test15-a-dropin-directory-regular
+ ln -s /tmp/TEST-15-DROPIN-test15-a-dropin-directory-regular /usr/lib/systemd/system/test15-a.service.d
+ check_ok test15-a Description hogehoge
+
+ clear_units test15-a.service
+}
+
+run_testcases
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-16-EXTEND-TIMEOUT
+# Testsuite: Assess all other testsuite-*.services worked as expected
+
+Wants=success-all.service
+Wants=success-start.service
+Wants=success-runtime.service
+Wants=success-stop.service
+Wants=fail-start.service
+Wants=fail-stop.service
+Wants=fail-runtime.service
+StopWhenUnneeded=yes
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+Type=exec
+TimeoutStartSec=infinity
+ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStart=true
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+rm -f /test.log
+
+TESTLOG=/test.log.XXXXXXXX
+
+wait_for()
+{
+ local service="${1:-wait_for: missing service argument}"
+ local result="${2:-success}"
+ local time="${3:-45}"
+
+ while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]]; do
+ sleep 1
+ time=$((time - 1))
+ done
+
+ if [[ ! -f /${service}.${result} ]]; then
+ journalctl -u "${service/_/-}.service" >>"$TESTLOG"
+ fi
+}
+
+wait_for_timeout()
+{
+ local unit="$1"
+ local time="$2"
+
+ while [[ $time -gt 0 ]]; do
+ if [[ "$(systemctl show --property=Result "$unit")" == "Result=timeout" ]]; then
+ return 0
+ fi
+
+ sleep 1
+ time=$((time - 1))
+ done
+
+ journalctl -u "$unit" >>"$TESTLOG"
+
+ return 1
+}
+
+# This checks all stages, start, runtime and stop, can be extended by
+# EXTEND_TIMEOUT_USEC
+
+wait_for success_all
+
+# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the
+# extend timeout interval but less then the stage limit (TimeoutStartSec,
+# RuntimeMaxSec, TimeoutStopSec) still succeed.
+
+wait_for success_start
+wait_for success_runtime
+wait_for success_stop
+
+# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the
+# appropriate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC
+# message isn't sent within the extend timeout interval.
+
+wait_for fail_start startfail
+wait_for fail_stop stopfail
+wait_for fail_runtime runtimefail
+
+# These ensure that RuntimeMaxSec is honored for scope and service units
+# when they are created.
+runtime_max_sec=5
+
+systemd-run \
+ --property=RuntimeMaxSec=${runtime_max_sec}s \
+ -u runtime-max-sec-test-1.service \
+ /usr/bin/sh -c "while true; do sleep 1; done"
+wait_for_timeout runtime-max-sec-test-1.service $((runtime_max_sec + 2))
+
+systemd-run \
+ --property=RuntimeMaxSec=${runtime_max_sec}s \
+ --scope \
+ -u runtime-max-sec-test-2.scope \
+ /usr/bin/sh -c "while true; do sleep 1; done" &
+wait_for_timeout runtime-max-sec-test-2.scope $((runtime_max_sec + 2))
+
+# These ensure that RuntimeMaxSec is honored for scope and service
+# units if the value is changed and then the manager is reloaded.
+systemd-run \
+ -u runtime-max-sec-test-3.service \
+ /usr/bin/sh -c "while true; do sleep 1; done"
+mkdir -p /etc/systemd/system/runtime-max-sec-test-3.service.d/
+cat > /etc/systemd/system/runtime-max-sec-test-3.service.d/override.conf << EOF
+[Service]
+RuntimeMaxSec=${runtime_max_sec}s
+EOF
+systemctl daemon-reload
+wait_for_timeout runtime-max-sec-test-3.service $((runtime_max_sec + 2))
+
+systemd-run \
+ --scope \
+ -u runtime-max-sec-test-4.scope \
+ /usr/bin/sh -c "while true; do sleep 1; done" &
+
+# Wait until the unit is running to avoid race with creating the override.
+until systemctl is-active runtime-max-sec-test-4.scope; do
+ sleep 1
+done
+mkdir -p /etc/systemd/system/runtime-max-sec-test-4.scope.d/
+cat > /etc/systemd/system/runtime-max-sec-test-4.scope.d/override.conf << EOF
+[Scope]
+RuntimeMaxSec=${runtime_max_sec}s
+EOF
+systemctl daemon-reload
+wait_for_timeout runtime-max-sec-test-4.scope $((runtime_max_sec + 2))
+
+if [[ -f "$TESTLOG" ]]; then
+ # no mv
+ cp "$TESTLOG" /test.log
+ exit 1
+fi
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Tests for issue #28588 and #28653.
+
+# On boot, services need to be started in the following order:
+# 1. systemd-tmpfiles-setup-dev-early.service
+# 2. systemd-sysusers.service
+# 3. systemd-tmpfiles-setup-dev.service
+# 4. systemd-udevd.service
+
+output="$(systemctl show --property After --value systemd-udevd.service)"
+assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
+assert_in "systemd-sysusers.service" "$output"
+assert_in "systemd-tmpfiles-setup-dev.service" "$output"
+
+output="$(systemctl show --property After --value systemd-tmpfiles-setup-dev.service)"
+assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
+assert_in "systemd-sysusers.service" "$output"
+
+output="$(systemctl show --property After --value systemd-sysusers.service)"
+assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
+
+check_owner_and_mode() {
+ local dev=${1?}
+ local user=${2?}
+ local group=${3?}
+ local mode=${4:-}
+
+ if [[ -e "$dev" ]]; then
+ assert_in "$user" "$(stat --format=%U "$dev")"
+ assert_in "$group" "$(stat --format=%G "$dev")"
+ if [[ -n "$mode" ]]; then
+ assert_in "$mode" "$(stat --format=%#0a "$dev")"
+ fi
+ fi
+
+ return 0
+}
+
+# Check owner and access mode specified in static-nodes-permissions.conf
+check_owner_and_mode /dev/snd/seq root audio 0660
+check_owner_and_mode /dev/snd/timer root audio 0660
+check_owner_and_mode /dev/loop-control root disk 0660
+check_owner_and_mode /dev/net/tun root root 0666
+check_owner_and_mode /dev/fuse root root 0666
+check_owner_and_mode /dev/vfio/vfio root root 0666
+check_owner_and_mode /dev/kvm root kvm
+check_owner_and_mode /dev/vhost-net root kvm
+check_owner_and_mode /dev/vhost-vsock root kvm
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+ (
+ udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+ udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+ systemctl show -p WantedBy foobar.service | grep -q -v sda
+ systemctl show -p WantedBy waldo.service | grep -q -v sda
+ ) && break
+
+ sleep .5
+done
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
+EOF
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+ (
+ udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service
+ udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+ systemctl show -p WantedBy foobar.service | grep -q sda
+ systemctl show -p WantedBy waldo.service | grep -q -v sda
+ ) && break
+
+ sleep .5
+done
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
+EOF
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+ (
+ udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+ udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service
+ systemctl show -p WantedBy foobar.service | grep -q -v sda
+ systemctl show -p WantedBy waldo.service | grep -q sda
+ ) && break
+
+ sleep .5
+done
+
+rm /run/udev/rules.d/50-testsuite.rules
+
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+ (
+ udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+ udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+ systemctl show -p WantedBy foobar.service | grep -q -v sda
+ systemctl show -p WantedBy waldo.service | grep -q -v sda
+ ) && break
+
+ sleep .5
+done
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# disable shellcheck warning about '"aaa"' type quotation
+# shellcheck disable=SC2016
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+mkdir -p /run/udev/rules.d/
+
+# test for ID_RENAMING= udev property and device unit state
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="remove", GOTO="hoge_end"
+SUBSYSTEM!="net", GOTO="hoge_end"
+KERNEL!="hoge", GOTO="hoge_end"
+
+OPTIONS="log_level=debug"
+
+# emulate renaming
+ACTION=="online", ENV{ID_RENAMING}="1"
+
+LABEL="hoge_end"
+EOF
+
+udevadm control --log-priority=debug --reload --timeout=30
+
+ip link add hoge type dummy
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+
+udevadm trigger --action=online --settle /sys/devices/virtual/net/hoge
+assert_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
+
+udevadm trigger --action=move --settle /sys/devices/virtual/net/hoge
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+
+# test for renaming interface with NAME= (issue #25106)
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION!="add", GOTO="hoge_end"
+SUBSYSTEM!="net", GOTO="hoge_end"
+
+OPTIONS="log_level=debug"
+
+KERNEL=="hoge", NAME="foobar"
+KERNEL=="foobar", NAME="hoge"
+
+LABEL="hoge_end"
+EOF
+
+udevadm control --log-priority=debug --reload --timeout=30
+
+udevadm trigger --action=add --settle /sys/devices/virtual/net/hoge
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/foobar)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
+
+udevadm trigger --action=add --settle /sys/devices/virtual/net/foobar
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload --timeout=30
+
+# test for renaming interface with an external tool (issue #16967)
+
+ip link set hoge name foobar
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
+
+ip link set foobar name hoge
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
+
+# cleanup
+ip link del hoge
+
+# shellcheck disable=SC2317
+teardown_netif_renaming_conflict() {
+ set +ex
+
+ if [[ -n "$KILL_PID" ]]; then
+ kill "$KILL_PID"
+ fi
+
+ rm -rf "$TMPDIR"
+
+ rm -f /run/udev/rules.d/50-testsuite.rules
+ udevadm control --reload --timeout=30
+
+ ip link del hoge
+ ip link del foobar
+}
+
+test_netif_renaming_conflict() {
+ local since found=
+
+ trap teardown_netif_renaming_conflict RETURN
+
+ cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION!="add", GOTO="hoge_end"
+SUBSYSTEM!="net", GOTO="hoge_end"
+
+OPTIONS="log_level=debug"
+
+KERNEL=="foobar", NAME="hoge"
+
+LABEL="hoge_end"
+EOF
+
+ udevadm control --log-priority=debug --reload --timeout=30
+
+ ip link add hoge type dummy
+ udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+
+ TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+ udevadm monitor --udev --property --subsystem-match=net >"$TMPDIR"/monitor.txt &
+ KILL_PID="$!"
+
+ # make sure that 'udevadm monitor' actually monitor uevents
+ sleep 1
+
+ since="$(date '+%H:%M:%S')"
+
+ # add another interface which will conflict with an existing interface
+ ip link add foobar type dummy
+
+ for _ in {1..40}; do
+ if (
+ grep -q 'ACTION=add' "$TMPDIR"/monitor.txt
+ grep -q 'DEVPATH=/devices/virtual/net/foobar' "$TMPDIR"/monitor.txt
+ grep -q 'SUBSYSTEM=net' "$TMPDIR"/monitor.txt
+ grep -q 'INTERFACE=foobar' "$TMPDIR"/monitor.txt
+ grep -q 'ID_NET_DRIVER=dummy' "$TMPDIR"/monitor.txt
+ grep -q 'ID_NET_NAME=foobar' "$TMPDIR"/monitor.txt
+ # Even when network interface renaming is failed, SYSTEMD_ALIAS with the conflicting name will be broadcast.
+ grep -q 'SYSTEMD_ALIAS=/sys/subsystem/net/devices/hoge' "$TMPDIR"/monitor.txt
+ grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
+ grep -q 'UDEV_WORKER_ERRNO=17' "$TMPDIR"/monitor.txt
+ grep -q 'UDEV_WORKER_ERRNO_NAME=EEXIST' "$TMPDIR"/monitor.txt
+ ); then
+ cat "$TMPDIR"/monitor.txt
+ found=1
+ break
+ fi
+ sleep .5
+ done
+ test -n "$found"
+
+ timeout 30 bash -c "until journalctl _PID=1 _COMM=systemd --since $since | grep -q 'foobar: systemd-udevd failed to process the device, ignoring: File exists'; do sleep 1; done"
+ # check if the invalid SYSTEMD_ALIAS property for the interface foobar is ignored by PID1
+ assert_eq "$(systemctl show --property=SysFSPath --value /sys/subsystem/net/devices/hoge)" "/sys/devices/virtual/net/hoge"
+}
+
+test_netif_renaming_conflict
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+
+TMPDIR=
+TEST_RULE="/run/udev/rules.d/49-test.rules"
+TEST_CONF="/run/udev/udev.conf.d/test-17.conf"
+KILL_PID=
+
+setup() {
+ mkdir -p "${TEST_RULE%/*}"
+ mkdir -p /run/udev/udev.conf.d
+
+ cat >"${TEST_RULE}" <<EOF
+ACTION!="add", GOTO="test_end"
+SUBSYSTEM!="mem", GOTO="test_end"
+KERNEL!="null", GOTO="test_end"
+
+OPTIONS="log_level=debug"
+PROGRAM=="/bin/touch /tmp/test-udev-marker"
+PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
+
+LABEL="test_end"
+EOF
+ cat >"$TEST_CONF" <<EOF
+event_timeout=10
+timeout_signal=SIGABRT
+EOF
+
+ systemctl restart systemd-udevd.service
+}
+
+# shellcheck disable=SC2317
+teardown() {
+ set +e
+
+ if [[ -n "$KILL_PID" ]]; then
+ kill "$KILL_PID"
+ fi
+
+ rm -rf "$TMPDIR"
+ rm -f "$TEST_RULE" "$TEST_CONF"
+ systemctl restart systemd-udevd.service
+}
+
+run_test_timeout() {
+ TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+ udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+ KILL_PID="$!"
+
+ SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+ for _ in {1..40}; do
+ if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
+ sleep .5
+ kill "$KILL_PID"
+ KILL_PID=
+
+ cat "$TMPDIR"/monitor.txt
+ (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
+ (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
+ (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
+ grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
+ rm -rf "$TMPDIR"
+ return 0
+ fi
+ sleep .5
+ done
+
+ return 1
+}
+
+run_test_killed() {
+ local killed=
+
+ TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+ udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+ KILL_PID="$!"
+
+ rm -f /tmp/test-udev-marker
+ SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+ for _ in {1..40}; do
+ if [[ -z "$killed" ]]; then
+ if [[ -e /tmp/test-udev-marker ]]; then
+ killall --signal ABRT --regexp udev-worker
+ killed=1
+ fi
+ elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
+ sleep .5
+ kill "$KILL_PID"
+ KILL_PID=
+
+ cat "$TMPDIR"/monitor.txt
+ grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
+ grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
+ grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
+ (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
+ rm -rf "$TMPDIR"
+ return 0
+ fi
+ sleep .5
+ done
+
+ return 1
+}
+
+trap teardown EXIT
+
+setup
+run_test_timeout
+run_test_killed
+
+exit 0
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+test ! -f /run/udev/tags/added/c1:3
+test ! -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
+ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added"
+ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed"
+EOF
+
+udevadm control --reload
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
+
+test -f /run/udev/tags/added/c1:3
+test ! -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && { echo 'unexpected TAGS='; exit 1; }
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
+
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action change /dev/null
+
+test -f /run/udev/tags/added/c1:3
+test -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
+udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
+
+test -f /run/udev/tags/added/c1:3
+test -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
+
+rm /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+exit 0
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
+ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/echo -e HOGE=aa\\\\x20\\\\x20\\\\x20bb\nFOO=\\\\x20aaa\\\\x20\n\n\n"
+EOF
+
+udevadm control --reload
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
+
+test -f /run/udev/data/c1:3
+udevadm info /dev/null | grep -q 'E: HOGE=aa\\x20\\x20\\x20bb'
+udevadm info /dev/null | grep -q 'E: FOO=\\x20aaa\\x20'
+
+rm /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+exit 0
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# tests for udev watch
+
+function check_validity() {
+ local f ID_OR_HANDLE
+
+ for f in /run/udev/watch/*; do
+ ID_OR_HANDLE="$(readlink "$f")"
+ test -L "/run/udev/watch/${ID_OR_HANDLE}"
+ test "$(readlink "/run/udev/watch/${ID_OR_HANDLE}")" = "$(basename "$f")"
+ done
+}
+
+function check() {
+ for _ in {1..2}; do
+ systemctl restart systemd-udevd.service
+ udevadm control --ping
+ udevadm settle
+ check_validity
+
+ for _ in {1..2}; do
+ udevadm trigger -w --action add --subsystem-match=block
+ check_validity
+ done
+
+ for _ in {1..2}; do
+ udevadm trigger -w --action change --subsystem-match=block
+ check_validity
+ done
+ done
+}
+
+mkdir -p /run/udev/rules.d/
+
+cat >/run/udev/rules.d/00-debug.rules <<EOF
+SUBSYSTEM=="block", KERNEL=="sda*", OPTIONS="log_level=debug"
+EOF
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="add", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="watch"
+EOF
+
+check
+
+MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
+MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
+test -L "/run/udev/watch/b${MAJOR}:${MINOR}"
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="change", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="nowatch"
+EOF
+
+check
+
+MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
+MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
+test ! -e "/run/udev/watch/b${MAJOR}:${MINOR}"
+
+rm /run/udev/rules.d/00-debug.rules
+rm /run/udev/rules.d/50-testsuite.rules
+
+udevadm control --reload
+systemctl reset-failed systemd-udevd.service
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+wait_service_active() {(
+ set +ex
+ for i in {1..20}; do
+ (( i > 1 )) && sleep 0.5
+ if systemctl --quiet is-active "${1?}"; then
+ return 0
+ fi
+ done
+ return 1
+)}
+
+wait_service_inactive() {(
+ set +ex
+ for i in {1..20}; do
+ (( i > 1 )) && sleep 0.5
+ systemctl --quiet is-active "${1?}"
+ if [[ "$?" == "3" ]]; then
+ return 0
+ fi
+ done
+ return 1
+)}
+
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/both.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+systemctl daemon-reload
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="net", KERNEL=="dummy9?", OPTIONS="log_level=debug"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-add.service"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-change.service"
+EOF
+
+udevadm control --reload
+
+# StopWhenUnneeded=no
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+systemctl stop both.service on-add.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+udevadm info /sys/class/net/dummy99
+wait_service_active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+systemctl stop both.service on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 3 systemctl --quiet is-active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes
+cat >/run/systemd/system/both.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=echo changed
+RemainAfterExit=true
+Type=oneshot
+EOF
+
+systemctl daemon-reload
+
+# StopWhenUnneeded=yes (single device, only add event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (single device, add and change event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+wait_service_active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, only add events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, add and change events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+assert_rc 0 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+rm -f /run/systemd/system/on-add.service
+rm -f /run/systemd/system/on-change.service
+systemctl daemon-reload
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# This is a test for issue #24518.
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug", TAG+="systemd"
+SUBSYSTEM=="mem", KERNEL=="null", ACTION=="add", SYMLINK+="test/symlink-to-null-on-add", ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-add"
+SUBSYSTEM=="mem", KERNEL=="null", ACTION=="change", SYMLINK+="test/symlink-to-null-on-change", ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-change"
+EOF
+
+udevadm control --reload
+
+udevadm trigger --settle --action add /dev/null
+for i in {1..20}; do
+ ((i > 1)) && sleep .5
+
+ (
+ systemctl -q is-active /dev/test/symlink-to-null-on-add
+ ! systemctl -q is-active /dev/test/symlink-to-null-on-change
+ systemctl -q is-active /sys/test/alias-to-null-on-add
+ ! systemctl -q is-active /sys/test/alias-to-null-on-change
+ ) && break
+done
+assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
+assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
+assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
+assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
+
+udevadm trigger --settle --action change /dev/null
+for i in {1..20}; do
+ ((i > 1)) && sleep .5
+
+ (
+ ! systemctl -q is-active /dev/test/symlink-to-null-on-add
+ systemctl -q is-active /dev/test/symlink-to-null-on-change
+ ! systemctl -q is-active /sys/test/alias-to-null-on-add
+ systemctl -q is-active /sys/test/alias-to-null-on-change
+ ) && break
+done
+assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-add
+assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-change
+assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-add
+assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-change
+
+udevadm trigger --settle --action add /dev/null
+for i in {1..20}; do
+ ((i > 1)) && sleep .5
+
+ (
+ systemctl -q is-active /dev/test/symlink-to-null-on-add
+ ! systemctl -q is-active /dev/test/symlink-to-null-on-change
+ systemctl -q is-active /sys/test/alias-to-null-on-add
+ ! systemctl -q is-active /sys/test/alias-to-null-on-change
+ ) && break
+done
+assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
+assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
+assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
+assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# This is a test for issue #24987.
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM!="mem", GOTO="test-end"
+KERNEL!="null", GOTO="test-end"
+ACTION=="remove", GOTO="test-end"
+
+# add 100 * 100byte of properties
+$(for i in {1..100}; do printf 'ENV{XXX%03i}="0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"\n' "$i"; done)
+
+LABEL="test-end"
+EOF
+
+udevadm control --reload
+
+TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+SYSTEMD_LOG_LEVEL=debug udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt 2>&1 &
+KILL_PID="$!"
+
+FOUND=
+for _ in {1..40}; do
+ if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then
+ FOUND=1
+ break
+ fi
+ sleep .5
+done
+[[ -n "$FOUND" ]]
+
+udevadm trigger --verbose --settle --action add /dev/null
+
+FOUND=
+for _ in {1..40}; do
+ if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then
+ sleep .5
+ continue
+ fi
+
+ FOUND=1
+ for i in {1..100}; do
+ if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then
+ FOUND=
+ break
+ fi
+ done
+ if [[ -n "$FOUND" ]]; then
+ break;
+ fi
+
+ sleep .5
+done
+[[ -n "$FOUND" ]]
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+kill "$KILL_PID"
+rm -rf "$TMPDIR"
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Coverage test for udevadm
+
+# shellcheck disable=SC2317
+cleanup_17_10() {
+ set +e
+
+ losetup -d "$loopdev"
+ rm -f "$blk"
+
+ ip link delete "$netdev"
+}
+
+# Set up some test devices
+trap cleanup_17_10 EXIT
+
+netdev=dummy17.10
+ip link add $netdev type dummy
+
+blk="$(mktemp)"
+dd if=/dev/zero of="$blk" bs=1M count=1
+loopdev="$(losetup --show -f "$blk")"
+
+udevadm -h
+
+udevadm control -e
+udevadm control -l emerg
+udevadm control -l alert
+udevadm control -l crit
+udevadm control -l err
+udevadm control -l warning
+udevadm control -l notice
+udevadm control --log-level info
+udevadm control --log-level debug
+(! udevadm control -l hello)
+udevadm control -s
+udevadm control -S
+udevadm control -R
+udevadm control -p HELLO=world
+udevadm control -m 42
+udevadm control --ping -t 5
+udevadm control --load-credentials
+udevadm control -h
+
+udevadm info /dev/null
+udevadm info /sys/class/net/$netdev
+udevadm info "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm info --property DEVNAME /sys/class/net/$netdev
+udevadm info --property DEVNAME --value /sys/class/net/$netdev
+udevadm info --property HELLO /sys/class/net/$netdev
+udevadm info -p class/net/$netdev
+udevadm info -p /class/net/$netdev
+udevadm info --json=off -p class/net/$netdev
+udevadm info --json=pretty -p class/net/$netdev | jq .
+udevadm info --json=short -p class/net/$netdev | jq .
+udevadm info -n null
+udevadm info -q all /sys/class/net/$netdev
+udevadm info -q name /dev/null
+udevadm info -q path /sys/class/net/$netdev
+udevadm info -q property /sys/class/net/$netdev
+udevadm info -q symlink /sys/class/net/$netdev
+udevadm info -q name -r /dev/null
+udevadm info --query symlink --root /sys/class/net/$netdev
+(! udevadm info -q hello -r /sys/class/net/$netdev)
+udevadm info -a /sys/class/net/$netdev
+udevadm info -t >/dev/null
+udevadm info --tree /sys/class/net/$netdev
+udevadm info -x /sys/class/net/$netdev
+udevadm info -x -q path /sys/class/net/$netdev
+udevadm info -P TEST_ /sys/class/net/$netdev
+udevadm info -d /dev/null
+udevadm info -e >/dev/null
+udevadm info -e --json=off >/dev/null
+udevadm info -e --json=pretty | jq . >/dev/null
+udevadm info -e --json=short | jq . >/dev/null
+udevadm info -e --subsystem-match acpi >/dev/null
+udevadm info -e --subsystem-nomatch acpi >/dev/null
+udevadm info -e --attr-match ifindex=2 >/dev/null
+udevadm info -e --attr-nomatch ifindex=2 >/dev/null
+udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null
+udevadm info -e --tag-match systemd >/dev/null
+udevadm info -e --sysname-match lo >/dev/null
+udevadm info -e --name-match /sys/class/net/$netdev >/dev/null
+udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null
+udevadm info -e --initialized-match >/dev/null
+udevadm info -e --initialized-nomatch >/dev/null
+# udevadm info -c
+udevadm info -w /sys/class/net/$netdev
+udevadm info --wait-for-initialization=5 /sys/class/net/$netdev
+udevadm info -h
+
+assert_rc 124 timeout 1 udevadm monitor
+assert_rc 124 timeout 1 udevadm monitor -k
+assert_rc 124 timeout 1 udevadm monitor -u
+assert_rc 124 timeout 1 udevadm monitor -s net
+assert_rc 124 timeout 1 udevadm monitor --subsystem-match net/$netdev
+assert_rc 124 timeout 1 udevadm monitor -t systemd
+assert_rc 124 timeout 1 udevadm monitor --tag-match hello
+udevadm monitor -h
+
+udevadm settle
+udevadm settle -t 5
+udevadm settle -E /sys/class/net/$netdev
+udevadm settle -h
+
+udevadm test /dev/null
+udevadm info /sys/class/net/$netdev
+udevadm test "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm test -a add /sys/class/net/$netdev
+udevadm test -a change /sys/class/net/$netdev
+udevadm test -a move /sys/class/net/$netdev
+udevadm test -a online /sys/class/net/$netdev
+udevadm test -a offline /sys/class/net/$netdev
+udevadm test -a bind /sys/class/net/$netdev
+udevadm test -a unbind /sys/class/net/$netdev
+udevadm test -a help /sys/class/net/$netdev
+udevadm test --action help
+(! udevadm test -a hello /sys/class/net/$netdev)
+udevadm test -N early /sys/class/net/$netdev
+udevadm test -N late /sys/class/net/$netdev
+udevadm test --resolve-names never /sys/class/net/$netdev
+(! udevadm test -N hello /sys/class/net/$netdev)
+udevadm test -h
+
+# udevadm test-builtin path_id "$loopdev"
+udevadm test-builtin net_id /sys/class/net/$netdev
+udevadm test-builtin net_id "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm test-builtin -a add net_id /sys/class/net/$netdev
+udevadm test-builtin -a remove net_id /sys/class/net/$netdev
+udevadm test-builtin -a change net_id /sys/class/net/$netdev
+udevadm test-builtin -a move net_id /sys/class/net/$netdev
+udevadm test-builtin -a online net_id /sys/class/net/$netdev
+udevadm test-builtin -a offline net_id /sys/class/net/$netdev
+udevadm test-builtin -a bind net_id /sys/class/net/$netdev
+udevadm test-builtin -a unbind net_id /sys/class/net/$netdev
+udevadm test-builtin -a help net_id /sys/class/net/$netdev
+udevadm test-builtin net_setup_link /sys/class/net/$netdev
+udevadm test-builtin blkid "$loopdev"
+udevadm test-builtin input_id /sys/class/net/$netdev
+udevadm test-builtin keyboard /dev/null
+# udevadm test-builtin kmod /sys/class/net/$netdev
+udevadm test-builtin uaccess /dev/null
+# udevadm test-builtin usb_id dev/null
+(! udevadm test-builtin hello /sys/class/net/$netdev)
+# systemd-hwdb update is extremely slow when combined with sanitizers and run
+# in a VM without acceleration, so let's just skip the one particular test
+# if we detect this combination
+if ! [[ -v ASAN_OPTIONS && "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ modprobe scsi_debug
+ scsidev=$(readlink -f /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/[0-9]*)
+ mkdir -p /etc/udev/hwdb.d
+ cat >/etc/udev/hwdb.d/99-test.hwdb <<EOF
+scsi:*
+ ID_TEST=test
+EOF
+ systemd-hwdb update
+
+ udevadm test-builtin hwdb "$scsidev"
+
+ rmmod scsi_debug || :
+ rm -fv /etc/udev/hwdb.d/99-test.hwdb
+ systemd-hwdb update
+fi
+
+
+udevadm trigger
+udevadm trigger /dev/null
+udevadm trigger /sys/class/net/$netdev
+udevadm trigger "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm trigger -v /sys/class/net/$netdev
+udevadm trigger -n /sys/class/net/$netdev
+udevadm trigger -q /sys/class/net/$netdev
+udevadm trigger -t all /sys/class/net/$netdev
+udevadm trigger -t devices /sys/class/net/$netdev
+udevadm trigger --type subsystems /sys/class/net/$netdev
+(! udevadm trigger -t hello /sys/class/net/$netdev)
+udevadm trigger -c add /sys/class/net/$netdev
+udevadm trigger -c remove /sys/class/net/$netdev
+udevadm trigger -c change /sys/class/net/$netdev
+udevadm trigger -c move /sys/class/net/$netdev
+udevadm trigger -c online /sys/class/net/$netdev
+udevadm trigger -c offline /sys/class/net/$netdev
+udevadm trigger -c bind /sys/class/net/$netdev
+udevadm trigger -c unbind /sys/class/net/$netdev
+udevadm trigger -c help /sys/class/net/$netdev
+udevadm trigger --action help /sys/class/net/$netdev
+(! udevadm trigger -c hello /sys/class/net/$netdev)
+udevadm trigger --prioritized-subsystem block
+udevadm trigger --prioritized-subsystem block,net
+udevadm trigger --prioritized-subsystem hello
+udevadm trigger -s net
+udevadm trigger -S net
+udevadm trigger -a subsystem=net
+udevadm trigger --attr-match hello=world
+udevadm trigger -p DEVNAME=null
+udevadm trigger --property-match HELLO=world
+udevadm trigger -g systemd
+udevadm trigger --tag-match hello
+udevadm trigger -y net
+udevadm trigger --sysname-match hello
+udevadm trigger --name-match /sys/class/net/$netdev
+udevadm trigger --name-match /sys/class/net/$netdev --name-match /dev/null
+udevadm trigger -b /sys/class/net/$netdev
+udevadm trigger --parent-match /sys/class/net/$netdev --name-match /dev/null
+udevadm trigger --initialized-match
+udevadm trigger --initialized-nomatch
+udevadm trigger -w
+udevadm trigger --uuid /sys/class/net/$netdev
+udevadm settle -t 300
+udevadm trigger --wait-daemon
+udevadm settle -t 300
+udevadm trigger --wait-daemon=5
+udevadm trigger -h
+
+# https://github.com/systemd/systemd/issues/29863
+if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
+ udevadm control --log-level=0
+ for _ in {0..9}; do
+ timeout 30 udevadm trigger --settle
+ done
+ udevadm control --log-level=debug
+fi
+
+udevadm wait /dev/null
+udevadm wait /sys/class/net/$netdev
+udevadm wait -t 5 /sys/class/net/$netdev
+udevadm wait --initialized true /sys/class/net/$netdev
+udevadm wait --initialized false /sys/class/net/$netdev
+(! udevadm wait --initialized hello /sys/class/net/$netdev)
+assert_rc 124 timeout 5 udevadm wait --removed /sys/class/net/$netdev
+udevadm wait --settle /sys/class/net/$netdev
+udevadm wait -h
+
+udevadm lock --help
+udevadm lock --version
+for i in /dev/block/*; do
+ udevadm lock --device "$i" --print
+ udevadm lock --device "$i" true
+ (! udevadm lock --device "$i" false)
+done
+for i in / /usr; do
+ udevadm lock --backing "$i" --print
+ udevadm lock --backing "$i" true
+ (! udevadm lock --backing "$i" false)
+done
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# Test for udevadm verify.
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# shellcheck disable=SC2317
+cleanup() {
+ cd /
+ rm -rf "${workdir}"
+ workdir=
+}
+
+workdir="$(mktemp -d)"
+trap cleanup EXIT
+cd "${workdir}"
+
+cat >"${workdir}/default_output_1_success" <<EOF
+
+1 udev rules files have been checked.
+ Success: 1
+ Fail: 0
+EOF
+cat >"${workdir}/default_output_1_fail" <<EOF
+
+1 udev rules files have been checked.
+ Success: 0
+ Fail: 1
+EOF
+cat >"${workdir}/output_0_files" <<EOF
+
+0 udev rules files have been checked.
+ Success: 0
+ Fail: 0
+EOF
+
+test_number=0
+rules=
+exp=
+err=
+out=
+next_test_number() {
+ : $((++test_number))
+
+ local num_str
+ num_str=$(printf %05d "${test_number}")
+
+ rules="sample-${num_str}.rules"
+ exp="sample-${num_str}.exp"
+ err="sample-${num_str}.err"
+ exo="sample-${num_str}.exo"
+ out="sample-${num_str}.out"
+}
+
+assert_0_impl() {
+ udevadm verify "$@" >"${out}"
+ if [ -f "${exo}" ]; then
+ diff -u "${exo}" "${out}"
+ elif [ -f "${rules}" ]; then
+ diff -u "${workdir}/default_output_1_success" "${out}"
+ fi
+}
+
+assert_0() {
+ assert_0_impl "$@"
+ next_test_number
+}
+
+assert_1_impl() {
+ local rc
+ set +e
+ udevadm verify "$@" >"${out}" 2>"${err}"
+ rc=$?
+ set -e
+ assert_eq "$rc" 1
+
+ if [ -f "${exp}" ]; then
+ diff -u "${exp}" "${err}"
+ fi
+
+ if [ -f "${exo}" ]; then
+ diff -u "${exo}" "${out}"
+ elif [ -f "${rules}" ]; then
+ diff -u "${workdir}/default_output_1_fail" "${out}"
+ fi
+}
+
+assert_1() {
+ assert_1_impl "$@"
+ next_test_number
+}
+
+# initialize variables
+next_test_number
+
+assert_0 -h
+assert_0 --help
+assert_0 -V
+assert_0 --version
+assert_0 /dev/null
+
+# unrecognized option '--unknown'
+assert_1 --unknown
+# option requires an argument -- 'N'
+assert_1 -N
+# --resolve-names= takes "early" or "never"
+assert_1 -N now
+# option '--resolve-names' requires an argument
+assert_1 --resolve-names
+# --resolve-names= takes "early" or "never"
+assert_1 --resolve-names=now
+# Failed to parse rules file ./nosuchfile: No such file or directory
+assert_1 ./nosuchfile
+# Failed to parse rules file ./nosuchfile: No such file or directory
+cat >"${exo}" <<EOF
+
+3 udev rules files have been checked.
+ Success: 2
+ Fail: 1
+EOF
+assert_1 /dev/null ./nosuchfile /dev/null
+
+rules_dir='etc/udev/rules.d'
+mkdir -p "${rules_dir}"
+# No rules files found in $PWD
+assert_1 --root="${workdir}"
+
+# Directory without rules.
+cp "${workdir}/output_0_files" "${exo}"
+assert_0 "${rules_dir}"
+
+# Directory with a loop.
+ln -s . "${rules_dir}/loop.rules"
+assert_1 "${rules_dir}"
+rm "${rules_dir}/loop.rules"
+
+# Empty rules.
+touch "${rules_dir}/empty.rules"
+assert_0 --root="${workdir}"
+: >"${exo}"
+assert_0 --root="${workdir}" --no-summary
+
+# Directory with a single *.rules file.
+cp "${workdir}/default_output_1_success" "${exo}"
+assert_0 "${rules_dir}"
+
+# Combination of --root= and FILEs is not supported.
+assert_1 --root="${workdir}" /dev/null
+# No rules files found in nosuchdir
+assert_1 --root=nosuchdir
+
+cd "${rules_dir}"
+
+# UDEV_LINE_SIZE 16384
+printf '%16383s\n' ' ' >"${rules}"
+assert_0 "${rules}"
+
+# Failed to parse rules file ${rules}: No buffer space available
+printf '%16384s\n' ' ' >"${rules}"
+echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}"
+assert_1 "${rules}"
+
+{
+ printf 'RUN+="/bin/true",%8174s\\\n' ' '
+ printf 'RUN+="/bin/false"%8174s\\\n' ' '
+ echo
+} >"${rules}"
+assert_0 "${rules}"
+
+printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}"
+echo >>"${rules}"
+cat >"${exp}" <<EOF
+${rules}:5 Line is too long, ignored.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+printf '\\\n' >"${rules}"
+cat >"${exp}" <<EOF
+${rules}:1 Unexpected EOF after line continuation, line ignored.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+test_syntax_error() {
+ local rule msg
+
+ rule="$1"; shift
+ msg="$1"; shift
+
+ printf '%s\n' "${rule}" >"${rules}"
+ cat >"${exp}" <<EOF
+${rules}:1 ${msg}
+${rules}: udev rules check failed.
+EOF
+ assert_1 "${rules}"
+}
+
+test_style_error() {
+ local rule msg
+
+ rule="$1"; shift
+ msg="$1"; shift
+
+ printf '%s\n' "${rule}" >"${rules}"
+ cat >"${exp}" <<EOF
+${rules}:1 ${msg}
+${rules}: udev rules have style issues.
+EOF
+ assert_0_impl --no-style "${rules}"
+ assert_1_impl "${rules}"
+ next_test_number
+}
+
+test_syntax_error '=' 'Invalid key/value pair, ignoring.'
+test_syntax_error 'ACTION{a}=="b"' 'Invalid attribute for ACTION.'
+test_syntax_error 'ACTION:="b"' 'Invalid operator for ACTION.'
+test_syntax_error 'ACTION=="b"' 'The line has no effect, ignoring.'
+test_syntax_error 'DEVPATH{a}=="b"' 'Invalid attribute for DEVPATH.'
+test_syntax_error 'DEVPATH:="b"' 'Invalid operator for DEVPATH.'
+test_syntax_error 'KERNEL{a}=="b"' 'Invalid attribute for KERNEL.'
+test_syntax_error 'KERNEL:="b"' 'Invalid operator for KERNEL.'
+test_syntax_error 'KERNELS{a}=="b"' 'Invalid attribute for KERNELS.'
+test_syntax_error 'KERNELS:="b"' 'Invalid operator for KERNELS.'
+test_syntax_error 'SYMLINK{a}=="b"' 'Invalid attribute for SYMLINK.'
+test_syntax_error 'SYMLINK:="%?"' 'Invalid value "%?" for SYMLINK (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'NAME{a}=="b"' 'Invalid attribute for NAME.'
+test_syntax_error 'NAME-="b"' 'Invalid operator for NAME.'
+test_syntax_error 'NAME+="a"' "NAME key takes '==', '!=', '=', or ':=' operator, assuming '='."
+test_syntax_error 'NAME:=""' 'Ignoring NAME="", as udev will not delete any network interfaces.'
+test_syntax_error 'NAME="%k"' 'Ignoring NAME="%k", as it will take no effect.'
+test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
+test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
+test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
+test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
+test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
+test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
+test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
+test_syntax_error 'TAG{a}=="b"' 'Invalid attribute for TAG.'
+test_syntax_error 'TAG:="a"' "TAG key takes '==', '!=', '=', or '+=' operator, assuming '='."
+test_syntax_error 'TAG="%?"' 'Invalid value "%?" for TAG (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'TAGS{a}=="b"' 'Invalid attribute for TAGS.'
+test_syntax_error 'TAGS:="a"' 'Invalid operator for TAGS.'
+test_syntax_error 'SUBSYSTEM{a}=="b"' 'Invalid attribute for SUBSYSTEM.'
+test_syntax_error 'SUBSYSTEM:="b"' 'Invalid operator for SUBSYSTEM.'
+test_syntax_error 'SUBSYSTEM=="bus", NAME="b"' '"bus" must be specified as "subsystem".'
+test_syntax_error 'SUBSYSTEMS{a}=="b"' 'Invalid attribute for SUBSYSTEMS.'
+test_syntax_error 'SUBSYSTEMS:="b"' 'Invalid operator for SUBSYSTEMS.'
+test_syntax_error 'DRIVER{a}=="b"' 'Invalid attribute for DRIVER.'
+test_syntax_error 'DRIVER:="b"' 'Invalid operator for DRIVER.'
+test_syntax_error 'DRIVERS{a}=="b"' 'Invalid attribute for DRIVERS.'
+test_syntax_error 'DRIVERS:="b"' 'Invalid operator for DRIVERS.'
+test_syntax_error 'ATTR="b"' 'Invalid attribute for ATTR.'
+test_syntax_error 'ATTR{%}="b"' 'Invalid attribute "%" for ATTR (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'ATTR{a}-="b"' 'Invalid operator for ATTR.'
+test_syntax_error 'ATTR{a}+="b"' "ATTR key takes '==', '!=', or '=' operator, assuming '='."
+test_syntax_error 'ATTR{a}="%?"' 'Invalid value "%?" for ATTR (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SYSCTL=""' 'Invalid attribute for SYSCTL.'
+test_syntax_error 'SYSCTL{%}="b"' 'Invalid attribute "%" for SYSCTL (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SYSCTL{a}-="b"' 'Invalid operator for SYSCTL.'
+test_syntax_error 'SYSCTL{a}+="b"' "SYSCTL key takes '==', '!=', or '=' operator, assuming '='."
+test_syntax_error 'SYSCTL{a}="%?"' 'Invalid value "%?" for SYSCTL (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'ATTRS=""' 'Invalid attribute for ATTRS.'
+test_syntax_error 'ATTRS{%}=="b", NAME="b"' 'Invalid attribute "%" for ATTRS (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'ATTRS{a}-="b"' 'Invalid operator for ATTRS.'
+test_syntax_error 'ATTRS{device/}!="a", NAME="b"' "'device' link may not be available in future kernels."
+test_syntax_error 'ATTRS{../}!="a", NAME="b"' 'Direct reference to parent sysfs directory, may break in future kernels.'
+test_syntax_error 'TEST{a}=="b"' "Failed to parse mode 'a': Invalid argument"
+test_syntax_error 'TEST{0}=="%", NAME="b"' 'Invalid value "%" for TEST (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
+test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
+test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
+test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
+test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
+test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
+test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
+test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
+test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'
+test_syntax_error 'OPTIONS{a}="b"' 'Invalid attribute for OPTIONS.'
+test_syntax_error 'OPTIONS-="b"' 'Invalid operator for OPTIONS.'
+test_syntax_error 'OPTIONS!="b"' 'Invalid operator for OPTIONS.'
+test_syntax_error 'OPTIONS+="link_priority=a"' "Failed to parse link priority 'a': Invalid argument"
+test_syntax_error 'OPTIONS:="log_level=a"' "Failed to parse log level 'a': Invalid argument"
+test_syntax_error 'OPTIONS="a", NAME="b"' "Invalid value for OPTIONS key, ignoring: 'a'"
+test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
+test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
+test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
+test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
+test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring."
+test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
+test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
+test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
+test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
+test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring."
+test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
+test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
+test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
+test_syntax_error 'MODE+="0"' "MODE key takes '=' or ':=' operator, assuming '='."
+test_syntax_error 'MODE="%"' 'Invalid value "%" for MODE (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SECLABEL="b"' 'Invalid attribute for SECLABEL.'
+test_syntax_error 'SECLABEL{a}="%"' 'Invalid value "%" for SECLABEL (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SECLABEL{a}!="b"' 'Invalid operator for SECLABEL.'
+test_syntax_error 'SECLABEL{a}-="b"' 'Invalid operator for SECLABEL.'
+test_syntax_error 'SECLABEL{a}:="b"' "SECLABEL key takes '=' or '+=' operator, assuming '='."
+test_syntax_error 'RUN=="b"' 'Invalid operator for RUN.'
+test_syntax_error 'RUN-="b"' 'Invalid operator for RUN.'
+test_syntax_error 'RUN="%"' 'Invalid value "%" for RUN (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'RUN{builtin}+="foo"' "Unknown builtin command 'foo', ignoring."
+test_syntax_error 'GOTO{a}="b"' 'Invalid attribute for GOTO.'
+test_syntax_error 'GOTO=="b"' 'Invalid operator for GOTO.'
+test_syntax_error 'NAME="a", GOTO="b"' 'GOTO="b" has no matching label, ignoring.'
+test_syntax_error 'GOTO="a", GOTO="b"
+LABEL="a"' 'Contains multiple GOTO keys, ignoring GOTO="b".'
+test_syntax_error 'LABEL{a}="b"' 'Invalid attribute for LABEL.'
+test_syntax_error 'LABEL=="b"' 'Invalid operator for LABEL.'
+test_style_error 'LABEL="b"' 'style: LABEL="b" is unused.'
+test_syntax_error 'a="b"' "Invalid key 'a'."
+test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL=="a|b", KERNEL=="c|d|e", NAME="f"' 'conflicting match expressions, the line has no effect.'
+# shellcheck disable=SC2016
+test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}!="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
+ 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'ACTION=="a*", ACTION=="bc*", NAME="d"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'ACTION=="a*|bc*", ACTION=="d*|ef*", NAME="g"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions.'
+test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions.'
+# shellcheck disable=SC2016
+test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
+ 'duplicate expressions.'
+test_style_error ',ACTION=="a", NAME="b"' 'style: stray leading comma.'
+test_style_error ' ,ACTION=="a", NAME="b"' 'style: stray leading comma.'
+test_style_error ', ACTION=="a", NAME="b"' 'style: stray leading comma.'
+test_style_error 'ACTION=="a", NAME="b",' 'style: stray trailing comma.'
+test_style_error 'ACTION=="a", NAME="b", ' 'style: stray trailing comma.'
+test_style_error 'ACTION=="a" NAME="b"' 'style: a comma between tokens is expected.'
+test_style_error 'ACTION=="a",, NAME="b"' 'style: more than one comma between tokens.'
+test_style_error 'ACTION=="a" , NAME="b"' 'style: stray whitespace before comma.'
+test_style_error 'ACTION=="a",NAME="b"' 'style: whitespace after comma is expected.'
+test_syntax_error 'RESULT=="a", PROGRAM="b"' 'Reordering RESULT check after PROGRAM assignment.'
+test_syntax_error 'RESULT=="a*", PROGRAM="b", RESULT=="*c", PROGRAM="d"' \
+ 'Reordering RESULT check after PROGRAM assignment.'
+
+cat >"${rules}" <<'EOF'
+KERNEL=="a|b", KERNEL=="a|c", NAME="d"
+KERNEL=="a|b", KERNEL!="a|c", NAME="d"
+KERNEL!="a", KERNEL!="b", NAME="c"
+KERNEL=="|a", KERNEL=="|b", NAME="c"
+KERNEL=="*", KERNEL=="a*", NAME="b"
+KERNEL=="a*", KERNEL=="c*|ab*", NAME="d"
+PROGRAM="a", RESULT=="b"
+EOF
+assert_0 "${rules}"
+
+echo 'GOTO="a"' >"${rules}"
+cat >"${exp}" <<EOF
+${rules}:1 GOTO="a" has no matching label, ignoring.
+${rules}:1 The line has no effect any more, dropping.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+cat >"${rules}" <<'EOF'
+GOTO="a"
+LABEL="a"
+EOF
+assert_0 "${rules}"
+
+cat >"${rules}" <<'EOF'
+GOTO="b"
+LABEL="b"
+LABEL="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:3 style: LABEL="b" is unused.
+${rules}: udev rules have style issues.
+EOF
+assert_0_impl --no-style "${rules}"
+assert_1_impl "${rules}"
+
+cat >"${rules}" <<'EOF'
+GOTO="a"
+LABEL="a", LABEL="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
+${rules}:1 GOTO="a" has no matching label, ignoring.
+${rules}:1 The line has no effect any more, dropping.
+${rules}:2 style: LABEL="b" is unused.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+cat >"${rules}" <<'EOF'
+KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
+EOF
+cat >"${exp}" <<EOF
+${rules}:1 duplicate expressions.
+${rules}:1 conflicting match expressions, the line has no effect.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+cat >"${rules}" <<'EOF'
+ACTION=="a"NAME="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:1 style: a comma between tokens is expected.
+${rules}:1 style: whitespace between tokens is expected.
+${rules}: udev rules have style issues.
+EOF
+assert_0_impl --no-style "${rules}"
+assert_1_impl "${rules}"
+next_test_number
+
+cat >"${rules}" <<'EOF'
+ACTION=="a" ,NAME="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:1 style: stray whitespace before comma.
+${rules}:1 style: whitespace after comma is expected.
+${rules}: udev rules have style issues.
+EOF
+assert_0_impl --no-style "${rules}"
+assert_1_impl "${rules}"
+next_test_number
+
+# udevadm verify --root
+sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
+cd -
+assert_1 --root="${workdir}"
+cd -
+
+# udevadm verify path/
+sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
+cd -
+assert_1 "${rules_dir}"
+cd -
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+create_link_file() {
+ name=${1?}
+
+ mkdir -p /run/systemd/network/
+ cat >/run/systemd/network/10-test.link <<EOF
+[Match]
+Kind=dummy
+MACAddress=00:50:56:c0:00:18
+
+[Link]
+Name=$name
+AlternativeName=test1 test2 test3 test4
+EOF
+ udevadm control --reload
+}
+
+udevadm control --log-level=debug
+
+create_link_file test1
+ip link add address 00:50:56:c0:00:18 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/test1
+output=$(ip link show dev test1)
+if ! [[ "$output" =~ altname ]]; then
+ echo "alternative name for network interface not supported, skipping test."
+ exit 0
+fi
+assert_not_in "altname test1" "$output"
+assert_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+
+# By triggering add event, Name= and AlternativeNames= are re-applied
+create_link_file test2
+udevadm trigger --action add --settle /sys/class/net/test1
+udevadm wait --settle --timeout=30 /sys/class/net/test2
+output=$(ip link show dev test2)
+assert_in "altname test1" "$output"
+assert_not_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+
+# Name= and AlternativeNames= are not applied on move event
+create_link_file test3
+udevadm trigger --action move --settle /sys/class/net/test2
+udevadm wait --settle --timeout=30 /sys/class/net/test2
+output=$(ip link show dev test2)
+assert_in "altname test1" "$output"
+assert_not_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+
+# Test move event triggered by manual renaming
+ip link set dev test2 name hoge
+udevadm wait --settle --timeout=30 /sys/class/net/hoge
+output=$(ip link show dev hoge)
+assert_in "altname test1" "$output"
+assert_not_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+assert_not_in "altname hoge" "$output"
+
+# Re-test add event
+udevadm trigger --action add --settle /sys/class/net/hoge
+udevadm wait --settle --timeout=30 /sys/class/net/test3
+output=$(ip link show dev test3)
+assert_in "altname test1" "$output"
+assert_in "altname test2" "$output"
+assert_not_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+assert_not_in "altname hoge" "$output"
+
+# cleanup
+ip link del dev test3
+
+rm -f /run/systemd/network/10-test.link
+udevadm control --reload --log-level=info
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Test for `udevadm control -p`
+
+test_not_property() {
+ assert_eq "$(udevadm info --query property --property "$2" --value "$1")" ""
+}
+
+test_property() {
+ assert_eq "$(udevadm info --query property --property "$2" --value "$1")" "$3"
+}
+
+# shellcheck disable=SC2317
+cleanup() {
+ set +e
+
+ udevadm control -p FOO= -p BAR=
+
+ rm -f "$rules"
+}
+
+# Set up a test device
+trap cleanup EXIT
+
+rules="/run/udev/rules.d/99-test-17.13.rules"
+
+mkdir -p "${rules%/*}"
+cat > "$rules" <<'EOF'
+ENV{FOO}=="?*", ENV{PROP_FOO}="$env{FOO}"
+ENV{BAR}=="?*", ENV{PROP_BAR}="$env{BAR}"
+EOF
+
+udevadm control --reload
+
+test_not_property /dev/null PROP_FOO
+test_not_property /dev/null PROP_BAR
+
+: Setting of a property works
+
+udevadm control --property FOO=foo
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO foo
+test_not_property /dev/null PROP_BAR
+
+: Change of a property works
+
+udevadm control --property FOO=goo
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO goo
+
+: Removal of a property works
+
+udevadm control --property FOO=
+udevadm trigger --action change --settle /dev/null
+test_not_property /dev/null PROP_FOO
+
+: Repeated removal of a property does nothing
+
+udevadm control --property FOO=
+udevadm trigger --action change --settle /dev/null
+test_not_property /dev/null PROP_FOO
+
+: Multiple properties can be set at once
+
+udevadm control --property FOO=foo --property BAR=bar
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO foo
+test_property /dev/null PROP_BAR bar
+
+: Multiple setting of the same property is handled correctly
+
+udevadm control --property FOO=foo --property FOO=42
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO 42
+
+: Mix of settings and removals of the same property is handled correctly
+
+udevadm control -p FOO= -p FOO=foo -p BAR=car -p BAR=
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO foo
+test_not_property /dev/null PROP_BAR
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+if [[ $(systemctl is-enabled systemd-udev-load-credentials.service) == not-found ]]; then
+ echo "Missing systemd-udev-load-credentials.service" >>/skipped
+ exit 0
+fi
+
+at_exit() {
+ rm -f /run/credstore/udev.*
+ rm -f /run/udev/udev.conf.d/*
+ rm -f /run/udev/rules.d/*
+ rm -rf /run/systemd/system/systemd-udev-load-credentials.service.d
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/udev.conf.50-testme <<EOF
+udev_log=debug
+EOF
+cat > /run/credstore/udev.rules.50-testme <<EOF
+SUBSYSTEM=="net", OPTIONS="log_level=debug"
+EOF
+
+systemctl edit systemd-udev-load-credentials.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=udev.conf.50-testme
+LoadCredential=udev.rules.50-testme
+EOF
+
+systemctl restart systemd-udev-load-credentials.service
+
+diff /run/credstore/udev.conf.50-testme /run/udev/udev.conf.d/50-testme.conf
+diff /run/credstore/udev.rules.50-testme /run/udev/rules.d/50-testme.rules
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+udevadm control --log-level=debug
+
+mkdir -p /run/systemd/network/
+cat >/run/systemd/network/10-test.link <<EOF
+[Match]
+Kind=dummy
+MACAddress=00:50:56:c0:00:19
+
+[Link]
+Name=test1
+EOF
+
+mkdir /run/systemd/network/10-test.link.d
+cat >/run/systemd/network/10-test.link.d/10-override.conf <<EOF
+[Link]
+Property=HOGE=foo BAR=baz SHOULD_BE_UNSET=unset
+UnsetProperty=SHOULD_BE_UNSET
+EOF
+
+udevadm control --reload
+
+ip link add address 00:50:56:c0:00:19 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "HOGE=foo" "$output"
+assert_in "BAR=baz" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+cat >/run/systemd/network/10-test.link.d/11-override.conf <<EOF
+[Link]
+Property=
+Property=HOGE2=foo2 BAR2=baz2 SHOULD_BE_UNSET=unset
+ImportProperty=HOGE
+EOF
+
+udevadm control --reload
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "HOGE=foo" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# On change event, .link file will not be applied.
+udevadm trigger --settle --action change /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+### testing with udevadm test-builtin
+output=$(udevadm test-builtin --action add net_setup_link /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_in "SHOULD_BE_UNSET=" "$output" # this is expected, as an empty assignment is also logged.
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# check that test-builtin command does not update udev database.
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm test-builtin --action change net_setup_link /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+### testing with udevadm test
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# check that test command does not update udev database.
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm test --action change /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# test for specifiers
+cat >/run/systemd/network/10-test.link.d/12-override.conf <<EOF
+[Link]
+Property=
+Property=LINK_VERSION=%v
+EOF
+
+udevadm control --reload
+
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_in "LINK_VERSION=$(uname -r)" "$output"
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "LINK_VERSION=$(uname -r)" "$output"
+
+# test for constant properties
+cat >/run/systemd/network/10-test.link.d/13-override.conf <<EOF
+[Link]
+Property=
+Property=ACTION=foo IFINDEX=bar
+UnsetProperty=DEVPATH
+EOF
+
+udevadm control --reload
+
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_in "ACTION=add" "$output"
+assert_not_in "ACTION=foo" "$output"
+assert_in "IFINDEX=" "$output"
+assert_not_in "IFINDEX=bar" "$output"
+assert_in "DEVPATH=" "$output"
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "ACTION=foo" "$output"
+assert_in "IFINDEX=" "$output"
+assert_not_in "IFINDEX=bar" "$output"
+assert_in "DEVPATH=" "$output"
+
+# cleanup
+ip link del dev test1
+
+rm -f /run/systemd/network/10-test.link
+rm -rf /run/systemd/network/10-test.link.d
+udevadm control --reload --log-level=info
+
+exit 0
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-17-UDEV
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+udevadm settle
+
+run_subtests
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-18-FAILUREACTION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-run --wait -p FailureAction=poweroff true
+(! systemd-run --wait -p SuccessAction=poweroff false)
+
+if ! test -f /firstphase ; then
+ echo OK >/firstphase
+ systemd-run --wait -p SuccessAction=reboot true
+else
+ echo OK >/testok
+ systemd-run --wait -p FailureAction=exit -p FailureActionExitStatus=123 false
+fi
+
+sleep infinity
--- /dev/null
+#!/usr/bin/env bash
+set -eux
+
+# Test ExitType=cgroup
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
+ echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
+ exit 0
+fi
+
+systemd-analyze log-level debug
+
+# Multiple level process tree, parent process stays up
+cat >/tmp/test19-exit-cgroup.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+
+# process tree: systemd -> sleep
+sleep infinity &
+disown
+
+# process tree: systemd -> bash -> bash -> sleep
+((sleep infinity); true) &
+
+systemd-notify --ready
+
+# Run the stop/kill command
+\$1 &
+
+# process tree: systemd -> bash -> sleep
+sleep infinity
+EOF
+chmod +x /tmp/test19-exit-cgroup.sh
+
+# service should be stopped cleanly
+systemd-run --wait \
+ --unit=one \
+ --property="Type=notify" \
+ --property="ExitType=cgroup" \
+ /tmp/test19-exit-cgroup.sh 'systemctl stop one'
+
+# same thing with a truthy exec condition
+systemd-run --wait \
+ --unit=two \
+ --property="Type=notify" \
+ --property="ExitType=cgroup" \
+ --property="ExecCondition=true" \
+ /tmp/test19-exit-cgroup.sh 'systemctl stop two'
+
+# false exec condition: systemd-run should exit immediately with status code: 1
+(! systemd-run --wait \
+ --unit=three \
+ --property="Type=notify" \
+ --property="ExitType=cgroup" \
+ --property="ExecCondition=false" \
+ /tmp/test19-exit-cgroup.sh)
+
+# service should exit uncleanly (main process exits with SIGKILL)
+(! systemd-run --wait \
+ --unit=four \
+ --property="Type=notify" \
+ --property="ExitType=cgroup" \
+ /tmp/test19-exit-cgroup.sh 'systemctl kill --signal 9 four')
+
+
+# Multiple level process tree, parent process exits quickly
+cat >/tmp/test19-exit-cgroup-parentless.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+
+# process tree: systemd -> sleep
+sleep infinity &
+
+# process tree: systemd -> bash -> sleep
+((sleep infinity); true) &
+
+systemd-notify --ready
+
+# Run the stop/kill command after this bash process exits
+(sleep 1; \$1) &
+EOF
+chmod +x /tmp/test19-exit-cgroup-parentless.sh
+
+# service should be stopped cleanly
+systemd-run --wait \
+ --unit=five \
+ --property="Type=notify" \
+ --property="ExitType=cgroup" \
+ /tmp/test19-exit-cgroup-parentless.sh 'systemctl stop five'
+
+# service should still exit cleanly despite SIGKILL (the main process already exited cleanly)
+systemd-run --wait \
+ --unit=six \
+ --property="Type=notify" \
+ --property="ExitType=cgroup" \
+ /tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six'
+
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+# Create service with KillMode=none inside a slice
+cat <<EOF >/run/systemd/system/test19cleanup.service
+[Unit]
+Description=Test 19 cleanup Service
+[Service]
+Slice=test19cleanup.slice
+Type=exec
+ExecStart=sleep infinity
+KillMode=none
+EOF
+cat <<EOF >/run/systemd/system/test19cleanup.slice
+[Unit]
+Description=Test 19 cleanup Slice
+EOF
+
+# Start service
+systemctl start test19cleanup.service
+assert_rc 0 systemd-cgls /test19cleanup.slice
+
+pid=$(systemctl show --property MainPID --value test19cleanup)
+ps "$pid"
+
+# Stop slice
+# The sleep process will not be killed because of KillMode=none
+# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed
+systemctl stop test19cleanup.slice
+
+ps "$pid"
+
+# Kill sleep process manually
+kill -s TERM "$pid"
+while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done
+
+timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done'
+assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service
+
+# Check that empty cgroup /test19cleanup.slice has been removed
+timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done'
+assert_rc 1 systemd-cgls /test19cleanup.slice
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test cgroup delegation in the unified hierarchy
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
+ echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
+ exit 0
+fi
+
+at_exit() {
+ set +e
+ userdel -r test
+}
+
+systemd-run --wait \
+ --unit=test-0.service \
+ --property="DynamicUser=1" \
+ --property="Delegate=" \
+ test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
+ -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \
+ -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control
+
+# Test if this also works for some of the more recent attrs the kernel might or might not support
+for attr in cgroup.threads memory.oom.group memory.reclaim ; do
+
+ if grep -q "$attr" /sys/kernel/cgroup/delegate ; then
+ systemd-run --wait \
+ --unit=test-0.service \
+ --property="DynamicUser=1" \
+ --property="Delegate=" \
+ test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
+ -w /sys/fs/cgroup/system.slice/test-0.service/"$attr"
+ fi
+done
+
+systemd-run --wait \
+ --unit=test-1.service \
+ --property="DynamicUser=1" \
+ --property="Delegate=memory pids" \
+ grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers
+
+systemd-run --wait \
+ --unit=test-2.service \
+ --property="DynamicUser=1" \
+ --property="Delegate=memory pids" \
+ grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers
+
+# "io" is not among the controllers enabled by default for all units, verify that
+grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+# Run a service with "io" enabled, and verify it works
+systemd-run --wait \
+ --unit=test-3.service \
+ --property="IOAccounting=yes" \
+ --property="Slice=system-foo-bar-baz.slice" \
+ grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers
+
+# We want to check if "io" is removed again from the controllers
+# list. However, PID 1 (rightfully) does this asynchronously. In order
+# to force synchronization on this, let's start a short-lived service
+# which requires PID 1 to refresh the cgroup tree, so that we can
+# verify that this all works.
+systemd-run --wait --unit=test-4.service true
+
+# And now check again, "io" should have vanished
+grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+# Check that unprivileged delegation works for scopes
+useradd test ||:
+systemd-run --uid=test \
+ --property="User=test" \
+ --property="Delegate=yes" \
+ --slice workload.slice \
+ --unit test-workload0.scope\
+ --scope \
+ test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \
+ -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \
+ -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control
+
+# Verify that DelegateSubgroup= affects ownership correctly
+unit="test-subgroup-$RANDOM.service"
+systemd-run --wait \
+ --unit="$unit" \
+ --property="DynamicUser=1" \
+ --property="Delegate=pids" \
+ --property="DelegateSubgroup=foo" \
+ test -w "/sys/fs/cgroup/system.slice/$unit" -a \
+ -w "/sys/fs/cgroup/system.slice/$unit/foo"
+
+# Check that for the subgroup also attributes that aren't covered by
+# regular (i.e. main cgroup) delegation ownership rules are delegated properly
+if test -f /sys/fs/cgroup/cgroup.max.depth; then
+ unit="test-subgroup-$RANDOM.service"
+ systemd-run --wait \
+ --unit="$unit" \
+ --property="DynamicUser=1" \
+ --property="Delegate=pids" \
+ --property="DelegateSubgroup=zzz" \
+ test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth"
+fi
+
+# Check that the invoked process itself is also in the subgroup
+unit="test-subgroup-$RANDOM.service"
+systemd-run --wait \
+ --unit="$unit" \
+ --property="DynamicUser=1" \
+ --property="Delegate=pids" \
+ --property="DelegateSubgroup=bar" \
+ grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-19-CGROUP
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+. /etc/os-release
+# FIXME: This test fails on opensuse with the following error and others:
+# Apr 25 10:24:04 H (cat)[910]: device-mapper: create ioctl on ... failed: Device or resource busy
+if [[ "$ID" =~ "opensuse" ]]; then
+ echo "Skipping due to known unexpected behaviour in OpenSUSE kernels" >>/skipped
+ exit 77
+fi
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fuzz our D-Bus interfaces with dfuzzer
+After=dbus.service multi-user.target
+Wants=dbus.service multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /skipped /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# check dfuzzer is present before testing
+if ! command -v dfuzzer &>/dev/null; then
+ echo "dfuzzer is not installed, skipping" | tee --append /skipped
+ exit 77
+fi
+
+# Save the end.service state before we start fuzzing, as it might get changed
+# on the fly by one of the fuzzers
+systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0
+
+# shellcheck disable=SC2317
+at_exit() {
+ set +e
+ # We have to call the end.service/poweroff explicitly even if it's specified on
+ # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls
+ # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service
+ # from the queue
+ if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then
+ # PID1 is down let's try to save the journal
+ journalctl --sync # journal can be down as well so let's ignore exit codes here
+ systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF)
+ fi
+}
+
+add_suppression() {
+ local interface="${1:?}"
+ local suppression="${2:?}"
+
+ sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
+}
+
+trap at_exit EXIT
+
+systemctl log-level info
+
+# FIXME: systemd-run doesn't play well with daemon-reexec
+# See: https://github.com/systemd/systemd/issues/27204
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"
+
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
+add_suppression "org.freedesktop.login1" "Sleep destructive"
+
+# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
+# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
+# degree) by the respective method counterparts on the manager object.
+for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
+ add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
+done
+
+cat /etc/dfuzzer.conf
+
+# TODO
+# * check for possibly newly introduced buses?
+BUS_LIST=(
+ org.freedesktop.home1
+ org.freedesktop.hostname1
+ org.freedesktop.import1
+ org.freedesktop.locale1
+ org.freedesktop.login1
+ org.freedesktop.machine1
+ org.freedesktop.portable1
+ org.freedesktop.resolve1
+ org.freedesktop.systemd1
+ org.freedesktop.timedate1
+)
+
+# systemd-oomd requires PSI
+if tail -n +1 /proc/pressure/{cpu,io,memory}; then
+ BUS_LIST+=(
+ org.freedesktop.oom1
+ )
+fi
+
+# Some services require specific conditions:
+# - systemd-timesyncd can't run in a container
+# - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability
+if ! systemd-detect-virt --container; then
+ BUS_LIST+=(
+ org.freedesktop.network1
+ org.freedesktop.timesync1
+ )
+elif busctl introspect org.freedesktop.network1 / &>/dev/null; then
+ BUS_LIST+=(
+ org.freedesktop.network1
+ )
+fi
+
+SESSION_BUS_LIST=(
+ org.freedesktop.systemd1
+)
+
+# Maximum payload size generated by dfuzzer (in bytes) - default: 50K
+PAYLOAD_MAX=50000
+# Tweak the maximum payload size if we're running under sanitizers, since
+# with larger payloads we start hitting reply timeouts
+if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
+ PAYLOAD_MAX=10000 # 10K
+fi
+
+# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing
+# the org.freedesktop.machine1 stuff makes quite a mess
+mount -t tmpfs -o size=50M tmpfs /var/lib/machines
+
+# Fuzz both the system and the session buses (where applicable)
+for bus in "${BUS_LIST[@]}"; do
+ echo "Bus: $bus (system)"
+ systemd-run --pipe --wait \
+ -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
+
+ # Let's reload the systemd daemon to test (de)serialization as well
+ systemctl daemon-reload
+ # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
+ systemctl daemon-reexec
+done
+
+umount /var/lib/machines
+
+for bus in "${SESSION_BUS_LIST[@]}"; do
+ echo "Bus: $bus (session)"
+ systemd-run --machine 'testuser@.host' --user --pipe --wait \
+ -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
+
+ # Let's reload the systemd user daemon to test (de)serialization as well
+ systemctl --machine 'testuser@.host' --user daemon-reload
+ # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
+ systemctl --machine 'testuser@.host' --user daemon-reexec
+done
+
+touch /testok
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# With "e" don't attempt to set permissions when file doesn't exist, see
+# https://github.com/systemd/systemd/pull/6682.
+set -eux
+set -o pipefail
+
+rm -fr /tmp/test
+
+echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
+
+test ! -e /tmp/test
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Basic tests for types creating directories
+set -eux
+set -o pipefail
+
+rm -fr /tmp/{C,d,D,e}
+mkdir /tmp/{C,d,D,e}
+
+#
+# 'd'
+#
+mkdir /tmp/d/2
+chmod 777 /tmp/d/2
+
+systemd-tmpfiles --dry-run --create - <<EOF
+d /tmp/d/1 0755 daemon daemon - -
+d /tmp/d/2 0755 daemon daemon - -
+EOF
+
+test ! -d /tmp/d/1
+test -d /tmp/d/2
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/d/1 0755 daemon daemon - -
+d /tmp/d/2 0755 daemon daemon - -
+EOF
+
+test -d /tmp/d/1
+test "$(stat -c %U:%G:%a /tmp/d/1)" = "daemon:daemon:755"
+
+test -d /tmp/d/2
+test "$(stat -c %U:%G:%a /tmp/d/2)" = "daemon:daemon:755"
+
+#
+# 'D'
+#
+mkdir /tmp/D/2
+chmod 777 /tmp/D/2
+touch /tmp/D/2/foo
+
+systemd-tmpfiles --create - <<EOF
+D /tmp/D/1 0755 daemon daemon - -
+D /tmp/D/2 0755 daemon daemon - -
+EOF
+
+test -d /tmp/D/1
+test "$(stat -c %U:%G:%a /tmp/D/1)" = "daemon:daemon:755"
+
+test -d /tmp/D/2
+test "$(stat -c %U:%G:%a /tmp/D/2)" = "daemon:daemon:755"
+
+systemd-tmpfiles --remove - <<EOF
+D /tmp/D/2 0755 daemon daemon - -
+EOF
+
+# the content of '2' should be removed
+test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
+
+#
+# 'e'
+#
+mkdir -p /tmp/e/2/{d1,d2}
+chmod 777 /tmp/e/2
+chmod 777 /tmp/e/2/d*
+
+systemd-tmpfiles --create - <<EOF
+e /tmp/e/1 0755 daemon daemon - -
+e /tmp/e/2/* 0755 daemon daemon - -
+EOF
+
+test ! -d /tmp/e/1
+
+test -d /tmp/e/2
+test "$(stat -c %U:%G:%a /tmp/e/2)" = "root:root:777"
+
+test -d /tmp/e/2/d1
+test "$(stat -c %U:%G:%a /tmp/e/2/d1)" = "daemon:daemon:755"
+test -d /tmp/e/2/d2
+test "$(stat -c %U:%G:%a /tmp/e/2/d2)" = "daemon:daemon:755"
+
+# 'e' operates on directories only
+mkdir -p /tmp/e/3/{d1,d2}
+chmod 777 /tmp/e/3
+chmod 777 /tmp/e/3/d*
+touch /tmp/e/3/f1
+chmod 644 /tmp/e/3/f1
+
+systemd-tmpfiles --create - <<EOF
+e /tmp/e/3/* 0755 daemon daemon - -
+EOF
+
+# the directories should have been processed although systemd-tmpfiles failed
+# previously due to the presence of a file.
+test -d /tmp/e/3/d1
+test "$(stat -c %U:%G:%a /tmp/e/3/d1)" = "daemon:daemon:755"
+test -d /tmp/e/3/d2
+test "$(stat -c %U:%G:%a /tmp/e/3/d2)" = "daemon:daemon:755"
+
+test -f /tmp/e/3/f1
+test "$(stat -c %U:%G:%a /tmp/e/3/f1)" = "root:root:644"
+
+#
+# 'C'
+#
+
+mkdir /tmp/C/{0,1,2,3}-origin
+touch /tmp/C/{1,2,3}-origin/f1
+chmod 755 /tmp/C/{1,2,3}-origin/f1
+
+mkdir /tmp/C/{2,3}
+touch /tmp/C/3/f1
+
+systemd-tmpfiles --dry-run --create - <<EOF
+C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin
+C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin
+EOF
+
+test ! -d /tmp/C/1
+test -d /tmp/C/2
+
+systemd-tmpfiles --create - <<EOF
+C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin
+C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin
+EOF
+
+test -d /tmp/C/1
+test "$(stat -c %U:%G:%a /tmp/C/1/f1)" = "daemon:daemon:755"
+test -d /tmp/C/2
+test "$(stat -c %U:%G:%a /tmp/C/2/f1)" = "daemon:daemon:755"
+
+systemd-tmpfiles --create - <<EOF
+C /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin
+C /tmp/C/4 0755 daemon daemon - /tmp/C/definitely-missing
+EOF
+
+test "$(stat -c %U:%G:%a /tmp/C/3/f1)" = "root:root:644"
+test ! -e /tmp/C/4
+
+touch /tmp/C/3-origin/f{2,3,4}
+echo -n ABC > /tmp/C/3/f1
+
+systemd-tmpfiles --create - <<EOF
+C+ /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin
+EOF
+
+# Test that the trees got merged, even though /tmp/C/3 already exists.
+test -e /tmp/C/3/f1
+test -e /tmp/C/3/f2
+test -e /tmp/C/3/f3
+test -e /tmp/C/3/f4
+
+# Test that /tmp/C/3/f1 did not get overwritten.
+test "$(cat /tmp/C/3/f1)" = "ABC"
+
+# Check that %U expands to 0, both in the path and in the argument.
+home='/tmp/C'
+systemd-tmpfiles --create - <<EOF
+C $home/%U - - - - $home/%U-origin
+EOF
+
+test -d "$home/0"
+
+# Check that %h expands to $home, both in the path and in the argument.
+HOME="$home" \
+systemd-tmpfiles --create - <<EOF
+C %h/5 - - - - %h/3-origin
+EOF
+
+test -f "$home/5/f1"
+
+# Check that %h in the path is expanded, but
+# the result of this expansion is not expanded once again.
+root='/tmp/C/6'
+home='/%U'
+mkdir -p "$root/usr/share/factory$home"
+HOME="$home" \
+systemd-tmpfiles --create --root="$root" - <<EOF
+C %h - - - -
+EOF
+
+test -d "$root$home"
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Basic tests for types creating/writing files
+set -eux
+set -o pipefail
+
+rm -fr /tmp/{f,F,w}
+mkdir /tmp/{f,F,w}
+touch /tmp/file-owned-by-root
+
+#
+# 'f'
+#
+systemd-tmpfiles --dry-run --create - <<EOF
+f /tmp/f/1 0644 - - - -
+f /tmp/f/2 0644 - - - This string should be written
+EOF
+
+test ! -e /tmp/f/1
+test ! -e /tmp/f/2
+
+systemd-tmpfiles --create - <<EOF
+f /tmp/f/1 0644 - - - -
+f /tmp/f/2 0644 - - - This string should be written
+EOF
+
+### '1' should exist and be empty
+test -f /tmp/f/1; test ! -s /tmp/f/1
+test "$(stat -c %U:%G:%a /tmp/f/1)" = "root:root:644"
+
+test "$(stat -c %U:%G:%a /tmp/f/2)" = "root:root:644"
+test "$(< /tmp/f/2)" = "This string should be written"
+
+### The perms are supposed to be updated even if the file already exists.
+systemd-tmpfiles --create - <<EOF
+f /tmp/f/1 0666 daemon daemon - This string should not be written
+EOF
+
+# file should be empty
+test ! -s /tmp/f/1
+test "$(stat -c %U:%G:%a /tmp/f/1)" = "daemon:daemon:666"
+
+### But we shouldn't try to set perms on an existing file which is not a
+### regular one.
+mkfifo /tmp/f/fifo
+chmod 644 /tmp/f/fifo
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/f/fifo 0666 daemon daemon - This string should not be written
+EOF
+
+test -p /tmp/f/fifo
+test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
+
+### 'f' should not follow symlinks.
+ln -s missing /tmp/f/dangling
+ln -s /tmp/file-owned-by-root /tmp/f/symlink
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/f/dangling 0644 daemon daemon - -
+f /tmp/f/symlink 0644 daemon daemon - -
+EOF
+test ! -e /tmp/f/missing
+test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and have the correct perms.
+mkdir /tmp/f/rw-fs
+mkdir /tmp/f/ro-fs
+
+touch /tmp/f/rw-fs/foo
+chmod 644 /tmp/f/rw-fs/foo
+
+mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written
+EOF
+test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/f/ro-fs/foo 0666 - - - -
+EOF
+test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/f/ro-fs/bar 0644 - - - -
+EOF
+test ! -e /tmp/f/ro-fs/bar
+
+### 'f' shouldn't follow unsafe paths.
+mkdir /tmp/f/daemon
+ln -s /root /tmp/f/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/f/daemon
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
+EOF
+test ! -e /tmp/f/daemon/unsafe-symlink/exploit
+
+#
+# 'F'
+#
+echo "This should be truncated" >/tmp/F/truncated
+echo "This should be truncated" >/tmp/F/truncated-with-content
+
+systemd-tmpfiles --create - <<EOF
+F /tmp/F/created 0644 - - - -
+F /tmp/F/created-with-content 0644 - - - new content
+F /tmp/F/truncated 0666 daemon daemon - -
+F /tmp/F/truncated-with-content 0666 daemon daemon - new content
+EOF
+
+test -f /tmp/F/created; test ! -s /tmp/F/created
+test -f /tmp/F/created-with-content
+test "$(< /tmp/F/created-with-content)" = "new content"
+test -f /tmp/F/truncated; test ! -s /tmp/F/truncated
+test "$(stat -c %U:%G:%a /tmp/F/truncated)" = "daemon:daemon:666"
+test -s /tmp/F/truncated-with-content
+test "$(stat -c %U:%G:%a /tmp/F/truncated-with-content)" = "daemon:daemon:666"
+
+### We shouldn't try to truncate anything but regular files since the behavior is
+### unspecified in the other cases.
+mkfifo /tmp/F/fifo
+
+(! systemd-tmpfiles --create -) <<EOF
+F /tmp/F/fifo 0644 - - - -
+EOF
+
+test -p /tmp/F/fifo
+
+### 'F' should not follow symlinks.
+ln -s missing /tmp/F/dangling
+ln -s /tmp/file-owned-by-root /tmp/F/symlink
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/F/dangling 0644 daemon daemon - -
+f /tmp/F/symlink 0644 daemon daemon - -
+EOF
+test ! -e /tmp/F/missing
+test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and is empty.
+mkdir /tmp/F/rw-fs
+mkdir /tmp/F/ro-fs
+
+touch /tmp/F/rw-fs/foo
+chmod 644 /tmp/F/rw-fs/foo
+
+mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+F /tmp/F/ro-fs/foo 0644 - - - -
+EOF
+test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo
+
+echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
+(! systemd-tmpfiles --create -) <<EOF
+F /tmp/F/ro-fs/foo 0644 - - - -
+EOF
+
+(! systemd-tmpfiles --create -) <<EOF
+F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written
+EOF
+test -f /tmp/F/ro-fs/foo
+grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo
+
+# Trying to change the perms should fail.
+: >/tmp/F/rw-fs/foo
+(! systemd-tmpfiles --create -) <<EOF
+F /tmp/F/ro-fs/foo 0666 - - - -
+EOF
+test "$(stat -c %U:%G:%a /tmp/F/ro-fs/foo)" = "root:root:644"
+
+### Try to create a new file.
+(! systemd-tmpfiles --create -) <<EOF
+F /tmp/F/ro-fs/bar 0644 - - - -
+EOF
+test ! -e /tmp/F/ro-fs/bar
+
+### 'F' shouldn't follow unsafe paths.
+mkdir /tmp/F/daemon
+ln -s /root /tmp/F/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/F/daemon
+
+(! systemd-tmpfiles --create -) <<EOF
+F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
+EOF
+test ! -e /tmp/F/daemon/unsafe-symlink/exploit
+
+#
+# 'w'
+#
+touch /tmp/w/overwritten
+touch /tmp/w/appended
+
+### nop if the target does not exist.
+systemd-tmpfiles --dry-run --create - <<EOF
+w /tmp/w/unexistent 0644 - - - new content
+EOF
+test ! -e /tmp/w/unexistent
+
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/unexistent 0644 - - - new content
+EOF
+test ! -e /tmp/w/unexistent
+
+### no argument given -> fails.
+(! systemd-tmpfiles --create -) <<EOF
+w /tmp/w/unexistent 0644 - - - -
+EOF
+
+### write into an empty file.
+systemd-tmpfiles --dry-run --create - <<EOF
+w /tmp/w/overwritten 0644 - - - old content
+EOF
+test -f /tmp/w/overwritten
+test -z "$(< /tmp/w/overwritten)"
+
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/overwritten 0644 - - - old content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "old content"
+
+### old content is overwritten
+systemd-tmpfiles --dry-run --create - <<EOF
+w /tmp/w/overwritten 0644 - - - new content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "old content"
+
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/overwritten 0644 - - - new content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "new content"
+
+### append lines
+systemd-tmpfiles --create - <<EOF
+w+ /tmp/w/appended 0644 - - - 1
+w+ /tmp/w/appended 0644 - - - 2\n
+w+ /tmp/w/appended 0644 - - - 3
+EOF
+test -f /tmp/w/appended
+test "$(< /tmp/w/appended)" = "$(echo -ne '12\n3')"
+
+### writing into an 'exotic' file should be allowed.
+systemd-tmpfiles --dry-run --create - <<EOF
+w /dev/null - - - - new content
+EOF
+
+systemd-tmpfiles --create - <<EOF
+w /dev/null - - - - new content
+EOF
+
+### 'w' follows symlinks
+ln -s ./overwritten /tmp/w/symlink
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/symlink - - - - $(readlink -e /tmp/w/symlink)
+EOF
+readlink -e /tmp/w/symlink
+test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
+
+### 'w' shouldn't follow unsafe paths.
+mkdir /tmp/w/daemon
+ln -s /root /tmp/w/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/w/daemon
+
+(! systemd-tmpfiles --create -) <<EOF
+f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
+EOF
+test ! -e /tmp/w/daemon/unsafe-symlink/exploit
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Basic tests for types creating fifos
+set -eux
+set -o pipefail
+
+rm -fr /tmp/p
+mkdir /tmp/p
+touch /tmp/p/f1
+
+systemd-tmpfiles --dry-run --create - <<EOF
+p /tmp/p/fifo1 0666 - - - -
+EOF
+
+test ! -p /tmp/p/fifo1
+
+systemd-tmpfiles --create - <<EOF
+p /tmp/p/fifo1 0666 - - - -
+EOF
+
+test -p /tmp/p/fifo1
+test "$(stat -c %U:%G:%a /tmp/p/fifo1)" = "root:root:666"
+
+# Refuse to overwrite an existing file. Error is not propagated.
+systemd-tmpfiles --create - <<EOF
+p /tmp/p/f1 0666 - - - -
+EOF
+
+test -f /tmp/p/f1
+
+# unless '+' prefix is used
+systemd-tmpfiles --create - <<EOF
+p+ /tmp/p/f1 0666 - - - -
+EOF
+
+test -p /tmp/p/f1
+test "$(stat -c %U:%G:%a /tmp/p/f1)" = "root:root:666"
+
+#
+# Must be fixed
+#
+# mkdir /tmp/p/daemon
+# #ln -s /root /tmp/F/daemon/unsafe-symlink
+# chown -R --no-dereference daemon:daemon /tmp/p/daemon
+#
+# systemd-tmpfiles --create - <<EOF
+# p /tmp/p/daemon/fifo2 0666 daemon daemon - -
+# EOF
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+rm -fr /tmp/{z,Z}
+mkdir /tmp/{z,Z}
+
+#
+# 'z'
+#
+mkdir /tmp/z/d{1,2}
+touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
+
+systemd-tmpfiles --dry-run --create - <<EOF
+z /tmp/z/f1 0755 daemon daemon - -
+z /tmp/z/d1 0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U /tmp/z/f1)" = "$USER"
+test "$(stat -c %U /tmp/z/d1)" = "$USER"
+test "$(stat -c %U /tmp/z/d1/f11)" = "$USER"
+
+systemd-tmpfiles --create - <<EOF
+z /tmp/z/f1 0755 daemon daemon - -
+z /tmp/z/d1 0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U:%G /tmp/z/f1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d1/f11)" = "root:root"
+
+systemd-tmpfiles --create - <<EOF
+z /tmp/z/d2/* 0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U:%G /tmp/z/d2/f21)" = "daemon:daemon"
+
+#
+# 'Z'
+#
+mkdir /tmp/Z/d1 /tmp/Z/d1/d11
+touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
+
+systemd-tmpfiles --create - <<EOF
+Z /tmp/Z/f1 0755 daemon daemon - -
+Z /tmp/Z/d1 0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U:%G /tmp/Z/f1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/d11)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/f11)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/d11/f111)" = "daemon:daemon"
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Inspired by https://github.com/systemd/systemd/issues/9508
+set -eux
+set -o pipefail
+
+test_snippet() {
+ # First call with --dry-run to test the code paths
+ systemd-tmpfiles --dry-run "$@" - <<EOF
+d /var/tmp/foobar-test-06
+d /var/tmp/foobar-test-06/important
+R /var/tmp/foobar-test-06
+EOF
+
+ systemd-tmpfiles "$@" - <<EOF
+d /var/tmp/foobar-test-06
+d /var/tmp/foobar-test-06/important
+R /var/tmp/foobar-test-06
+EOF
+}
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+test_snippet --remove
+test ! -f /var/tmp/foobar-test-06
+test ! -f /var/tmp/foobar-test-06/important
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+touch /var/tmp/foobar-test-06/something-else
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+test -f /var/tmp/foobar-test-06/something-else
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+test ! -f /var/tmp/foobar-test-06/something-else
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
+set -eux
+set -o pipefail
+
+rm -rf /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix
+r /tmp/test-prefix/file
+EOF
+
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix/file
+r /tmp/test-prefix
+EOF
+
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Verify tmpfiles can run in a root directory under a path prefix that contains
+# directories owned by unprivileged users, for example when a root file system
+# is mounted in a regular user's home directory.
+#
+# https://github.com/systemd/systemd/pull/11820
+set -eux
+set -o pipefail
+
+rm -fr /tmp/root /tmp/user
+mkdir -p /tmp/root /tmp/user/root
+chown daemon:daemon /tmp/user
+
+# Verify the command works as expected with no prefix or a root-owned prefix.
+echo 'd /tmp/root/test1' | systemd-tmpfiles --create -
+test -d /tmp/root/test1
+echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create -
+test -d /tmp/root/test2
+
+# Verify the command fails to write to a root-owned subdirectory under an
+# unprivileged user's directory when it's not part of the prefix, as expected
+# by the unsafe_transition function.
+echo 'd /tmp/user/root/test' | (! systemd-tmpfiles --create -)
+test ! -e /tmp/user/root/test
+echo 'd /user/root/test' | (! systemd-tmpfiles --root=/tmp --create -)
+test ! -e /tmp/user/root/test
+
+# Verify the above works when all user-owned directories are in the prefix.
+echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
+test -d /tmp/user/root/test
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Make sure that the "stat" output is not locale dependent.
+export LANG=C LC_ALL=C
+
+# first, create file without suid/sgid
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 0755 1 1 - -
+f /tmp/yyy 0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# then, add suid/sgid
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 04755
+f /tmp/yyy 02755
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
+
+# then, chown the files to somebody else
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx - 2 2
+f /tmp/yyy - 2 2
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
+
+# then, chown the files to a third user/group but also drop to a mask that has
+# both more and fewer bits set
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 0770 3 3
+f /tmp/yyy 0770 3 3
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
+
+# return to the beginning
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 0755 1 1 - -
+f /tmp/yyy 0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# remove everything
+systemd-tmpfiles --remove - <<EOF
+r /tmp/xxx
+r /tmp/yyy
+EOF
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx1 0644 - - - foo
+f /tmp/xxx2 0644 - - - foo bar
+f /tmp/xxx3 0644 - - - foo\x20bar
+f /tmp/xxx4 0644 - - - \x20foobar
+f /tmp/xxx5 0644 - - - foobar\x20
+f /tmp/xxx6 0644 - - - foo bar
+f /tmp/xxx7 0644 - - - foo bar \n
+f /tmp/xxx8 0644 - - - " foo bar "
+f /tmp/xxx9 0644 - - - ' foo bar '
+EOF
+
+echo -n "foo" | cmp /tmp/xxx1 -
+echo -n "foo bar" | cmp /tmp/xxx2 -
+echo -n "foo bar" | cmp /tmp/xxx3 -
+echo -n " foobar" | cmp /tmp/xxx4 -
+echo -n "foobar " | cmp /tmp/xxx5 -
+echo -n "foo bar" | cmp /tmp/xxx6 -
+echo "foo bar " | cmp /tmp/xxx7 -
+echo -n "\" foo bar \"" | cmp /tmp/xxx8 -
+echo -n "' foo bar '" | cmp /tmp/xxx9 -
+
+rm /tmp/xxx{1,2,3,4,5,6,7,8,9}
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+set -x
+
+rm -fr /tmp/x
+mkdir /tmp/x
+
+#
+# 'x'
+#
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean --dry-run - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'X'
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'x' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/[1345]
+x /tmp/x/z*
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/[1345]
+x /tmp/x/z*
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'X' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/[1345]
+X /tmp/x/?[12]
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/[1345]
+X /tmp/x/?[12]
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'x' with 'r'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+# x/X is not supposed to influence r
+x /tmp/x/1/a
+X /tmp/x/2/a
+r /tmp/x/1
+r /tmp/x/2
+EOF
+
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+systemd-tmpfiles --clean - <<EOF
+# x/X is not supposed to influence r
+x /tmp/x/1/a
+X /tmp/x/2/a
+r /tmp/x/1
+r /tmp/x/2
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -d /tmp/x/1/a
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+#
+# 'x' with 'R'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --dry-run --remove - <<EOF
+# Check that X is honoured below R
+X /tmp/x/1/a
+X /tmp/x/2/a
+R /tmp/x/1
+EOF
+
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+systemd-tmpfiles --remove - <<EOF
+# Check that X is honoured below R
+X /tmp/x/1/a
+X /tmp/x/2/a
+R /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -d /tmp/x/1/a
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+#
+# 'r/R/D' and non-directories
+#
+
+touch /tmp/x/{11,22,33}
+
+systemd-tmpfiles --dry-run --remove - <<EOF
+# Check that X is honoured below R
+r /tmp/x/11
+R /tmp/x/22
+D /tmp/x/33
+EOF
+
+test -f /tmp/x/11
+test -f /tmp/x/22
+test -f /tmp/x/33
+
+systemd-tmpfiles --remove - <<EOF
+# Check that X is honoured below R
+r /tmp/x/11
+R /tmp/x/22
+D /tmp/x/33
+EOF
+
+find /tmp/x | sort
+test ! -f /tmp/x/11
+test ! -f /tmp/x/22
+test -f /tmp/x/33
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
+set -e
+set -x
+
+# Test directory structure looks like this:
+# /tmp/ageby/
+# ├── d1
+# │ ├── f1
+# │ ├── f2
+# │ ├── f3
+# │ └── f4
+# ├── d2
+# │ ├── f1
+# │ ├── f2
+# ...
+
+export SYSTEMD_LOG_LEVEL="debug"
+
+rm -rf /tmp/ageby
+mkdir -p /tmp/ageby/d{1..4}
+
+# TODO: There is probably a better way to figure this out.
+# Test for [bB] age-by arguments only on filesystems that expose
+# the creation time. Note that this is _not_ an accurate way to
+# check if the filesystem or kernel version don't provide the
+# timestamp. But, if the timestamp is visible in "stat" it is a
+# good indicator that the test can be run.
+TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
+if stat --format "%w" /tmp/ageby 2>/dev/null | grep -qv '^[\?\-]$'; then
+ TEST_TMPFILES_AGEBY_BTIME=1
+fi
+
+touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
+touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
+
+# Create a bunch of other files.
+touch /tmp/ageby/d{1,2}/f{2..4}
+
+# For "ctime".
+touch /tmp/ageby/d3/f1
+chmod +x /tmp/ageby/d3/f1
+sleep 1
+
+# For "btime".
+touch /tmp/ageby/d4/f1
+sleep 1
+
+# More files with recent "{a,b}time" values.
+touch /tmp/ageby/d{3,4}/f{2..4}
+
+# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
+systemd-tmpfiles --dry-run --clean - <<-EOF
+d /tmp/ageby/d1 - - - a:1m -
+e /tmp/ageby/d2 - - - m:3m -
+D /tmp/ageby/d3 - - - c:2s -
+EOF
+
+for d in d{1..3}; do
+ test -f "/tmp/ageby/${d}/f1"
+done
+
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/d1 - - - a:1m -
+e /tmp/ageby/d2 - - - m:3m -
+D /tmp/ageby/d3 - - - c:2s -
+EOF
+
+for d in d{1..3}; do
+ test ! -f "/tmp/ageby/${d}/f1"
+done
+
+if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
+ systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/d4 - - - b:1s -
+EOF
+
+ test ! -f "/tmp/ageby/d4/f1"
+else
+ # Remove the file manually.
+ rm "/tmp/ageby/d4/f1"
+fi
+
+# Check for an invalid "age" and "age-by" arguments.
+for a in ':' ':1s' '2:1h' 'nope:42h' '" :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do
+ systemd-tmpfiles --clean - <<EOF 2>&1 | grep -q -F 'Invalid age'
+d /tmp/ageby - - - ${a} -
+EOF
+done
+
+for d in d{1..4}; do
+ for f in f{2..4}; do
+ test -f "/tmp/ageby/${d}/${f}"
+ done
+done
+
+# Check for parsing with whitespace, repeated values
+# for "age-by" (valid arguments).
+for a in '" a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
+ systemd-tmpfiles --clean - <<EOF
+d /tmp/ageby - - - ${a} -
+EOF
+done
+
+for d in d{1..4}; do
+ for f in f{2..4}; do
+ test -f "/tmp/ageby/${d}/${f}"
+ done
+done
+
+# Check that all files are removed if the "Age" is
+# set to "0" (regardless of "age-by" argument).
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/d1 - - - abc:0 -
+e /tmp/ageby/d2 - - - cmb:0 -
+EOF
+
+for d in d{1,2}; do
+ for f in f{2..4}; do
+ test ! -f "/tmp/ageby/${d}/${f}"
+ done
+done
+
+# Check for combinations:
+# - "/tmp/ageby/d3/f2" has file timestamps that
+# are older than the specified age, it will be
+# removed
+# - "/tmp/ageby/d4/f2", has not aged for the given
+# timestamp combination, it will not be removed
+touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
+touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
+systemd-tmpfiles --clean - <<-EOF
+e /tmp/ageby/d3 - - - am:3m -
+D /tmp/ageby/d4 - - - mc:7m -
+EOF
+
+test ! -f "/tmp/ageby/d3/f2"
+test -f "/tmp/ageby/d4/f2"
+
+# Check that all files are removed if only "Age" is set to 0.
+systemd-tmpfiles --clean - <<-EOF
+e /tmp/ageby/d3 - - - 0s
+d /tmp/ageby/d4 - - - 0s
+EOF
+
+for d in d{3,4}; do
+ for f in f{2..4}; do
+ test ! -f "/tmp/ageby/$d/${f}"
+ done
+done
+
+# Check "age-by" argument for sub-directories in "/tmp/ageby".
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/ - - - A:1m -
+EOF
+
+for d in d{1..4}; do
+ test -d "/tmp/ageby/${d}"
+done
+
+# Check for combinations.
+touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
+systemd-tmpfiles --clean - <<-EOF
+e /tmp/ageby/ - - - AM:4s -
+EOF
+
+for d in d{1,2}; do
+ test ! -d "/tmp/ageby/${d}"
+done
+
+for d in d{3,4}; do
+ test -d "/tmp/ageby/${d}"
+done
+
+# Check "btime" for directories.
+if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
+ systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/ - - - B:8s -
+EOF
+
+ for d in d{3,4}; do
+ test -d "/tmp/ageby/${d}"
+ done
+fi
+
+# To bump "atime".
+touch -a --date "1 second ago" /tmp/ageby/d3
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/ - - - A:2s -
+EOF
+
+test -d /tmp/ageby/d3
+test ! -d /tmp/ageby/d4
+
+# Check if sub-directories are removed regardless
+# of "age-by", when "Age" is set to "0".
+systemd-tmpfiles --clean - <<-EOF
+D /tmp/ageby/ - - - AM:0 -
+EOF
+
+test ! -d /tmp/ageby/d3
+
+# Cleanup the test directory (fail if not empty).
+rmdir /tmp/ageby
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for configuration directory and file precedences
+#
+set -eux
+
+rm -f /{usr/lib,etc}/tmpfiles.d/{L,w}-*.conf
+rm -fr /tmp/precedence/{L,w}
+
+mkdir -p /{usr/lib,etc}/tmpfiles.d
+mkdir -p /tmp/precedence/{L,w}
+
+#
+# 'L'
+#
+ln -s /dev/null /tmp/precedence/L
+
+# Overwrite the existing symlink
+cat >/usr/lib/tmpfiles.d/L-z.conf<<EOF
+L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-z.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-z.conf"
+
+# Files in /etc should override those in /usr
+cat >/etc/tmpfiles.d/L-z.conf<<EOF
+L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-z.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-z.conf"
+
+# /usr/…/L-a.conf has higher prio than /etc/…/L-z.conf
+cat >/usr/lib/tmpfiles.d/L-a.conf<<EOF
+L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-a.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-a.conf"
+
+# Files in /etc should override those in /usr
+cat >/etc/tmpfiles.d/L-a.conf<<EOF
+L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-a.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-a.conf"
+
+#
+# 'w'
+#
+touch /tmp/precedence/w/f
+
+# Multiple configuration files specifying 'w+' for the same path is allowed.
+for i in a c; do
+ cat >/usr/lib/tmpfiles.d/w-$i.conf<<EOF
+w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-$i.conf\n
+EOF
+ cat >/etc/tmpfiles.d/w-$i.conf<<EOF
+w+ /tmp/precedence/w/f - - - - /etc/tmpfiles.d/w-$i.conf\n
+EOF
+done
+
+cat >/usr/lib/tmpfiles.d/w-b.conf<<EOF
+w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-b.conf\n
+EOF
+
+systemd-tmpfiles --create
+cmp /tmp/precedence/w/f <<EOF
+/etc/tmpfiles.d/w-a.conf
+/usr/lib/tmpfiles.d/w-b.conf
+/etc/tmpfiles.d/w-c.conf
+EOF
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for the ":" uid/gid/mode modifier
+#
+set -eux
+
+rm -rf /tmp/someinode
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0123 :1 :1
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0321 :2 :2
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode 0321 2 2
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0123 :1 :1
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
+
+rm -rf /tmp/someinode
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0123 :1 :1
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
+
+rm -rf /tmp/someinode
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Check specifier expansion in L lines.
+#
+set -eux
+
+rm -fr /tmp/L
+mkdir /tmp/L
+
+# Check that %h expands to $home.
+home='/somewhere'
+dst='/tmp/L/1'
+src="$home"
+HOME="$home" \
+systemd-tmpfiles --dry-run --create - <<EOF
+L $dst - - - - %h
+EOF
+test ! -h "$dst"
+
+HOME="$home" \
+systemd-tmpfiles --create - <<EOF
+L $dst - - - - %h
+EOF
+test "$(readlink "$dst")" = "$src"
+
+# Check that %h in the path is expanded, but
+# the result of this expansion is not expanded once again.
+root='/tmp/L/2'
+home='/%U'
+src="/usr/share/factory$home"
+mkdir -p "$root$src"
+dst="$root$home"
+HOME="$home" \
+systemd-tmpfiles --create --dry-run --root="$root" - <<EOF
+L %h - - - -
+EOF
+test ! -h "$dst"
+
+HOME="$home" \
+systemd-tmpfiles --create --root="$root" - <<EOF
+L %h - - - -
+EOF
+test "$(readlink "$dst")" = "$src"
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test for conditionalized execute bit ('X' bit)
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+rm -f /tmp/acl_exec
+touch /tmp/acl_exec
+
+# No ACL set yet
+systemd-tmpfiles --dry-run --create - <<EOF
+a /tmp/acl_exec - - - - u:root:rwX
+EOF
+assert_not_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
+
+systemd-tmpfiles --create - <<EOF
+a /tmp/acl_exec - - - - u:root:rwX
+EOF
+assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
+
+# Set another ACL and append
+setfacl -m g:root:x /tmp/acl_exec
+
+systemd-tmpfiles --create - <<EOF
+a+ /tmp/acl_exec - - - - u:root:rwX
+EOF
+acl="$(getfacl -Ec /tmp/acl_exec)"
+assert_in 'user:root:rwx' "$acl"
+assert_in 'group:root:--x' "$acl"
+
+# Reset ACL (no append)
+systemd-tmpfiles --create - <<EOF
+a /tmp/acl_exec - - - - u:root:rwX
+EOF
+assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
+
+rm -f /tmp/acl_exec
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test for C-style escapes in file names and contents
+set -eux
+set -o pipefail
+
+data="\x20foo\nbar"
+dst="/tmp/x/\x20a\nb"
+
+systemd-tmpfiles --create - <<EOF
+f "$dst" 0644 0 0 - $data
+EOF
+
+diff "$(printf "/tmp/x/\x20a\nb")" <(printf "\x20foo\nbar")
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for the --purge switch
+#
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+c='
+d /tmp/somedir
+f /tmp/somedir/somefile - - - - baz
+'
+
+systemd-tmpfiles --create - <<<"$c"
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
+
+systemd-tmpfiles --purge --dry-run - <<<"$c"
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
+
+systemd-tmpfiles --purge - <<<"$c"
+test ! -f /tmp/somedir/somefile
+test ! -d /tmp/somedir/
+
+systemd-tmpfiles --create --purge --dry-run - <<<"$c"
+test ! -f /tmp/somedir/somefile
+test ! -d /tmp/somedir/
+
+systemd-tmpfiles --create --purge - <<<"$c"
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for character and block device creation
+#
+set -eux
+set -o pipefail
+
+rm -rf /tmp/dev
+mkdir /tmp/dev
+
+# We are running tests in /tmp, which would normally be mounted nodev,
+# so we only try with --dry-run.
+
+systemd-tmpfiles --dry-run --create - <<EOF
+c /tmp/dev/char - - - - 11:12
+b /tmp/dev/block - - - - 11:14
+EOF
+
+test ! -e /tmp/dev/char
+test ! -e /tmp/dev/block
+
+systemd-tmpfiles --dry-run --create - <<EOF
+c+ /tmp/dev/char - - - - 11:12
+b+ /tmp/dev/block - - - - 11:14
+EOF
+
+test ! -e /tmp/dev/char
+test ! -e /tmp/dev/block
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-22-TMPFILES
+After=systemd-tmpfiles-setup.service
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+assert_eq "$LISTEN_FDS" "$1"
+assert_eq "$LISTEN_FDNAMES" "$2"
+
+for ((i = 3; i < 3 + LISTEN_FDS; i++)); do
+ read -r -u "$i" text
+ assert_eq "$text" "${!i}" # Dereference $i to get i'th arg
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+
+if [ -f /tmp/TEST-23-UNIT-FILE.counter ] ; then
+ read -r counter < /tmp/TEST-23-UNIT-FILE.counter
+ counter=$((counter + 1))
+else
+ counter=0
+fi
+
+echo "$counter" >/tmp/TEST-23-UNIT-FILE.counter
+
+if [ "$counter" -eq 5 ] ; then
+ systemctl kill --kill-whom=main -sUSR1 TEST-23-UNIT-FILE.service
+fi
+
+exec sleep 1.5
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test ExecReload= (PR #13098)
+
+systemd-analyze log-level debug
+
+export SYSTEMD_PAGER=
+SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
+SERVICE_NAME="${SERVICE_PATH##*/}"
+
+echo "[#1] Failing ExecReload= should not kill the service"
+cat >"$SERVICE_PATH" <<EOF
+[Service]
+ExecStart=sleep infinity
+ExecReload=/bin/false
+EOF
+
+systemctl daemon-reload
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+# The reload SHOULD fail but SHOULD NOT affect the service state
+(! systemctl reload "$SERVICE_NAME")
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
+
+
+echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
+cat >"$SERVICE_PATH" <<EOF
+[Service]
+ExecStart=sleep infinity
+ExecReload=/bin/true
+ExecReload=/bin/false
+ExecReload=/bin/true
+EOF
+
+systemctl daemon-reload
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+# The reload SHOULD fail but SHOULD NOT affect the service state
+(! systemctl reload "$SERVICE_NAME")
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
+
+echo "[#3] Failing ExecReload=- should not affect reload's exit code"
+cat >"$SERVICE_PATH" <<EOF
+[Service]
+ExecStart=sleep infinity
+ExecReload=-/bin/false
+EOF
+
+systemctl daemon-reload
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+systemctl reload "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+
+# Test that ExecStopPost= is always run
+
+systemd-analyze log-level debug
+
+systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
+ -p ExecStopPost='/bin/touch /run/simple1' true
+test -f /run/simple1
+
+(! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
+ -p ExecStopPost='/bin/touch /run/simple2' false)
+test -f /run/simple2
+
+systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
+ -p ExecStopPost='/bin/touch /run/exec1' sleep 1
+test -f /run/exec1
+
+(! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
+ -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false')
+test -f /run/exec2
+
+cat >/tmp/forking1.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+sleep 4 &
+MAINPID=\$!
+disown
+
+systemd-notify MAINPID=\$MAINPID
+EOF
+chmod +x /tmp/forking1.sh
+
+systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
+ -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
+test -f /run/forking1
+
+cat >/tmp/forking2.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+(sleep 4; exit 1) &
+MAINPID=\$!
+disown
+
+systemd-notify MAINPID=\$MAINPID
+EOF
+chmod +x /tmp/forking2.sh
+
+(! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
+ -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh)
+test -f /run/forking2
+
+systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
+ -p ExecStopPost='/bin/touch /run/oneshot1' true
+test -f /run/oneshot1
+
+(! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
+ -p ExecStopPost='/bin/touch /run/oneshot2' false)
+test -f /run/oneshot2
+
+systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
+ -p ExecStopPost='/bin/touch /run/dbus1' \
+ busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 || :
+test -f /run/dbus1
+
+systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
+ -p ExecStopPost='/bin/touch /run/dbus2' true
+test -f /run/dbus2
+
+# https://github.com/systemd/systemd/issues/19920
+(! systemd-run --unit=dbus3.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus \
+ -p ExecStopPost='/bin/touch /run/dbus3' true)
+
+cat >/tmp/notify1.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+systemd-notify --ready
+EOF
+chmod +x /tmp/notify1.sh
+
+systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
+ -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
+test -f /run/notify1
+
+(! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
+ -p ExecStopPost='/bin/touch /run/notify2' true)
+test -f /run/notify2
+
+systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
+test -f /run/idle1
+
+(! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle \
+ -p ExecStopPost='/bin/touch /run/idle2' false)
+test -f /run/idle2
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# Test JoinsNamespaceOf= with PrivateTmp=yes
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# simple case
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-1.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-2.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-3.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-1.service
+
+# inverse dependency
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-4.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-5.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-4.service
+
+# transitive dependency
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-6.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-7.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-8.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-9.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-6.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test RuntimeDirectoryPreserve=yes
+
+at_exit() {
+ set +e
+
+ rm -fr /run/hoge /tmp/aaa
+}
+
+trap at_exit EXIT
+
+systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
+
+touch /run/hoge/foo
+touch /tmp/aaa/bbb
+
+systemctl restart tmp-aaa.mount
+
+test -e /run/hoge/foo
+test ! -e /tmp/aaa/bbb
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test StandardOutput=file:
+
+systemd-analyze log-level debug
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-one \
+ -p StandardOutput=file:/tmp/stdout \
+ -p StandardError=file:/tmp/stderr \
+ -p Type=exec \
+ sh -c 'echo x ; echo y >&2'
+cmp /tmp/stdout <<EOF
+x
+EOF
+cmp /tmp/stderr <<EOF
+y
+EOF
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-two \
+ -p StandardOutput=file:/tmp/stdout \
+ -p StandardError=file:/tmp/stderr \
+ -p Type=exec \
+ sh -c 'echo z ; echo a >&2'
+cmp /tmp/stdout <<EOF
+z
+EOF
+cmp /tmp/stderr <<EOF
+a
+EOF
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-three \
+ -p StandardOutput=append:/tmp/stdout \
+ -p StandardError=append:/tmp/stderr \
+ -p Type=exec \
+ sh -c 'echo b ; echo c >&2'
+cmp /tmp/stdout <<EOF
+z
+b
+EOF
+cmp /tmp/stderr <<EOF
+a
+c
+EOF
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-four \
+ -p StandardOutput=truncate:/tmp/stdout \
+ -p StandardError=truncate:/tmp/stderr \
+ -p Type=exec \
+ sh -c 'echo a ; echo b >&2'
+cmp /tmp/stdout <<EOF
+a
+EOF
+cmp /tmp/stderr <<EOF
+b
+EOF
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# Test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo=
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# Idea is this:
+# 1. we start TEST-23-UNIT-FILE-success.service
+# 2. which through OnSuccess= starts TEST-23-UNIT-FILE-fail.service,
+# 3. which through OnFailure= starts TEST-23-UNIT-FILE-uphold.service,
+# 4. which through Uphold= starts/keeps TEST-23-UNIT-FILE-short-lived.service running,
+# 5. which will sleep 1s when invoked, and on the 5th invocation send us a SIGUSR1
+# 6. once we got that we finish cleanly
+
+sigusr1=0
+trap sigusr1=1 SIGUSR1
+
+trap -p SIGUSR1
+
+systemctl start TEST-23-UNIT-FILE-success.service
+
+while [ "$sigusr1" -eq 0 ] ; do
+ sleep .5
+done
+
+systemctl stop TEST-23-UNIT-FILE-uphold.service
+
+systemctl enable TEST-23-UNIT-FILE-upheldby-install.service
+
+# Idea is this:
+# 1. we start TEST-23-UNIT-FILE-retry-uphold.service
+# 2. which through Uphold= starts TEST-23-UNIT-FILE-retry-upheld.service
+# 3. which through Requires= starts TEST-23-UNIT-FILE-retry-fail.service
+# 4. which fails as /tmp/TEST-23-UNIT-FILE-retry-fail does not exist, so TEST-23-UNIT-FILE-retry-upheld.service
+# is no longer restarted
+# 5. we create /tmp/TEST-23-UNIT-FILE-retry-fail
+# 6. now TEST-23-UNIT-FILE-retry-upheld.service will be restarted since upheld, and its dependency will
+# be satisfied
+
+rm -f /tmp/TEST-23-UNIT-FILE-retry-fail
+systemctl start TEST-23-UNIT-FILE-retry-uphold.service
+systemctl is-active TEST-23-UNIT-FILE-upheldby-install.service
+
+until systemctl is-failed TEST-23-UNIT-FILE-retry-fail.service ; do
+ sleep .5
+done
+
+(! systemctl is-active TEST-23-UNIT-FILE-retry-upheld.service)
+
+touch /tmp/TEST-23-UNIT-FILE-retry-fail
+
+until systemctl is-active TEST-23-UNIT-FILE-retry-upheld.service ; do
+ sleep .5
+done
+
+systemctl stop TEST-23-UNIT-FILE-retry-uphold.service TEST-23-UNIT-FILE-retry-fail.service TEST-23-UNIT-FILE-retry-upheld.service
+
+# Idea is this:
+# 1. we start TEST-23-UNIT-FILE-prop-stop-one.service
+# 2. which through Wants=/After= pulls in TEST-23-UNIT-FILE-prop-stop-two.service as well
+# 3. TEST-23-UNIT-FILE-prop-stop-one.service then sleeps indefinitely
+# 4. TEST-23-UNIT-FILE-prop-stop-two.service sleeps a short time and exits
+# 5. the StopPropagatedFrom= dependency between the two should ensure *both* will exit as result
+# 6. an ExecStopPost= line on TEST-23-UNIT-FILE-prop-stop-one.service will send us a SIGUSR2
+# 7. once we got that we finish cleanly
+
+sigusr2=0
+trap sigusr2=1 SIGUSR2
+
+systemctl start TEST-23-UNIT-FILE-prop-stop-one.service
+
+while [ "$sigusr2" -eq 0 ] ; do
+ sleep .5
+done
+
+
+# Idea is this:
+# 1. we start TEST-23-UNIT-FILE-binds-to.service
+# 2. which through BindsTo=/After= pulls in TEST-23-UNIT-FILE-bound-by.service as well
+# 3. TEST-23-UNIT-FILE-bound-by.service suddenly dies
+# 4. TEST-23-UNIT-FILE-binds-to.service should then also be pulled down (it otherwise just hangs)
+# 6. an ExecStopPost= line on TEST-23-UNIT-FILE-binds-to.service will send us a SIGRTMIN1+1
+# 7. once we got that we finish cleanly
+
+sigrtmin1=0
+trap sigrtmin1=1 SIGRTMIN+1
+
+systemctl start TEST-23-UNIT-FILE-binds-to.service
+
+while [ "$sigrtmin1" -eq 0 ] ; do
+ sleep .5
+done
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test unit configuration/state/cache/log/runtime data cleanup
+
+at_exit() {
+ set +e
+
+ rm -fr /{etc,run,var/lib,var/cache,var/log}/test-service
+ rm -fr /{etc,run,var/lib,var/cache,var/log}/private/test-service
+ rm -fr /{etc,run,var/lib,var/cache,var/log}/hoge
+ rm -fr /{etc,run,var/lib,var/cache,var/log}/test-socket
+}
+
+trap at_exit EXIT
+
+cat >/run/systemd/system/test-service.service <<EOF
+[Service]
+ConfigurationDirectory=test-service
+RuntimeDirectory=test-service
+StateDirectory=test-service
+CacheDirectory=test-service
+LogsDirectory=test-service
+RuntimeDirectoryPreserve=yes
+ExecStart=sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test ! -e /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+systemctl start test-service
+
+test -d /etc/test-service
+test -d /run/test-service
+test -d /var/lib/test-service
+test -d /var/cache/test-service
+test -d /var/log/test-service
+
+(! systemctl clean test-service)
+
+systemctl stop test-service
+
+test -d /etc/test-service
+test -d /run/test-service
+test -d /var/lib/test-service
+test -d /var/cache/test-service
+test -d /var/log/test-service
+
+systemctl clean test-service --what=configuration
+
+test ! -e /etc/test-service
+test -d /run/test-service
+test -d /var/lib/test-service
+test -d /var/cache/test-service
+test -d /var/log/test-service
+
+systemctl clean test-service
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test -d /var/lib/test-service
+test ! -e /var/cache/test-service
+test -d /var/log/test-service
+
+systemctl clean test-service --what=logs
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test -d /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+systemctl clean test-service --what=all
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test ! -e /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+cat >/run/systemd/system/test-service.service <<EOF
+[Service]
+DynamicUser=yes
+ConfigurationDirectory=test-service
+RuntimeDirectory=test-service
+StateDirectory=test-service
+CacheDirectory=test-service
+LogsDirectory=test-service
+RuntimeDirectoryPreserve=yes
+ExecStart=sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test ! -e /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+systemctl restart test-service
+
+test -d /etc/test-service
+test -d /run/private/test-service
+test -d /var/lib/private/test-service
+test -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test -L /run/test-service
+test -L /var/lib/test-service
+test -L /var/cache/test-service
+test -L /var/log/test-service
+
+(! systemctl clean test-service)
+
+systemctl stop test-service
+
+test -d /etc/test-service
+test -d /run/private/test-service
+test -d /var/lib/private/test-service
+test -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test -L /run/test-service
+test -L /var/lib/test-service
+test -L /var/cache/test-service
+test -L /var/log/test-service
+
+systemctl clean test-service --what=configuration
+
+test ! -d /etc/test-service
+test -d /run/private/test-service
+test -d /var/lib/private/test-service
+test -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test -L /run/test-service
+test -L /var/lib/test-service
+test -L /var/cache/test-service
+test -L /var/log/test-service
+
+systemctl clean test-service
+
+test ! -d /etc/test-service
+test ! -d /run/private/test-service
+test -d /var/lib/private/test-service
+test ! -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test ! -L /run/test-service
+test -L /var/lib/test-service
+test ! -L /var/cache/test-service
+test -L /var/log/test-service
+
+systemctl clean test-service --what=logs
+
+test ! -d /etc/test-service
+test ! -d /run/private/test-service
+test -d /var/lib/private/test-service
+test ! -d /var/cache/private/test-service
+test ! -d /var/log/private/test-service
+test ! -L /run/test-service
+test -L /var/lib/test-service
+test ! -L /var/cache/test-service
+test ! -L /var/log/test-service
+
+systemctl clean test-service --what=all
+
+test ! -d /etc/test-service
+test ! -d /run/private/test-service
+test ! -d /var/lib/private/test-service
+test ! -d /var/cache/private/test-service
+test ! -d /var/log/private/test-service
+test ! -L /run/test-service
+test ! -L /var/lib/test-service
+test ! -L /var/cache/test-service
+test ! -L /var/log/test-service
+
+cat >/run/systemd/system/tmp-hoge.mount <<EOF
+[Mount]
+What=tmpfs
+Type=tmpfs
+ConfigurationDirectory=hoge
+RuntimeDirectory=hoge
+StateDirectory=hoge
+CacheDirectory=hoge
+LogsDirectory=hoge
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/hoge
+test ! -e /run/hoge
+test ! -e /var/lib/hoge
+test ! -e /var/cache/hoge
+test ! -e /var/log/hoge
+
+systemctl start tmp-hoge.mount
+
+test -d /etc/hoge
+test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+(! systemctl clean tmp-hoge.mount)
+
+test -d /etc/hoge
+test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl stop tmp-hoge.mount
+
+test -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=configuration
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=logs
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=all
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test ! -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
+
+cat >/run/systemd/system/test-service.socket <<EOF
+[Socket]
+ListenSequentialPacket=/run/test-service.socket
+RemoveOnStop=yes
+ExecStartPre=true
+ConfigurationDirectory=test-socket
+RuntimeDirectory=test-socket
+StateDirectory=test-socket
+CacheDirectory=test-socket
+LogsDirectory=test-socket
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test ! -e /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test ! -e /var/log/test-socket
+
+systemctl start test-service.socket
+
+test -d /etc/test-socket
+test -d /run/test-socket
+test -d /var/lib/test-socket
+test -d /var/cache/test-socket
+test -d /var/log/test-socket
+
+(! systemctl clean test-service.socket)
+
+systemctl stop test-service.socket
+
+test -d /etc/test-socket
+test ! -d /run/test-socket
+test -d /var/lib/test-socket
+test -d /var/cache/test-socket
+test -d /var/log/test-socket
+
+systemctl clean test-service.socket --what=configuration
+
+test ! -e /etc/test-socket
+test ! -d /run/test-socket
+test -d /var/lib/test-socket
+test -d /var/cache/test-socket
+test -d /var/log/test-socket
+
+systemctl clean test-service.socket
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test -d /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test -d /var/log/test-socket
+
+systemctl clean test-service.socket --what=logs
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test -d /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test ! -e /var/log/test-socket
+
+systemctl clean test-service.socket --what=all
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test ! -e /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test ! -e /var/log/test-socket
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test ExecXYZEx= service unit dbus hookups
+
+systemd-analyze log-level debug
+
+declare -A property
+
+property[1_one]=ExecCondition
+property[2_two]=ExecStartPre
+property[3_three]=ExecStart
+property[4_four]=ExecStartPost
+property[5_five]=ExecReload
+property[6_six]=ExecStop
+property[7_seven]=ExecStopPost
+
+# These should all get upgraded to the corresponding Ex property as the non-Ex variant
+# does not support the ":" prefix (no-env-expand).
+for c in "${!property[@]}"; do
+ systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
+ systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+ systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+declare -A property_ex
+
+property_ex[1_one_ex]=ExecConditionEx
+property_ex[2_two_ex]=ExecStartPreEx
+property_ex[3_three_ex]=ExecStartEx
+property_ex[4_four_ex]=ExecStartPostEx
+property_ex[5_five_ex]=ExecReloadEx
+property_ex[6_six_ex]=ExecStopEx
+property_ex[7_seven_ex]=ExecStopPostEx
+
+for c in "${!property_ex[@]}"; do
+ systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
+ systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+ systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Test oneshot unit restart on failure
+
+# wait this many secs for each test service to succeed in what is being tested
+MAX_SECS=60
+
+systemctl log-level debug
+
+# test one: Restart=on-failure should restart the service
+(! systemd-run --unit=oneshot-restart-one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1")
+
+for ((secs = 0; secs < MAX_SECS; secs++)); do
+ [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]] || break
+ sleep 1
+done
+if [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]]; then
+ exit 1
+fi
+
+TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
+
+: >$TMP_FILE
+
+# test two: make sure StartLimitBurst correctly limits the number of restarts
+# and restarts execution of the unit from the first ExecStart=
+(! systemd-run --unit=oneshot-restart-two \
+ -p StartLimitIntervalSec=120 \
+ -p StartLimitBurst=3 \
+ -p Type=oneshot \
+ -p Restart=on-failure \
+ -p ExecStart="/bin/bash -c 'printf a >>$TMP_FILE'" /bin/bash -c "exit 1")
+
+# wait for at least 3 restarts
+for ((secs = 0; secs < MAX_SECS; secs++)); do
+ [[ $(cat $TMP_FILE) != "aaa" ]] || break
+ sleep 1
+done
+if [[ $(cat $TMP_FILE) != "aaa" ]]; then
+ exit 1
+fi
+
+# wait for 5 more seconds to make sure there aren't excess restarts
+sleep 5
+if [[ $(cat $TMP_FILE) != "aaa" ]]; then
+ exit 1
+fi
+rm "$TMP_FILE"
+
+# Test RestartForceExitStatus=. Note that success exit statuses are meant to be skipped
+
+TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
+UNIT_NAME="TEST-23-UNIT-FILE-oneshot-restartforce.service"
+ONSUCCESS_UNIT_NAME="TEST-23-UNIT-FILE-oneshot-restartforce-onsuccess.service"
+FIFO_FILE="/tmp/test-23-oneshot-restart-test-fifo"
+
+cat >"/run/systemd/system/$UNIT_NAME" <<EOF
+[Unit]
+OnSuccess=$ONSUCCESS_UNIT_NAME
+
+[Service]
+Type=oneshot
+RestartForceExitStatus=0 2
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh "$TMP_FILE"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+cat >"/run/systemd/system/$ONSUCCESS_UNIT_NAME" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c 'echo finished >$FIFO_FILE'
+EOF
+
+mkfifo "$FIFO_FILE"
+
+# Pin the unit in memory
+systemctl enable "$UNIT_NAME"
+# Initial run should fail
+(! systemctl start "$UNIT_NAME")
+# Wait for OnSuccess=
+read -r x <"$FIFO_FILE"
+assert_eq "$x" "finished"
+
+cmp -b <(systemctl show "$UNIT_NAME" -p Result -p NRestarts -p SubState) <<EOF
+Result=success
+NRestarts=1
+SubState=dead
+EOF
+
+systemctl disable "$UNIT_NAME"
+rm "$TMP_FILE" /run/systemd/system/{"$UNIT_NAME","$ONSUCCESS_UNIT_NAME"} "$FIFO_FILE"
+
+systemctl log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ set +e
+
+ rm -rf /tmp/test-open-file/
+}
+
+trap at_exit EXIT
+
+systemctl log-level debug
+
+# Existing files
+
+mkdir /tmp/test-open-file
+echo "Open" >'/tmp/test-open-file/open.txt'
+echo "File" >'/tmp/test-open-file/file:colon.txt'
+
+systemd-run -p DynamicUser=yes -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+ -p OpenFile='/tmp/test-open-file/open.txt::read-only' \
+ -p OpenFile='/tmp/test-open-file/file\x3Acolon.txt:colon' \
+ -p RemainAfterExit=yes \
+ --unit=test-23-openfile-existing.service \
+ --service-type=oneshot \
+ /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-openfile-child.sh 2 "open.txt:colon" "Open" "File"
+
+cmp <(systemctl show -p OpenFile test-23-openfile-existing.service) <<EOF
+OpenFile=/tmp/test-open-file/open.txt::read-only
+OpenFile=/tmp/test-open-file/file\\x3acolon.txt:colon
+EOF
+
+systemctl stop test-23-openfile-existing.service
+
+# Sockets
+
+systemctl start TEST-23-UNIT-FILE-openfile-server.socket
+
+systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
+ --wait \
+ /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-openfile-child.sh 1 "socket" "Socket"
+
+systemctl stop TEST-23-UNIT-FILE-openfile-server.socket
+
+# Ignore when missing
+
+assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
+assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
+
+systemctl log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Ensure %j Wants directives work
+systemd-run --wait \
+ --property="Type=oneshot" \
+ --property="Wants=TEST-23-UNIT-FILE-specifier-j-wants.service" \
+ --property="After=TEST-23-UNIT-FILE-specifier-j-wants.service" \
+ true
+
+test -f /tmp/tetsuite-23-specifier-j-done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# Test adding new BindPaths while unit is already running
+
+at_exit() {
+ set +e
+
+ rm -f /run/TEST-23-UNIT-FILE-marker-{fixed,runtime}
+ rm -fr /run/inaccessible
+}
+
+trap at_exit EXIT
+
+echo "MARKER_FIXED" >/run/TEST-23-UNIT-FILE-marker-fixed
+mkdir /run/inaccessible
+
+systemctl start TEST-23-UNIT-FILE-namespaced.service
+
+# Ensure that inaccessible paths aren't bypassed by the runtime setup,
+(! systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-fixed /run/inaccessible/testfile-marker-fixed)
+
+echo "MARKER_WRONG" >/run/TEST-23-UNIT-FILE-marker-wrong
+echo "MARKER_RUNTIME" >/run/TEST-23-UNIT-FILE-marker-runtime
+
+# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
+systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-wrong /tmp/testfile-marker-runtime
+test "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" = "running"
+systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime
+
+timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" == running ]]; do sleep .5; done'
+systemctl is-active TEST-23-UNIT-FILE-namespaced.service
+
+# Now test that systemctl bind fails when attempted on a non-namespaced unit
+systemctl start TEST-23-UNIT-FILE-non-namespaced.service
+
+(! systemctl bind --mkdir TEST-23-UNIT-FILE-non-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime)
+
+timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-non-namespaced.service)" == running ]]; do sleep .5; done'
+(! systemctl is-active TEST-23-UNIT-FILE-non-namespaced.service)
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-23-UNIT-FILE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# Note: the signal shenanigans are necessary for the Upholds= tests
+run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test start & stop operations without daemon-reload
+
+at_exit() {
+ set +e
+
+ rm -f /run/systemd/system/TEST-23-UNIT-FILE-no-reload.{service,target}
+}
+
+trap at_exit EXIT
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.target <<EOF
+[Unit]
+Wants=TEST-23-UNIT-FILE-no-reload.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start TEST-23-UNIT-FILE-no-reload.target
+
+# The filesystem on the test image, despite being ext4, seems to have a mtime
+# granularity of one second, which means the manager's unit cache won't be
+# marked as dirty when writing the unit file, unless we wait at least a full
+# second after the previous daemon-reload.
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[30]: + cat
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[30]: + ls -l --full-time /etc/systemd/system/TEST-23-UNIT-FILE-no-reload.service
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 /
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[30]: + stat -f --format=%t /etc/systemd/system/TEST-23-UNIT-FILE-no-reload.servic
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[53]: ef53
+sleep 3.1
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.service <<EOF
+[Service]
+ExecStart=sleep infinity
+EOF
+
+systemctl start TEST-23-UNIT-FILE-no-reload.service
+
+systemctl is-active TEST-23-UNIT-FILE-no-reload.service
+
+# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992
+systemctl stop TEST-23-UNIT-FILE-no-reload.service
+rm -f /run/systemd/system/TEST-23-UNIT-FILE-no-reload.service
+systemctl daemon-reload
+
+sleep 3.1
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.service <<EOF
+[Service]
+ExecStart=sleep infinity
+EOF
+
+# Start a non-existing unit first, so that the cache is reloaded for an unrelated
+# reason. Starting the existing unit later should still work thanks to the check
+# for the last load attempt vs cache timestamp.
+systemctl start TEST-23-UNIT-FILE-no-reload-nonexistent.service || true
+
+systemctl start TEST-23-UNIT-FILE-no-reload.service
+
+systemctl is-active TEST-23-UNIT-FILE-no-reload.service
+
+# Stop and remove, and try again to exercise the transaction setup code path by
+# having the target pull in the unloaded but available unit
+systemctl stop TEST-23-UNIT-FILE-no-reload.service TEST-23-UNIT-FILE-no-reload.target
+rm -f /run/systemd/system/TEST-23-UNIT-FILE-no-reload.service /run/systemd/system/TEST-23-UNIT-FILE-no-reload.target
+systemctl daemon-reload
+
+sleep 3.1
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.target <<EOF
+[Unit]
+Conflicts=shutdown.target
+Wants=TEST-23-UNIT-FILE-no-reload.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start TEST-23-UNIT-FILE-no-reload.target
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.service <<EOF
+[Service]
+ExecStart=sleep infinity
+EOF
+
+systemctl restart TEST-23-UNIT-FILE-no-reload.target
+
+systemctl is-active TEST-23-UNIT-FILE-no-reload.service
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test unit configuration/state/cache/log/runtime data cleanup
+
+export HOME=/root
+export XDG_RUNTIME_DIR=/run/user/0
+
+systemctl start user@0.service
+
+( ! test -d "$HOME"/.local/state/foo)
+( ! test -d "$HOME"/.config/foo)
+
+systemd-run --user -p StateDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+( ! test -L "$HOME"/.local/state/foo)
+( ! test -d "$HOME"/.config/foo)
+
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+( ! test -L "$HOME"/.local/state/foo)
+test -d "$HOME"/.config/foo
+
+rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
+
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+( ! test -L "$HOME"/.local/state/foo)
+test -d "$HOME"/.config/foo
+
+rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
+
+# Now trigger an update scenario by creating a config dir first
+systemd-run --user -p ConfigurationDirectory=foo --wait /bin/true
+
+( ! test -d "$HOME"/.local/state/foo)
+test -d "$HOME"/.config/foo
+
+# This will look like an update and result in a symlink
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+test -L "$HOME"/.local/state/foo
+test -d "$HOME"/.config/foo
+
+test "$(readlink "$HOME"/.local/state/foo)" = ../../.config/foo
+
+# Check that this will work safely a second time
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+rm "$HOME"/.local/state/foo
+rmdir "$HOME"/.config/foo
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test OnSuccess=/OnFailure= in combination
+
+systemd-analyze log-level debug
+
+# Start-up should fail, but the automatic restart should fix it
+(! systemctl start success-failure-test )
+
+# Wait until the first invocation finished & failed
+while test ! -f /tmp/success-failure-test-ran ; do
+ sleep .5
+done
+
+# Wait until the second invocation finished & succeeded
+while test ! -f /tmp/success-failure-test-ran2 ; do
+ sleep .5
+done
+
+# Verify it is indeed running
+systemctl is-active -q success-failure-test
+
+# The above should have caused the failure service to start (asynchronously)
+while test "$(systemctl is-active success-failure-test-failure)" != "active" ; do
+ sleep .5
+done
+
+# But the success service should not have started
+test "$(systemctl is-active success-failure-test-success)" = "inactive"
+
+systemctl stop success-failure-test-failure
+
+# Do a clean kill of the service now
+systemctl kill success-failure-test
+
+# This should result in the success service to start
+while test "$(systemctl is-active success-failure-test-success)" != "active" ; do
+ sleep .5
+done
+
+# But the failure service should not have started again
+test "$(systemctl is-active success-failure-test-failure)" = "inactive"
+
+systemctl stop success-failure-test success-failure-test-success
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test Type=exec
+
+systemd-analyze log-level debug
+
+# Create a binary for which execve() will fail
+touch /tmp/brokenbinary
+chmod +x /tmp/brokenbinary
+
+# These three commands should succeed.
+systemd-run --unit=exec-one -p Type=simple /bin/sleep infinity
+systemd-run --unit=exec-two -p Type=simple -p User=idontexist /bin/sleep infinity
+systemd-run --unit=exec-three -p Type=simple /tmp/brokenbinary
+
+# And now, do the same with Type=exec, where the latter two should fail
+systemd-run --unit=exec-four -p Type=exec /bin/sleep infinity
+(! systemd-run --unit=exec-five -p Type=exec -p User=idontexist /bin/sleep infinity)
+(! systemd-run --unit=exec-six -p Type=exec /tmp/brokenbinary)
+
+systemd-run --unit=exec-seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
+# Both TERM and SIGINT happen to have the same number on all architectures
+test "$(systemctl show --value -p KillSignal exec-seven.service)" -eq 15
+test "$(systemctl show --value -p RestartKillSignal exec-seven.service)" -eq 2
+
+systemctl restart exec-seven.service
+systemctl stop exec-seven.service
+
+# For issue #20933
+
+# Should work normally
+busctl call \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager StartTransientUnit \
+ "ssa(sv)a(sa(sv))" test-20933-ok.service replace 1 \
+ ExecStart "a(sasb)" 1 \
+ /usr/bin/sleep 2 /usr/bin/sleep 1 true \
+ 0
+
+# DBus call should fail but not crash systemd
+(! busctl call \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager StartTransientUnit \
+ "ssa(sv)a(sa(sv))" test-20933-bad.service replace 1 \
+ ExecStart "a(sasb)" 1 \
+ /usr/bin/sleep 0 true \
+ 0)
+
+# Same but with the empty argv in the middle
+(! busctl call \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager StartTransientUnit \
+ "ssa(sv)a(sa(sv))" test-20933-bad-middle.service replace 1 \
+ ExecStart "a(sasb)" 3 \
+ /usr/bin/sleep 2 /usr/bin/sleep 1 true \
+ /usr/bin/sleep 0 true \
+ /usr/bin/sleep 2 /usr/bin/sleep 1 true \
+ 0)
+
+systemd-analyze log-level info
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+USER="test-23-utmp"
+
+cleanup() {
+ userdel "$USER"
+}
+
+trap cleanup EXIT
+useradd "$USER"
+
+assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user -p User=$USER whoami)" "$USER"
+assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user whoami)" "$(whoami)"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# Verify our own unit files (where applicable)
+
+# This is generated by meson during build
+UNIT_FILE_LIST="/usr/lib/systemd/tests/testdata/installed-unit-files.txt"
+
+if [[ ! -f "$UNIT_FILE_LIST" ]]; then
+ echo "Couldn't find the list of installed units, skipping the test"
+ exit 0
+fi
+
+if ! command -v systemd-analyze >/dev/null; then
+ echo "Built without systemd-analyze, skipping the test"
+ exit 0
+fi
+
+mapfile -t UNIT_FILES <"$UNIT_FILE_LIST"
+
+if [[ "${#UNIT_FILES[@]}" -le 0 ]]; then
+ echo >&2 "The unit file list is empty, this is most likely a bug"
+ exit 1
+fi
+
+for unit_file in "${UNIT_FILES[@]}"; do
+ # Skip the check for a couple of units, namely:
+ # - syslog.socket: the corresponding syslog.service might not be installed
+ # - rc-local.service: compat API, /etc/rc.d/rc.local most likely won't be present
+ if [[ "$unit_file" =~ /(syslog.socket|rc-local.service)$ ]]; then
+ continue
+ fi
+
+ # Skip missing unit files - this is useful for $NO_BUILD runs, where certain unit files might be dropped
+ # in distro packaging
+ if [[ ! -e "$unit_file" ]]; then
+ echo "$unit_file not found, skipping"
+ continue
+ fi
+
+ systemd-analyze --recursive-errors=no --man=no verify "$unit_file"
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+test "$(systemctl whoami)" = TEST-23-UNIT-FILE.service
+test "$(systemctl whoami $$)" = TEST-23-UNIT-FILE.service
+
+systemctl whoami 1 $$ 1 | cmp - /dev/fd/3 3<<'EOF'
+init.scope
+TEST-23-UNIT-FILE.service
+init.scope
+EOF
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-24-CRYPTSETUP
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# TODO:
+# - /proc/cmdline parsing
+# - expect + interactive auth?
+
+# We set up an encrypted /var partition which should get mounted automatically
+# on boot
+mountpoint /var
+
+systemctl --state=failed --no-legend --no-pager | tee /failed
+if [[ -s /failed ]]; then
+ echo >&2 "Found units in failed state"
+ exit 1
+fi
+
+at_exit() {
+ set +e
+
+ mountpoint -q /proc/cmdline && umount /proc/cmdline
+ rm -f /etc/crypttab
+ [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab
+ [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP"
+ [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"
+
+ systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+cryptsetup_start_and_check() {
+ local expect_fail=0
+ local umount_header_and_key=0
+ local ec volume unit
+
+ if [[ "${1:?}" == "-f" ]]; then
+ expect_fail=1
+ shift
+ fi
+
+ if [[ "${1:?}" == "-u" ]]; then
+ umount_header_and_key=1
+ shift
+ fi
+
+ for volume in "$@"; do
+ unit="systemd-cryptsetup@$volume.service"
+
+ # The unit existence check should always pass
+ [[ "$(systemctl show -P LoadState "$unit")" == loaded ]]
+ systemctl list-unit-files "$unit"
+
+ systemctl start "$unit" && ec=0 || ec=$?
+ if [[ "$expect_fail" -ne 0 ]]; then
+ if [[ "$ec" -eq 0 ]]; then
+ echo >&2 "Unexpected pass when starting $unit"
+ return 1
+ fi
+
+ return 0
+ fi
+
+ if [[ "$ec" -ne 0 ]]; then
+ echo >&2 "Unexpected fail when starting $unit"
+ return 1
+ fi
+
+ if [[ "$umount_header_and_key" -ne 0 ]]; then
+ umount "$TMPFS_DETACHED_KEYFILE"
+ umount "$TMPFS_DETACHED_HEADER"
+ udevadm settle --timeout=30
+ fi
+
+ systemctl status "$unit"
+ test -e "/dev/mapper/$volume"
+ systemctl stop "$unit"
+ test ! -e "/dev/mapper/$volume"
+ done
+
+ return 0
+}
+
+# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2,
+# so focus more on other areas instead
+
+# Use a common workdir to make the cleanup easier
+WORKDIR="$(mktemp -d)"
+
+# Prepare a couple of LUKS2-encrypted disk images
+#
+# 1) Image with an empty password
+IMAGE_EMPTY="$WORKDIR/empty.img)"
+IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile"
+IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile"
+IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)"
+truncate -s 32M "$IMAGE_EMPTY"
+echo -n passphrase >"$IMAGE_EMPTY_KEYFILE"
+chmod 0600 "$IMAGE_EMPTY_KEYFILE"
+cryptsetup luksFormat --batch-mode \
+ --pbkdf pbkdf2 \
+ --pbkdf-force-iterations 1000 \
+ --use-urandom \
+ "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE"
+PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY"
+# Duplicate the key file to test keyfile-erase as well
+cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE"
+# The key should get erased even on a failed attempt, so test that too
+cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+
+# 2) Image with a detached header and a key file offset + size
+IMAGE_DETACHED="$WORKDIR/detached.img"
+IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile"
+IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2"
+IMAGE_DETACHED_HEADER="$WORKDIR/detached.header"
+truncate -s 32M "$IMAGE_DETACHED"
+dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1
+dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1
+chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2"
+cryptsetup luksFormat --batch-mode \
+ --pbkdf pbkdf2 \
+ --pbkdf-force-iterations 1000 \
+ --use-urandom \
+ --header "$IMAGE_DETACHED_HEADER" \
+ --keyfile-offset 32 \
+ --keyfile-size 16 \
+ "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE"
+# Also, add a second key file to key slot 8
+# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility
+cryptsetup luksAddKey --batch-mode \
+ --header "$IMAGE_DETACHED_HEADER" \
+ --key-file "$IMAGE_DETACHED_KEYFILE" \
+ --keyfile-offset 32 \
+ --keyfile-size 16 \
+ --key-slot 8 \
+ "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2"
+
+# Prepare a couple of dummy devices we'll store a copy of the detached header
+# and one of the keys on to test if systemd-cryptsetup correctly mounts them
+# when necessary
+STORE_IMAGE="$WORKDIR/store.img"
+truncate -s 64M "$STORE_IMAGE"
+STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")"
+sfdisk "$STORE_LOOP" <<EOF
+label: gpt
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=header_store size=32M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=keyfile_store
+EOF
+udevadm settle --timeout=30
+mkdir -p /mnt
+mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store"
+mount "/dev/disk/by-partlabel/header_store" /mnt
+cp "$IMAGE_DETACHED_HEADER" /mnt/header
+umount /mnt
+mkfs.ext4 -L keyfile_store "/dev/disk/by-partlabel/keyfile_store"
+mount "/dev/disk/by-partlabel/keyfile_store" /mnt
+cp "$IMAGE_DETACHED_KEYFILE2" /mnt/keyfile
+umount /mnt
+
+# Also copy the key and header on a tmpfs that we will umount after unlocking
+TMPFS_DETACHED_KEYFILE="$(mktemp -d)"
+TMPFS_DETACHED_HEADER="$(mktemp -d)"
+mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_KEYFILE"
+mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_HEADER"
+cp "$IMAGE_DETACHED_KEYFILE" "$TMPFS_DETACHED_KEYFILE/keyfile"
+cp "$IMAGE_DETACHED_HEADER" "$TMPFS_DETACHED_HEADER/header"
+
+udevadm settle --timeout=30
+
+# Prepare our test crypttab
+[[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak
+cat >/etc/crypttab <<EOF
+# headless should translate to headless=1
+empty_key $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE headless,x-systemd.device-timeout=1m
+empty_key_erase $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE_ERASE headless=1,keyfile-erase=1
+empty_key_erase_fail $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE_ERASE_FAIL headless=1,keyfile-erase=1,keyfile-offset=4
+# Empty passphrase without try-empty-password(=yes) shouldn't work
+empty_fail0 $IMAGE_EMPTY - headless=1
+empty_fail1 $IMAGE_EMPTY - headless=1,try-empty-password=0
+empty0 $IMAGE_EMPTY - headless=1,try-empty-password
+empty1 $IMAGE_EMPTY - headless=1,try-empty-password=1
+# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
+empty_nokey $IMAGE_EMPTY - headless=1
+empty_pkcs11_auto $IMAGE_EMPTY - headless=1,pkcs11-uri=auto
+
+detached $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
+detached_store0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
+detached_store1 $IMAGE_DETACHED /keyfile:LABEL=keyfile_store headless=1,header=$IMAGE_DETACHED_HEADER
+detached_store2 $IMAGE_DETACHED /keyfile:LABEL=keyfile_store headless=1,header=/header:LABEL=header_store
+detached_fail0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32
+detached_fail1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER
+detached_fail2 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1
+detached_fail3 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=16,keyfile-size=16
+detached_fail4 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=8
+detached_slot0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER
+detached_slot1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=8
+detached_slot_fail $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=0
+detached_nofail $IMAGE_DETACHED $TMPFS_DETACHED_KEYFILE/keyfile headless=1,header=$TMPFS_DETACHED_HEADER/header,keyfile-offset=32,keyfile-size=16,nofail
+EOF
+
+# Temporarily drop luks.name=/luks.uuid= from the kernel command line, as it makes
+# systemd-cryptsetup-generator ignore mounts from /etc/crypttab that are not also
+# specified on the kernel command line
+sed -r 's/luks.(name|uuid)=[^[:space:]+]//' /proc/cmdline >/tmp/cmdline.tmp
+mount --bind /tmp/cmdline.tmp /proc/cmdline
+# Run the systemd-cryptsetup-generator once explicitly, to collect coverage,
+# as during daemon-reload we run generators in a sandbox
+mkdir -p /tmp/systemd-cryptsetup-generator.out
+/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/
+systemctl daemon-reload
+systemctl list-unit-files "systemd-cryptsetup@*"
+
+cryptsetup_start_and_check empty_key
+test -e "$IMAGE_EMPTY_KEYFILE_ERASE"
+cryptsetup_start_and_check empty_key_erase
+test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE"
+test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+cryptsetup_start_and_check -f empty_key_erase_fail
+test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+cryptsetup_start_and_check -f empty_fail{0..1}
+cryptsetup_start_and_check empty{0..1}
+# First, check if we correctly fail without any key
+cryptsetup_start_and_check -f empty_nokey
+# And now provide the key via /{etc,run}/cryptsetup-keys.d/
+mkdir -p /run/cryptsetup-keys.d
+cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
+cryptsetup_start_and_check empty_nokey
+
+if [[ -r /etc/softhsm2.conf ]]; then
+ # Test unlocking with a PKCS#11 token
+ export SOFTHSM2_CONF="/etc/softhsm2.conf"
+
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+fi
+
+cryptsetup_start_and_check detached
+cryptsetup_start_and_check detached_store{0..2}
+cryptsetup_start_and_check -f detached_fail{0..4}
+cryptsetup_start_and_check detached_slot{0..1}
+cryptsetup_start_and_check -f detached_slot_fail
+cryptsetup_start_and_check -u detached_nofail
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-25-IMPORT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
+
+# Test import
+machinectl import-raw /var/tmp/testimage.raw
+machinectl image-status testimage
+test -f /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+
+# Test export
+machinectl export-raw testimage /var/tmp/testimage2.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test compressed export (gzip)
+machinectl export-raw testimage /var/tmp/testimage2.raw.gz
+gunzip /var/tmp/testimage2.raw.gz
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test clone
+machinectl clone testimage testimage3
+test -f /var/lib/machines/testimage3.raw
+machinectl image-status testimage3
+test -f /var/lib/machines/testimage.raw
+machinectl image-status testimage
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
+
+# Test removal
+machinectl remove testimage
+test ! -f /var/lib/machines/testimage.raw
+(! machinectl image-status testimage)
+
+# Test export of clone
+machinectl export-raw testimage3 /var/tmp/testimage3.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
+rm /var/tmp/testimage3.raw
+
+# Test rename
+machinectl rename testimage3 testimage4
+test -f /var/lib/machines/testimage4.raw
+machinectl image-status testimage4
+test ! -f /var/lib/machines/testimage3.raw
+(! machinectl image-status testimage3)
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
+
+# Test export of rename
+machinectl export-raw testimage4 /var/tmp/testimage4.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
+rm /var/tmp/testimage4.raw
+
+# Test removal
+machinectl remove testimage4
+test ! -f /var/lib/machines/testimage4.raw
+(! machinectl image-status testimage4)
+
+# → And now, let's test directory trees ← #
+
+# Set up a directory we can import
+mkdir /var/tmp/scratch
+mv /var/tmp/testimage.raw /var/tmp/scratch/
+touch /var/tmp/scratch/anotherfile
+mkdir /var/tmp/scratch/adirectory
+echo "piep" >/var/tmp/scratch/adirectory/athirdfile
+
+# Test import-fs
+machinectl import-fs /var/tmp/scratch/
+test -d /var/lib/machines/scratch
+machinectl image-status scratch
+
+# Test export-tar
+machinectl export-tar scratch /var/tmp/scratch.tar.gz
+test -f /var/tmp/scratch.tar.gz
+mkdir /var/tmp/extract
+(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+# Test import-tar
+machinectl import-tar /var/tmp/scratch.tar.gz scratch2
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
+
+# Test removal
+machinectl remove scratch
+test ! -f /var/lib/machines/scratch
+(! machinectl image-status scratch)
+
+# Test clone
+machinectl clone scratch2 scratch3
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+test -d /var/lib/machines/scratch3
+machinectl image-status scratch3
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
+
+# Test removal
+machinectl remove scratch2
+test ! -f /var/lib/machines/scratch2
+(! machinectl image-status scratch2)
+
+# Test rename
+machinectl rename scratch3 scratch4
+test -d /var/lib/machines/scratch4
+machinectl image-status scratch4
+test ! -f /var/lib/machines/scratch3
+(! machinectl image-status scratch3)
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
+
+# Test removal
+machinectl remove scratch4
+test ! -f /var/lib/machines/scratch4
+(! machinectl image-status scratch4)
+
+# Test import-tar hyphen/stdin pipe behavior
+# shellcheck disable=SC2002
+cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
+test -d /var/lib/machines/scratch5
+machinectl image-status scratch5
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
+
+# Test export-tar hyphen/stdout pipe behavior
+mkdir -p /var/tmp/extract
+machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+rm -rf /var/tmp/scratch
+
+# Test removal
+machinectl remove scratch5
+test ! -f /var/lib/machines/scratch5
+(! machinectl image-status scratch5)
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-26-SYSTEMCTL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then
+ rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
+ fi
+
+ rm -f /etc/init.d/issue-24990
+ return 0
+}
+
+# Create a simple unit file for testing
+# Note: the service file is created under /usr on purpose to test
+# the 'revert' verb as well
+export UNIT_NAME="systemctl-test-$RANDOM.service"
+export UNIT_NAME2="systemctl-test-$RANDOM.service"
+
+cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
+[Unit]
+Description=systemctl test
+
+[Service]
+ExecStart=sleep infinity
+ExecReload=true
+
+# For systemctl clean
+CacheDirectory=%n
+ConfigurationDirectory=%n
+LogsDirectory=%n
+RuntimeDirectory=%n
+StateDirectory=%n
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+# Configure the preset setting for the unit file
+mkdir /run/systemd/system-preset/
+echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset
+
+EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ]
+
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4"
+EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+
+printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
+EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+
+systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf <<EOF
+[Unit]
+Description=spectacular
+# this comment should remain
+
+EOF
+printf '%s\n' '[Unit]' 'Description=spectacular' '# this comment should remain' | \
+ cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
+
+# Test simultaneous editing of two units and creation of drop-in for a nonexistent unit
+systemctl edit "$UNIT_NAME" "$UNIT_NAME2" --stdin --force --drop-in=override2.conf <<<'[X-Section]'
+printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
+printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME2.d/override2.conf"
+
+# Double free when editing a template unit (#26483)
+EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
+
+# Argument help
+systemctl --state help
+systemctl --signal help
+systemctl --type help
+
+# list-dependencies
+systemctl list-dependencies systemd-journald
+systemctl list-dependencies --after systemd-journald
+systemctl list-dependencies --before systemd-journald
+systemctl list-dependencies --after --reverse systemd-journald
+systemctl list-dependencies --before --reverse systemd-journald
+systemctl list-dependencies --plain systemd-journald
+
+# list-* verbs
+systemctl list-units
+systemctl list-units --recursive
+systemctl list-units --type=socket
+systemctl list-units --type=service,timer
+# Compat: --type= allows load states for compatibility reasons
+systemctl list-units --type=loaded
+systemctl list-units --type=loaded,socket
+systemctl list-units --legend=yes -a "systemd-*"
+systemctl list-units --state=active
+systemctl list-units --with-dependencies systemd-journald.service
+systemctl list-units --with-dependencies --after systemd-journald.service
+systemctl list-units --with-dependencies --before --reverse systemd-journald.service
+systemctl list-sockets
+systemctl list-sockets --legend=no -a "*journal*"
+systemctl list-sockets --show-types
+systemctl list-sockets --state=listening
+systemctl list-timers -a -l
+systemctl list-jobs
+systemctl list-jobs --after
+systemctl list-jobs --before
+systemctl list-jobs --after --before
+systemctl list-jobs "*"
+systemctl list-dependencies sysinit.target --type=socket,mount
+systemctl list-dependencies multi-user.target --state=active
+systemctl list-dependencies sysinit.target --state=mounted --all
+systemctl list-paths
+systemctl list-paths --legend=no -a "systemd*"
+
+test_list_unit_files() {
+ systemctl list-unit-files "$@"
+ systemctl list-unit-files "$@" "*journal*"
+}
+
+test_list_unit_files
+test_list_unit_files --root=/
+
+# is-* verbs
+# Should return 4 for a missing unit file
+assert_rc 4 systemctl --quiet is-active not-found.service
+assert_rc 4 systemctl --quiet is-failed not-found.service
+assert_rc 4 systemctl --quiet is-enabled not-found.service
+# is-active: return 3 when the unit exists but inactive
+assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
+# is-enabled: return 1 when the unit exists but disabled
+assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
+
+# Basic service management
+systemctl start --show-transaction "$UNIT_NAME"
+systemctl status -n 5 "$UNIT_NAME"
+systemctl is-active "$UNIT_NAME"
+systemctl reload -T "$UNIT_NAME"
+systemctl restart -T "$UNIT_NAME"
+systemctl try-restart --show-transaction "$UNIT_NAME"
+systemctl try-reload-or-restart --show-transaction "$UNIT_NAME"
+timeout 10 systemctl kill --wait "$UNIT_NAME"
+(! systemctl is-active "$UNIT_NAME")
+systemctl restart "$UNIT_NAME"
+systemctl is-active "$UNIT_NAME"
+systemctl restart "$UNIT_NAME"
+systemctl stop "$UNIT_NAME"
+(! systemctl is-active "$UNIT_NAME")
+
+assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)"
+
+# enable/disable/preset
+test_enable_disable_preset() {
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+ systemctl enable "$@" "$UNIT_NAME"
+ systemctl is-enabled "$@" -l "$UNIT_NAME"
+ # We created a preset file for this unit above with a "disable" policy
+ systemctl preset "$@" "$UNIT_NAME"
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+ systemctl reenable "$@" "$UNIT_NAME"
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME"
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME"
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+ systemctl enable "$@" --runtime "$UNIT_NAME"
+ [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]]
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl disable "$@" "$UNIT_NAME"
+ # The unit should be still enabled, as we didn't use the --runtime switch
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl disable "$@" --runtime "$UNIT_NAME"
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+}
+
+test_enable_disable_preset
+test_enable_disable_preset --root=/
+
+# mask/unmask/revert
+test_mask_unmask_revert() {
+ systemctl disable "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+ systemctl mask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
+ systemctl unmask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+ systemctl mask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
+ systemctl revert "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+ systemctl mask "$@" --runtime "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
+ # This should be a no-op without the --runtime switch
+ systemctl unmask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
+ systemctl unmask "$@" --runtime "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+}
+
+test_mask_unmask_revert
+test_mask_unmask_revert --root=/
+
+# disable --now with template unit
+cat >/run/systemd/system/test-disable@.service <<EOF
+[Service]
+ExecStart=sleep infinity
+
+[Install]
+WantedBy=multi-user.target
+EOF
+systemctl enable --now test-disable@1.service test-disable@2.service
+systemctl is-active test-disable@1.service
+systemctl is-active test-disable@2.service
+systemctl disable --now test-disable@.service
+for u in test-disable@{1,2}.service; do
+ (! systemctl is-active "$u")
+ (! systemctl is-enabled "$u")
+done
+rm /run/systemd/system/test-disable@.service
+
+# add-wants/add-requires
+(! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service")
+systemctl add-wants "$UNIT_NAME" "systemd-journald.service"
+systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service"
+(! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service")
+systemctl add-requires "$UNIT_NAME" "systemd-journald.service"
+systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service"
+
+# set-property
+systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567
+systemctl cat "$UNIT_NAME"
+# These properties should be saved to a persistent storage
+grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/"
+grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d"
+systemctl revert "$UNIT_NAME"
+(! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
+(! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
+# Same stuff, but with --runtime, which should use /run
+systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10%
+systemctl cat "$UNIT_NAME"
+grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/"
+grep -r "CPUQuota=10%" "/run/systemd/system.control/${UNIT_NAME}.d/"
+systemctl revert "$UNIT_NAME"
+(! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/")
+(! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/")
+
+# Failed-unit related tests
+(! systemd-run --wait --unit "failed.service" /bin/false)
+systemctl is-failed failed.service
+systemctl --state=failed | grep failed.service
+systemctl --failed | grep failed.service
+systemctl reset-failed "fail*.service"
+(! systemctl is-failed failed.service)
+
+# clean
+systemctl restart "$UNIT_NAME"
+systemctl stop "$UNIT_NAME"
+# Check if the directories from *Directory= directives exist
+# (except RuntimeDirectory= in /run, which is removed when the unit is stopped)
+for path in /var/lib /var/cache /var/log /etc; do
+ [[ -e "$path/$UNIT_NAME" ]]
+done
+# Run the cleanup
+for what in "" configuration state cache logs runtime all; do
+ systemctl clean ${what:+--what="$what"} "$UNIT_NAME"
+done
+# All respective directories should be removed
+for path in /run /var/lib /var/cache /var/log /etc; do
+ [[ ! -e "$path/$UNIT_NAME" ]]
+done
+
+# --timestamp
+for value in pretty us µs utc us+utc µs+utc; do
+ systemctl show -P KernelTimestamp --timestamp="$value"
+done
+
+# set-default/get-default
+test_get_set_default() {
+ target="$(systemctl get-default "$@")"
+ systemctl set-default "$@" emergency.target
+ [[ "$(systemctl get-default "$@")" == emergency.target ]]
+ systemctl set-default "$@" "$target"
+ [[ "$(systemctl get-default "$@")" == "$target" ]]
+}
+
+test_get_set_default
+test_get_set_default --root=/
+
+# show/status
+systemctl show --property ""
+# Pick a heavily sandboxed unit for the best effect on coverage
+systemctl show systemd-logind.service
+systemctl status
+# Ignore the exit code in this case, as it might try to load non-existing units
+systemctl status -a >/dev/null || :
+systemctl status -a --state active,running,plugged >/dev/null
+systemctl status "systemd-*.timer"
+systemctl status "systemd-journald*.socket"
+systemctl status "sys-devices-*-ttyS0.device"
+systemctl status -- -.mount
+systemctl status 1
+
+# --marked
+systemctl restart "$UNIT_NAME"
+systemctl set-property "$UNIT_NAME" Markers=needs-restart
+systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
+systemctl reload-or-restart --marked
+(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart)
+
+# --dry-run with destructive verbs
+# kexec is skipped intentionally, as it requires a bit more involved setup
+VERBS=(
+ default
+ emergency
+ exit
+ halt
+ hibernate
+ hybrid-sleep
+ poweroff
+ reboot
+ rescue
+ suspend
+ suspend-then-hibernate
+)
+
+for verb in "${VERBS[@]}"; do
+ systemctl --dry-run "$verb"
+
+ if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then
+ systemctl --dry-run --message "Hello world" "$verb"
+ systemctl --dry-run --no-wall "$verb"
+ systemctl --dry-run -f "$verb"
+ systemctl --dry-run -ff "$verb"
+ fi
+done
+
+# Aux verbs & assorted checks
+systemctl is-active "*-journald.service"
+systemctl cat "*udevd*"
+systemctl cat "$UNIT_NAME"
+systemctl help "$UNIT_NAME"
+systemctl service-watchdogs
+systemctl service-watchdogs "$(systemctl service-watchdogs)"
+
+# show/set-environment
+# Make sure PATH is set
+systemctl show-environment | grep -q '^PATH='
+# Let's add an entry and override a built-in one
+systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
+# Check that both are set
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+systemctl daemon-reload
+# Check again after the reload
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+# Check that JSON output is supported
+systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
+# Drop both
+systemctl unset-environment FOO PATH
+# Check that one is gone and the other reverted to the built-in
+systemctl show-environment | grep '^FOO=$' && exit 1
+systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
+systemctl show-environment | grep -q '^PATH='
+# Check import-environment
+export IMPORT_THIS=hello
+export IMPORT_THIS_TOO=world
+systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO
+systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS"
+systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO"
+systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
+(! systemctl show-environment | grep "^IMPORT_THIS=")
+(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
+
+# test for sysv-generator (issue #24990)
+if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
+ # This is configurable via -Dsysvinit-path=, but we can't get the value
+ # at runtime, so let's just support the two most common paths for now.
+ [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
+
+ # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
+ # but may not create the directory if there's no services that use it.
+ mkdir -p "$SYSVINIT_PATH"
+
+ # invalid dependency
+ cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:test1 test2
+# Required-Start:test1 $remote_fs $network
+# Required-Stop:test1 $remote_fs $network
+# Description:Test
+# Short-Description: Test
+### END INIT INFO
+
+case "$1" in
+ start)
+ echo "Starting issue-24990.service"
+ sleep 1000 &
+ ;;
+ stop)
+ echo "Stopping issue-24990.service"
+ sleep 10 &
+ ;;
+ *)
+ echo "Usage: service test {start|stop|restart|status}"
+ ;;
+esac
+EOF
+
+ chmod +x "$SYSVINIT_PATH/issue-24990"
+ systemctl daemon-reload
+ [[ -L /run/systemd/generator.late/test1.service ]]
+ [[ -L /run/systemd/generator.late/test2.service ]]
+ assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
+ assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
+ output=$(systemctl cat issue-24990)
+ assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
+ assert_in "Description=LSB: Test" "$output"
+ assert_in "After=test1.service" "$output"
+ assert_in "After=remote-fs.target" "$output"
+ assert_in "After=network-online.target" "$output"
+ assert_in "Wants=network-online.target" "$output"
+ assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+ assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
+ systemctl status issue-24990 || :
+ systemctl show issue-24990
+ assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
+ assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
+
+ if ! systemctl is-active network-online.target; then
+ systemctl start network-online.target
+ fi
+
+ systemctl restart issue-24990
+ systemctl stop issue-24990
+
+ # valid dependency
+ cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:test1 test2
+# Required-Start:$remote_fs
+# Required-Stop:$remote_fs
+# Description:Test
+# Short-Description: Test
+### END INIT INFO
+
+case "$1" in
+ start)
+ echo "Starting issue-24990.service"
+ sleep 1000 &
+ ;;
+ stop)
+ echo "Stopping issue-24990.service"
+ sleep 10 &
+ ;;
+ *)
+ echo "Usage: service test {start|stop|restart|status}"
+ ;;
+esac
+EOF
+
+ chmod +x "$SYSVINIT_PATH/issue-24990"
+ systemctl daemon-reload
+ [[ -L /run/systemd/generator.late/test1.service ]]
+ [[ -L /run/systemd/generator.late/test2.service ]]
+ assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
+ assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
+ output=$(systemctl cat issue-24990)
+ assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
+ assert_in "Description=LSB: Test" "$output"
+ assert_in "After=remote-fs.target" "$output"
+ assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+ assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
+ systemctl status issue-24990 || :
+ systemctl show issue-24990
+ assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
+ assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
+
+ systemctl restart issue-24990
+ systemctl stop issue-24990
+fi
+
+# %J in WantedBy= causes ABRT (#26467)
+cat >/run/systemd/system/test-WantedBy.service <<EOF
+[Service]
+ExecStart=true
+
+[Install]
+WantedBy=user-%i@%J.service
+EOF
+systemctl daemon-reload
+systemctl enable --now test-WantedBy.service || :
+systemctl daemon-reload
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-29-PORTABLE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+install_extension_images
+
+# Set longer timeout for slower machines, e.g. non-KVM vm.
+mkdir -p /run/systemd/system.conf.d
+cat >/run/systemd/system.conf.d/10-timeout.conf <<EOF
+[Manager]
+DefaultEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
+ManagerEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
+EOF
+
+systemctl daemon-reexec
+
+export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
+
+udevadm control --log-level debug
+
+ARGS=()
+STATE_DIRECTORY=/var/lib/private/
+if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
+ # If we're running under sanitizers, we need to use a less restrictive
+ # profile, otherwise LSan syscall would get blocked by seccomp
+ ARGS+=(--profile=trusted)
+ # With the trusted profile DynamicUser is disabled, so the storage is not in private/
+ STATE_DIRECTORY=/var/lib/
+fi
+
+systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
+systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service'
+systemd-dissect --no-pager /tmp/app0.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /tmp/app1.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /tmp/conf0.raw | grep -q '✓ confext for portable service'
+
+export SYSTEMD_LOG_LEVEL=debug
+mkdir -p /run/systemd/system/systemd-portabled.service.d/
+cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
+
+portablectl is-attached minimal-app0
+portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
+systemctl is-active minimal-app0-bar.service && exit 1
+
+portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl is-attached minimal-app0
+portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
+systemctl is-active minimal-app0-foo.service && exit 1
+
+portablectl list | grep -q -F "minimal_1"
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
+
+portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl list | grep -q -F "No images."
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
+
+# Ensure we don't regress (again) when using --force
+
+portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
+
+portablectl is-attached --force minimal-app0
+portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
+systemctl is-active minimal-app0-bar.service && exit 1
+
+portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl is-attached --force minimal-app0
+portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
+systemctl is-active minimal-app0-foo.service && exit 1
+
+portablectl list | grep -q -F "minimal_1"
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
+
+portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl list | grep -q -F "No images."
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
+
+# portablectl also works with directory paths rather than images
+
+unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
+unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
+
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
+systemctl is-active minimal-app0-bar.service && exit 1
+
+portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
+
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
+systemctl is-active minimal-app0-foo.service && exit 1
+
+portablectl list | grep -q -F "minimal_1"
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
+
+portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
+
+portablectl list | grep -q -F "No images."
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
+
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
+
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
+
+portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
+
+# Ensure versioned images are accepted without needing to use --force to override the extension-release
+# matching
+
+cp /tmp/app0.raw /tmp/app0_1.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0_1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
+rm -f /tmp/app0_1.0.raw
+
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+# Ensure that adding or removing a version to the image doesn't break reattaching
+cp /tmp/app1.raw /tmp/app1_2.raw
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_2 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
+portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+systemctl daemon-reload
+systemctl restart app1.service
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+
+# Ensure vpick works, including reattaching to a new image
+mkdir -p /tmp/app1.v/
+cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw
+cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+rm -f /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
+rm -f /tmp/app1.v/app1_1.0.raw
+
+# Ensure that the combination of read-only images, state directory and dynamic user works, and that
+# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
+# after the service is attached before the file appears.
+grep -q -F bar "${STATE_DIRECTORY}/app0/foo"
+grep -q -F baz "${STATE_DIRECTORY}/app1/foo"
+
+# Ensure that we can override the check on extension-release.NAME
+cp /tmp/app0.raw /tmp/app10.raw
+portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw"
+
+# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
+# portablectl won't find it)
+rm -f /tmp/app10.raw
+systemctl stop app0.service
+portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
+
+# portablectl also accepts confexts
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw"
+
+portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
+
+# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
+portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+test -f /run/portables/app0.raw
+test -f /run/portables/minimal_0.raw
+test -f /run/systemd/system.attached/app0.service
+test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
+portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+
+# Ensure that when two portables share the same base image, removing one doesn't remove the other too
+
+portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+(! portablectl detach --runtime /usr/share/minimal_0.raw app)
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+# Ensure 'portablectl list' shows the correct status for both images
+portablectl list
+portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
+
+portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app
+
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app
+
+# portablectl also works with directory paths rather than images
+
+mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+mount /tmp/app0.raw /tmp/app0
+mount /tmp/app1.raw /tmp/app1
+mount /usr/share/minimal_0.raw /tmp/rootdir
+
+# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
+# bypassing the sysext logic in portabled here it will otherwise not see the
+# extensions additional valid prefix)
+grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
+
+mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
+
+grep . /tmp/overlay/usr/lib/extension-release.d/*
+grep . /tmp/overlay/etc/os-release
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
+
+systemctl is-active app1.service
+
+portablectl detach --now --runtime overlay app1
+
+umount /tmp/overlay
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+systemctl is-active app0.service
+systemctl is-active app1.service
+
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
+
+grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
+
+grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
+
+portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+# Ensure --clean remove state and other directories belonging to the portable image being detached
+test ! -d /var/lib/app0
+test ! -d /run/app0
+
+# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
+portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+test -d /run/portables/app0
+test -d /run/portables/app1
+test -d /run/portables/rootdir
+test -f /run/systemd/system.attached/app0.service
+test -f /run/systemd/system.attached/app1.service
+test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
+test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
+portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
+# Provides coverage for https://github.com/systemd/systemd/issues/23481
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
+portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
+# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
+portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
+
+umount /tmp/rootdir
+umount /tmp/app0
+umount /tmp/app1
+
+# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead
+mkdir -p /tmp/emptyroot/usr/lib
+mkdir -p /tmp/emptyext/usr/lib/extension-release.d
+touch /tmp/emptyroot/usr/lib/os-release
+touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext
+
+# Remote peer disconnected -> portabled crashed
+res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))"
+test -z "${res}"
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-30-ONCLOCKCHANGE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-analyze log-level debug
+
+systemctl disable --now systemd-timesyncd.service
+
+timedatectl set-timezone Europe/Berlin
+timedatectl set-time 1980-10-15
+
+systemd-run --on-timezone-change touch /tmp/timezone-changed
+systemd-run --on-clock-change touch /tmp/clock-changed
+
+test ! -f /tmp/timezone-changed
+test ! -f /tmp/clock-changed
+
+timedatectl set-timezone Europe/Kyiv
+
+while test ! -f /tmp/timezone-changed ; do sleep .5 ; done
+
+timedatectl set-time 2018-1-1
+
+while test ! -f /tmp/clock-changed ; do sleep .5 ; done
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-31-DEVICE-ENUMERATION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
+ exit 1
+fi
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-32-OOMPOLICY
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+MemoryAccounting=yes
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Let's run this test only if the "memory.oom.group" cgroupfs attribute
+# exists. This test is a bit too strict, since the "memory.events"/"oom_kill"
+# logic has been around since a longer time than "memory.oom.group", but it's
+# an easier thing to test for, and also: let's not get confused by older
+# kernels where the concept was still new.
+
+if test -f /sys/fs/cgroup/system.slice/TEST-32-OOMPOLICY.service/memory.oom.group; then
+ systemd-analyze log-level debug
+
+ # Run a service that is guaranteed to be the first candidate for OOM killing
+ systemd-run --unit=oomtest.service \
+ -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \
+ sleep infinity
+
+ # Trigger an OOM killer run
+ echo 1 >/proc/sys/kernel/sysrq
+ echo f >/proc/sysrq-trigger
+
+ while : ; do
+ STATE="$(systemctl show -P ActiveState oomtest.service)"
+ [ "$STATE" = "failed" ] && break
+ sleep .5
+ done
+
+ RESULT="$(systemctl show -P Result oomtest.service)"
+ test "$RESULT" = "oom-kill"
+
+ systemd-analyze log-level info
+fi
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-34-DYNAMICUSERMIGRATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-analyze log-level debug
+
+test_directory() {
+ local directory="$1"
+ local path="$2"
+
+ # cleanup for previous invocation
+ for i in xxx xxx2 yyy zzz x:yz x:yz2; do
+ rm -rf "${path:?}/${i}" "${path:?}/private/${i}"
+ done
+
+ # Set everything up without DynamicUser=1
+
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
+ (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
+
+ test -d "${path}"/zzz
+ test ! -L "${path}"/zzz
+ test ! -e "${path}"/private/zzz
+
+ test ! -e "${path}"/xxx
+ test ! -e "${path}"/private/xxx
+ test ! -e "${path}"/xxx2
+ test ! -e "${path}"/private/xxx2
+ test -L "${path}"/yyy
+ test ! -e "${path}"/private/yyy
+
+ test -f "${path}"/zzz/test
+ test ! -e "${path}"/zzz/test-missing
+
+ # Convert to DynamicUser=1
+
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}=zzz:xxx zzz:xxx2" \
+ -p TemporaryFileSystem="${path}" -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
+ (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
+
+ test -L "${path}"/zzz
+ test -d "${path}"/private/zzz
+
+ test ! -e "${path}"/xxx
+ test ! -e "${path}"/private/xxx
+ test ! -e "${path}"/xxx2
+ test ! -e "${path}"/private/xxx2
+ test -L "${path}"/yyy # previous symlink is not removed
+ test ! -e "${path}"/private/yyy
+
+ test -f "${path}"/zzz/test
+ test ! -e "${path}"/zzz/test-missing
+
+ # Convert back
+
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
+ systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
+ (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
+
+ test -d "${path}"/zzz
+ test ! -L "${path}"/zzz
+ test ! -e "${path}"/private/zzz
+
+ test ! -e "${path}"/xxx
+ test ! -e "${path}"/private/xxx
+ test ! -e "${path}"/xxx2
+ test ! -e "${path}"/private/xxx2
+ test -L "${path}"/yyy
+ test ! -e "${path}"/private/yyy
+
+ test -f "${path}"/zzz/test
+ test ! -e "${path}"/zzz/test-missing
+
+ # Exercise the unit parsing paths too
+ cat >/run/systemd/system/testservice-34.service <<EOF
+[Service]
+Type=oneshot
+TemporaryFileSystem=${path}
+RuntimeDirectoryPreserve=yes
+${directory}=zzz:x\:yz zzz:x\:yz2
+ExecStart=test -f ${path}/x:yz2/test
+ExecStart=test -f ${path}/x:yz/test
+ExecStart=test -f ${path}/zzz/test
+EOF
+ systemctl daemon-reload
+ systemctl start --wait testservice-34.service
+
+ test -d "${path}"/zzz
+ test ! -L "${path}"/zzz
+ test ! -e "${path}"/private/zzz
+
+ test ! -L "${path}"/x:yz
+ test ! -L "${path}"/x:yz2
+}
+
+test_check_writable() {
+ # cleanup for previous invocation
+ for i in aaa quux waldo xxx; do
+ rm -rf "/var/lib/$i" "/var/lib/private/$i"
+ done
+
+ cat >/run/systemd/system/testservice-34-check-writable.service <<\EOF
+[Unit]
+Description=Check writable directories when DynamicUser= with StateDirectory=
+
+[Service]
+# Relevant only for sanitizer runs
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
+
+Type=oneshot
+DynamicUser=yes
+StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333
+
+# Make sure that the state directories are really the only writable directory besides the obvious candidates
+ExecStart=bash -c ' \
+ set -eux; \
+ set -o pipefail; \
+ declare -a writable_dirs; \
+ readarray -t writable_dirs < <(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o \
+ -path /sys/fs/bpf -o -path /dev/.lxc -o -path /sys/devices/system/cpu \) \
+ -prune -o -type d -writable -print 2>/dev/null | sort -u); \
+ [[ "$${#writable_dirs[@]}" == "8" ]]; \
+ [[ "$${writable_dirs[0]}" == "/var/lib/private/aaa" ]]; \
+ [[ "$${writable_dirs[1]}" == "/var/lib/private/aaa/bbb" ]]; \
+ [[ "$${writable_dirs[2]}" == "/var/lib/private/aaa/ccc" ]]; \
+ [[ "$${writable_dirs[3]}" == "/var/lib/private/quux/pief" ]]; \
+ [[ "$${writable_dirs[4]}" == "/var/lib/private/waldo" ]]; \
+ [[ "$${writable_dirs[5]}" == "/var/lib/private/xxx" ]]; \
+ [[ "$${writable_dirs[6]}" == "/var/lib/private/xxx/yyy" ]]; \
+ [[ "$${writable_dirs[7]}" == "/var/lib/private/xxx/zzz" ]]; \
+'
+EOF
+ systemctl daemon-reload
+ systemctl start testservice-34-check-writable.service
+}
+
+test_directory "StateDirectory" "/var/lib"
+test_directory "RuntimeDirectory" "/run"
+test_directory "CacheDirectory" "/var/cache"
+test_directory "LogsDirectory" "/var/log"
+
+test_check_writable
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-35-LOGIN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+cleanup_test_user() (
+ set +ex
+
+ pkill -u "$(id -u logind-test-user)"
+ sleep 1
+ pkill -KILL -u "$(id -u logind-test-user)"
+ userdel -r logind-test-user
+
+ return 0
+)
+
+setup_test_user() {
+ mkdir -p /var/spool/cron /var/spool/mail
+ useradd -m -s /bin/bash logind-test-user
+ trap cleanup_test_user EXIT
+}
+
+test_write_dropin() {
+ systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+ # We test "coldplug" (completely stop and start logind) here. So we need to preserve
+ # the fdstore, which might contain session leader pidfds. This is extremely rare use case
+ # and shall not be considered fully supported.
+ # See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
+ systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
+[Service]
+FileDescriptorStorePreserve=yes
+EOF
+
+ systemctl restart systemd-logind.service
+}
+
+testcase_properties() {
+ mkdir -p /run/systemd/logind.conf.d
+
+ cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
+[Login]
+KillUserProcesses=no
+EOF
+
+ systemctl restart systemd-logind.service
+ assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b false"
+
+ cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
+[Login]
+KillUserProcesses=yes
+EOF
+
+ systemctl restart systemd-logind.service
+ assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b true"
+
+ rm -rf /run/systemd/logind.conf.d
+}
+
+testcase_sleep_automated() {
+ assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 3 "suspend-then-hibernate" "suspend" "hibernate"'
+
+ mkdir -p /run/systemd/logind.conf.d
+
+ cat >/run/systemd/logind.conf.d/sleep-operations.conf <<EOF
+[Login]
+SleepOperation=suspend hybrid-sleep
+EOF
+
+ systemctl restart systemd-logind.service
+
+ assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 2 "hybrid-sleep" "suspend"'
+
+ busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSleep
+
+ rm -rf /run/systemd/logind.conf.d
+}
+
+testcase_started() {
+ local pid
+
+ systemctl restart systemd-logind.service
+
+ # should start at boot, not with D-BUS activation
+ pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
+
+ # loginctl should succeed
+ loginctl
+
+ # logind should still be running
+ assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+}
+
+wait_suspend() {
+ timeout "${1?}" bash -c "while [[ ! -e /run/suspend.flag ]]; do sleep 1; done"
+ rm /run/suspend.flag
+}
+
+teardown_suspend() (
+ set +eux
+
+ pkill evemu-device
+
+ rm -rf /run/systemd/system/systemd-suspend.service.d
+ systemctl daemon-reload
+
+ rm -f /run/udev/rules.d/70-logindtest-lid.rules
+ udevadm control --reload
+
+ return 0
+)
+
+testcase_suspend_on_lid() {
+ local pid input_name lid_dev
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping suspend test in container"
+ return
+ fi
+ if ! grep -s -q mem /sys/power/state; then
+ echo "suspend not supported on this testbed, skipping"
+ return
+ fi
+ if ! command -v evemu-device >/dev/null; then
+ echo "command evemu-device not found, skipping"
+ return
+ fi
+ if ! command -v evemu-event >/dev/null; then
+ echo "command evemu-event not found, skipping"
+ return
+ fi
+
+ trap teardown_suspend RETURN
+
+ # save pid
+ pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
+
+ # create fake suspend
+ mkdir -p /run/systemd/system/systemd-suspend.service.d
+ cat >/run/systemd/system/systemd-suspend.service.d/override.conf <<EOF
+[Service]
+ExecStart=
+ExecStart=touch /run/suspend.flag
+EOF
+ systemctl daemon-reload
+
+ # create fake lid switch
+ mkdir -p /run/udev/rules.d
+ cat >/run/udev/rules.d/70-logindtest-lid.rules <<EOF
+SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="Fake Lid Switch", TAG+="power-switch"
+EOF
+ udevadm control --reload
+
+ cat >/run/lidswitch.evemu <<EOF
+# EVEMU 1.2
+# Input device name: "Lid Switch"
+# Input device ID: bus 0x19 vendor 0000 product 0x05 version 0000
+# Supported events:
+# Event type 0 (EV_SYN)
+# Event code 0 (SYN_REPORT)
+# Event code 5 (FF_STATUS_MAX)
+# Event type 5 (EV_SW)
+# Event code 0 (SW_LID)
+# Properties:
+N: Fake Lid Switch
+I: 0019 0000 0005 0000
+P: 00 00 00 00 00 00 00 00
+B: 00 21 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 02 00 00 00 00 00 00 00 00
+B: 03 00 00 00 00 00 00 00 00
+B: 04 00 00 00 00 00 00 00 00
+B: 05 01 00 00 00 00 00 00 00
+B: 11 00 00 00 00 00 00 00 00
+B: 12 00 00 00 00 00 00 00 00
+B: 15 00 00 00 00 00 00 00 00
+B: 15 00 00 00 00 00 00 00 00
+EOF
+
+ evemu-device /run/lidswitch.evemu &
+
+ timeout 20 bash -c 'until grep "^Fake Lid Switch" /sys/class/input/*/device/name; do sleep .5; done'
+ input_name=$(grep -l '^Fake Lid Switch' /sys/class/input/*/device/name || :)
+ if [[ -z "$input_name" ]]; then
+ echo "cannot find fake lid switch." >&2
+ exit 1
+ fi
+ input_name=${input_name%/device/name}
+ lid_dev=/dev/${input_name#/sys/class/}
+ udevadm info --wait-for-initialization=10s "$lid_dev"
+ udevadm settle
+
+ # close lid
+ evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
+ # need to wait for 30s suspend inhibition after boot
+ wait_suspend 31
+ # open lid again
+ evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
+
+ # waiting for 30s inhibition time between suspends
+ sleep 30
+
+ # now closing lid should cause instant suspend
+ evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
+ wait_suspend 2
+ evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
+
+ assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+}
+
+testcase_shutdown() {
+ local pid
+
+ # save pid
+ pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
+
+ # scheduled shutdown with wall message
+ shutdown 2>&1
+ sleep 5
+ shutdown -c || :
+ # logind should still be running
+ assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+
+ # scheduled shutdown without wall message
+ shutdown --no-wall 2>&1
+ sleep 5
+ shutdown -c --no-wall || true
+ assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+}
+
+cleanup_session() (
+ set +ex
+
+ local uid s
+
+ uid=$(id -u logind-test-user)
+
+ loginctl disable-linger logind-test-user
+
+ systemctl stop getty@tty2.service
+
+ for s in $(loginctl --no-legend list-sessions | grep -v manager | awk '$3 == "logind-test-user" { print $1 }'); do
+ echo "INFO: stopping session $s"
+ loginctl terminate-session "$s"
+ done
+
+ loginctl terminate-user logind-test-user
+
+ if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
+ echo "WARNING: session for logind-test-user still active, ignoring."
+ fi
+
+ pkill -u "$uid"
+ sleep 1
+ pkill -KILL -u "$uid"
+
+ if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then
+ echo "WARNING: user@${uid}.service is still active, ignoring."
+ fi
+
+ if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then
+ echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring."
+ fi
+
+ if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then
+ echo "WARNING: user-${uid}.slice is still active, ignoring."
+ fi
+
+ rm -rf /run/systemd/system/getty@tty2.service.d
+ systemctl daemon-reload
+
+ return 0
+)
+
+teardown_session() (
+ set +ex
+
+ cleanup_session
+
+ rm -f /run/udev/rules.d/70-logindtest-scsi_debug-user.rules
+ udevadm control --reload
+ rmmod scsi_debug
+
+ return 0
+)
+
+check_session() (
+ set +ex
+
+ local seat session leader_pid
+
+ if [[ $(loginctl --no-legend | grep -v manager | grep -c "logind-test-user") != 1 ]]; then
+ echo "no session or multiple sessions for logind-test-user." >&2
+ return 1
+ fi
+
+ seat=$(loginctl --no-legend | grep -v manager | grep 'logind-test-user *seat' | awk '{ print $4 }')
+ if [[ -z "$seat" ]]; then
+ echo "no seat found for user logind-test-user" >&2
+ return 1
+ fi
+
+ session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
+ if [[ -z "$session" ]]; then
+ echo "no session found for user logind-test-user" >&2
+ return 1
+ fi
+
+ if ! loginctl session-status "$session" | grep -q "Unit: session-${session}\.scope"; then
+ echo "cannot find scope unit for session $session" >&2
+ return 1
+ fi
+
+ leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }')
+ if [[ -z "$leader_pid" ]]; then
+ echo "cannot found leader process for session $session" >&2
+ return 1
+ fi
+
+ # cgroup v1: "1:name=systemd:/user.slice/..."; unified hierarchy: "0::/user.slice"
+ if ! grep -q -E '(name=systemd|^0:):.*session.*scope' /proc/"$leader_pid"/cgroup; then
+ echo "FAIL: process $leader_pid is not in the session cgroup" >&2
+ cat /proc/self/cgroup
+ return 1
+ fi
+)
+
+create_session() {
+ # login with the test user to start a session
+ mkdir -p /run/systemd/system/getty@tty2.service.d
+ cat >/run/systemd/system/getty@tty2.service.d/override.conf <<EOF
+[Service]
+Type=simple
+ExecStart=
+ExecStart=-/sbin/agetty --autologin logind-test-user --noclear %I $TERM
+Restart=no
+EOF
+ systemctl daemon-reload
+
+ systemctl restart getty@tty2.service
+
+ # check session
+ for i in {1..30}; do
+ (( i > 1 )) && sleep 1
+ check_session && break
+ done
+ check_session
+ assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2"
+}
+
+testcase_sanity_check() {
+ # Exercise basic loginctl options
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ trap cleanup_session RETURN
+ create_session
+
+ # Run most of the loginctl commands from a user session to make
+ # the seat/session autodetection work-ish
+ systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF
+ loginctl list-sessions
+ loginctl list-sessions -j
+ loginctl list-sessions --json=short
+ loginctl session-status
+ loginctl show-session
+ loginctl show-session -P DelayInhibited
+
+ # We're not in the same session scope, so in this case we need to specify
+ # the session ID explicitly
+ session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1; exit; }')
+ loginctl kill-session --signal=SIGCONT "$session"
+ # FIXME(?)
+ #loginctl kill-session --signal=SIGCONT --kill-whom=leader "$session"
+
+ loginctl list-users
+ loginctl user-status
+ loginctl show-user -a
+ loginctl show-user -P IdleAction
+ loginctl kill-user --signal=SIGCONT ""
+
+ loginctl list-seats
+ loginctl seat-status
+ loginctl show-seat
+ loginctl show-seat -P IdleActionUSec
+EOF
+
+ # Requires root privileges
+ loginctl lock-sessions
+ loginctl unlock-sessions
+ loginctl flush-devices
+}
+
+testcase_session() {
+ local dev
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping ACL tests in container"
+ return
+ fi
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ trap teardown_session RETURN
+
+ create_session
+
+ # scsi_debug should not be loaded yet
+ if [[ -d /sys/bus/pseudo/drivers/scsi_debug ]]; then
+ echo "scsi_debug module is already loaded." >&2
+ exit 1
+ fi
+
+ # we use scsi_debug to create new devices which we can put ACLs on
+ # tell udev about the tagging, so that logind can pick it up
+ mkdir -p /run/udev/rules.d
+ cat >/run/udev/rules.d/70-logindtest-scsi_debug-user.rules <<EOF
+SUBSYSTEM=="block", ATTRS{model}=="scsi_debug*", TAG+="uaccess"
+EOF
+ udevadm control --reload
+
+ # coldplug: logind started with existing device
+ systemctl stop systemd-logind.service
+ modprobe scsi_debug
+ timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
+ dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
+ if [[ ! -b "$dev" ]]; then
+ echo "cannot find suitable scsi block device" >&2
+ exit 1
+ fi
+ udevadm settle
+ udevadm info "$dev"
+
+ # trigger logind and activate session
+ loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
+
+ # check ACL
+ sleep 1
+ assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
+
+ # hotplug: new device appears while logind is running
+ rmmod scsi_debug
+ modprobe scsi_debug
+ timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
+ dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
+ if [[ ! -b "$dev" ]]; then
+ echo "cannot find suitable scsi block device" >&2
+ exit 1
+ fi
+ udevadm settle
+
+ # check ACL
+ sleep 1
+ assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
+}
+
+teardown_lock_idle_action() (
+ set +eux
+
+ rm -f /run/systemd/logind.conf.d/idle-action-lock.conf
+ systemctl restart systemd-logind.service
+
+ cleanup_session
+
+ return 0
+)
+
+testcase_lock_idle_action() {
+ local ts
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ if loginctl --no-legend | grep -v manager | grep -q logind-test-user; then
+ echo >&2 "Session of the 'logind-test-user' is already present."
+ exit 1
+ fi
+
+ trap teardown_lock_idle_action RETURN
+
+ create_session
+
+ ts="$(date '+%H:%M:%S')"
+
+ mkdir -p /run/systemd/logind.conf.d
+ cat >/run/systemd/logind.conf.d/idle-action-lock.conf <<EOF
+[Login]
+IdleAction=lock
+IdleActionSec=1s
+EOF
+ systemctl restart systemd-logind.service
+
+ # Wait for 35s, in that interval all sessions should have become idle
+ # and "Lock" signal should have been sent out. Then we wrote to tty to make
+ # session active again and next we slept for another 35s so sessions have
+ # become idle again. 'Lock' signal is sent out for each session, we have at
+ # least one session, so minimum of 2 "Lock" signals must have been sent.
+ timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt 1 ]]; do sleep 1; done"
+
+ # We need to know that a new message was sent after waking up,
+ # so we must track how many happened before sleeping to check we have extra.
+ locks="$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'Sent message type=signal .* member=Lock')"
+
+ # Wakeup
+ touch /dev/tty2
+
+ # Wait again
+ timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt $((locks + 1)) ]]; do sleep 1; done"
+
+ if [[ "$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'System idle. Will be locked now.')" -lt 2 ]]; then
+ echo >&2 "System haven't entered idle state at least 2 times."
+ exit 1
+ fi
+}
+
+testcase_session_properties() {
+ local s
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ trap cleanup_session RETURN
+ create_session
+
+ s=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
+ /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2
+}
+
+testcase_list_users_sessions_seats() {
+ local session seat
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ trap cleanup_session RETURN
+ create_session
+
+ # Activate the session
+ loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
+
+ session=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
+ : check that we got a valid session id
+ busctl get-property org.freedesktop.login1 "/org/freedesktop/login1/session/_3${session?}" org.freedesktop.login1.Session Id
+ assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
+ seat=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $4 }')
+ assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $6 }')" user
+ assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $7 }')" tty2
+ assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $8 }')" no
+ assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $9 }')" '-'
+
+ loginctl list-seats --no-legend | grep -Fwq "${seat?}"
+
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)"
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" active
+
+ loginctl enable-linger logind-test-user
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
+
+ for s in $(loginctl list-sessions --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1 }'); do
+ loginctl terminate-session "$s"
+ done
+ if ! timeout 30 bash -c "while loginctl --no-legend | grep tty | grep -q logind-test-user; do sleep 1; done"; then
+ echo "WARNING: session for logind-test-user still active, ignoring."
+ return
+ fi
+
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" lingering
+}
+
+teardown_stop_idle_session() (
+ set +eux
+
+ rm -f /run/systemd/logind.conf.d/stop-idle-session.conf
+ systemctl restart systemd-logind.service
+
+ cleanup_session
+)
+
+testcase_stop_idle_session() {
+ local id ts
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ create_session
+ trap teardown_stop_idle_session RETURN
+
+ id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')"
+ ts="$(date '+%H:%M:%S')"
+
+ mkdir -p /run/systemd/logind.conf.d
+ cat >/run/systemd/logind.conf.d/stop-idle-session.conf <<EOF
+[Login]
+StopIdleSessionSec=2s
+EOF
+ systemctl restart systemd-logind.service
+ sleep 5
+
+ assert_eq "$(journalctl -b -u systemd-logind.service --since="$ts" --grep "Session \"$id\" of user \"logind-test-user\" is idle, stopping." | wc -l)" 1
+ assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0
+}
+
+testcase_ambient_caps() {
+ local PAMSERVICE TRANSIENTUNIT SCRIPT
+
+ # Verify that pam_systemd works and assigns ambient caps as it should
+
+ if ! grep -q 'CapAmb:' /proc/self/status ; then
+ echo "ambient caps not available, skipping test." >&2
+ return
+ fi
+
+ typeset -i BND MASK
+
+ # Get PID 1's bounding set
+ BND="0x$(grep 'CapBnd:' /proc/1/status | cut -d: -f2 | tr -d '[:space:]')"
+
+ # CAP_CHOWN | CAP_KILL
+ MASK=$(((1 << 0) | (1 << 5)))
+
+ if [ $((BND & MASK)) -ne "$MASK" ] ; then
+ echo "CAP_CHOWN or CAP_KILL not available in bounding set, skipping test." >&2
+ return
+ fi
+
+ PAMSERVICE="pamserv$RANDOM"
+ TRANSIENTUNIT="capwakealarm$RANDOM.service"
+ SCRIPT="/tmp/capwakealarm$RANDOM.sh"
+
+ cat > /etc/pam.d/"$PAMSERVICE" <<EOF
+auth sufficient pam_unix.so
+auth required pam_deny.so
+account sufficient pam_unix.so
+account required pam_permit.so
+session optional pam_systemd.so default-capability-ambient-set=CAP_CHOWN,CAP_KILL debug
+session required pam_unix.so
+EOF
+
+ cat > "$SCRIPT" <<'EOF'
+#!/bin/bash
+set -ex
+typeset -i AMB MASK
+AMB="0x$(grep 'CapAmb:' /proc/self/status | cut -d: -f2 | tr -d '[:space:]')"
+MASK=$(((1 << 0) | (1 << 5)))
+test "$AMB" -eq "$MASK"
+EOF
+
+ chmod +x "$SCRIPT"
+
+ systemd-run -u "$TRANSIENTUNIT" -p PAMName="$PAMSERVICE" -p Type=oneshot -p User=logind-test-user -p StandardError=tty "$SCRIPT"
+
+ rm -f "$SCRIPT" "$PAMSERVICE"
+}
+
+background_at_return() {
+ rm -f /etc/pam.d/"$PAMSERVICE"
+ unset PAMSERVICE
+}
+
+testcase_background() {
+
+ local uid TRANSIENTUNIT1 TRANSIENTUNIT2
+
+ uid=$(id -u logind-test-user)
+
+ systemctl stop user@"$uid".service
+
+ PAMSERVICE="pamserv$RANDOM"
+ TRANSIENTUNIT1="bg$RANDOM.service"
+ TRANSIENTUNIT2="bgg$RANDOM.service"
+
+ trap background_at_return RETURN
+
+ cat > /etc/pam.d/"$PAMSERVICE" <<EOF
+auth sufficient pam_unix.so
+auth required pam_deny.so
+account sufficient pam_unix.so
+account required pam_permit.so
+session optional pam_systemd.so debug
+session required pam_unix.so
+EOF
+
+ systemd-run -u "$TRANSIENTUNIT1" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background-light" -p Type=exec -p User=logind-test-user sleep infinity
+
+ # This was a 'light' background service, hence the service manager should not be running
+ (! systemctl is-active user@"$uid".service )
+
+ systemctl stop "$TRANSIENTUNIT1"
+
+ systemd-run -u "$TRANSIENTUNIT2" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background" -p Type=exec -p User=logind-test-user sleep infinity
+
+ # This was a regular background service, hence the service manager should be running
+ systemctl is-active user@"$uid".service
+
+ systemctl stop "$TRANSIENTUNIT2"
+
+ systemctl stop user@"$uid".service
+}
+
+setup_test_user
+test_write_dropin
+run_testcases
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-36-NUMAPOLICY
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck disable=SC2317
+at_exit() {
+ # shellcheck disable=SC2181
+ if [[ $? -ne 0 ]]; then
+ # We're exiting with a non-zero EC, let's dump test artifacts
+ # for easier debugging
+ [[ -v straceLog && -f "$straceLog" ]] && cat "$straceLog"
+ [[ -v journalLog && -f "$journalLog" ]] && cat "$journalLog"
+ fi
+}
+
+trap at_exit EXIT
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# Log files
+straceLog='strace.log'
+journalLog='journal.log'
+
+# Systemd config files
+testUnit='numa-test.service'
+testUnitFile="/run/systemd/system/$testUnit"
+testUnitNUMAConf="$testUnitFile.d/numa.conf"
+
+# Sleep constants (we should probably figure out something better but nothing comes to mind)
+sleepAfterStart=3
+
+# Journal cursor for easier navigation
+journalCursorFile="jounalCursorFile"
+
+startStrace() {
+ coproc strace -qq -p 1 -o "$straceLog" -e set_mempolicy -s 1024 ${1:+"$1"}
+ # Wait for strace to properly "initialize", i.e. until PID 1 has the TracerPid
+ # field set to the current strace's PID
+ until awk -v spid="$COPROC_PID" '/^TracerPid:/ {exit !($2 == spid);}' /proc/1/status; do sleep 0.1; done
+}
+
+stopStrace() {
+ [[ -v COPROC_PID ]] || return
+
+ local PID=$COPROC_PID
+ kill -s TERM "$PID"
+ # Make sure the strace process is indeed dead
+ while kill -0 "$PID" 2>/dev/null; do sleep 0.1; done
+}
+
+startJournalctl() {
+ : >"$journalCursorFile"
+ # Save journal's cursor for later navigation
+ journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat
+}
+
+stopJournalctl() {
+ local unit="${1:-init.scope}"
+ # Using journalctl --sync should be better than using SIGRTMIN+1, as
+ # the --sync wait until the synchronization is complete
+ echo "Force journald to write all queued messages"
+ journalctl --sync
+ journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog"
+}
+
+checkNUMA() {
+ # NUMA enabled system should have at least NUMA node0
+ test -e /sys/devices/system/node/node0
+}
+
+writePID1NUMAPolicy() {
+ cat >"$confDir/numa.conf" <<EOF
+[Manager]
+NUMAPolicy=${1:?}
+NUMAMask=${2:-""}
+EOF
+}
+
+writeTestUnit() {
+ mkdir -p "$testUnitFile.d/"
+ printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile"
+}
+
+writeTestUnitNUMAPolicy() {
+ cat >"$testUnitNUMAConf" <<EOF
+[Service]
+NUMAPolicy=${1:?}
+NUMAMask=${2:-""}
+EOF
+ systemctl daemon-reload
+}
+
+pid1ReloadWithStrace() {
+ startStrace
+ systemctl daemon-reload
+ sleep $sleepAfterStart
+ stopStrace
+}
+
+pid1ReloadWithJournal() {
+ startJournalctl
+ systemctl daemon-reload
+ stopJournalctl
+}
+
+pid1StartUnitWithStrace() {
+ startStrace '-f'
+ systemctl start "${1:?}"
+ sleep $sleepAfterStart
+ stopStrace
+}
+
+pid1StartUnitWithJournal() {
+ startJournalctl
+ systemctl start "${1:?}"
+ sleep $sleepAfterStart
+ stopJournalctl
+}
+
+pid1StopUnit() {
+ systemctl stop "${1:?}"
+}
+
+systemctlCheckNUMAProperties() {
+ local UNIT_NAME="${1:?}"
+ local NUMA_POLICY="${2:?}"
+ local NUMA_MASK="${3:-""}"
+ local LOGFILE
+
+ LOGFILE="$(mktemp)"
+
+ systemctl show -p NUMAPolicy "$UNIT_NAME" >"$LOGFILE"
+ grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE"
+
+ : >"$LOGFILE"
+
+ if [ -n "$NUMA_MASK" ]; then
+ systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE"
+ grep "NUMAMask=$NUMA_MASK" "$LOGFILE"
+ fi
+}
+
+writeTestUnit
+
+# Create systemd config drop-in directory
+confDir="/run/systemd/system.conf.d/"
+mkdir -p "$confDir"
+
+if ! checkNUMA; then
+ echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check"
+
+ echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support"
+ writePID1NUMAPolicy "default" "0"
+ startJournalctl
+ systemctl daemon-reload
+ stopJournalctl
+ grep "NUMA support not available, ignoring" "$journalLog"
+
+ echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support"
+ runUnit='numa-systemd-run-test.service'
+ startJournalctl
+ systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ sleep $sleepAfterStart
+ pid1StopUnit "$runUnit"
+ stopJournalctl "$runUnit"
+ grep "NUMA support not available, ignoring" "$journalLog"
+
+else
+ echo "PID1 NUMAPolicy support - Default policy w/o mask"
+ writePID1NUMAPolicy "default"
+ pid1ReloadWithStrace
+ # Kernel requires that nodemask argument is set to NULL when setting default policy
+ grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Default policy w/ mask"
+ writePID1NUMAPolicy "default" "0"
+ pid1ReloadWithStrace
+ grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Bind policy w/o mask"
+ writePID1NUMAPolicy "bind"
+ pid1ReloadWithJournal
+ grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
+
+ echo "PID1 NUMAPolicy support - Bind policy w/ mask"
+ writePID1NUMAPolicy "bind" "0"
+ pid1ReloadWithStrace
+ grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Interleave policy w/o mask"
+ writePID1NUMAPolicy "interleave"
+ pid1ReloadWithJournal
+ grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
+
+ echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
+ writePID1NUMAPolicy "interleave" "0"
+ pid1ReloadWithStrace
+ grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Preferred policy w/o mask"
+ writePID1NUMAPolicy "preferred"
+ pid1ReloadWithJournal
+ # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default
+ grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; }
+
+ echo "PID1 NUMAPolicy support - Preferred policy w/ mask"
+ writePID1NUMAPolicy "preferred" "0"
+ pid1ReloadWithStrace
+ grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Local policy w/o mask"
+ writePID1NUMAPolicy "local"
+ pid1ReloadWithStrace
+ # Kernel requires that nodemask argument is set to NULL when setting default policy
+ # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and
+ # return a numerical constant instead (with a comment):
+ # set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0
+ # Let's cover this scenario as well
+ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Local policy w/ mask"
+ writePID1NUMAPolicy "local" "0"
+ pid1ReloadWithStrace
+ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Default policy w/o mask"
+ writeTestUnitNUMAPolicy "default"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "default"
+ pid1StopUnit "$testUnit"
+ grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Default policy w/ mask"
+ writeTestUnitNUMAPolicy "default" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "default" "0"
+ pid1StopUnit $testUnit
+ # Mask must be ignored
+ grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Bind policy w/o mask"
+ writeTestUnitNUMAPolicy "bind"
+ pid1StartUnitWithJournal "$testUnit"
+ pid1StopUnit "$testUnit"
+ [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
+
+ echo "Unit file NUMAPolicy support - Bind policy w/ mask"
+ writeTestUnitNUMAPolicy "bind" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "bind" "0"
+ pid1StopUnit "$testUnit"
+ grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Interleave policy w/o mask"
+ writeTestUnitNUMAPolicy "interleave"
+ pid1StartUnitWithStrace "$testUnit"
+ pid1StopUnit "$testUnit"
+ [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
+
+ echo "Unit file NUMAPolicy support - Interleave policy w/ mask"
+ writeTestUnitNUMAPolicy "interleave" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "interleave" "0"
+ pid1StopUnit "$testUnit"
+ grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Preferred policy w/o mask"
+ writeTestUnitNUMAPolicy "preferred"
+ pid1StartUnitWithJournal "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "preferred"
+ pid1StopUnit "$testUnit"
+ [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] && { echo >&2 "unexpected pass"; exit 1; }
+
+ echo "Unit file NUMAPolicy support - Preferred policy w/ mask"
+ writeTestUnitNUMAPolicy "preferred" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "preferred" "0"
+ pid1StopUnit "$testUnit"
+ grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Local policy w/o mask"
+ writeTestUnitNUMAPolicy "local"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "local"
+ pid1StopUnit "$testUnit"
+ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Local policy w/ mask"
+ writeTestUnitNUMAPolicy "local" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "local" "0"
+ pid1StopUnit "$testUnit"
+ # Mask must be ignored
+ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+ echo "Unit file CPUAffinity=NUMA support"
+ writeTestUnitNUMAPolicy "bind" "0"
+ echo "CPUAffinity=numa" >>"$testUnitNUMAConf"
+ systemctl daemon-reload
+ systemctl start "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "bind" "0"
+ cpulist="$(cat /sys/devices/system/node/node0/cpulist)"
+ affinity_systemd="$(systemctl show --value -p CPUAffinity "$testUnit")"
+ [ "$cpulist" = "$affinity_systemd" ]
+ pid1StopUnit "$testUnit"
+
+ echo "systemd-run NUMAPolicy support"
+ runUnit='numa-systemd-run-test.service'
+
+ systemd-run -p NUMAPolicy=default --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "default"
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "default" ""
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "bind" "0"
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "interleave" "0"
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "preferred" "0"
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=local --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "local"
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "local" ""
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "local" ""
+ systemctl cat "$runUnit" | grep -q 'CPUAffinity=numa'
+ pid1StopUnit "$runUnit"
+fi
+
+# Cleanup
+rm -rf "$confDir"
+systemctl daemon-reload
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStart=sleep 3600
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-38-FREEZER
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2317
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+systemd-analyze log-level debug
+
+unit=TEST-38-FREEZER-sleep.service
+
+start_test_service() {
+ systemctl daemon-reload
+ systemctl start "${unit}"
+}
+
+dbus_freeze() {
+ local name object_path suffix
+
+ suffix="${1##*.}"
+ name="${1%".$suffix"}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+ busctl call \
+ org.freedesktop.systemd1 \
+ "${object_path}" \
+ org.freedesktop.systemd1.Unit \
+ Freeze
+}
+
+dbus_thaw() {
+ local name object_path suffix
+
+ suffix="${1##*.}"
+ name="${1%".$suffix"}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+ busctl call \
+ org.freedesktop.systemd1 \
+ "${object_path}" \
+ org.freedesktop.systemd1.Unit \
+ Thaw
+}
+
+dbus_freeze_unit() {
+ busctl call \
+ org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager \
+ FreezeUnit \
+ s \
+ "$1"
+}
+
+dbus_thaw_unit() {
+ busctl call \
+ org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager \
+ ThawUnit \
+ s \
+ "$1"
+}
+
+dbus_can_freeze() {
+ local name object_path suffix
+
+ suffix="${1##*.}"
+ name="${1%".$suffix"}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+ busctl get-property \
+ org.freedesktop.systemd1 \
+ "${object_path}" \
+ org.freedesktop.systemd1.Unit \
+ CanFreeze
+}
+
+check_freezer_state() {
+ local name object_path suffix
+
+ suffix="${1##*.}"
+ name="${1%".$suffix"}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+ for _ in {0..10}; do
+ state=$(busctl get-property \
+ org.freedesktop.systemd1 \
+ "${object_path}" \
+ org.freedesktop.systemd1.Unit \
+ FreezerState | cut -d " " -f2 | tr -d '"')
+
+ # Ignore the intermediate freezing & thawing states in case we check
+ # the unit state too quickly
+ [[ "$state" =~ ^(freezing|thawing) ]] || break
+ sleep .5
+ done
+
+ [ "$state" = "$2" ] || {
+ echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
+ exit 1
+ }
+}
+
+check_cgroup_state() {
+ # foo.unit -> /system.slice/foo.unit/
+ # foo.slice/ -> /foo.slice/./
+ # foo.slice/foo.unit -> /foo.slice/foo.unit/
+ local slice unit
+ unit="${1##*/}"
+ slice="${1%"$unit"}"
+ slice="${slice%/}"
+ grep -q "frozen $2" /sys/fs/cgroup/"${slice:-system.slice}"/"${unit:-.}"/cgroup.events
+}
+
+testcase_dbus_api() {
+ echo "Test that DBus API works:"
+ echo -n " - Freeze(): "
+ dbus_freeze "${unit}"
+ check_freezer_state "${unit}" "frozen"
+ check_cgroup_state "$unit" 1
+ echo "[ OK ]"
+
+ echo -n " - Thaw(): "
+ dbus_thaw "${unit}"
+ check_freezer_state "${unit}" "running"
+ check_cgroup_state "$unit" 0
+ echo "[ OK ]"
+
+ echo -n " - FreezeUnit(): "
+ dbus_freeze_unit "${unit}"
+ check_freezer_state "${unit}" "frozen"
+ check_cgroup_state "$unit" 1
+ echo "[ OK ]"
+
+ echo -n " - ThawUnit(): "
+ dbus_thaw_unit "${unit}"
+ check_freezer_state "${unit}" "running"
+ check_cgroup_state "$unit" 0
+ echo "[ OK ]"
+
+ echo -n " - CanFreeze(): "
+ output=$(dbus_can_freeze "${unit}")
+ [ "$output" = "b true" ]
+ echo "[ OK ]"
+
+ echo
+}
+
+testcase_systemctl() {
+ echo "Test that systemctl freeze/thaw verbs:"
+
+ systemctl start "$unit"
+
+ echo -n " - freeze: "
+ systemctl freeze "$unit"
+ check_freezer_state "${unit}" "frozen"
+ check_cgroup_state "$unit" 1
+ # Freezing already frozen unit should be NOP and return quickly
+ timeout 3s systemctl freeze "$unit"
+ echo "[ OK ]"
+
+ echo -n " - thaw: "
+ systemctl thaw "$unit"
+ check_freezer_state "${unit}" "running"
+ check_cgroup_state "$unit" 0
+ # Likewise thawing already running unit shouldn't block
+ timeout 3s systemctl thaw "$unit"
+ echo "[ OK ]"
+
+ systemctl stop "$unit"
+
+ echo
+}
+
+testcase_systemctl_show() {
+ echo "Test systemctl show integration:"
+
+ systemctl start "$unit"
+
+ echo -n " - FreezerState property: "
+ state=$(systemctl show -p FreezerState --value "$unit")
+ [ "$state" = "running" ]
+ systemctl freeze "$unit"
+ state=$(systemctl show -p FreezerState --value "$unit")
+ [ "$state" = "frozen" ]
+ systemctl thaw "$unit"
+ echo "[ OK ]"
+
+ echo -n " - CanFreeze property: "
+ state=$(systemctl show -p CanFreeze --value "$unit")
+ [ "$state" = "yes" ]
+ echo "[ OK ]"
+
+ systemctl stop "$unit"
+ echo
+}
+
+testcase_recursive() {
+ local slice="bar.slice"
+ local unit="baz.service"
+
+ systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
+
+ echo "Test recursive freezing:"
+
+ echo -n " - freeze/thaw parent: "
+ systemctl freeze "$slice"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen-by-parent"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$slice"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "running"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 0
+ echo "[ OK ]"
+
+ echo -n " - child freeze/thaw during frozen parent: "
+ systemctl freeze "$slice"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen-by-parent"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl freeze "$unit"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$unit"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen-by-parent"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$slice"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "running"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 0
+ echo "[ OK ]"
+
+ echo -n " - pre-frozen child not thawed by parent: "
+ systemctl freeze "$unit"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 1
+ systemctl freeze "$slice"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$slice"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 1
+ echo "[ OK ]"
+
+ echo -n " - pre-frozen child demoted and thawed by parent: "
+ systemctl freeze "$slice"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$unit"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen-by-parent"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$slice"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "running"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 0
+ echo "[ OK ]"
+
+ echo -n " - child promoted and not thawed by parent: "
+ systemctl freeze "$slice"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen-by-parent"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl freeze "$unit"
+ check_freezer_state "$slice" "frozen"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ systemctl thaw "$slice"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "frozen"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 1
+ echo "[ OK ]"
+
+ echo -n " - can't stop a frozen unit: "
+ (! systemctl -q stop "$unit" )
+ echo "[ OK ]"
+ systemctl thaw "$unit"
+
+ systemctl stop "$unit"
+ systemctl stop "$slice"
+
+ echo
+}
+
+testcase_preserve_state() {
+ local slice="bar.slice"
+ local unit="baz.service"
+
+ systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
+
+ echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
+
+ echo -n " - freeze from outside: "
+ echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+ # Give kernel some time to freeze the slice
+ sleep 1
+
+ # Our state should not be affected
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "running"
+
+ # However actual kernel state should be frozen
+ check_cgroup_state "$slice/" 1
+ check_cgroup_state "$slice/$unit" 1
+ echo "[ OK ]"
+
+ echo -n " - thaw from outside: "
+ echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+ sleep 1
+
+ check_freezer_state "$unit" "running"
+ check_freezer_state "$slice" "running"
+ check_cgroup_state "$slice/" 0
+ check_cgroup_state "$slice/$unit" 0
+ echo "[ OK ]"
+
+ echo -n " - thaw from outside while inner service is frozen: "
+ systemctl freeze "$unit"
+ check_freezer_state "$unit" "frozen"
+ echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+ echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "frozen"
+ echo "[ OK ]"
+
+ systemctl thaw "$unit"
+ systemctl stop "$unit"
+ systemctl stop "$slice"
+
+ echo
+}
+
+if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then
+ start_test_service
+ run_testcases
+fi
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-43-PRIVATEUSER-UNPRIV
+After=systemd-logind.service user@4711.service
+Wants=user@4711.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+install_extension_images
+
+if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
+ echo "Cannot create unprivileged user namespaces" >/skipped
+ exit 77
+fi
+
+systemd-analyze log-level debug
+
+runas testuser systemd-run --wait --user --unit=test-private-users \
+ -p PrivateUsers=yes -P echo hello
+
+runas testuser systemctl --user log-level debug
+
+runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
+ -p PrivateTmp=yes \
+ -P touch /tmp/innerfile.txt
+# File should not exist outside the job's tmp directory.
+test ! -e /tmp/innerfile.txt
+
+touch /tmp/outerfile.txt
+# File should not appear in unit's private tmp.
+runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \
+ -p PrivateTmp=yes \
+ -P test ! -e /tmp/outerfile.txt
+
+# Confirm that creating a file in home works
+runas testuser systemd-run --wait --user --unit=test-unprotected-home \
+ -P touch /home/testuser/works.txt
+test -e /home/testuser/works.txt
+
+# Confirm that creating a file in home is blocked under read-only
+(! runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
+ -p ProtectHome=read-only \
+ -P bash -c '
+ test -e /home/testuser/works.txt || exit 10
+ touch /home/testuser/blocked.txt && exit 11
+ ')
+test ! -e /home/testuser/blocked.txt
+
+# Check that tmpfs hides the whole directory
+runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \
+ -p ProtectHome=tmpfs \
+ -P test ! -e /home/testuser
+
+# Confirm that home, /root, and /run/user are inaccessible under "yes"
+# shellcheck disable=SC2016
+runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
+ -p ProtectHome=yes \
+ -P bash -c '
+ test "$(stat -c %a /home)" = "0"
+ test "$(stat -c %a /root)" = "0"
+ test "$(stat -c %a /run/user)" = "0"
+ '
+
+# Confirm we cannot change groups because we only have one mapping in the user
+# namespace (no CAP_SETGID in the parent namespace to write the additional
+# mapping of the user supplied group and thus cannot change groups to an
+# unmapped group ID)
+(! runas testuser systemd-run --wait --user --unit=test-group-fail \
+ -p PrivateUsers=yes -p Group=daemon \
+ -P true)
+
+# Check that with a new user namespace we can bind mount
+# files and use a different root directory
+runas testuser systemd-run --wait --user --unit=test-bind-mount \
+ -p BindPaths=/dev/null:/etc/os-release \
+ test ! -s /etc/os-release
+
+runas testuser systemd-run --wait --user --unit=test-read-write \
+ -p ReadOnlyPaths=/ \
+ -p ReadWritePaths="/var /run /tmp" \
+ -p NoExecPaths=/ -p ExecPaths=/usr \
+ test ! -w /etc/os-release
+
+runas testuser systemd-run --wait --user --unit=test-caps \
+ -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \
+ -p CapabilityBoundingSet=CAP_SYS_ADMIN \
+ test -s /etc/os-release
+
+runas testuser systemd-run --wait --user --unit=test-devices \
+ -p PrivateDevices=yes -p PrivateIPC=yes \
+ sh -c "ls -1 /dev/ | wc -l | grep -q -F 18"
+
+# Same check as test/test-execute/exec-privatenetwork-yes.service
+runas testuser systemd-run --wait --user --unit=test-network \
+ -p PrivateNetwork=yes \
+ /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"'
+
+(! runas testuser systemd-run --wait --user --unit=test-hostname \
+ -p ProtectHostname=yes \
+ hostnamectl hostname foo)
+
+(! runas testuser systemd-run --wait --user --unit=test-clock \
+ -p ProtectClock=yes \
+ timedatectl set-time "2012-10-30 18:17:16")
+
+(! runas testuser systemd-run --wait --user --unit=test-kernel-tunable \
+ -p ProtectKernelTunables=yes \
+ sh -c "echo 0 >/proc/sys/user/max_user_namespaces")
+
+(! runas testuser systemd-run --wait --user --unit=test-kernel-mod \
+ -p ProtectKernelModules=yes \
+ sh -c "modprobe -r overlay && modprobe overlay")
+
+if sysctl kernel.dmesg_restrict=0; then
+ (! runas testuser systemd-run --wait --user --unit=test-kernel-log \
+ -p ProtectKernelLogs=yes -p LogNamespace=yes \
+ dmesg)
+fi
+
+unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw
+runas testuser systemd-run --wait --user --unit=test-root-dir \
+ -p RootDirectory=/tmp/img \
+ grep MARKER=1 /etc/os-release
+
+mkdir /tmp/img_bind
+mount --bind /tmp/img /tmp/img_bind
+runas testuser systemd-run --wait --user --unit=test-root-dir-bind \
+ -p RootDirectory=/tmp/img_bind -p MountFlags=private \
+ grep MARKER=1 /etc/os-release
+umount /tmp/img_bind
+
+# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first
+mkdir -p /tmp/a /tmp/b /tmp/c
+if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then
+ unsquashfs -no-xattrs -d /tmp/app2 /tmp/app1.raw
+ runas testuser systemd-run --wait --user --unit=test-extension-dir \
+ -p ExtensionDirectories=/tmp/app2 \
+ -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \
+ -p MountAPIVFS=yes \
+ grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2
+fi
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-44-LOG-NAMESPACE
+Before=getty-pre.target
+Wants=getty-pre.target
+Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+
+systemd-analyze log-level debug
+
+journalctl --list-namespaces -o json | jq .
+
+systemd-run --wait -p LogNamespace=foobar echo "hello world"
+systemd-run --wait -p LogNamespace=foobaz echo "hello world"
+
+journalctl --namespace=foobar --sync
+journalctl --namespace=foobaz --sync
+ls -l /var/log/journal/
+journalctl --list-namespaces
+
+journalctl -o cat --namespace=foobar >/tmp/hello-world
+journalctl -o cat >/tmp/no-hello-world
+
+journalctl --list-namespaces | grep foobar
+journalctl --list-namespaces | grep foobaz
+journalctl --list-namespaces -o json | jq .
+[[ "$(journalctl --root=/tmp --list-namespaces --quiet)" == "" ]]
+
+grep "^hello world$" /tmp/hello-world
+(! grep "^hello world$" /tmp/no-hello-world)
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-45-TIMEDATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+testcase_timedatectl() {
+ timedatectl --no-pager --help
+ timedatectl --version
+
+ timedatectl
+ timedatectl --no-ask-password
+ timedatectl status --machine=testuser@.host
+ timedatectl status
+ timedatectl show
+ timedatectl show --all
+ timedatectl show -p NTP
+ timedatectl show -p NTP --value
+ timedatectl list-timezones
+
+ if ! systemd-detect-virt -qc; then
+ systemctl enable --runtime --now systemd-timesyncd
+ timedatectl timesync-status
+ timedatectl show-timesync
+ fi
+}
+
+restore_timezone() {
+ if [[ -f /tmp/timezone.bak ]]; then
+ mv /tmp/timezone.bak /etc/timezone
+ else
+ rm -f /etc/timezone
+ fi
+}
+
+testcase_timezone() {
+ local ORIG_TZ=
+
+ # Debian/Ubuntu specific file
+ if [[ -f /etc/timezone ]]; then
+ mv /etc/timezone /tmp/timezone.bak
+ fi
+
+ trap restore_timezone RETURN
+
+ if [[ -L /etc/localtime ]]; then
+ ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')
+ echo "original tz: $ORIG_TZ"
+ fi
+
+ echo 'timedatectl works'
+ assert_in "Local time:" "$(timedatectl --no-pager)"
+
+ echo 'change timezone'
+ assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" ""
+ assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv"
+ if [[ -f /etc/timezone ]]; then
+ assert_eq "$(cat /etc/timezone)" "Europe/Kyiv"
+ fi
+ assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
+
+ if [[ -n "$ORIG_TZ" ]]; then
+ echo 'reset timezone to original'
+ assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" ""
+ assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ"
+ if [[ -f /etc/timezone ]]; then
+ assert_eq "$(cat /etc/timezone)" "$ORIG_TZ"
+ fi
+ fi
+}
+
+restore_adjtime() {
+ if [[ -e /etc/adjtime.bak ]]; then
+ mv /etc/adjtime.bak /etc/adjtime
+ else
+ rm /etc/adjtime
+ fi
+}
+
+check_adjtime_not_exist() {
+ if [[ -e /etc/adjtime ]]; then
+ echo "/etc/adjtime unexpectedly exists." >&2
+ exit 1
+ fi
+}
+
+testcase_adjtime() {
+ # test setting UTC vs. LOCAL in /etc/adjtime
+ if [[ -e /etc/adjtime ]]; then
+ mv /etc/adjtime /etc/adjtime.bak
+ fi
+
+ trap restore_adjtime RETURN
+
+ echo 'no adjtime file'
+ rm -f /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+
+ echo 'UTC set in adjtime file'
+ printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+UTC"
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'non-zero values in adjtime file'
+ printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ assert_eq "$(cat /etc/adjtime)" "0.1 123 0
+0
+UTC"
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.1 123 0
+0
+LOCAL"
+
+ echo 'fourth line adjtime file'
+ printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+UTC
+somethingelse"
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL
+somethingelse"
+
+ echo 'no final newline in adjtime file'
+ printf '0.0 0 0\n0\nUTC' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0\nUTC' >/etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only one line in adjtime file'
+ printf '0.0 0 0\n' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n' >/etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only one line in adjtime file, no final newline'
+ printf '0.0 0 0' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0' >/etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only two lines in adjtime file'
+ printf '0.0 0 0\n0\n' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0\n' >/etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only two lines in adjtime file, no final newline'
+ printf '0.0 0 0\n0' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0' >/etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'unknown value in 3rd line of adjtime file'
+ printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+}
+
+assert_ntp() {
+ local value="${1:?}"
+
+ for _ in {0..9}; do
+ [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0
+ sleep .5
+ done
+
+ return 1
+}
+
+assert_timedated_signal() {
+ local timestamp="${1:?}"
+ local value="${2:?}"
+ local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
+
+ journalctl --sync
+
+ for _ in {0..9}; do
+ if journalctl "${args[@]}" --grep .; then
+ [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]];
+ return 0
+ fi
+
+ sleep .5
+ done
+
+ return 1
+}
+
+assert_timesyncd_state() {
+ local state="${1:?}"
+
+ for _ in {0..9}; do
+ [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0
+ sleep .5
+ done
+
+ return 1
+}
+
+testcase_ntp() {
+ # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing
+ if systemd-detect-virt --container --quiet; then
+ systemctl disable --quiet --now systemd-timesyncd
+ mkdir -p /run/systemd/system/systemd-timesyncd.service.d
+ cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf <<EOF
+[Unit]
+ConditionVirtualization=
+
+[Service]
+Type=simple
+AmbientCapabilities=
+ExecStart=
+ExecStart=sleep infinity
+EOF
+ systemctl daemon-reload
+ fi
+
+ systemd-run --unit busctl-monitor.service --service-type=notify \
+ busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timedate1,member=PropertiesChanged,path=/org/freedesktop/timedate1"
+
+ : 'Disable NTP'
+ ts="$(date +"%F %T.%6N")"
+ timedatectl set-ntp false
+ assert_timedated_signal "$ts" "false"
+ assert_timesyncd_state "inactive"
+ assert_ntp "false"
+ assert_rc 3 systemctl is-active --quiet systemd-timesyncd
+
+ : 'Enable NTP'
+ ts="$(date +"%F %T.%6N")"
+ timedatectl set-ntp true
+ assert_timedated_signal "$ts" "true"
+ assert_ntp "true"
+ assert_timesyncd_state "active"
+ assert_rc 0 systemctl is-active --quiet systemd-timesyncd
+
+ : 'Re-disable NTP'
+ ts="$(date +"%F %T.%6N")"
+ timedatectl set-ntp false
+ assert_timedated_signal "$ts" "false"
+ assert_ntp "false"
+ assert_rc 3 systemctl is-active --quiet systemd-timesyncd
+
+ systemctl stop busctl-monitor.service
+ rm -rf /run/systemd/system/systemd-timesyncd.service.d/
+ systemctl daemon-reload
+}
+
+assert_timesyncd_signal() {
+ local timestamp="${1:?}"
+ local property="${2:?}"
+ local value="${3:?}"
+ local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
+
+ journalctl --sync
+
+ for _ in {0..9}; do
+ if journalctl "${args[@]}" --grep .; then
+ [[ "$(journalctl "${args[@]}" -o cat | jq -r ".payload.data[1].$property.data | join(\" \")")" == "$value" ]];
+ return 0
+ fi
+
+ sleep .5
+ done
+
+ return 1
+}
+
+assert_networkd_ntp() {
+ local interface="${1:?}"
+ local value="${2:?}"
+ # Go through the array of NTP servers and for each entry do:
+ # - if the entry is an IPv4 address, join the Address array into a dot separated string
+ # - if the entry is a server address, select it unchanged
+ # These steps produce an array of strings, that is then joined into a space-separated string
+ # Note: this doesn't support IPv6 addresses, since converting them to a string is a bit more
+ # involved than a simple join(), but let's leave that to another time
+ local expr='[.NTP[] | (select(.Family == 2).Address | join(".")), select(has("Server")).Server] | join(" ")'
+
+ [[ "$(networkctl status "$interface" --json=short | jq -r "$expr")" == "$value" ]]
+}
+
+testcase_timesyncd() {
+ if systemd-detect-virt -cq; then
+ echo "This test case requires a VM, skipping..."
+ return 0
+ fi
+
+ if ! command -v networkctl >/dev/null; then
+ echo "This test requires systemd-networkd, skipping..."
+ return 0
+ fi
+
+ # Create a dummy interface managed by networkd, so we can configure link NTP servers
+ mkdir -p /run/systemd/network/
+ cat >/etc/systemd/network/10-ntp99.netdev <<EOF
+[NetDev]
+Name=ntp99
+Kind=dummy
+EOF
+ cat >/etc/systemd/network/10-ntp99.network <<EOF
+[Match]
+Name=ntp99
+
+[Network]
+Address=10.0.0.1/24
+EOF
+
+ systemctl unmask systemd-timesyncd systemd-networkd
+ systemctl restart systemd-timesyncd
+ systemctl restart systemd-networkd
+ networkctl status ntp99
+
+ systemd-run --unit busctl-monitor.service --service-type=notify \
+ busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timesync1,member=PropertiesChanged,path=/org/freedesktop/timesync1"
+
+ # LinkNTPServers
+ #
+ # Single IP
+ ts="$(date +"%F %T.%6N")"
+ timedatectl ntp-servers ntp99 10.0.0.1
+ assert_networkd_ntp ntp99 10.0.0.1
+ assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1
+ # Setting NTP servers to the same value shouldn't emit a PropertiesChanged signal
+ ts="$(date +"%F %T.%6N")"
+ timedatectl ntp-servers ntp99 10.0.0.1
+ assert_networkd_ntp ntp99 10.0.0.1
+ (! assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1)
+ # Multiple IPs
+ ts="$(date +"%F %T.%6N")"
+ timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99
+ assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99"
+ assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99"
+ # Multiple IPs + servers
+ ts="$(date +"%F %T.%6N")"
+ timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13
+ assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
+ assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
+
+ # RuntimeNTPServers
+ #
+ # There's no user-facing API that allows changing this property (afaik), so let's
+ # call SetRuntimeNTPServers() directly to test things out. The inner workings should
+ # be exactly the same as in the previous case, so do just one test to make sure
+ # things work
+ ts="$(date +"%F %T.%6N")"
+ busctl call org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager \
+ SetRuntimeNTPServers as 4 "10.0.0.1" foo "192.168.99.1" bar
+ servers="$(busctl get-property org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager RuntimeNTPServers)"
+ [[ "$servers" == 'as 4 "10.0.0.1" "foo" "192.168.99.1" "bar"' ]]
+ assert_timesyncd_signal "$ts" RuntimeNTPServers "10.0.0.1 foo 192.168.99.1 bar"
+
+ # Cleanup
+ systemctl stop systemd-networkd systemd-timesyncd
+ rm -f /run/systemd/network/ntp99.*
+}
+
+run_testcases
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-46-HOMED
+Wants=getty-pre.target
+Before=getty-pre.target
+Requires=systemd-homed.service systemd-userdbd.socket
+After=systemd-homed.service systemd-userdbd.socket
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+NotifyAccess=all
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check if homectl is installed, and if it isn't bail out early instead of failing
+if ! test -x /usr/bin/homectl ; then
+ echo "no homed" >/skipped
+ exit 77
+fi
+
+inspect() {
+ # As updating disk-size-related attributes can take some time on some
+ # filesystems, let's drop these fields before comparing the outputs to
+ # avoid unexpected fails. To see the full outputs of both homectl &
+ # userdbctl (for debugging purposes) drop the fields just before the
+ # comparison.
+ local USERNAME="${1:?}"
+ homectl inspect "$USERNAME" | tee /tmp/a
+ userdbctl user "$USERNAME" | tee /tmp/b
+
+ # diff uses the grep BREs for pattern matching
+ diff -I '^\s*Disk \(Size\|Free\|Floor\|Ceiling\|Usage\):' /tmp/{a,b}
+ rm /tmp/{a,b}
+
+ homectl inspect --json=pretty "$USERNAME"
+}
+
+wait_for_state() {
+ for i in {1..10}; do
+ (( i > 1 )) && sleep 0.5
+ homectl inspect "$1" | grep -qF "State: $2" && break
+ done
+}
+
+FSTYPE="$(stat --file-system --format "%T" /)"
+
+systemd-analyze log-level debug
+systemctl service-log-level systemd-homed debug
+
+# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
+mkdir -p /home
+mount -t tmpfs tmpfs /home -o size=290M
+
+# we enable --luks-discard= since we run our tests in a tight VM, hence don't
+# needlessly pressure for storage. We also set the cheapest KDF, since we don't
+# want to waste CI CPU cycles on it. We also effectively disable rate-limiting on
+# the user by allowing 1000 logins per second
+NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
+ --disk-size=min \
+ --luks-discard=yes \
+ --image-path=/home/test-user.home \
+ --luks-pbkdf-type=pbkdf2 \
+ --luks-pbkdf-time-cost=1ms \
+ --rate-limit-interval=1s \
+ --rate-limit-burst=1000
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
+inspect test-user
+
+PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
+inspect test-user
+
+SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+homectl update test-user --real-name "Offline test" --offline
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+# Ensure that the offline changes were propagated in
+grep "Offline test" /home/test-user/.identity
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inactive test"
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
+# (See: https://github.com/systemd/systemd/issues/17606)
+if ! systemd-detect-virt -cq ; then
+ PASSWORD=xEhErW0ndafV4s homectl activate test-user
+ inspect test-user
+
+ # Key should now be in the keyring
+ homectl update test-user --real-name "Keyring Test"
+ inspect test-user
+
+ # These commands shouldn't use the keyring
+ (! timeout 5s homectl authenticate test-user )
+ (! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
+
+ homectl lock test-user
+ inspect test-user
+
+ # Key should be gone from keyring
+ (! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
+
+ PASSWORD=xEhErW0ndafV4s homectl unlock test-user
+ inspect test-user
+
+ # Key should have been re-instantiated into the keyring
+ homectl update test-user --real-name "Keyring Test 3"
+ inspect test-user
+
+ homectl deactivate test-user
+ inspect test-user
+fi
+
+# Do some resize tests, but only if we run on real kernels and are on btrfs, as quota inside of containers
+# will fail and minimizing while active only works on btrfs.
+if ! systemd-detect-virt -cq && [[ "$FSTYPE" == "btrfs" ]]; then
+ # grow while inactive
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
+ inspect test-user
+
+ # minimize while inactive
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user min
+ inspect test-user
+
+ PASSWORD=xEhErW0ndafV4s homectl activate test-user
+ inspect test-user
+
+ # grow while active
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user max
+ inspect test-user
+
+ # minimize while active
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user 0
+ inspect test-user
+
+ # grow while active
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
+ inspect test-user
+
+ # shrink to original size while active
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user 256M
+ inspect test-user
+
+ # minimize again
+ PASSWORD=xEhErW0ndafV4s homectl resize test-user min
+ inspect test-user
+
+ # Increase space, so that we can reasonably rebalance free space between to home dirs
+ mount /home -o remount,size=800M
+
+ # create second user
+ NEWPASSWORD=uuXoo8ei homectl create test-user2 \
+ --disk-size=min \
+ --luks-discard=yes \
+ --image-path=/home/test-user2.home \
+ --luks-pbkdf-type=pbkdf2 \
+ --luks-pbkdf-time-cost=1ms \
+ --rate-limit-interval=1s \
+ --rate-limit-burst=1000
+ inspect test-user2
+
+ # activate second user
+ PASSWORD=uuXoo8ei homectl activate test-user2
+ inspect test-user2
+
+ # set second user's rebalance weight to 100
+ PASSWORD=uuXoo8ei homectl update test-user2 --rebalance-weight=100
+ inspect test-user2
+
+ # set first user's rebalance weight to quarter of that of the second
+ PASSWORD=xEhErW0ndafV4s homectl update test-user --rebalance-weight=25
+ inspect test-user
+
+ # synchronously rebalance
+ homectl rebalance
+ inspect test-user
+ inspect test-user2
+
+ wait_for_state test-user2 active
+ homectl deactivate test-user2
+ wait_for_state test-user2 inactive
+ homectl remove test-user2
+fi
+
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
+
+# Regression tests
+wait_for_state test-user inactive
+/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
+
+wait_for_state test-user inactive
+homectl remove test-user
+
+# blob directory tests
+# See docs/USER_RECORD_BLOB_DIRS.md
+checkblob() {
+ test -f "/var/cache/systemd/home/blob-user/$1"
+ stat -c "%u %#a" "/var/cache/systemd/home/blob-user/$1" | grep "^0 0644"
+ test -f "/home/blob-user/.identity-blob/$1"
+ stat -c "%u %#a" "/home/blob-user/.identity-blob/$1" | grep "^12345 0644"
+
+ diff "/var/cache/systemd/home/blob-user/$1" "$2"
+ diff "/var/cache/systemd/home/blob-user/$1" "/home/blob-user/.identity-blob/$1"
+}
+
+mkdir /tmp/blob1 /tmp/blob2
+echo data1 blob1 >/tmp/blob1/test1
+echo data1 blob2 >/tmp/blob2/test1
+echo data2 blob1 >/tmp/blob1/test2
+echo data2 blob2 >/tmp/blob2/test2
+echo invalid filename >/tmp/blob1/файл
+echo data3 >/tmp/external-test3
+echo avatardata >/tmp/external-avatar
+ln -s /tmp/external-avatar /tmp/external-avatar-lnk
+dd if=/dev/urandom of=/tmp/external-barely-fits bs=1M count=64
+dd if=/dev/urandom of=/tmp/external-toobig bs=1M count=65
+
+# create w/ prepopulated blob dir
+NEWPASSWORD=EMJuc3zQaMibJo homectl create blob-user \
+ --disk-size=min --luks-discard=yes \
+ --luks-pbkdf-type=pbkdf2 --luks-pbkdf-time-cost=1ms \
+ --rate-limit-interval=1s --rate-limit-burst=1000 \
+ --uid=12345 \
+ --blob=/tmp/blob1
+inspect blob-user
+PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
+inspect blob-user
+
+test -d /var/cache/systemd/home/blob-user
+stat -c "%u %#a" /var/cache/systemd/home/blob-user | grep "^0 0755"
+test -d /home/blob-user/.identity-blob
+stat -c "%u %#a" /home/blob-user/.identity-blob | grep "^12345 0700"
+
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+checkblob test2 /tmp/blob1/test2
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# append files to existing blob, both well-known and other
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+ -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar
+inspect blob-user
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+checkblob test2 /tmp/blob1/test2
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+checkblob test3 /tmp/external-test3
+checkblob avatar /tmp/external-avatar
+
+# delete files from existing blob, both well-known and other
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+ -b test3= --avatar=
+inspect blob-user
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+checkblob test2 /tmp/blob1/test2
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# swap entire blob directory
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+ -b /tmp/blob2
+inspect blob-user
+(! checkblob test1 /tmp/blob1/test1 )
+checkblob test1 /tmp/blob2/test1
+(! checkblob test2 /tmp/blob1/test2 )
+checkblob test2 /tmp/blob2/test2
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# create and delete files while swapping blob directory. Also symlinks.
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+ -b /tmp/blob1 -b test2= -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar-lnk
+inspect blob-user
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+(! checkblob test2 /tmp/blob1/test2 )
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+checkblob test3 /tmp/external-test3
+checkblob avatar /tmp/external-avatar # target of the link
+
+# clear the blob directory
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+ -b /tmp/blob2 -b test3=/tmp/external-test3 --blob=
+inspect blob-user
+(! checkblob test1 /tmp/blob1/test1 )
+(! checkblob test1 /tmp/blob2/test1 )
+(! checkblob test2 /tmp/blob1/test2 )
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# file that's exactly 64M still fits
+# FIXME: Figure out why this fails on ext4.
+if [[ "$FSTYPE" != "ext2/ext3" ]]; then
+ PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+ -b barely-fits=/tmp/external-barely-fits
+ (! checkblob test1 /tmp/blob1/test1 )
+ (! checkblob test1 /tmp/blob2/test1 )
+ (! checkblob test2 /tmp/blob1/test2 )
+ (! checkblob test2 /tmp/blob2/test2 )
+ (! checkblob фаил /tmp/blob1/фаил )
+ (! checkblob test3 /tmp/external-test3 )
+ (! checkblob avatar /tmp/external-avatar )
+ checkblob barely-fits /tmp/external-barely-fits
+fi
+
+# error out if the file is too big
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b huge=/tmp/external-toobig )
+
+# error out if filenames are invalid
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b .hidden=/tmp/external-test3 )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b "with spaces=/tmp/external-test3" )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b with=equals=/tmp/external-test3 )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b файл=/tmp/external-test3 )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b special@chars=/tmp/external-test3 )
+
+# Make sure offline updates to blobs get propagated in
+homectl deactivate blob-user
+inspect blob-user
+homectl update blob-user --offline -b barely-fits= -b propagated=/tmp/external-test3
+inspect blob-user
+PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
+inspect blob-user
+(! checkblob barely-fits /tmp/external-barely-fits )
+checkblob propagated /tmp/external-test3
+
+homectl deactivate blob-user
+wait_for_state blob-user inactive
+homectl remove blob-user
+
+# userdbctl tests
+export PAGER=
+
+# Create a couple of user/group records to test io.systemd.DropIn
+# See docs/USER_RECORD.md and docs/GROUP_RECORD.md
+mkdir -p /run/userdb/
+cat >"/run/userdb/dropingroup.group" <<\EOF
+{
+ "groupName" : "dropingroup",
+ "gid" : 1000000
+}
+EOF
+cat >"/run/userdb/dropinuser.user" <<\EOF
+{
+ "userName" : "dropinuser",
+ "uid" : 2000000,
+ "realName" : "🐱",
+ "memberOf" : [
+ "dropingroup"
+ ]
+}
+EOF
+cat >"/run/userdb/dropinuser.user-privileged" <<\EOF
+{
+ "privileged" : {
+ "hashedPassword" : [
+ "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
+ ],
+ "sshAuthorizedKeys" : [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA//dxI2xLg4MgxIKKZv1nqwTEIlE/fdakii2Fb75pG+ foo@bar.tld",
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMlaqG2rTMje5CQnfjXJKmoSpEVJ2gWtx4jBvsQbmee2XbU/Qdq5+SRisssR9zVuxgg5NA5fv08MgjwJQMm+csc= hello@world.tld"
+ ]
+ }
+}
+EOF
+# Set permissions and create necessary symlinks as described in nss-systemd(8)
+chmod 0600 "/run/userdb/dropinuser.user-privileged"
+ln -svrf "/run/userdb/dropingroup.group" "/run/userdb/1000000.group"
+ln -svrf "/run/userdb/dropinuser.user" "/run/userdb/2000000.user"
+ln -svrf "/run/userdb/dropinuser.user-privileged" "/run/userdb/2000000.user-privileged"
+
+userdbctl
+userdbctl --version
+userdbctl --help --no-pager
+userdbctl --no-legend
+userdbctl --output=classic
+userdbctl --output=friendly
+userdbctl --output=table
+userdbctl --output=json | jq
+userdbctl -j --json=pretty | jq
+userdbctl -j --json=short | jq
+userdbctl --with-varlink=no
+
+userdbctl user
+userdbctl user testuser
+userdbctl user root
+userdbctl user testuser root
+userdbctl user -j testuser root | jq
+# Check only UID for the nobody user, since the name is build-configurable
+userdbctl user --with-nss=no --synthesize=yes
+userdbctl user --with-nss=no --synthesize=yes 0 root 65534
+userdbctl user dropinuser
+userdbctl user 2000000
+userdbctl user --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropinuser
+userdbctl user --with-nss=no 2000000
+(! userdbctl user '')
+(! userdbctl user 🐱)
+(! userdbctl user 🐱 '' bar)
+(! userdbctl user i-do-not-exist)
+(! userdbctl user root i-do-not-exist testuser)
+(! userdbctl user --with-nss=no --synthesize=no 0 root 65534)
+(! userdbctl user -N root nobody)
+(! userdbctl user --with-dropin=no dropinuser)
+(! userdbctl user --with-dropin=no 2000000)
+
+userdbctl group
+userdbctl group testuser
+userdbctl group root
+userdbctl group testuser root
+userdbctl group -j testuser root | jq
+# Check only GID for the nobody group, since the name is build-configurable
+userdbctl group --with-nss=no --synthesize=yes
+userdbctl group --with-nss=no --synthesize=yes 0 root 65534
+userdbctl group dropingroup
+userdbctl group 1000000
+userdbctl group --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropingroup
+userdbctl group --with-nss=no 1000000
+(! userdbctl group '')
+(! userdbctl group 🐱)
+(! userdbctl group 🐱 '' bar)
+(! userdbctl group i-do-not-exist)
+(! userdbctl group root i-do-not-exist testuser)
+(! userdbctl group --with-nss=no --synthesize=no 0 root 65534)
+(! userdbctl group --with-dropin=no dropingroup)
+(! userdbctl group --with-dropin=no 1000000)
+
+userdbctl users-in-group
+userdbctl users-in-group testuser
+userdbctl users-in-group testuser root
+userdbctl users-in-group -j testuser root | jq
+userdbctl users-in-group 🐱
+(! userdbctl users-in-group '')
+(! userdbctl users-in-group foo '' bar)
+
+userdbctl groups-of-user
+userdbctl groups-of-user testuser
+userdbctl groups-of-user testuser root
+userdbctl groups-of-user -j testuser root | jq
+userdbctl groups-of-user 🐱
+(! userdbctl groups-of-user '')
+(! userdbctl groups-of-user foo '' bar)
+
+userdbctl services
+userdbctl services -j | jq
+
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"testuser","service":"io.systemd.Multiplexer"}'
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"root","service":"io.systemd.Multiplexer"}'
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"uid":2000000,"service":"io.systemd.Multiplexer"}'
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"","service":"io.systemd.Multiplexer"}')
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"🐱","service":"io.systemd.Multiplexer"}')
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
+
+userdbctl ssh-authorized-keys dropinuser | tee /tmp/authorized-keys
+grep "ssh-ed25519" /tmp/authorized-keys
+grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
+echo "my-top-secret-key 🐱" >/tmp/my-top-secret-key
+userdbctl ssh-authorized-keys dropinuser --chain /bin/cat /tmp/my-top-secret-key | tee /tmp/authorized-keys
+grep "ssh-ed25519" /tmp/authorized-keys
+grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
+grep "my-top-secret-key 🐱" /tmp/authorized-keys
+(! userdbctl ssh-authorized-keys 🐱)
+(! userdbctl ssh-authorized-keys dropin-user --chain)
+(! userdbctl ssh-authorized-keys dropin-user --chain '')
+(! SYSTEMD_LOG_LEVEL=debug userdbctl ssh-authorized-keys dropin-user --chain /bin/false)
+
+(! userdbctl '')
+for opt in json multiplexer output synthesize with-dropin with-nss with-varlink; do
+ (! userdbctl "--$opt=''")
+ (! userdbctl "--$opt='🐱'")
+ (! userdbctl "--$opt=foo")
+ (! userdbctl "--$opt=foo" "--$opt=''" "--$opt=🐱")
+done
+
+# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
+if command -v ssh &>/dev/null && command -v sshd &>/dev/null && ! [[ -v ASAN_OPTIONS ]]; then
+ at_exit() {
+ set +e
+
+ systemctl is-active -q mysshserver.socket && systemctl stop mysshserver.socket
+ rm -f /tmp/homed.id_ecdsa /run/systemd/system/mysshserver{@.service,.socket}
+ systemctl daemon-reload
+ homectl remove homedsshtest
+ for dir in /etc /usr/lib; do
+ if [[ -f "$dir/pam.d/sshd.bak" ]]; then
+ mv "$dir/pam.d/sshd.bak" "$dir/pam.d/sshd"
+ fi
+ done
+ }
+
+ trap at_exit EXIT
+
+ # Test that SSH logins work with delayed unlocking
+ ssh-keygen -N '' -C '' -t ecdsa -f /tmp/homed.id_ecdsa
+ NEWPASSWORD=hunter4711 homectl create \
+ --disk-size=min \
+ --luks-discard=yes \
+ --luks-pbkdf-type=pbkdf2 \
+ --luks-pbkdf-time-cost=1ms \
+ --rate-limit-interval=1s \
+ --rate-limit-burst=1000 \
+ --enforce-password-policy=no \
+ --ssh-authorized-keys=@/tmp/homed.id_ecdsa.pub \
+ --stop-delay=0 \
+ homedsshtest
+ homectl inspect homedsshtest
+
+ mkdir -p /etc/ssh
+ test -f /etc/ssh/ssh_host_ecdsa_key || ssh-keygen -t ecdsa -C '' -N '' -f /etc/ssh/ssh_host_ecdsa_key
+
+ # ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that
+ # are aware of distros use
+ mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
+
+ for dir in /etc /usr/lib; do
+ if [[ -f "$dir/pam.d/sshd" ]]; then
+ mv "$dir/pam.d/sshd" "$dir/pam.d/sshd.bak"
+ cat >"$dir/pam.d/sshd" <<EOF
+auth sufficient pam_unix.so nullok
+auth sufficient pam_systemd_home.so debug
+auth required pam_deny.so
+account sufficient pam_systemd_home.so debug
+account sufficient pam_unix.so
+account required pam_permit.so
+session optional pam_systemd_home.so debug
+session optional pam_systemd.so
+session required pam_unix.so
+EOF
+ break
+ fi
+ done
+
+ mkdir -p /etc/sshd/
+ cat >/etc/ssh/sshd_config <<EOF
+AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
+AuthorizedKeysCommandUser root
+UsePAM yes
+AcceptEnv PASSWORD
+LogLevel DEBUG3
+EOF
+
+ cat >/run/systemd/system/mysshserver.socket <<EOF
+[Socket]
+ListenStream=4711
+Accept=yes
+EOF
+
+ cat >/run/systemd/system/mysshserver@.service <<EOF
+[Service]
+ExecStart=-/usr/sbin/sshd -i -d -e
+StandardInput=socket
+StandardOutput=socket
+StandardError=journal
+EOF
+
+ systemctl daemon-reload
+ systemctl start mysshserver.socket
+
+ userdbctl user -j homedsshtest
+
+ ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
+ -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
+ homedsshtest@localhost echo zzz | tr -d '\r' | tee /tmp/homedsshtest.out
+ grep -E "^zzz$" /tmp/homedsshtest.out
+ rm /tmp/homedsshtest.out
+
+ ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
+ -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
+ homedsshtest@localhost env
+
+ wait_for_state homedsshtest inactive
+fi
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that the /sbin/mount.ddi helper works
+dir="/tmp/mounthelper.$RANDOM"
+mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard
+umount -R "$dir"
+
+# Test systemd-repart --make-ddi=:
+if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then
+ echo "Skipping --make-ddi= tests"
+ exit 0
+fi
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+ -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+ -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
+mkdir -p /tmp/test-50-confext/etc/extension-release.d/
+echo "foobar50" >/tmp/test-50-confext/etc/waldo
+{
+ grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+ echo IMAGE_ID=waldo
+ echo IMAGE_VERSION=7
+} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
+mkdir -p /run/confexts
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+ systemd-repart -C \
+ -s /tmp/test-50-confext \
+ --certificate=/tmp/test-50-cert.crt \
+ --private-key=/tmp/test-50-privkey.key \
+ /run/confexts/waldo.confext.raw
+rm -rf /tmp/test-50-confext
+
+mkdir -p /run/verity.d
+cp /tmp/test-50-cert.crt /run/verity.d/
+systemd-dissect --mtree /run/confexts/waldo.confext.raw
+
+systemd-confext refresh
+test "$(</etc/waldo)" = foobar50
+rm /run/confexts/waldo.confext.raw
+systemd-confext refresh
+test ! -f /etc/waldo
+
+mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
+# Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
+# Minimize= logic is working.
+truncate --size=50M /tmp/test-50-sysext/usr/waldo
+{
+ grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+ echo IMAGE_ID=waldo
+ echo IMAGE_VERSION=7
+} >/tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
+mkdir -p /run/extensions
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+ systemd-repart -S \
+ -s /tmp/test-50-sysext \
+ --certificate=/tmp/test-50-cert.crt \
+ --private-key=/tmp/test-50-privkey.key \
+ /run/extensions/waldo.sysext.raw
+
+systemd-dissect --mtree /run/extensions/waldo.sysext.raw
+systemd-sysext refresh
+test -f /usr/waldo
+rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
+systemd-sysext refresh
+test ! -f /usr/waldo
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+# shellcheck disable=SC2233,SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+BIND_LOG_SOCKETS=(
+ --property BindReadOnlyPaths=/dev/log
+ --property BindReadOnlyPaths=/run/systemd/journal/socket
+ --property BindReadOnlyPaths=/run/systemd/journal/stdout
+)
+
+systemd-dissect --json=short "$MINIMAL_IMAGE.raw" | \
+ grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F "MARKER=1"
+# shellcheck disable=SC2153
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+systemd-dissect --list "$MINIMAL_IMAGE.raw" | grep -q '^etc/os-release$'
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash yes | \
+ grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash no | \
+ grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "$MINIMAL_IMAGE.raw" etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "$MINIMAL_IMAGE.raw" sha256sum etc/os-release)
+test "$SHA256SUM2" != ""
+test "$SHA256SUM1" = "$SHA256SUM2"
+
+if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
+ # Make sure tarballs are reproducible
+ read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+ test "$SHA256SUM1" != ""
+ read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+ test "$SHA256SUM2" != ""
+ test "$SHA256SUM1" = "$SHA256SUM2"
+ # Also check that a file we expect to be there is there
+ systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | tar t | grep etc/os-release
+fi
+
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+ --json=short \
+ --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+ --verity-data="$MINIMAL_IMAGE.fooverity" | \
+ grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+ --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+ --verity-data="$MINIMAL_IMAGE.fooverity" | \
+ grep -q -F "MARKER=1"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+ --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+ --verity-data="$MINIMAL_IMAGE.fooverity" | \
+ grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+mkdir -p "$IMAGE_DIR/mount" "$IMAGE_DIR/mount2"
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+# Verity volume should be shared (opened only once)
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount2"
+verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
+# In theory we should check that count is exactly one. In practice, libdevmapper
+# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
+# (and even mounted and in use), so best-effort is the most we can do for now
+if [[ "$verity_count" -lt 1 ]]; then
+ echo "Verity device $MINIMAL_IMAGE.raw not found in /dev/mapper/"
+ exit 1
+fi
+systemd-dissect --umount "$IMAGE_DIR/mount"
+systemd-dissect --umount "$IMAGE_DIR/mount2"
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p RootHash="$MINIMAL_IMAGE.foohash" \
+ -p RootVerity="$MINIMAL_IMAGE.fooverity" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+# Let's use the long option name just here as a test
+systemd-run -P \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ --property RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ --property RootVerity="$MINIMAL_IMAGE.fooverity" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+# Derive partition UUIDs from root hash, in UUID syntax
+ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+
+systemd-dissect --json=short \
+ --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+ "$MINIMAL_IMAGE.gpt" | \
+ grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$ARCHITECTURE"'","verity":"signed",'
+systemd-dissect --json=short \
+ --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+ "$MINIMAL_IMAGE.gpt" | \
+ grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$ARCHITECTURE"'","verity":null,'
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ systemd-dissect --json=short \
+ --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+ "$MINIMAL_IMAGE.gpt" | \
+ grep -qE '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$ARCHITECTURE"'","verity":null,'
+fi
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F "MARKER=1"
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+# Test image policies
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt"
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='*'
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='~')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='-')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=swap=unprotected+encrypted+verity)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=unprotected
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent+unprotected
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity=unused+absent)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity=unused+absent)
+
+# Test RootImagePolicy= unit file setting
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='*' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='~' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='-' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=absent' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=verity' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=signed' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=encrypted' \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" --in-memory "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+# add explicit -p MountAPIVFS=yes once to test the parser
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p MountAPIVFS=yes \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootImageOptions="root:ro,noatime root:ro,dev" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ mount | grep -F "squashfs" | grep -q -F "noatime"
+
+mkdir -p "$IMAGE_DIR/result"
+cat >/run/systemd/system/testservice-50a.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/a"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
+RootImageOptions=nosuid,dev
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+EOF
+systemctl start testservice-50a.service
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid"
+
+cat >/run/systemd/system/testservice-50b.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/b"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.gpt
+RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
+RootImageOptions=home:ro,dev nosuid,dev,%%foo
+# this is the default, but let's specify once to test the parser
+MountAPIVFS=yes
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+EOF
+systemctl start testservice-50b.service
+grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime"
+
+# Check that specifier escape is applied %%foo → %foo
+busctl get-property org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
+ org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
+
+# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \
+ cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p TemporaryFileSystem=/run \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p TemporaryFileSystem=/run \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p TemporaryFileSystem=/run \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+cat >/run/systemd/system/testservice-50c.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+MountImages=$MINIMAL_IMAGE.gpt:/run/img1:root:noatime:home:relatime
+MountImages=$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid
+ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
+ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
+ExecStart=bash -c "mount >>/run/result/c"
+BindPaths=$IMAGE_DIR/result:/run/result
+Type=oneshot
+EOF
+systemctl start testservice-50c.service
+grep -q -F "MARKER=1" "$IMAGE_DIR/result/c"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid"
+
+# Adding a new mounts at runtime works if the unit is in the active state,
+# so use Type=notify to make sure there's no race condition in the test
+cat >/run/systemd/system/testservice-50d.service <<EOF
+[Service]
+RuntimeMaxSec=300
+Type=notify
+RemainAfterExit=yes
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=sh -c ' \\
+ systemd-notify --ready; \\
+ while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
+ sleep 0.1; \\
+ done; \\
+ mount; \\
+ mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
+'
+EOF
+systemctl start testservice-50d.service
+
+# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
+mkdir -p /tmp/wrong/foo
+mksquashfs /tmp/wrong/foo /tmp/wrong.raw
+systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
+test "$(systemctl show -P SubState testservice-50d.service)" = "running"
+systemctl mount-image --mkdir testservice-50d.service "$MINIMAL_IMAGE.raw" /tmp/img root:nosuid
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'while [[ $(systemctl show -P SubState testservice-50d.service) == running ]]; do sleep .2; done'
+systemctl is-active testservice-50d.service
+
+# ExtensionImages will set up an overlay
+systemd-run -P \
+ --property ExtensionImages=/tmp/app0.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionImages=/tmp/app0.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+ --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages=/tmp/app-nodistro.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages=/etc/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
+mkdir -p /tmp/symlink-test/
+cp /tmp/app-nodistro.raw /tmp/symlink-test/app-nodistro-v1.raw
+ln -fs /tmp/symlink-test/app-nodistro-v1.raw /tmp/symlink-test/app-nodistro.raw
+systemd-run -P \
+ --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+# Symlink check again but for confext
+mkdir -p /etc/symlink-test/
+cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
+ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
+systemd-run -P \
+ --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# And again mixing sysext and confext
+systemd-run -P \
+ --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
+ --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+systemd-run -P \
+ --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
+ --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+cat >/run/systemd/system/testservice-50e.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+ExtensionImages=/tmp/app0.raw /tmp/app1.raw:nosuid
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50e.service
+systemctl is-active testservice-50e.service
+
+# Check vpick support in ExtensionImages=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
+ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
+
+systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
+
+rm -rf "$VDIR"
+
+# ExtensionDirectories will set up an overlay
+mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
+(! systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/nonexistent" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh)
+(! systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh)
+systemd-dissect --mount /tmp/app0.raw "$IMAGE_DIR/app0"
+systemd-dissect --mount /tmp/app1.raw "$IMAGE_DIR/app1"
+systemd-dissect --mount /tmp/app-nodistro.raw "$IMAGE_DIR/app-nodistro"
+systemd-dissect --mount /etc/service-scoped-test.raw "$IMAGE_DIR/service-scoped-test"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app-nodistro" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/service-scoped-test" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+cat >/run/systemd/system/testservice-50f.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+ExtensionDirectories=$IMAGE_DIR/app0 $IMAGE_DIR/app1
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50f.service
+systemctl is-active testservice-50f.service
+
+# Check vpick support in ExtensionDirectories=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
+ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
+
+systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+
+rm -rf "$VDIR"
+
+systemd-dissect --umount "$IMAGE_DIR/app0"
+systemd-dissect --umount "$IMAGE_DIR/app1"
+
+# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
+mkdir -p /var/lib/extensions/
+ln -s /tmp/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
+systemd-sysext merge
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+mkdir -p /etc/extensions/app-nodistro
+systemd-sysext merge
+test ! -e /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+rmdir /etc/extensions/app-nodistro
+
+# Similar, but go via varlink
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+
+# Check that extensions cannot contain os-release
+mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
+touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
+(! systemd-sysext merge)
+test ! -e /usr/lib/systemd/system/some_file
+test ! -e /usr/lib/systemd/system/other_file
+systemd-sysext unmerge
+rm -rf /run/extensions/app-reject
+rm /var/lib/extensions/app-nodistro.raw
+
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
+mkdir -p /run/machines /run/portables /run/extensions
+touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+systemd-dissect --discover --json=short >/tmp/discover.json
+grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
+grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
+grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
+rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
+
+# Wait until the symlinks we want to test are established
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/loop/* symlinks really reference the right device
+test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
+
+if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then
+ # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
+ # instead. Let's simply skip the test on such old systems.
+ test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP"
+fi
+
+# Detach by loopback device
+systemd-dissect --detach "$LOOP"
+
+# Test long reference name.
+# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
+# and --loop-ref accepts upto 63 characters, and udev creates symlink
+# based on the name when it has upto _62_ characters.
+name="$(for _ in {1..62}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
+test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
+
+# Detach by the /dev/disk/by-loop-ref symlink
+systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
+
+name="$(for _ in {1..63}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
+test ! -e "/dev/disk/by-loop-ref/$name"
+
+# Detach by backing inode
+systemd-dissect --detach "$MINIMAL_IMAGE.raw"
+(! systemd-dissect --detach "$MINIMAL_IMAGE.raw")
+
+# check for confext functionality
+mkdir -p /run/confexts/test/etc/extension-release.d
+echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
+cat <<EOF >/run/confexts/test/etc/testscript
+#!/bin/bash
+echo "This should not happen"
+EOF
+chmod +x /run/confexts/test/etc/testscript
+systemd-confext merge
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+(! /etc/testscript)
+systemd-confext status
+systemd-confext unmerge
+rm -rf /run/confexts/
+
+unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw"
+systemd-run --unit=test-root-ephemeral \
+ -p RootDirectory=/tmp/img \
+ -p RootEphemeral=yes \
+ -p Type=exec \
+ "${BIND_LOG_SOCKETS[@]}" \
+ bash -c "touch /abc && sleep infinity"
+test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
+systemctl stop test-root-ephemeral
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
+test ! -f /tmp/img/abc
+
+systemd-dissect --mtree /tmp/img >/dev/null
+systemd-dissect --list /tmp/img >/dev/null
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+
+echo abc > abc
+systemd-dissect --copy-to /tmp/img abc /abc
+test -f /tmp/img/abc
+
+# Test for dissect tool support with systemd-sysext
+mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
+echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
+mksquashfs testkit/ testkit.raw
+cp testkit.raw /run/extensions/
+unsquashfs -l /run/extensions/testkit.raw
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
+systemd-sysext merge
+systemd-sysext status
+grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
+systemd-sysext unmerge
+rm -rf /run/extensions/ testkit/
+
+# Test for dissect tool support with systemd-confext
+mkdir -p /run/confexts/ testjob/etc/extension-release.d/
+echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
+echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
+echo "MARKER_CONFEXT_123" >testjob/etc/testfile
+mksquashfs testjob/ testjob.raw
+cp testjob.raw /run/confexts/
+unsquashfs -l /run/confexts/testjob.raw
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
+systemd-confext merge
+systemd-confext status
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+systemd-confext unmerge
+rm -rf /run/confexts/ testjob/
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /run/host/os-release | cmp "$OS_RELEASE"
+
+# Test that systemd-sysext reloads the daemon.
+mkdir -p /var/lib/extensions/
+ln -s /tmp/app-reload.raw /var/lib/extensions/app-reload.raw
+systemd-sysext merge --no-reload
+# the service should not be running
+(! systemctl --quiet is-active foo.service)
+systemd-sysext unmerge --no-reload
+systemd-sysext merge
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done'
+systemd-sysext unmerge --no-reload
+# Grep on the Warning to find the warning helper mentioning the daemon reload.
+systemctl status foo.service 2>&1 | grep -q -F "Warning"
+systemd-sysext merge
+systemd-sysext unmerge
+systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
+rm /var/lib/extensions/app-reload.raw
+
+# Sneak in a couple of expected-to-fail invocations to cover
+# https://github.com/systemd/systemd/issues/29610
+(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
+(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \
+ [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \
+ ! command -v mksquashfs || \
+ ! grep -q bpf /sys/kernel/security/lsm ||
+ ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep . || \
+ systemd-analyze compare-versions "$(uname -r)" lt 6.5 || \
+ systemd-analyze compare-versions "$(pkcheck --version | awk '{print $3}')" lt 124; then
+ echo "Skipping mountfsd/nsresourced tests"
+ exit 0
+fi
+
+at_exit() {
+ set +e
+
+ umount -R /tmp/unpriv/mount
+ rmdir /tmp/unpriv
+ rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt
+ rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+ rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3
+}
+
+trap at_exit EXIT
+
+systemctl start systemd-mountfsd.socket systemd-nsresourced.socket
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+ -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+ -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt
+
+systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+ systemd-repart -P \
+ -s /tmp/unpriv/mount \
+ --certificate=/tmp/test-50-unpriv-cert.crt \
+ --private-key=/tmp/test-50-unpriv-privkey.key \
+ /var/tmp/unpriv.raw
+systemd-dissect --rmdir --umount /tmp/unpriv/mount
+
+systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw
+systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw >/tmp/unpriv.raw.mtree
+
+# Run unpriv, should fail due to lack of privs
+(! runas testuser systemd-dissect /var/tmp/unpriv.raw)
+(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw)
+
+if (SYSTEMD_LOG_TARGET=console varlinkctl call \
+ /run/systemd/userdb/io.systemd.NamespaceResource \
+ io.systemd.NamespaceResource.AllocateUserRange \
+ '{"name":"test-supported","size":65536,"userNamespaceFileDescriptor":0}' 2>&1 || true) |
+ grep -q "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported"; then
+ echo "User namespace interface not supported, skipping mountfsd/nsresourced tests"
+ exit 0
+fi
+
+# Install key in keychain
+cp /tmp/test-50-unpriv-cert.crt /run/verity.d
+
+# Now run unpriv again, should be OK now.
+runas testuser systemd-dissect /var/tmp/unpriv.raw
+runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw >/tmp/unpriv2.raw.mtree
+
+# Check that unpriv and priv run yielded same results
+cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+
+# Make sure nspawn works unpriv, too (for now do not nest)
+if ! systemd-detect-virt -c; then
+ systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out
+ echo thisisatest | cmp /tmp/unpriv.out -
+
+ # The unpriv user has no rights to lock the image or write to it. Let's
+ # turn off both for this test, so that we don't have to copy the image
+ # around.
+ systemd-run -M testuser@ --user --pipe \
+ -p Environment=SYSTEMD_NSPAWN_LOCK=0 \
+ -p Delegate=1 \
+ -p DelegateSubgroup=supervisor \
+ -p Environment=SYSTEMD_LOG_LEVEL=debug \
+ --wait -- \
+ systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2
+ echo thisisatest | cmp /tmp/unpriv.out2 -
+fi
+
+systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3
+echo thisisatest | cmp /tmp/unpriv.out3 -
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-50-DISSECT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Setup shared stuff & run all subtests
+
+at_exit() {
+ set +e
+
+ if [[ -z "${IMAGE_DIR:-}" ]]; then
+ return
+ fi
+
+ while read -r dir; do
+ if mountpoint -q "$dir"; then
+ umount -Rv "$dir"
+ fi
+ done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
+
+ rm -rf "$IMAGE_DIR"
+}
+
+trap at_exit EXIT
+
+: "Setup base images"
+
+export SYSTEMD_LOG_LEVEL=debug
+export ARCHITECTURE
+export IMAGE_DIR
+export MACHINE
+export MINIMAL_IMAGE
+export MINIMAL_IMAGE_ROOTHASH
+export OPENSSL_CONFIG
+export OS_RELEASE
+export ROOT_GUID
+export SIGNATURE_GUID
+export VERITY_GUID
+
+machine="$(uname -m)"
+if [[ "$machine" == "x86_64" ]]; then
+ ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
+ VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
+ SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176
+ ARCHITECTURE="x86-64"
+elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then
+ ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a
+ VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
+ SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676
+ ARCHITECTURE="x86"
+elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then
+ ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae
+ VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820
+ SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3
+ ARCHITECTURE="arm64"
+elif [[ "$machine" == "arm" ]]; then
+ ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3
+ VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6
+ SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037
+ ARCHITECTURE="arm"
+elif [[ "$machine" == "ia64" ]]; then
+ ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97
+ VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571
+ SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a
+ ARCHITECTURE="ia64"
+elif [[ "$machine" == "loongarch64" ]]; then
+ ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6
+ VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535
+ SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
+ ARCHITECTURE="loongarch64"
+elif [[ "$machine" == "s390x" ]]; then
+ ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306
+ VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b
+ SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9
+ ARCHITECTURE="s390x"
+elif [[ "$machine" == "ppc64le" ]]; then
+ ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599
+ VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a
+ SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
+ ARCHITECTURE="ppc64-le"
+elif [[ "$machine" == "riscv64" ]]; then
+ ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224
+ VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d
+ SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a
+ ARCHITECTURE="riscv64"
+elif [[ "$machine" == "riscv32" ]]; then
+ ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1
+ VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de
+ SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448
+ ARCHITECTURE="riscv32"
+else
+ echo "Unexpected uname -m: $machine in TEST-50-DISSECT.sh, please fix me"
+ exit 1
+fi
+
+udevadm control --log-level=debug
+
+IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)"
+cp -v /usr/share/minimal* "$IMAGE_DIR/"
+MINIMAL_IMAGE="$IMAGE_DIR/minimal_0"
+MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")"
+
+install_extension_images
+
+OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
+
+if systemctl --version | grep -q -- +OPENSSL ; then
+ # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is
+ # missing, fail early with a proper error message.
+ if ! command -v openssl &>/dev/null; then
+ echo "openssl binary is missing" >/failed
+ exit 1
+ fi
+
+ OPENSSL_CONFIG="$(mktemp)"
+ # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
+ cat >"${OPENSSL_CONFIG:?}" <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = DE
+ST = Test State
+L = Test Locality
+O = Org Name
+OU = Org Unit Name
+CN = Common Name
+emailAddress = test@email.com
+EOF
+fi
+
+# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
+#
+# du rounds up to block size, which is more helpful for partitioning
+root_size="$(du --apparent-size -k "$MINIMAL_IMAGE.raw" | cut -f1)"
+verity_size="$(du --apparent-size -k "$MINIMAL_IMAGE.verity" | cut -f1)"
+signature_size=4
+# 4MB seems to be the minimum size blkid will accept, below that probing fails
+dd if=/dev/zero of="$MINIMAL_IMAGE.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
+# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
+# so do some basic rounding up if the minimal image is more than 1 MB
+if [[ "$root_size" -ge 1024 ]]; then
+ root_size="$((root_size/1024 + 1))MiB"
+else
+ root_size="${root_size}KiB"
+fi
+verity_size="$((verity_size * 2))KiB"
+signature_size="$((signature_size * 2))KiB"
+
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ # Create key pair
+ openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 \
+ -keyout "$MINIMAL_IMAGE.key" -out "$MINIMAL_IMAGE.crt" -days 365 -nodes
+ # Sign Verity root hash with it
+ openssl smime -sign -nocerts -noattr -binary \
+ -in "$MINIMAL_IMAGE.roothash" \
+ -inkey "$MINIMAL_IMAGE.key" \
+ -signer "$MINIMAL_IMAGE.crt" \
+ -outform der \
+ -out "$MINIMAL_IMAGE.roothash.p7s"
+ # Generate signature partition JSON data
+ echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig"
+ # Pad it
+ truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig"
+ # Register certificate in the (userspace) verity key ring
+ mkdir -p /run/verity.d
+ ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt
+fi
+
+# Construct a UUID from hash
+# input: 11111111222233334444555566667777
+# output: 11111111-2222-3333-4444-555566667777
+uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt"
+uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append
+fi
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition"
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition"
+fi
+loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")"
+partitions=(
+ "${loop:?}p1"
+ "${loop:?}p2"
+)
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ partitions+=("${loop:?}p3")
+fi
+# The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
+# Let's not expect the devices to be initialized.
+udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
+udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1"
+udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3"
+fi
+losetup -d "$loop"
+
+: "Run subtests"
+
+run_subtests
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
+FSTYPE=$(stat --file-system --format "%T" /usr)
+
+shopt -s nullglob
+
+# shellcheck disable=SC2317
+at_exit() {
+ set +ex
+
+ local target
+
+ # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise
+ # things get very confusing once we start unmounting things, due to changing file offsets.
+ # shellcheck disable=SC2002
+ cat /proc/mounts | while read -r _ target _ _ _ _; do
+ if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then
+ umount -Rv "$target"
+ fi
+ done
+
+ rm -rf "${FAKE_ROOTS_DIR}"
+}
+
+trap at_exit EXIT
+
+# Clears the trap command - it needs to be invoked for every test-case subshell
+# so prepending commands with prepend_trap inside the subshell won't preserve
+# the trap commands from outer shell.
+init_trap() {
+ trap - EXIT
+}
+
+prepend_trap() {
+ set +x
+
+ local command=${1}; shift
+ local previous_commands
+
+ previous_commands=$(trap -p EXIT)
+ if [[ -z $previous_commands ]]; then
+ previous_commands=':'
+ else
+ previous_commands=${previous_commands#'trap -- '}
+ previous_commands=${previous_commands%' EXIT'}
+ previous_commands=$(xargs <<<"$previous_commands")
+ fi
+
+ # shellcheck disable=SC2064 # We use double quotes on purpose here.
+ trap "${command}; ${previous_commands}" EXIT
+
+ set -x
+}
+
+prepare_root() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+ local dir
+
+ if [[ -n $root ]] && [[ -d $root ]]; then
+ echo >&2 "Directory $root already exists, possible copy-paste error?"
+ exit 1
+ fi
+
+ local -a leftovers=( "$root/var/lib/extensions/"* "$root/var/lib/extensions.mutable/"* )
+ if [[ ${#leftovers[@]} -gt 0 ]]; then
+ echo >&2 "Leftovers remained, make sure to clean them up in the test case: ${leftovers[*]}"
+ exit 1
+ fi
+
+ for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
+ mkdir -p "$root$dir"
+ done
+
+ if [[ -e $root/usr/lib/os-release ]]; then
+ mv "$root/usr/lib/os-release" "$root/usr/lib/os-release.orig"
+ fi
+
+ {
+ echo "ID=testtest"
+ echo "VERSION=1.2.3"
+ } >"$root/usr/lib/os-release"
+
+ prepend_trap "cleanup_os_release ${root@Q}"
+}
+
+cleanup_os_release() {
+ # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+ local root=${1:-}
+
+ # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+ rm -f "$root/usr/lib/os-release"
+ # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+ if [[ -e $root/usr/lib/os-release.orig ]]; then
+ # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+ mv "$root/usr/lib/os-release.orig" "$root/usr/lib/os-release"
+ fi
+}
+
+prepare_extension_image() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+ local ext_dir ext_release name
+
+ name="test-extension"
+ ext_dir="$root/var/lib/extensions/$name"
+ ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
+ mkdir -p "${ext_release%/*}"
+ echo "ID=_any" >"$ext_release"
+ mkdir -p "$ext_dir/$hierarchy"
+ touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
+
+ prepend_trap "rm -rf ${ext_dir@Q}"
+}
+
+prepare_extension_mutable_dir() {
+ local dir=${1:?}
+
+ mkdir -p "$dir"
+ touch "$dir/preexisting-file-in-extensions-mutable"
+ prepend_trap "rm -rf ${dir@Q}"
+}
+
+make_read_only() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+
+ mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
+ prepend_trap "umount ${root@Q}${hierarchy@Q}"
+}
+
+prepare_hierarchy() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+ local file
+
+ file="$root$hierarchy/preexisting-file-in-hierarchy"
+ touch "$file"
+ prepend_trap "rm -f ${file@Q}"
+}
+
+prepare_read_only_hierarchy() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+
+ prepare_hierarchy "$root" "$hierarchy"
+ make_read_only "$root" "$hierarchy"
+}
+
+move_existing_hierarchy_aside() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+
+ if [[ -z $root ]] && [[ $hierarchy = /usr ]]; then
+ echo >&2 "Hell no, not moving /usr aside"
+ exit 1
+ fi
+
+ local path=$root$hierarchy
+
+ if [[ -e $path ]]; then
+ mv "$path" "$path.orig"
+ prepend_trap "mv ${path@Q}.orig ${path@Q}"
+ fi
+}
+
+# Extra arguments:
+# -e: check for a preexisting file in extension
+# -h: check for a preexisting file in hierarchy
+# -u: check for a preexisting file in upperdir
+extension_verify() {
+ local root=${1:-}
+ local hierarchy=${2:?}
+ local message=${3:?}
+ shift 3
+ # Map each option to a pre-defined file name
+ local -A option_files_map=(
+ [e]="preexisting-file-in-extension-image"
+ [h]="preexisting-file-in-hierarchy"
+ [u]="preexisting-file-in-extensions-mutable"
+ )
+ local -A args=(
+ [e]=0
+ [h]=0
+ [u]=0
+ )
+ local file full_path opt option
+
+ while getopts "ehu" opt; do
+ case "$opt" in
+ e|h|u)
+ args["$opt"]=1
+ ;;
+ *)
+ echo >&2 "Unxexpected option: $opt"
+ exit 1
+ esac
+ done
+
+ for option in "${!option_files_map[@]}"; do
+ file=${option_files_map["$option"]}
+ full_path="$root$hierarchy/$file"
+
+ if [[ ${args["$option"]} -ne 0 ]]; then
+ if [[ ! -f $full_path ]]; then
+ ls -la "$root$hierarchy"
+ echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
+ exit 1
+ fi
+ else
+ if [[ -f $full_path ]]; then
+ ls -la "$root$hierarchy"
+ echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
+ exit 1
+ fi
+ fi
+ done
+}
+
+extension_verify_after_merge() (
+ set +x
+
+ local root=${1:-}
+ local hierarchy=${2:?}
+ shift 2
+
+ extension_verify "$root" "$hierarchy" "after merge" "$@"
+)
+
+extension_verify_after_unmerge() (
+ set +x
+
+ local root=${1:-}
+ local hierarchy=${2:?}
+ shift 2
+
+ extension_verify "$root" "$hierarchy" "after unmerge" "$@"
+)
+
+run_systemd_sysext() {
+ local root=${1:-}
+ shift
+
+ local -a sysext_args
+ sysext_args=()
+
+ if [[ -n $root ]]; then
+ sysext_args+=( "--root=$root" )
+ fi
+ sysext_args+=( "$@" )
+ systemd-sysext "${sysext_args[@]}"
+}
+
+# General systemd-sysext tests
+
+run_sysext_tests() {
+ # The roots_dir variable may be empty - in such case all the tests will run
+ # on /, otherwise they will run on $roots_dir/<SEPARATE_DIR_FOR_TEST>.
+ local roots_dir=${1}; shift
+
+ # Each test runs in a subshell, so we can use traps for cleanups without
+ # clobbering toplevel traps, and we can do skips by invoking "exit 0".
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-read-only-hierarchy"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+)
+
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-mutable-hierarchy"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
+)
+
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-missing-hierarchy"}
+hierarchy=/opt
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+)
+
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-empty-hierarchy"}
+hierarchy=/opt
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy-disabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-missing-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-empty-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/mutable-symlink-with-read-only-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root/upperdir"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/mutable-self-upper"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/failure-self-upper-ro"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+
+(! run_systemd_sysext "$fake_root" --mutable=auto merge)
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/read-only-mutable-dangling-symlink"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+ln -sfTr "/should/not/exist/" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/disabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/imported"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=import merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, mutability enabled, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/enabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+run_systemd_sysext "$fake_root" --mutable=yes merge
+# systemd-sysext with --mutable=yes creates extensions.mutable directory for
+# the hierarchy, so delete it after the test
+prepend_trap "rm -rf ${extension_data_dir@Q}"
+# systemd-sysext with --mutable=yes creates extensions.mutable directory also
+# for the /usr hierarchy, because the image needs to have
+# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
+# /usr hierarchy to also become mutable
+prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, auto-mutability, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-explicit"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, mutability enabled through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/enabled-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" merge
+# systemd-sysext with --mutable=yes creates extensions.mutable directory for
+# the hierarchy, so delete it after the test
+prepend_trap "rm -rf ${extension_data_dir@Q}"
+# systemd-sysext with --mutable=yes creates extensions.mutable directory also
+# for the /usr hierarchy, because the image needs to have
+# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
+# /usr hierarchy to also become mutable
+prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, auto-mutability enabled through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/read-only-auto-env-var"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/auto-mutable-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/env-var-disabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/imported-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/env-var-overridden"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=ephemeral merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import-self"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge)
+)
+
+
+( init_trap
+: "Extension data pointing to mutable hierarchy, import mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/import-self"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! run_systemd_sysext "$fake_root" --mutable=import merge)
+)
+
+
+for mutable_mode in no yes ephemeral; do
+ ( init_trap
+ : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}"
+
+ fake_root=${roots_dir:+"$roots_dir/perm-checks-mutable-$mutable_mode"}
+ hierarchy=/opt
+ extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+ [[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+ prepare_root "$fake_root" "$hierarchy"
+ prepare_extension_image "$fake_root" "$hierarchy"
+ prepare_extension_mutable_dir "$extension_data_dir"
+ prepare_read_only_hierarchy "${fake_root}" "${hierarchy}"
+
+ full_path="$fake_root$hierarchy"
+ permissions_before_merge=$(stat --format=%A "$full_path")
+
+ run_systemd_sysext "$fake_root" "--mutable=$mutable_mode" merge
+ if [[ $mutable_mode = yes ]]; then
+ # systemd-sysext with --mutable=yes creates extensions.mutable
+ # directory also for the /usr hierarchy, because the image needs to
+ # have /usr/lib/extension-release.d/extension-release.<NAME> file -
+ # this causes the /usr hierarchy to also become mutable
+ extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+ prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+ fi
+
+ permissions_after_merge=$(stat --format=%A "$full_path")
+
+ run_systemd_sysext "$fake_root" unmerge
+
+ permissions_after_unmerge=$(stat --format=%A "$full_path")
+
+ if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then
+ echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}"
+ exit 1
+ fi
+
+ if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then
+ echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}"
+ exit 1
+ fi
+ )
+done
+
+
+( init_trap
+: "Check if merging fails in case of invalid mutable directory permissions"
+
+fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+
+old_mode=$(stat --format '%#a' "$fake_root$hierarchy")
+chmod 0755 "$fake_root$hierarchy"
+prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}"
+chmod 0700 "$extension_data_dir"
+
+(! run_systemd_sysext "$fake_root" --mutable=yes merge)
+)
+
+} # End of run_sysext_tests
+
+
+# For preparing /, we need mutable /usr/. If it is read only, skip running the
+# sysext tests on /.
+if [[ -w /usr ]]; then
+ run_sysext_tests ''
+fi
+run_sysext_tests "$FAKE_ROOTS_DIR"
+
+
+exit 0
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-52-HONORFIRSTSHUTDOWN
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+
+systemctl enable test-honor-first-shutdown.service
+systemctl start test-honor-first-shutdown.service
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-53-ISSUE-16347
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+: >/failed
+
+# Reset host date to current time, 3 days in the past.
+date -s "-3 days"
+
+# Run a timer for every 15 minutes.
+systemd-run --unit test-timer --on-calendar "*:0/15:0" true
+
+next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
+next_elapsed=$(date -d "${next_elapsed}" +%s)
+now=$(date +%s)
+time_delta=$((next_elapsed - now))
+
+# Check that the timer will elapse in less than 20 minutes.
+((0 < time_delta && time_delta < 1200)) || {
+ echo 'Timer elapse outside of the expected 20 minute window.'
+ echo " next_elapsed=${next_elapsed}"
+ echo " now=${now}"
+ echo " time_delta=${time_delta}"
+ echo ''
+} >>/failed
+
+if test ! -s /failed ; then
+ rm -f /failed
+ touch /testok
+fi
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-54-CREDS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-analyze log-level debug
+
+run_with_cred_compare() (
+ local cred="${1:?}"
+ local exp="${2?}"
+ local log_file
+ shift 2
+
+ log_file="$(mktemp)"
+ # shellcheck disable=SC2064
+ trap "rm -f '$log_file'" RETURN
+
+ set -o pipefail
+ systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
+ diff "$log_file" <(echo -ne "$exp")
+)
+
+# Sanity checks
+#
+# Create a dummy "full" disk (similar to /dev/full) to check out-of-space
+# scenarios
+mkdir /tmp/full
+mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full
+
+# verb: setup
+# Run this first, otherwise any encrypted credentials wouldn't be decryptable
+# as we regenerate the host key
+rm -fv /var/lib/systemd/credential.secret
+systemd-creds setup
+test -e /var/lib/systemd/credential.secret
+rm -fv /var/lib/systemd/credential.secret
+
+# Prepare a couple of dummy credentials for the cat/list verbs
+CRED_DIR="$(mktemp -d)"
+ENC_CRED_DIR="$(mktemp -d)"
+echo foo >"$CRED_DIR/secure-or-weak"
+echo foo >"$CRED_DIR/insecure"
+echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted"
+echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed"
+chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR"
+chmod -R 0444 "$CRED_DIR/insecure"
+mkdir /tmp/empty/
+
+systemd-creds --system
+systemd-creds --no-pager --help
+systemd-creds --version
+systemd-creds has-tpm2 || :
+systemd-creds has-tpm2 -q || :
+
+# verb: list
+systemd-creds list --system
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off
+ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list
+
+# verb: cat
+for cred in secure-or-weak insecure encrypted encrypted-unnamed; do
+ ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred"
+done
+run_with_cred_compare "mycred:" "" cat mycred
+run_with_cred_compare "mycred:\n" "\n" cat mycred
+run_with_cred_compare "mycred:foo" "foo" cat mycred
+run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred
+# Note: --newline= does nothing when stdout is not a tty, which is the case here
+run_with_cred_compare "mycred:foo" "foo" --newline=yes cat mycred
+run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred
+run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred
+run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred
+run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred
+run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred
+run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred
+run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred
+run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred
+run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred
+run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred
+run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred
+run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred
+run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred
+run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred
+systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq
+
+# verb: encrypt/decrypt
+echo "According to all known laws of aviation..." >/tmp/cred.orig
+systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc
+systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.{enc,dec}
+# --pretty
+cred_name="fo'''o''bar"
+cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)"
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/test-54-pretty-cred.service <<EOF
+[Service]
+Type=oneshot
+${cred_option:?}
+ExecStart=bash -c "diff <(systemd-creds cat \"$cred_name\") /tmp/cred.orig"
+EOF
+systemctl daemon-reload
+systemctl start test-54-pretty-cred
+rm /run/systemd/system/test-54-pretty-cred.service
+# Credential validation: name
+systemd-creds --name="foo" -H encrypt /tmp/cred.orig /tmp/cred.enc
+(! systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec)
+(! systemd-creds --name="bar" decrypt /tmp/cred.enc /tmp/cred.dec)
+systemd-creds --name="" decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.dec
+systemd-creds --name="foo" decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.{enc,dec}
+# Credential validation: time
+systemd-creds --not-after="+1d" encrypt /tmp/cred.orig /tmp/cred.enc
+(! systemd-creds --timestamp="+2d" decrypt /tmp/cred.enc /tmp/cred.dec)
+systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.{enc,dec}
+
+(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds list")
+(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds --system list")
+(! CREDENTIALS_DIRECTORY="" systemd-creds list)
+(! systemd-creds --system --foo)
+(! systemd-creds --system -@)
+(! systemd-creds --system --json=)
+(! systemd-creds --system --json="")
+(! systemd-creds --system --json=foo)
+(! systemd-creds --system cat)
+(! systemd-creds --system cat "")
+(! systemd-creds --system cat this-should-not-exist)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode= cat mycred)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode="" cat mycred)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode=foo cat mycred)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --newline=foo cat mycred)
+(! systemd-run -p SetCredential=mycred:notbase64 --wait --pipe -- systemd-creds --transcode=unbase64 cat mycred)
+(! systemd-run -p SetCredential=mycred:nothex --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
+(! systemd-run -p SetCredential=mycred:a --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
+(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=short cat mycred)
+(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=pretty cat mycred)
+(! systemd-creds encrypt /foo/bar/baz -)
+(! systemd-creds decrypt /foo/bar/baz -)
+(! systemd-creds decrypt / -)
+(! systemd-creds encrypt / -)
+(! echo foo | systemd-creds --with-key=foo encrypt - -)
+(! echo {0..20} | systemd-creds decrypt - -)
+(! systemd-creds --not-after= encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --not-after="" encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --not-after="-1d" encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --timestamp= encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --timestamp="" encrypt /tmp/cred.orig /tmp/cred.enc)
+(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent encrypt - /dev/null)
+(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent decrypt - /dev/null)
+(! echo foo | systemd-creds encrypt - /tmp/full/foo)
+(! echo foo | systemd-creds encrypt - - | systemd-creds decrypt - /tmp/full/foo)
+
+# Verify that the creds are properly loaded and we can read them from the service's unpriv user
+systemd-run -p LoadCredential=passwd:/etc/passwd \
+ -p LoadCredential=shadow:/etc/shadow \
+ -p SetCredential=dog:wuff \
+ -p DynamicUser=1 \
+ --unit=test-54-unpriv.service \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' \
+ >/tmp/ts54-concat
+(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat
+rm /tmp/ts54-concat
+
+# Test that SetCredential= acts as fallback for LoadCredential=
+echo piff >/tmp/ts54-fallback
+[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ]
+rm /tmp/ts54-fallback
+[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ]
+
+if systemd-detect-virt -q -c ; then
+ expected_credential=mynspawncredential
+ expected_value=strangevalue
+elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
+ # Verify that passing creds through kernel cmdline works
+ [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
+ [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
+
+ # And that it also works via SMBIOS
+ [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
+ [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
+
+ # If we aren't run in nspawn, we are run in qemu
+ systemd-detect-virt -q -v
+ expected_credential=myqemucredential
+ expected_value=othervalue
+
+ # Verify that writing a sysctl via the kernel cmdline worked
+ [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
+
+ # Verify that creating a user via sysusers via the kernel cmdline worked
+ grep -q ^credtestuser: /etc/passwd
+
+ # Verify that writing a file via tmpfiles worked
+ [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
+ [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
+ [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
+
+ # Verify that adding a unit and drop-in via credentials worked
+ systemctl start my-service
+ test -f /tmp/unit-cred
+ test -f /tmp/unit-dropin
+ test -f /tmp/unit-named-dropin
+else
+ echo "qemu_fw_cfg support missing in kernel. Sniff!"
+ expected_credential=""
+ expected_value=""
+fi
+
+if [ "$expected_credential" != "" ] ; then
+ # If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh
+ [ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ]
+
+ # Test that propagation from system credential to service credential works
+ [ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
+
+ # Check it also works, if we rename it while propagating it
+ [ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ]
+
+ # Combine it with a fallback (which should have no effect, given the cred should be passed down)
+ [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
+
+ # This should succeed
+ systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
+
+ # And this should fail
+ (! systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true)
+fi
+
+# Verify that the creds are immutable
+(! systemd-run -p LoadCredential=passwd:/etc/passwd \
+ -p DynamicUser=1 \
+ --unit=test-54-immutable-touch.service \
+ --wait \
+ touch '${CREDENTIALS_DIRECTORY}/passwd')
+(! systemd-run -p LoadCredential=passwd:/etc/passwd \
+ -p DynamicUser=1 \
+ --unit=test-54-immutable-rm.service \
+ --wait \
+ rm '${CREDENTIALS_DIRECTORY}/passwd')
+
+# Check directory-based loading
+mkdir -p /tmp/ts54-creds/sub
+echo -n a >/tmp/ts54-creds/foo
+echo -n b >/tmp/ts54-creds/bar
+echo -n c >/tmp/ts54-creds/baz
+echo -n d >/tmp/ts54-creds/sub/qux
+systemd-run -p LoadCredential=cred:/tmp/ts54-creds \
+ -p DynamicUser=1 \
+ --unit=test-54-dir.service \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/cred_foo' \
+ '${CREDENTIALS_DIRECTORY}/cred_bar' \
+ '${CREDENTIALS_DIRECTORY}/cred_baz' \
+ '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat
+cmp /tmp/ts54-concat <(echo -n abcd)
+rm /tmp/ts54-concat
+rm -rf /tmp/ts54-creds
+
+# Check that globs work as expected
+mkdir -p /run/credstore
+echo -n a >/run/credstore/test.creds.first
+echo -n b >/run/credstore/test.creds.second
+mkdir -p /etc/credstore
+echo -n c >/etc/credstore/test.creds.third
+systemd-run -p "ImportCredential=test.creds.*" \
+ --unit=test-54-ImportCredential.service \
+ -p DynamicUser=1 \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
+ '${CREDENTIALS_DIRECTORY}/test.creds.second' \
+ '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
+cmp /tmp/ts54-concat <(echo -n abc)
+
+# Now test encrypted credentials (only supported when built with OpenSSL though)
+if systemctl --version | grep -q -- +OPENSSL ; then
+ echo -n $RANDOM >/tmp/test-54-plaintext
+ systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
+ systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
+
+ systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
+
+ echo -n $RANDOM >/tmp/test-54-plaintext
+ systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
+ systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
+
+ systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
+
+ rm /tmp/test-54-plaintext /tmp/test-54-ciphertext
+fi
+
+# https://github.com/systemd/systemd/issues/27275
+systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
+ -p 'ExecStartPre=true' \
+ -p 'ExecStartPre=systemd-creds cat os' \
+ --unit=test-54-exec-start-pre.service \
+ --wait \
+ --pipe \
+ true | cmp /etc/os-release
+
+# https://github.com/systemd/systemd/issues/31194
+systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
+ -p 'ExecStartPost=systemd-creds cat os' \
+ --unit=test-54-exec-start-post.service \
+ --service-type=oneshot --wait --pipe \
+ true | cmp /etc/os-release
+
+# https://github.com/systemd/systemd/pull/24734#issuecomment-1925440546
+# Also ExecStartPre= should be able to update creds
+dd if=/dev/urandom of=/tmp/cred-huge bs=600K count=1
+chmod 777 /tmp/cred-huge
+systemd-run -p ProtectSystem=full \
+ -p 'LoadCredential=huge:/tmp/cred-huge' \
+ -p 'ExecStartPre=true' \
+ -p 'ExecStartPre=bash -c "echo fresh >/tmp/cred-huge"' \
+ --unit=test-54-huge-cred.service \
+ --wait --pipe \
+ systemd-creds cat huge | cmp - <(echo "fresh")
+rm /tmp/cred-huge
+
+echo stable >/tmp/cred-stable
+systemd-run -p 'LoadCredential=stable:/tmp/cred-stable' \
+ -p 'ExecStartPost=systemd-creds cat stable' \
+ --unit=test-54-stable.service \
+ --service-type=oneshot --wait --pipe \
+ bash -c "echo bogus >/tmp/cred-stable" | cmp - <(echo "stable")
+assert_eq "$(cat /tmp/cred-stable)" "bogus"
+rm /tmp/cred-stable
+
+if ! systemd-detect-virt -q -c ; then
+ # Validate that the credential we inserted via the initrd logic arrived
+ test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
+
+ # Check that the fstab credential logic worked
+ test -d /injected
+ grep -q /injected /proc/self/mountinfo
+
+ # Make sure the getty generator processed the credentials properly
+ systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
+fi
+
+# Decrypt/encrypt via varlink
+
+echo '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
+
+varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
+ varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
+
+cmp /tmp/vlcredsdata /tmp/vlcredsdata2
+rm /tmp/vlcredsdata /tmp/vlcredsdata2
+
+clean_usertest() {
+ rm -f /tmp/usertest.data /tmp/usertest.data
+}
+
+trap clean_usertest EXIT
+dd if=/dev/urandom of=/tmp/usertest.data bs=4096 count=1
+
+systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.cred
+
+systemd-creds decrypt --user /tmp/usertest.cred - | cmp /tmp/usertest.data
+
+# Decryption must fail if it's not done in user context
+(! systemd-creds decrypt /tmp/usertest.cred - )
+
+# Decryption must also fail if a different user is used
+(! systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - )
+
+# Try the reverse
+systemd-creds encrypt --user --uid=65534 /tmp/usertest.data /tmp/usertest.cred
+(! systemd-creds decrypt --user /tmp/usertest.cred - )
+systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - | cmp /tmp/usertest.data
+
+systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytest
+
+# Make sure we actually can decode this in user context
+systemctl start user@0.service
+XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Create a lot of memory pressure
+
+[Service]
+# A VERY small memory.high will cause the 'stress' (trying to use a lot of memory)
+# to throttle and be put under heavy pressure.
+MemoryHigh=3M
+Slice=TEST-55-OOMD-workload.slice
+ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=No memory pressure
+
+[Service]
+MemoryHigh=3M
+Slice=TEST-55-OOMD-workload.slice
+ExecStart=sleep infinity
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Create some memory pressure
+
+[Service]
+MemoryHigh=12M
+Slice=TEST-55-OOMD-workload.slice
+ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test slice for memory pressure kills
+
+[Slice]
+CPUAccounting=true
+MemoryAccounting=true
+IOAccounting=true
+TasksAccounting=true
+ManagedOOMMemoryPressure=kill
+ManagedOOMMemoryPressureLimit=20%
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-55-OOMD
+After=user@4711.service
+Wants=user@4711.service
+
+[Service]
+ExecStartPre=rm -f /failed /skipped /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+ . "$(dirname "$0")"/util.sh
+
+. /etc/os-release
+# OpenSUSE does not have the stress tool packaged. It does have stress-ng but the stress-ng does not support
+# --vm-stride which this test uses.
+if [[ "$ID" =~ "opensuse" ]]; then
+ echo "Skipping due to missing stress package in OpenSUSE" >>/skipped
+ exit 77
+fi
+
+systemd-analyze log-level debug
+
+# Ensure that the init.scope.d drop-in is applied on boot
+test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max"
+
+# Loose checks to ensure the environment has the necessary features for systemd-oomd
+[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped
+[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
+[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
+if [[ -s /skipped ]]; then
+ exit 77
+fi
+
+rm -rf /run/systemd/system/TEST-55-OOMD-testbloat.service.d
+
+# Activate swap file if we are in a VM
+if systemd-detect-virt --vm --quiet; then
+ swapoff --all
+ if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then
+ btrfs filesystem mkswapfile -s 64M /swapfile
+ else
+ dd if=/dev/zero of=/swapfile bs=1M count=64
+ chmod 0600 /swapfile
+ mkswap /swapfile
+ fi
+
+ swapon /swapfile
+ swapon --show
+fi
+
+# Configure oomd explicitly to avoid conflicts with distro dropins
+mkdir -p /run/systemd/oomd.conf.d/
+cat >/run/systemd/oomd.conf.d/99-oomd-test.conf <<EOF
+[OOM]
+DefaultMemoryPressureDurationSec=2s
+EOF
+
+mkdir -p /run/systemd/system/-.slice.d/
+cat >/run/systemd/system/-.slice.d/99-oomd-test.conf <<EOF
+[Slice]
+ManagedOOMSwap=auto
+EOF
+
+mkdir -p /run/systemd/system/user@.service.d/
+cat >/run/systemd/system/user@.service.d/99-oomd-test.conf <<EOF
+[Service]
+ManagedOOMMemoryPressure=auto
+ManagedOOMMemoryPressureLimit=0%
+EOF
+
+mkdir -p /run/systemd/system/systemd-oomd.service.d/
+cat >/run/systemd/system/systemd-oomd.service.d/debug.conf <<EOF
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+systemctl daemon-reload
+
+# enable the service to ensure dbus-org.freedesktop.oom1.service exists
+# and D-Bus activation works
+systemctl enable systemd-oomd.service
+
+# if oomd is already running for some reasons, then restart it to make sure the above settings to be applied
+if systemctl is-active systemd-oomd.service; then
+ systemctl restart systemd-oomd.service
+fi
+
+if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
+ # If we're running with sanitizers, sd-executor might pull in quite a significant chunk of shared
+ # libraries, which in turn causes a lot of pressure that can put us in the front when sd-oomd decides to
+ # go on a killing spree. This fact is exacerbated further on Arch Linux which ships unstripped gcc-libs,
+ # so sd-executor pulls in over 30M of libs on startup. Let's make the MemoryHigh= limit a bit more
+ # generous when running with sanitizers to make the test happy.
+ systemctl edit --runtime --stdin --drop-in=99-MemoryHigh.conf TEST-55-OOMD-testchill.service <<EOF
+[Service]
+MemoryHigh=60M
+EOF
+ # Do the same for the user instance as well
+ mkdir -p /run/systemd/user/
+ cp -rfv /run/systemd/system/TEST-55-OOMD-testchill.service.d/ /run/systemd/user/
+else
+ # Ensure that we can start services even with a very low hard memory cap without oom-kills, but skip
+ # under sanitizers as they balloon memory usage.
+ systemd-run -t -p MemoryMax=10M -p MemorySwapMax=0 -p MemoryZSwapMax=0 /bin/true
+fi
+
+systemctl start TEST-55-OOMD-testchill.service
+systemctl start TEST-55-OOMD-testbloat.service
+
+# Verify systemd-oomd is monitoring the expected units
+timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-workload.slice"; do sleep 1; done'
+oomctl | grep "/TEST-55-OOMD-workload.slice"
+oomctl | grep "20.00%"
+oomctl | grep "Default Memory Pressure Duration: 2s"
+
+systemctl status TEST-55-OOMD-testchill.service
+
+# systemd-oomd watches for elevated pressure for 2 seconds before acting.
+# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
+for _ in {0..59}; do
+ if ! systemctl status TEST-55-OOMD-testbloat.service; then
+ break
+ fi
+ oomctl
+ sleep 2
+done
+
+# testbloat should be killed and testchill should be fine
+if systemctl status TEST-55-OOMD-testbloat.service; then exit 42; fi
+if ! systemctl status TEST-55-OOMD-testchill.service; then exit 24; fi
+
+# Make sure we also work correctly on user units.
+loginctl enable-linger testuser
+
+systemctl start --machine "testuser@.host" --user TEST-55-OOMD-testchill.service
+systemctl start --machine "testuser@.host" --user TEST-55-OOMD-testbloat.service
+
+# Verify systemd-oomd is monitoring the expected units
+# Try to avoid racing the oomctl output check by checking in a loop with a timeout
+timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-workload.slice"; do sleep 1; done'
+oomctl | grep -E "/user.slice.*/TEST-55-OOMD-workload.slice"
+oomctl | grep "20.00%"
+oomctl | grep "Default Memory Pressure Duration: 2s"
+
+systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testchill.service
+
+# systemd-oomd watches for elevated pressure for 2 seconds before acting.
+# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
+for _ in {0..59}; do
+ if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then
+ break
+ fi
+ oomctl
+ sleep 2
+done
+
+# testbloat should be killed and testchill should be fine
+if systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then exit 42; fi
+if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testchill.service; then exit 24; fi
+
+loginctl disable-linger testuser
+
+# only run this portion of the test if we can set xattrs
+if cgroupfs_supports_user_xattrs; then
+ sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
+
+ mkdir -p /run/systemd/system/TEST-55-OOMD-testbloat.service.d/
+ cat >/run/systemd/system/TEST-55-OOMD-testbloat.service.d/override.conf <<EOF
+[Service]
+ManagedOOMPreference=avoid
+EOF
+
+ systemctl daemon-reload
+ systemctl start TEST-55-OOMD-testchill.service
+ systemctl start TEST-55-OOMD-testmunch.service
+ systemctl start TEST-55-OOMD-testbloat.service
+
+ for _ in {0..59}; do
+ if ! systemctl status TEST-55-OOMD-testmunch.service; then
+ break
+ fi
+ oomctl
+ sleep 2
+ done
+
+ # testmunch should be killed since testbloat had the avoid xattr on it
+ if ! systemctl status TEST-55-OOMD-testbloat.service; then exit 25; fi
+ if systemctl status TEST-55-OOMD-testmunch.service; then exit 43; fi
+ if ! systemctl status TEST-55-OOMD-testchill.service; then exit 24; fi
+fi
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-58-REPART
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2317
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if ! command -v systemd-repart >/dev/null; then
+ echo "no systemd-repart" >/skipped
+ exit 77
+fi
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+export PAGER=cat
+
+# Disable use of special glyphs such as →
+export SYSTEMD_UTF8=0
+
+seed=750b6cd5c4ae4012a15e7be3c29e6a47
+
+if ! systemd-detect-virt --quiet --container; then
+ udevadm control --log-level debug
+fi
+
+machine="$(uname -m)"
+if [ "${machine}" = "x86_64" ]; then
+ root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
+ root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0
+ root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC
+ usr_guid=8484680C-9521-48C6-9C11-B0720656F69E
+ usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16
+ architecture="x86-64"
+elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
+ root_guid=44479540-F297-41B2-9AF7-D131D5F0458A
+ root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87
+ root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC
+ usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812
+ usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648
+ architecture="x86"
+elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
+ root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE
+ root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483
+ root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A
+ usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9
+ usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F
+ architecture="arm64"
+elif [ "${machine}" = "arm" ]; then
+ root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3
+ root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034
+ root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E
+ usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625
+ usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966
+ architecture="arm"
+elif [ "${machine}" = "loongarch64" ]; then
+ root_guid=77055800-792C-4F94-B39A-98C91B762BB6
+ root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8
+ root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56
+ usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F
+ usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7
+ architecture="loongarch64"
+elif [ "${machine}" = "ia64" ]; then
+ root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97
+ root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D
+ root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38
+ usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA
+ usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241
+ architecture="ia64"
+elif [ "${machine}" = "s390x" ]; then
+ root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306
+ root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E
+ root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB
+ usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA
+ usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5
+ architecture="s390x"
+elif [ "${machine}" = "ppc64le" ]; then
+ root_guid=C31C45E6-3F39-412E-80FB-4809C4980599
+ root_uuid=061E67A1-092F-482F-8150-B525D50D6654
+ root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739
+ usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C
+ usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB
+ architecture="ppc64-le"
+else
+ echo "Unexpected uname -m: ${machine} in TEST-58-REPART.sh, please fix me"
+ exit 1
+fi
+
+testcase_basic() {
+ local defs imgs output
+ local loop volume
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** 1. create an empty image ***"
+
+ systemd-repart --offline="$OFFLINE" \
+ --empty=create \
+ --size=1G \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118"
+
+ echo "*** 2. Testing with root, root2, home, and swap ***"
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+EOF
+
+ ln -s root.conf "$defs/root2.conf"
+
+ tee "$defs/home.conf" <<EOF
+[Partition]
+Type=home
+Label=home-first
+Label=home-always-too-long-xxxxxxxxxxxxxx-%v
+EOF
+
+ tee "$defs/swap.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+PaddingMinBytes=92M
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ --include-partitions=home,swap \
+ --offline="$OFFLINE" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --empty=create \
+ --size=50M \
+ --seed="$seed" \
+ --include-partitions=root,home \
+ "$imgs/qqq"
+
+ sfdisk -d "$imgs/qqq" | grep -v -e 'sector-size' -e '^$'
+
+ systemd-repart --offline="$OFFLINE" \
+ --empty=create \
+ --size=1G \
+ --dry-run=no \
+ --seed="$seed" \
+ --definitions "" \
+ --copy-from="$imgs/qqq" \
+ --copy-from="$imgs/qqq" \
+ "$imgs/copy"
+
+ output=$(sfdisk -d "$imgs/copy" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/copy
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/copy1 : start= 2048, size= 33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/copy2 : start= 35480, size= 33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/copy3 : start= 68920, size= 33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/copy4 : start= 102360, size= 33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/copy5 : start= 135792, size= 33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/copy6 : start= 169232, size= 33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\""
+
+ rm "$imgs/qqq" "$imgs/copy" # Save disk space
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ --empty=force \
+ --defer-partitions=home,root \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+ echo "*** 3. Testing with root, root2, home, swap, and another partition ***"
+
+ tee "$defs/swap.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+EOF
+
+ tee "$defs/extra.conf" <<EOF
+[Partition]
+Type=linux-generic
+Label=custom_label
+UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
+EOF
+
+ echo "Label=ignored_label" >>"$defs/home.conf"
+ echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf"
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
+
+ echo "*** 4. Resizing to 2G ***"
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --size=2G \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 4194270
+$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
+
+ echo "*** 5. Testing with root, root2, home, swap, another partition, and partition copy ***"
+
+ dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240
+
+ tee "$defs/extra2.conf" <<EOF
+[Partition]
+Type=linux-generic
+Label=block-copy
+UUID=2a1d97e1d0a346cca26eadc643926617
+CopyBlocks=$imgs/block-copy
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --size=3G \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 6291422
+$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
+$imgs/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\""
+
+ cmp --bytes=$((4096*10240)) --ignore-initial=0:$((512*4194264)) "$imgs/block-copy" "$imgs/zzz"
+
+ echo "*** 6. Testing Format=/Encrypt=/CopyFiles= ***"
+
+ tee "$defs/extra3.conf" <<EOF
+[Partition]
+Type=linux-generic
+Label=luks-format-copy
+UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
+Format=ext4
+Encrypt=yes
+CopyFiles=$defs:/def
+SizeMinBytes=48M
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --size=auto \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+ assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 6389726
+$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
+$imgs/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\"
+$imgs/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\""
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping encrypt mount tests in container."
+ return
+ fi
+
+ loop="$(losetup -P --show --find "$imgs/zzz")"
+ udevadm wait --timeout 60 --settle "${loop:?}"
+
+ volume="test-repart-$RANDOM"
+
+ touch "$imgs/empty-password"
+ cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p7" "$volume"
+ mkdir -p "$imgs/mount"
+ mount -t ext4 "/dev/mapper/$volume" "$imgs/mount"
+ # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount
+ cryptsetup close --deferred "$volume"
+ losetup -d "$loop"
+ diff -r "$imgs/mount/def" "$defs" >/dev/null
+ umount "$imgs/mount"
+}
+
+testcase_dropin() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+EOF
+
+ mkdir -p "$defs/root.conf.d"
+ tee "$defs/root.conf.d/override1.conf" <<EOF
+[Partition]
+Label=label1
+SizeMaxBytes=32M
+EOF
+
+ tee "$defs/root.conf.d/override2.conf" <<EOF
+[Partition]
+Label=label2
+EOF
+
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --empty=create \
+ --size=100M \
+ --json=pretty \
+ "$imgs/zzz")
+
+ diff -u <(echo "$output") - <<EOF
+[
+ {
+ "type" : "swap",
+ "label" : "label2",
+ "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+ "partno" : 0,
+ "file" : "$defs/root.conf",
+ "node" : "$imgs/zzz1",
+ "offset" : 1048576,
+ "old_size" : 0,
+ "raw_size" : 33554432,
+ "size" : "-> 32.0M",
+ "old_padding" : 0,
+ "raw_padding" : 0,
+ "padding" : "-> 0B",
+ "activity" : "create",
+ "drop-in_files" : [
+ "$defs/root.conf.d/override1.conf",
+ "$defs/root.conf.d/override2.conf"
+ ]
+ }
+]
+EOF
+}
+
+testcase_multiple_definitions() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ mkdir -p "$defs/1"
+ tee "$defs/1/root1.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
+Label=label1
+EOF
+
+ mkdir -p "$defs/2"
+ tee "$defs/2/root2.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+Label=label2
+EOF
+
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs/1" \
+ --definitions="$defs/2" \
+ --empty=create \
+ --size=100M \
+ --json=pretty \
+ "$imgs/zzz")
+
+ diff -u <(echo "$output") - <<EOF
+[
+ {
+ "type" : "swap",
+ "label" : "label1",
+ "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
+ "partno" : 0,
+ "file" : "$defs/1/root1.conf",
+ "node" : "$imgs/zzz1",
+ "offset" : 1048576,
+ "old_size" : 0,
+ "raw_size" : 33554432,
+ "size" : "-> 32.0M",
+ "old_padding" : 0,
+ "raw_padding" : 0,
+ "padding" : "-> 0B",
+ "activity" : "create"
+ },
+ {
+ "type" : "swap",
+ "label" : "label2",
+ "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+ "partno" : 1,
+ "file" : "$defs/2/root2.conf",
+ "node" : "$imgs/zzz2",
+ "offset" : 34603008,
+ "old_size" : 0,
+ "raw_size" : 33554432,
+ "size" : "-> 32.0M",
+ "old_padding" : 0,
+ "raw_padding" : 0,
+ "padding" : "-> 0B",
+ "activity" : "create"
+ }
+]
+EOF
+}
+
+testcase_copy_blocks() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** First, create a disk image and verify its in order ***"
+
+ tee "$defs/esp.conf" <<EOF
+[Partition]
+Type=esp
+SizeMinBytes=10M
+Format=vfat
+EOF
+
+ tee "$defs/usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+SizeMinBytes=10M
+Format=ext4
+ReadOnly=yes
+EOF
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+SizeMinBytes=10M
+Format=ext4
+MakeDirectories=/usr /efi
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --empty=create \
+ --size=auto \
+ --seed="$seed" \
+ "$imgs/zzz"
+
+ output=$(sfdisk --dump "$imgs/zzz")
+
+ assert_in "$imgs/zzz1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name=\"esp\"" "$output"
+ assert_in "$imgs/zzz2 : start= 22528, size= 20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
+ assert_in "$imgs/zzz3 : start= 43008, size= 20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output"
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping second part of copy blocks tests in container."
+ return
+ fi
+
+ echo "*** Second, create another image with CopyBlocks=auto ***"
+
+ tee "$defs/esp.conf" <<EOF
+[Partition]
+Type=esp
+CopyBlocks=auto
+EOF
+
+ tee "$defs/usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+ReadOnly=yes
+CopyBlocks=auto
+EOF
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyBlocks=auto
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --empty=create \
+ --size=auto \
+ --seed="$seed" \
+ --image="$imgs/zzz" \
+ "$imgs/yyy"
+
+ cmp "$imgs/zzz" "$imgs/yyy"
+}
+
+testcase_unaligned_partition() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** Operate on an image with unaligned partition ***"
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+EOF
+
+ truncate -s 10g "$imgs/unaligned"
+ sfdisk "$imgs/unaligned" <<EOF
+label: gpt
+
+start=2048, size=69044
+start=71092, size=3591848
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/unaligned"
+
+ output=$(sfdisk --dump "$imgs/unaligned")
+
+ assert_in "$imgs/unaligned1 : start= 2048, size= 69044," "$output"
+ assert_in "$imgs/unaligned2 : start= 71092, size= 3591848," "$output"
+ assert_in "$imgs/unaligned3 : start= 3662944, size= 17308536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
+}
+
+testcase_issue_21817() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** testcase for #21817 ***"
+
+ tee "$defs/test.conf" <<EOF
+[Partition]
+Type=root
+EOF
+
+ truncate -s 100m "$imgs/21817.img"
+ sfdisk "$imgs/21817.img" <<EOF
+label: gpt
+
+size=50M, type=${root_guid}
+,
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --pretty=yes \
+ --definitions "$imgs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/21817.img"
+
+ output=$(sfdisk --dump "$imgs/21817.img")
+
+ assert_in "$imgs/21817.img1 : start= 2048, size= 102400, type=${root_guid}," "$output"
+ # Accept both unpadded (pre-v2.38 util-linux) and padded (v2.38+ util-linux) sizes
+ assert_in "$imgs/21817.img2 : start= 104448, size= (100319| 98304)," "$output"
+}
+
+testcase_issue_24553() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** testcase for #24553 ***"
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+SizeMinBytes=10G
+SizeMaxBytes=120G
+EOF
+
+ tee "$imgs/partscript" <<EOF
+label: gpt
+label-id: C9FFE979-A415-C449-B729-78C7AA664B10
+unit: sectors
+first-lba: 40
+
+start=40, size=524288, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=F2E89C8A-DC5D-4C4C-A29C-6CFB643B74FD, name="ESP System Partition"
+start=524328, size=14848000, type=${root_guid}, uuid=${root_uuid}, name="root-${architecture}"
+EOF
+
+ echo "*** 1. Operate on a small image compared with SizeMinBytes= ***"
+ truncate -s 8g "$imgs/zzz"
+ sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+ # This should fail, but not trigger assertions.
+ assert_rc 1 systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
+
+ output=$(sfdisk --dump "$imgs/zzz")
+ assert_in "$imgs/zzz2 : start= 524328, size= 14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+
+ echo "*** 2. Operate on an larger image compared with SizeMinBytes= ***"
+ rm -f "$imgs/zzz"
+ truncate -s 12g "$imgs/zzz"
+ sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+ # This should succeed.
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
+
+ output=$(sfdisk --dump "$imgs/zzz")
+ assert_in "$imgs/zzz2 : start= 524328, size= 24641456, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+
+ echo "*** 3. Multiple partitions with Priority= (small disk) ***"
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+SizeMinBytes=10G
+SizeMaxBytes=120G
+Priority=100
+EOF
+
+ tee "$defs/usr.conf" <<EOF
+[Partition]
+Type=usr
+SizeMinBytes=10M
+Priority=10
+EOF
+
+ rm -f "$imgs/zzz"
+ truncate -s 8g "$imgs/zzz"
+ sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+ # This should also succeed, but root is not extended.
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
+
+ output=$(sfdisk --dump "$imgs/zzz")
+ assert_in "$imgs/zzz2 : start= 524328, size= 14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+ assert_in "$imgs/zzz3 : start= 15372328, size= 1404848, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
+
+ echo "*** 4. Multiple partitions with Priority= (large disk) ***"
+ rm -f "$imgs/zzz"
+ truncate -s 12g "$imgs/zzz"
+ sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+ # This should also succeed, and root is extended.
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
+
+ output=$(sfdisk --dump "$imgs/zzz")
+ assert_in "$imgs/zzz2 : start= 524328, size= 20971520, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+ assert_in "$imgs/zzz3 : start= 21495848, size= 3669936, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
+}
+
+testcase_zero_uuid() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** Test image with zero UUID ***"
+
+ tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+UUID=null
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ "$imgs/zero"
+
+ output=$(sfdisk --dump "$imgs/zero")
+
+ assert_in "$imgs/zero1 : start= 2048, size= 20480, type=${root_guid}, uuid=00000000-0000-0000-0000-000000000000" "$output"
+}
+
+testcase_verity() {
+ local defs imgs output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** dm-verity ***"
+
+ tee "$defs/verity-data.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=${defs}
+Verity=data
+VerityMatchKey=root
+Minimize=guess
+EOF
+
+ tee "$defs/verity-hash.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity
+Verity=hash
+VerityMatchKey=root
+Minimize=yes
+EOF
+
+ tee "$defs/verity-sig.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity-sig
+Verity=signature
+VerityMatchKey=root
+EOF
+
+ # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
+ tee >"$defs/verity.openssl.cnf" <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = DE
+ST = Test State
+L = Test Locality
+O = Org Name
+OU = Org Unit Name
+CN = Common Name
+emailAddress = test@email.com
+EOF
+
+ openssl req \
+ -config "$defs/verity.openssl.cnf" \
+ -new -x509 \
+ -newkey rsa:1024 \
+ -keyout "$defs/verity.key" \
+ -out "$defs/verity.crt" \
+ -days 365 \
+ -nodes
+
+ mkdir -p /run/verity.d
+ ln -sf "$defs/verity.crt" /run/verity.d/ok.crt
+
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ --private-key="$defs/verity.key" \
+ --certificate="$defs/verity.crt" \
+ "$imgs/verity")
+
+ drh=$(jq -r ".[] | select(.type == \"root-${architecture}\") | .roothash" <<<"$output")
+ hrh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<<"$output")
+ srh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity-sig\") | .roothash" <<<"$output")
+
+ assert_eq "$drh" "$hrh"
+ assert_eq "$hrh" "$srh"
+
+ # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic)
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping verity test dissect part in container."
+ return
+ fi
+
+ systemd-dissect "$imgs/verity" --root-hash "$drh"
+ systemd-dissect "$imgs/verity" --root-hash "$drh" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"'
+ systemd-dissect "$imgs/verity" --root-hash "$drh" -M "$imgs/mnt"
+ systemd-dissect -U "$imgs/mnt"
+}
+
+testcase_verity_explicit_block_size() {
+ local defs imgs loop
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping verity block size tests in container."
+ return
+ fi
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** varying-dm-verity-block-sizes ***"
+
+ tee "$defs/verity-data.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=${defs}
+Verity=data
+VerityMatchKey=root
+Minimize=guess
+EOF
+
+ tee "$defs/verity-hash.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity
+Verity=hash
+VerityMatchKey=root
+VerityHashBlockSizeBytes=1024
+VerityDataBlockSizeBytes=4096
+Minimize=yes
+EOF
+
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ "$imgs/verity"
+
+ loop="$(losetup --partscan --show --find "$imgs/verity")"
+
+ # Make sure the loopback device gets cleaned up
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR
+
+ udevadm wait --timeout 60 --settle "${loop:?}"
+
+ # Check that the verity block sizes are as expected
+ veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096'
+ veritysetup dump "${loop}p2" | grep 'Hash block size:' | grep -q '1024'
+}
+
+testcase_exclude_files() {
+ local defs imgs root output
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ root="$(mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs' '$root'" RETURN
+ chmod 0755 "$defs"
+
+ echo "*** file exclusion ***"
+
+ touch "$root/abc"
+ mkdir "$root/usr"
+ touch "$root/usr/def"
+ touch "$root/usr/qed"
+ mkdir "$root/tmp"
+ touch "$root/tmp/prs"
+ mkdir "$root/proc"
+ touch "$root/proc/prs"
+ mkdir "$root/zzz"
+ mkdir "$root/zzz/usr"
+ touch "$root/zzz/usr/prs"
+ mkdir "$root/zzz/proc"
+ touch "$root/zzz/proc/prs"
+
+ tee "$defs/00-root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=/
+CopyFiles=/zzz:/
+CopyFiles=/:/oiu
+ExcludeFilesTarget=/oiu/usr
+EOF
+
+ tee "$defs/10-usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+CopyFiles=/usr:/
+ExcludeFiles=/usr/qed
+EOF
+
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ --root="$root" \
+ "$imgs/zzz")
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping issue 24786 test loop/mount parts in container."
+ return
+ fi
+
+ loop=$(losetup -P --show -f "$imgs/zzz")
+ udevadm wait --timeout 60 --settle "${loop:?}"
+
+ # Test that /usr/def did not end up in the root partition but other files did.
+ mkdir "$imgs/mnt"
+ mount -t ext4 "${loop}p1" "$imgs/mnt"
+ assert_rc 0 ls "$imgs/mnt/abc"
+ assert_rc 0 ls "$imgs/mnt/usr"
+ assert_rc 2 ls "$imgs/mnt/usr/def"
+
+ # Test that /zzz/usr/prs did not end up in the root partition under /usr but did end up in /zzz/usr/prs
+ assert_rc 2 ls "$imgs/mnt/usr/prs"
+ assert_rc 0 ls "$imgs/mnt/zzz/usr/prs"
+
+ # Test that /tmp/prs did not end up in the root partition but /tmp did.
+ assert_rc 0 ls "$imgs/mnt/tmp"
+ assert_rc 2 ls "$imgs/mnt/tmp/prs"
+
+ # Test that /usr/qed did not end up in the usr partition but /usr/def did.
+ mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
+ assert_rc 0 ls "$imgs/mnt/usr/def"
+ assert_rc 2 ls "$imgs/mnt/usr/qed"
+
+ # Test that /zzz/proc/prs did not end up in the root partition but /proc did.
+ assert_rc 0 ls "$imgs/mnt/proc"
+ assert_rc 2 ls "$imgs/mnt/proc/prs"
+
+ # Test that /zzz/usr/prs did not end up in the usr partition.
+ assert_rc 2 ls "$imgs/mnt/usr/prs"
+
+ # Test that /oiu/ and /oiu/zzz ended up in the root partition but /oiu/usr did not.
+ assert_rc 0 ls "$imgs/mnt/oiu"
+ assert_rc 0 ls "$imgs/mnt/oiu/zzz"
+ assert_rc 2 ls "$imgs/mnt/oiu/usr"
+
+ umount -R "$imgs/mnt"
+ losetup -d "$loop"
+}
+
+testcase_minimize() {
+ local defs imgs output
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping minimize test in container."
+ return
+ fi
+
+ echo "*** minimization ***"
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+
+ for format in ext4 vfat erofs; do
+ if ! command -v "mkfs.$format" >/dev/null; then
+ continue
+ fi
+
+ tee "$defs/root-$format.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+Format=${format}
+CopyFiles=${defs}
+Minimize=guess
+EOF
+ done
+
+ if command -v mksquashfs >/dev/null; then
+ tee "$defs/root-squashfs.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+Format=squashfs
+CopyFiles=${defs}
+Minimize=best
+EOF
+ fi
+
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ "$imgs/zzz")
+
+ # Check that we can dissect, mount and unmount a minimized image.
+
+ systemd-dissect "$imgs/zzz"
+ systemd-dissect "$imgs/zzz" -M "$imgs/mnt"
+ systemd-dissect -U "$imgs/mnt"
+}
+
+testcase_free_area_calculation() {
+ local defs imgs output
+
+ if ! command -v mksquashfs >/dev/null; then
+ echo "Skipping free area calculation test without squashfs."
+ return
+ fi
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+ chmod 0755 "$defs"
+
+ # https://github.com/systemd/systemd/issues/28225
+ echo "*** free area calculation ***"
+
+ tee "$defs/00-ESP.conf" <<EOF
+[Partition]
+Type = esp
+Label = ESP
+Format = vfat
+
+SizeMinBytes = 128M
+SizeMaxBytes = 128M
+
+# Sufficient for testing
+CopyFiles = /etc:/
+EOF
+
+ tee "$defs/10-os.conf" <<EOF
+[Partition]
+Type = root-${architecture}
+Label = test
+Format = squashfs
+
+Minimize = best
+# Sufficient for testing
+CopyFiles = /etc/:/
+
+VerityMatchKey = os
+Verity = data
+EOF
+
+ tee "$defs/11-os-verity.conf" <<EOF
+[Partition]
+Type = root-${architecture}-verity
+Label = test
+
+Minimize = best
+
+VerityMatchKey = os
+Verity = hash
+EOF
+
+ # Set sector size for VFAT to 512 bytes because there will not be enough FAT clusters otherwise
+ output1=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --sector-size=4096 \
+ --defer-partitions=esp \
+ --json=pretty \
+ "$imgs/zzz")
+
+ # The second invocation
+ output2=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=allow \
+ --size=auto \
+ --sector-size=4096 \
+ --defer-partitions=esp \
+ --json=pretty \
+ "$imgs/zzz")
+
+ diff -u <(echo "$output1" | grep -E "(offset|raw_size|raw_padding)") <(echo "$output2" | grep -E "(offset|raw_size|raw_padding)")
+}
+
+test_sector() {
+ local defs imgs output loop
+ local start size ratio
+ local sector="${1?}"
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping sector size tests in container."
+ return
+ fi
+
+ echo "*** sector sizes ***"
+
+ defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+
+ tee "$defs/a.conf" <<EOF
+[Partition]
+Type=root
+SizeMaxBytes=15M
+SizeMinBytes=15M
+EOF
+ tee "$defs/b.conf" <<EOF
+[Partition]
+Type=linux-generic
+Weight=250
+EOF
+
+ tee "$defs/c.conf" <<EOF
+[Partition]
+Type=linux-generic
+Weight=750
+EOF
+
+ truncate -s 100m "$imgs/$sector.img"
+ loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" )
+ udevadm wait --timeout 60 --settle "${loop:?}"
+
+ systemd-repart --offline="$OFFLINE" \
+ --pretty=yes \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --empty=require \
+ --dry-run=no \
+ "$loop"
+
+ sfdisk --verify "$loop"
+ output=$(sfdisk --dump "$loop")
+ losetup -d "$loop"
+
+ ratio=$(( sector / 512 ))
+ start=$(( 2048 / ratio ))
+ size=$(( 30720 / ratio ))
+ assert_in "${loop}p1 : start= *${start}, size= *${size}, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
+ start=$(( start + size ))
+ size=$(( 42992 / ratio ))
+ assert_in "${loop}p2 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DF71F5E3-080A-4D16-824B-18591B881380, name=\"linux-generic\"" "$output"
+ start=$(( start + size ))
+ size=$(( 129000 / ratio ))
+ assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output"
+}
+
+testcase_dropped_partitions() {
+ local workdir image defs
+
+ workdir="$(mktemp --directory "/tmp/test-repart.dropped-partitions.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '${workdir:?}'" RETURN
+
+ image="$workdir/image.img"
+ truncate -s 32M "$image"
+
+ defs="$workdir/defs"
+ mkdir "$defs"
+ echo -ne "[Partition]\nType=root\n" >"$defs/10-part1.conf"
+ echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf"
+ echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf"
+ echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf"
+
+ systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image"
+
+ sfdisk -q -l "$image"
+ [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]]
+}
+
+OFFLINE="yes"
+run_testcases
+
+# Online image builds need loop devices so we can't run them in nspawn.
+if ! systemd-detect-virt --container; then
+ OFFLINE="no"
+ run_testcases
+fi
+
+# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
+# must be powers of 2. Which leaves exactly four different ones to test on
+# typical hardware
+test_sector 512
+test_sector 1024
+test_sector 2048
+test_sector 4096
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-59-RELOADING-RESTART
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+fail() {
+ systemd-analyze log-level info
+ exit 1
+}
+
+# Wait for a service to enter a state within a timeout period, if it doesn't
+# enter the desired state within the timeout period then this function will
+# exit the test case with a non zero exit code.
+wait_on_state_or_fail() {
+ service=$1
+ expected_state=$2
+ timeout=$3
+
+ state=$(systemctl show "$service" --property=ActiveState --value)
+ while [ "$state" != "$expected_state" ]; do
+ if [ "$timeout" = "0" ]; then
+ fail
+ fi
+ timeout=$((timeout - 1))
+ sleep 1
+ state=$(systemctl show "$service" --property=ActiveState --value)
+ done
+}
+
+systemd-analyze log-level debug
+
+
+cat >/run/systemd/system/testservice-fail-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Normal exit
+
+[Service]
+Type=notify
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-fail-restart-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Restart=on-failure
+
+[Service]
+Type=notify
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+Restart=on-failure
+StartLimitBurst=1
+EOF
+
+
+cat >/run/systemd/system/testservice-abort-restart-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Restart=on-abort
+
+[Service]
+Type=notify
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
+Restart=on-abort
+EOF
+
+systemctl daemon-reload
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. Ensure it enters failed state and does not linger in reloading
+# state.
+systemctl start testservice-fail-59.service
+wait_on_state_or_fail "testservice-fail-59.service" "failed" "30"
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. It should automatically restart on failure. Ensure it enters failed
+# state and does not linger in reloading state.
+systemctl start testservice-fail-restart-59.service
+wait_on_state_or_fail "testservice-fail-restart-59.service" "failed" "30"
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. It should automatically restart on abort. It will sleep for 5s
+# to allow us to send it a SIGABRT. Ensure the service enters the failed state
+# and does not linger in reloading state.
+systemctl start testservice-abort-restart-59.service
+systemctl --signal=SIGABRT kill testservice-abort-restart-59.service
+wait_on_state_or_fail "testservice-abort-restart-59.service" "failed" "30"
+
+systemd-analyze log-level info
+
+# Test that rate-limiting daemon-reload works
+mkdir -p /run/systemd/system.conf.d/
+cat >/run/systemd/system.conf.d/50-test-59-reload.conf <<EOF
+[Manager]
+ReloadLimitIntervalSec=9
+ReloadLimitBurst=3
+EOF
+
+# Pick up the new config
+systemctl daemon-reload
+
+# The timeout will hit (and the test will fail) if the reloads are not rate-limited
+timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reload
+
+# Same test for reexec, but we wait here
+timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reexec
+
+# Let's now test the notify-reload logic
+
+cat >/run/notify-reload-test.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+set -o pipefail
+
+EXIT_STATUS=88
+LEAVE=0
+
+function reload() {
+ systemd-notify --reloading --status="Adding 11 to exit status"
+ EXIT_STATUS=\$((EXIT_STATUS + 11))
+ systemd-notify --ready --status="Back running"
+}
+
+function leave() {
+ systemd-notify --stopping --status="Adding 7 to exit status"
+ EXIT_STATUS=\$((EXIT_STATUS + 7))
+ LEAVE=1
+ return 0
+}
+
+trap reload SIGHUP
+trap leave SIGTERM
+
+systemd-notify --ready
+systemd-notify --status="Running now"
+
+while [ \$LEAVE = 0 ] ; do
+ sleep 1
+done
+
+systemd-notify --status="Adding 3 to exit status"
+EXIT_STATUS=\$((EXIT_STATUS + 3))
+exit \$EXIT_STATUS
+EOF
+
+chmod +x /run/notify-reload-test.sh
+
+systemd-analyze log-level debug
+
+systemd-run --unit notify-reload-test -p Type=notify-reload -p KillMode=process /run/notify-reload-test.sh
+systemctl reload notify-reload-test
+systemctl stop notify-reload-test
+
+test "$(systemctl show -p ExecMainStatus --value notify-reload-test)" = 109
+
+systemctl reset-failed notify-reload-test
+rm /run/notify-reload-test.sh
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-60-MOUNT-RATELIMIT
+
+[Service]
+Type=oneshot
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+teardown_test_dependencies() (
+ set +eux
+
+ if mountpoint /tmp/deptest; then
+ umount /tmp/deptest
+ fi
+
+ if [[ -n "${LOOP}" ]]; then
+ losetup -d "${LOOP}" || :
+ fi
+ if [[ -n "${LOOP_0}" ]]; then
+ losetup -d "${LOOP_0}" || :
+ fi
+ if [[ -n "${LOOP_1}" ]]; then
+ losetup -d "${LOOP_1}" || :
+ fi
+
+ rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
+ rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img
+
+ rm -f /run/systemd/system/tmp-deptest.mount
+ systemctl daemon-reload
+
+ return 0
+)
+
+setup_loop() {
+ truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
+ sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
+label:gpt
+
+name="loop${1?}-part1"
+EOF
+ LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
+ udevadm wait --settle --timeout=10 "${LOOP}"
+ udevadm lock --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
+}
+
+check_dependencies() {
+ local escaped_0 escaped_1 after
+
+ escaped_0=$(systemd-escape -p "${LOOP_0}p1")
+ escaped_1=$(systemd-escape -p "${LOOP_1}p1")
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+
+ # mount LOOP_0
+ mount -t ext4 "${LOOP_0}p1" /tmp/deptest
+ sleep 1
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_in "local-fs-pre.target" "$after"
+ assert_not_in "remote-fs-pre.target" "$after"
+ assert_not_in "network.target" "$after"
+ assert_in "${escaped_0}.device" "$after"
+ assert_in "blockdev@${escaped_0}.target" "$after"
+ assert_not_in "${escaped_1}.device" "$after"
+ assert_not_in "blockdev@${escaped_1}.target" "$after"
+ umount /tmp/deptest
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+
+ # mount LOOP_1 (using fake _netdev option)
+ mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
+ sleep 1
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ assert_not_in "${escaped_0}.device" "$after"
+ assert_not_in "blockdev@${escaped_0}.target" "$after"
+ assert_in "${escaped_1}.device" "$after"
+ assert_in "blockdev@${escaped_1}.target" "$after"
+ umount /tmp/deptest
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+
+ # mount tmpfs
+ mount -t tmpfs tmpfs /tmp/deptest
+ sleep 1
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_in "local-fs-pre.target" "$after"
+ assert_not_in "remote-fs-pre.target" "$after"
+ assert_not_in "network.target" "$after"
+ assert_not_in "${escaped_0}.device" "$after"
+ assert_not_in "blockdev@${escaped_0}.target" "$after"
+ assert_not_in "${escaped_1}.device" "$after"
+ assert_not_in "blockdev@${escaped_1}.target" "$after"
+ umount /tmp/deptest
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+}
+
+test_dependencies() {
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping test_dependencies in container"
+ return
+ fi
+
+ trap teardown_test_dependencies RETURN
+
+ setup_loop 0
+ LOOP_0="${LOOP}"
+ LOOP=
+ setup_loop 1
+ LOOP_1="${LOOP}"
+ LOOP=
+
+ mkdir -p /tmp/deptest
+
+ # without .mount file
+ check_dependencies
+
+ # create .mount file
+ mkdir -p /run/systemd/system
+ cat >/run/systemd/system/tmp-deptest.mount <<EOF
+[Mount]
+Where=/tmp/deptest
+What=192.168.0.1:/tmp/mnt
+Type=nfs
+EOF
+ systemctl daemon-reload
+
+ # with .mount file
+ check_dependencies
+}
+
+test_issue_20329() {
+ local tmpdir unit
+ tmpdir="$(mktemp -d)"
+ unit=$(systemd-escape --suffix mount --path "$tmpdir")
+
+ # Set up test mount unit
+ cat >/run/systemd/system/"$unit" <<EOF
+[Mount]
+What=tmpfs
+Where=$tmpdir
+Type=tmpfs
+Options=defaults,nofail
+EOF
+
+ # Start the unit
+ systemctl daemon-reload
+ systemctl start "$unit"
+
+ [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
+ echo >&2 "Test mount \"$unit\" unit isn't mounted"
+ return 1
+ }
+ mountpoint -q "$tmpdir"
+
+ trap 'systemctl stop $unit' RETURN
+
+ # Trigger the mount ratelimiting
+ cd "$(mktemp -d)"
+ mkdir foo
+ for _ in {1..50}; do
+ mount --bind foo foo
+ umount foo
+ done
+
+ # Unmount the test mount and start it immediately again via systemd
+ umount "$tmpdir"
+ systemctl start "$unit"
+
+ # Make sure it is seen as mounted by systemd and it actually is mounted
+ [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
+ echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state"
+ return 1
+ }
+
+ mountpoint -q "$tmpdir" || {
+ echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted"
+ return 1
+ }
+}
+
+test_issue_23796() {
+ local mount_path mount_mytmpfs
+
+ mount_path="$(command -v mount 2>/dev/null)"
+ mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
+ cat >"$mount_mytmpfs" <<EOF
+#!/bin/bash
+sleep ".\$RANDOM"
+exec -- $mount_path -t tmpfs tmpfs "\$2"
+EOF
+ chmod +x "$mount_mytmpfs"
+
+ mkdir -p /run/systemd/system
+ cat >/run/systemd/system/tmp-hoge.mount <<EOF
+[Mount]
+What=mytmpfs
+Where=/tmp/hoge
+Type=mytmpfs
+EOF
+
+ # shellcheck disable=SC2064
+ trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN
+
+ for _ in {1..10}; do
+ systemctl --no-block start tmp-hoge.mount
+ sleep ".$RANDOM"
+ systemctl daemon-reexec
+
+ sleep 1
+
+ if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
+ journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then
+ exit 1
+ fi
+
+ systemctl stop tmp-hoge.mount
+ done
+}
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+NUM_DIRS=20
+
+# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
+LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
+LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"
+TS="$(date '+%H:%M:%S')"
+
+mkdir -p "$LONGPATH"
+mount -t tmpfs tmpfs "$LONGPATH"
+systemctl daemon-reload
+
+# check that unit is active(mounted)
+systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted
+
+# check that relevant part of journal doesn't contain any errors related to unit
+[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]
+
+# check that we can successfully stop the mount unit
+systemctl stop "$LONGPATH"
+rm -rf "$LONGPATH"
+
+# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
+
+for ((i = 0; i < NUM_DIRS; i++)); do
+ mkdir "/tmp/meow${i}"
+done
+
+TS="$(date '+%H:%M:%S')"
+
+for ((i = 0; i < NUM_DIRS; i++)); do
+ mount -t tmpfs tmpfs "/tmp/meow${i}"
+done
+
+systemctl daemon-reload
+systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
+
+for ((i = 0; i < NUM_DIRS; i++)); do
+ umount "/tmp/meow${i}"
+done
+
+# Figure out if we have entered the rate limit state.
+# If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
+if timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) entered rate limit'; do sleep 1; done"; then
+ timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) left rate limit'; do sleep 1; done"
+fi
+
+# Verify that the mount units are always cleaned up at the end.
+# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
+timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
+
+# test for issue #19983 and #23552.
+test_dependencies
+
+# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
+test_issue_20329
+
+# test for reexecuting with background mount job
+test_issue_23796
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-all-pings-work
+[Service]
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-allow-list
+[Service]
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth1
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-deny-list
+[Service]
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=~veth0
+RestrictNetworkInterfaces=~veth1
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-empty-assignment
+[Service]
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-invert-assignment
+[Service]
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth0 veth1
+RestrictNetworkInterfaces=~veth0
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-altname
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars
+RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+setup() {
+ systemd-analyze log-level debug
+
+ for i in {0..3};
+ do
+ ip netns del "ns${i}" || true
+ ip link del "veth${i}" || true
+ ip netns add "ns${i}"
+ ip link add "veth${i}" type veth peer name "veth${i}_"
+ ip link set "veth${i}_" netns "ns${i}"
+ ip -n "ns${i}" link set dev "veth${i}_" up
+ ip -n "ns${i}" link set dev lo up
+ ip -n "ns${i}" addr add "192.168.113."$((4*i+1))/30 dev "veth${i}_"
+ ip link set dev "veth${i}" up
+ ip link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars"
+ ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}"
+ done
+}
+
+# shellcheck disable=SC2317
+teardown() {
+ set +e
+
+ for i in {0..3}; do
+ ip netns del "ns${i}"
+ ip link del "veth${i}"
+ done
+
+ systemd-analyze log-level info
+}
+
+if systemd-analyze compare-versions "$(uname -r)" lt 5.7; then
+ echo "kernel is not 5.7+" >>/skipped
+ exit 77
+fi
+
+if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
+ echo "bpf-framework is disabled" >>/skipped
+ exit 77
+fi
+
+trap teardown EXIT
+setup
+
+systemctl start --wait TEST-62-RESTRICT-IFACES-1.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-2.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-3.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-4.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-5.service
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-63-PATH
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemctl log-level debug
+
+# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
+# the trigger-limit-hit error.
+rm -f /tmp/nonexistent
+systemctl start test63.path
+touch /tmp/test63
+
+# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
+# shellcheck disable=SC2016
+timeout 30 bash -c 'until test "$(systemctl show test63.path -P ActiveState)" = failed; do sleep .2; done'
+test "$(systemctl show test63.service -P ActiveState)" = inactive
+test "$(systemctl show test63.service -P Result)" = success
+test "$(systemctl show test63.path -P Result)" = trigger-limit-hit
+
+# Test that starting the service manually doesn't affect the path unit.
+rm -f /tmp/test63
+systemctl reset-failed
+systemctl start test63.path
+systemctl start test63.service
+test "$(systemctl show test63.service -P ActiveState)" = inactive
+test "$(systemctl show test63.service -P Result)" = success
+test "$(systemctl show test63.path -P ActiveState)" = active
+test "$(systemctl show test63.path -P Result)" = success
+
+# Test that glob matching works too, with $TRIGGER_PATH
+systemctl start test63-glob.path
+touch /tmp/test63-glob-foo
+timeout 60 bash -c 'until systemctl -q is-active test63-glob.service; do sleep .2; done'
+test "$(systemctl show test63-glob.service -P ActiveState)" = active
+test "$(systemctl show test63-glob.service -P Result)" = success
+
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}'
+
+systemctl stop test63-glob.path test63-glob.service
+
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}'
+
+# tests for issue https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
+rm -f /tmp/hoge
+systemctl start test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_not_in "test63-issue-24577.service" "$output"
+assert_not_in "test63-issue-24577-dep.service" "$output"
+
+touch /tmp/hoge
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_in "test63-issue-24577.service" "$output"
+assert_in "test63-issue-24577-dep.service" "$output"
+
+# even if the service is stopped, it will be soon retriggered.
+systemctl stop test63-issue-24577.service
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_in "test63-issue-24577.service" "$output"
+assert_in "test63-issue-24577-dep.service" "$output"
+
+rm -f /tmp/hoge
+systemctl stop test63-issue-24577.service
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_not_in "test63-issue-24577.service" "$output"
+assert_in "test63-issue-24577-dep.service" "$output"
+
+# Test for race condition fixed by https://github.com/systemd/systemd/pull/30768
+# Here's the schedule of events that we to happen during this test:
+# (This test) (The service)
+# .path unit monitors /tmp/copyme for changes
+# Take lock on /tmp/noexeit ↓
+# Write to /tmp/copyme ↓
+# Wait for deactivating Started
+# ↓ Copies /tmp/copyme to /tmp/copied
+# ↓ Tells manager it's shutting down
+# Ensure service did the copy Tries to lock /tmp/noexit and blocks
+# Write to /tmp/copyme ↓
+#
+# Now at this point the test can diverge. If we regress, this second write is
+# missed and we'll see:
+# ... (second write) ... (blocked)
+# Drop lock on /tmp/noexit ↓
+# Wait for service to do copy Unblocks and exits
+# ↓ (dead)
+# ↓
+# (timeout)
+# Test fails
+#
+# Otherwise, we'll see:
+# ... (second write) ... (blocked)
+# Drop lock on /tmp/noexit ↓ and .path unit queues a new start job
+# Wait for service to do copy Unblocks and exits
+# ↓ Starts again b/c of queued job
+# ↓ Copies again
+# Test Passes
+systemctl start test63-pr-30768.path
+exec {lock}<>/tmp/noexit
+flock -e $lock
+echo test1 > /tmp/copyme
+# shellcheck disable=SC2016
+timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P ActiveState)" = deactivating; do sleep .2; done'
+diff /tmp/copyme /tmp/copied
+echo test2 > /tmp/copyme
+exec {lock}<&-
+timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied; do sleep .2; done'
+
+systemctl log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-64-UDEV-STORAGE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: ts=4 sw=4 tw=0 et:
+
+set -eux
+set -o pipefail
+
+# Check if all symlinks under /dev/disk/ are valid
+# shellcheck disable=SC2120
+helper_check_device_symlinks() {(
+ set +x
+
+ local dev link path paths target
+
+ [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper")
+
+ # Check if all given paths are valid
+ for path in "${paths[@]}"; do
+ if ! test -e "$path"; then
+ echo >&2 "Path '$path' doesn't exist"
+ return 1
+ fi
+ done
+
+ while read -r link; do
+ target="$(readlink -f "$link")"
+ # Both checks should do virtually the same thing, but check both to be
+ # on the safe side
+ if [[ ! -e "$link" || ! -e "$target" ]]; then
+ echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist"
+ return 1
+ fi
+
+ # Check if the symlink points to the correct device in /dev
+ dev="/dev/$(udevadm info -q name "$link")"
+ if [[ "$target" != "$dev" ]]; then
+ echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected"
+ return 1
+ fi
+ done < <(find "${paths[@]}" -type l)
+)}
+
+helper_check_udev_watch() {(
+ set +x
+
+ local link target id dev
+
+ while read -r link; do
+ target="$(readlink "$link")"
+ if [[ ! -L "/run/udev/watch/$target" ]]; then
+ echo >&2 "ERROR: symlink /run/udev/watch/$target does not exist"
+ return 1
+ fi
+ if [[ "$(readlink "/run/udev/watch/$target")" != "$(basename "$link")" ]]; then
+ echo >&2 "ERROR: symlink target of /run/udev/watch/$target is inconsistent with $link"
+ return 1
+ fi
+
+ if [[ "$target" =~ ^[0-9]+$ ]]; then
+ # $link is ID -> wd
+ id="$(basename "$link")"
+ else
+ # $link is wd -> ID
+ id="$target"
+ fi
+
+ if [[ "${id:0:1}" == "b" ]]; then
+ dev="/dev/block/${id:1}"
+ elif [[ "${id:0:1}" == "c" ]]; then
+ dev="/dev/char/${id:1}"
+ else
+ echo >&2 "ERROR: unexpected device ID '$id'"
+ return 1
+ fi
+
+ if [[ ! -e "$dev" ]]; then
+ echo >&2 "ERROR: device '$dev' corresponding to symlink '$link' does not exist"
+ return 1
+ fi
+ done < <(find /run/udev/watch -type l)
+)}
+
+check_device_unit() {(
+ set +x
+
+ local log_level link links path syspath unit
+
+ log_level="${1?}"
+ path="${2?}"
+ unit=$(systemd-escape --path --suffix=device "$path")
+
+ [[ "$log_level" == 1 ]] && echo "INFO: check_device_unit($unit)"
+
+ syspath=$(systemctl show --value --property SysFSPath "$unit" 2>/dev/null)
+ if [[ -z "$syspath" ]]; then
+ [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit not found."
+ return 1
+ fi
+
+ if [[ ! -L "$path" ]]; then
+ if [[ ! -d "$syspath" ]]; then
+ [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not exist."
+ return 1
+ fi
+ return 0
+ fi
+
+ if [[ ! -b "$path" && ! -c "$path" ]]; then
+ [[ "$log_level" == 1 ]] && echo >&2 "ERROR: invalid file type $path"
+ return 1
+ fi
+
+ read -r -a links < <(udevadm info -q symlink "$syspath" 2>/dev/null)
+ for link in "${links[@]}"; do
+ if [[ "/dev/$link" == "$path" ]]; then # DEVLINKS= given by -q symlink are relative to /dev
+ return 0
+ fi
+ done
+
+ read -r -a links < <(udevadm info "$syspath" | sed -ne '/SYSTEMD_ALIAS=/ { s/^E: SYSTEMD_ALIAS=//; p }' 2>/dev/null)
+ for link in "${links[@]}"; do
+ if [[ "$link" == "$path" ]]; then # SYSTEMD_ALIAS= are absolute
+ return 0
+ fi
+ done
+
+ [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not have the corresponding DEVLINKS or SYSTEMD_ALIAS."
+ return 1
+)}
+
+check_device_units() {(
+ set +x
+
+ local log_level path paths
+
+ log_level="${1?}"
+ shift
+ paths=("$@")
+
+ for path in "${paths[@]}"; do
+ if ! check_device_unit "$log_level" "$path"; then
+ return 1
+ fi
+ done
+
+ while read -r unit _; do
+ path=$(systemd-escape --path --unescape "$unit")
+ if ! check_device_unit "$log_level" "$path"; then
+ return 1
+ fi
+ done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
+
+ return 0
+)}
+
+helper_check_device_units() {(
+ set +x
+
+ local i
+
+ for i in {1..20}; do
+ (( i > 1 )) && sleep 0.5
+ if check_device_units 0 "$@"; then
+ return 0
+ fi
+ done
+
+ check_device_units 1 "$@"
+)}
+
+testcase_megasas2_basic() {
+ lsblk -S
+ [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
+}
+
+testcase_nvme_basic() {
+ local expected_symlinks=()
+ local i
+
+ for (( i = 0; i < 5; i++ )); do
+ expected_symlinks+=(
+ # both replace mode provides the same devlink
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"
+ # with nsid
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"_1
+ )
+ done
+ for (( i = 5; i < 10; i++ )); do
+ expected_symlinks+=(
+ # old replace mode
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl__deadbeef_"$i"
+ # newer replace mode
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"
+ # with nsid
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"_1
+ )
+ done
+ for (( i = 10; i < 15; i++ )); do
+ expected_symlinks+=(
+ # old replace mode does not provide devlink, as serial contains "/"
+ # newer replace mode
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"
+ # with nsid
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"_1
+ )
+ done
+ for (( i = 15; i < 20; i++ )); do
+ expected_symlinks+=(
+ # old replace mode does not provide devlink, as serial contains "/"
+ # newer replace mode
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"
+ # with nsid
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"_1
+ )
+ done
+
+ udevadm settle
+ ls /dev/disk/by-id
+ for i in "${expected_symlinks[@]}"; do
+ udevadm wait --settle --timeout=30 "$i"
+ done
+
+ lsblk --noheadings | grep "^nvme"
+ [[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]]
+}
+
+testcase_nvme_subsystem() {
+ local expected_symlinks=(
+ # Controller(s)
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16
+ /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17
+ # Shared namespaces
+ /dev/disk/by-path/pci-*-nvme-16
+ /dev/disk/by-path/pci-*-nvme-17
+ )
+
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+}
+
+testcase_virtio_scsi_identically_named_partitions() {
+ local num_part num_disk i j
+ local alphabet='abcdefghijklmnopqrstuvwxyz'
+
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ num_part=4
+ num_disk=4
+ else
+ num_part=8
+ num_disk=16
+ fi
+
+ for ((i = 0; i < num_disk; i++)); do
+ sfdisk "/dev/sd${alphabet:$i:1}" <<EOF
+label: gpt
+
+$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
+EOF
+ done
+
+ lsblk --noheadings -a -o NAME,PARTLABEL
+ [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
+}
+
+testcase_multipath_basic_failover() {
+ local dmpath i path wwid
+
+ # Configure multipath
+ cat >/etc/multipath.conf <<\EOF
+defaults {
+ # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX
+ user_friendly_names no
+ find_multipaths yes
+ enable_foreign "^$"
+}
+
+blacklist_exceptions {
+ property "(SCSI_IDENT_|ID_WWN)"
+}
+
+blacklist {
+}
+EOF
+
+ sfdisk /dev/sda <<EOF
+label: gpt
+
+name="first_partition", size=5M
+uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
+EOF
+ udevadm settle
+ mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "/dev/sda2"
+
+ modprobe -v dm_multipath
+ systemctl start multipathd.service
+ systemctl status multipathd.service
+ multipath -ll
+ udevadm settle
+ ls -l /dev/disk/by-id/
+
+ for i in {0..15}; do
+ wwid="deaddeadbeef$(printf "%.4d" "$i")"
+ path="/dev/disk/by-id/wwn-0x$wwid"
+ dmpath="$(readlink -f "$path")"
+
+ lsblk "$path"
+ multipath -C "$dmpath"
+ # We should have 4 active paths for each multipath device
+ [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]]
+ done
+
+ # Test failover (with the first multipath device that has a partitioned disk)
+ echo "${FUNCNAME[0]}: test failover"
+ local device expected link mpoint part
+ local -a devices
+ mkdir -p /mnt
+ mpoint="$(mktemp -d /mnt/mpathXXX)"
+ wwid="deaddeadbeef0000"
+ path="/dev/disk/by-id/wwn-0x$wwid"
+
+ # All following symlinks should exists and should be valid
+ local -a part_links=(
+ "/dev/disk/by-id/wwn-0x$wwid-part2"
+ "/dev/disk/by-partlabel/failover_part"
+ "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
+ "/dev/disk/by-label/failover_vol"
+ "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
+ )
+ udevadm wait --settle --timeout=30 "${part_links[@]}"
+ helper_check_device_units "${part_links[@]}"
+
+ # Choose a random symlink to the failover data partition each time, for
+ # a better coverage
+ part="${part_links[$RANDOM % ${#part_links[@]}]}"
+
+ # Get all devices attached to a specific multipath device (in H:C:T:L format)
+ # and sort them in a random order, so we cut off different paths each time
+ mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R)
+ if [[ "${#devices[@]}" -ne 4 ]]; then
+ echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead"
+ return 1
+ fi
+ # Drop the last path from the array, since we want to leave at least one path active
+ unset "devices[3]"
+ # Mount the first multipath partition, write some data we can check later,
+ # and then disconnect the remaining paths one by one while checking if we
+ # can still read/write from the mount
+ mount -t ext4 "$part" "$mpoint"
+ expected=0
+ echo -n "$expected" >"$mpoint/test"
+ # Sanity check we actually wrote what we wanted
+ [[ "$(<"$mpoint/test")" == "$expected" ]]
+
+ for device in "${devices[@]}"; do
+ echo offline >"/sys/class/scsi_device/$device/device/state"
+ [[ "$(<"$mpoint/test")" == "$expected" ]]
+ expected="$((expected + 1))"
+ echo -n "$expected" >"$mpoint/test"
+
+ # Make sure all symlinks are still valid
+ udevadm wait --settle --timeout=30 "${part_links[@]}"
+ helper_check_device_units "${part_links[@]}"
+ done
+
+ multipath -l "$path"
+ # Three paths should be now marked as 'offline' and one as 'running'
+ [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]]
+ [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]]
+
+ umount "$mpoint"
+ rm -fr "$mpoint"
+}
+
+testcase_simultaneous_events_1() {
+ local disk expected i iterations key link num_part part partscript rule target timeout
+ local -a devices symlinks
+ local -A running
+
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ num_part=2
+ iterations=10
+ timeout=240
+ else
+ num_part=10
+ iterations=100
+ timeout=30
+ fi
+
+ for disk in {0..9}; do
+ link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
+ target="$(readlink -f "$link")"
+ if [[ ! -b "$target" ]]; then
+ echo "ERROR: failed to find the test SCSI block device $link"
+ return 1
+ fi
+
+ devices+=("$target")
+ done
+
+ for ((part = 1; part <= num_part; part++)); do
+ symlinks+=(
+ "/dev/disk/by-partlabel/test${part}"
+ )
+ done
+
+ partscript="$(mktemp)"
+
+ cat >"$partscript" <<EOF
+$(for ((part = 1; part <= num_part; part++)); do printf 'name="test%d", size=2M\n' "$part"; done)
+EOF
+
+ rule=/run/udev/rules.d/50-test.rules
+ mkdir -p "${rule%/*}"
+ cat >"$rule" <<EOF
+SUBSYSTEM=="block", KERNEL=="${devices[4]##*/}*|${devices[5]##*/}*", OPTIONS="link_priority=10"
+EOF
+
+ udevadm control --reload
+
+ # initialize partition table
+ for disk in {0..9}; do
+ echo 'label: gpt' | udevadm lock --device="${devices[$disk]}" sfdisk -q "${devices[$disk]}"
+ done
+
+ # Delete the partitions, immediately recreate them, wait for udev to settle
+ # down, and then check if we have any dangling symlinks in /dev/disk/. Rinse
+ # and repeat.
+ #
+ # On unpatched udev versions the delete-recreate cycle may trigger a race
+ # leading to dead symlinks in /dev/disk/
+ for ((i = 1; i <= iterations; i++)); do
+ for disk in {0..9}; do
+ if ((disk % 2 == i % 2)); then
+ udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
+ else
+ udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$partscript" &
+ fi
+ running[$disk]=$!
+ done
+
+ for key in "${!running[@]}"; do
+ wait "${running[$key]}"
+ unset "running[$key]"
+ done
+
+ if ((i % 10 <= 1)); then
+ udevadm wait --settle --timeout="$timeout" "${devices[@]}" "${symlinks[@]}"
+ helper_check_device_symlinks
+ helper_check_udev_watch
+ for ((part = 1; part <= num_part; part++)); do
+ link="/dev/disk/by-partlabel/test${part}"
+ target="$(readlink -f "$link")"
+ if ((i % 2 == 0)); then
+ expected="${devices[5]}$part"
+ else
+ expected="${devices[4]}$part"
+ fi
+ if [[ "$target" != "$expected" ]]; then
+ echo >&2 "ERROR: symlink '/dev/disk/by-partlabel/test${part}' points to '$target' but '$expected' was expected"
+ return 1
+ fi
+ done
+ fi
+ done
+
+ helper_check_device_units
+ rm -f "$rule" "$partscript"
+
+ udevadm control --reload
+}
+
+testcase_simultaneous_events_2() {
+ local disk expected i iterations key link num_part part script_dir target timeout
+ local -a devices symlinks
+ local -A running
+
+ script_dir="$(mktemp --directory "/tmp/test-udev-storage.script.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$script_dir'" RETURN
+
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ num_part=20
+ iterations=1
+ timeout=2400
+ else
+ num_part=100
+ iterations=3
+ timeout=300
+ fi
+
+ for disk in {0..9}; do
+ link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
+ target="$(readlink -f "$link")"
+ if [[ ! -b "$target" ]]; then
+ echo "ERROR: failed to find the test SCSI block device $link"
+ return 1
+ fi
+
+ devices+=("$target")
+ done
+
+ for ((i = 1; i <= iterations; i++)); do
+ cat >"$script_dir/partscript-$i" <<EOF
+$(for ((part = 1; part <= num_part; part++)); do printf 'name="testlabel-%d", size=1M\n' "$i"; done)
+EOF
+ done
+
+ echo "## $iterations iterations start: $(date '+%H:%M:%S.%N')"
+ for ((i = 1; i <= iterations; i++)); do
+
+ for disk in {0..9}; do
+ udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
+ running[$disk]=$!
+ done
+
+ for key in "${!running[@]}"; do
+ wait "${running[$key]}"
+ unset "running[$key]"
+ done
+
+ for disk in {0..9}; do
+ udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$script_dir/partscript-$i" &
+ running[$disk]=$!
+ done
+
+ for key in "${!running[@]}"; do
+ wait "${running[$key]}"
+ unset "running[$key]"
+ done
+
+ udevadm wait --settle --timeout="$timeout" "${devices[@]}" "/dev/disk/by-partlabel/testlabel-$i"
+ done
+ echo "## $iterations iterations end: $(date '+%H:%M:%S.%N')"
+}
+
+testcase_simultaneous_events() {
+ testcase_simultaneous_events_1
+ testcase_simultaneous_events_2
+}
+
+testcase_lvm_basic() {
+ local i iterations partitions part timeout
+ local vgroup="MyTestGroup$RANDOM"
+ local devices=(
+ /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
+ )
+
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ timeout=180
+ else
+ timeout=30
+ fi
+ # Make sure all the necessary soon-to-be-LVM devices exist
+ ls -l "${devices[@]}"
+
+ # Add all test devices into a volume group, create two logical volumes,
+ # and check if necessary symlinks exist (and are valid)
+ lvm pvcreate -y "${devices[@]}"
+ lvm pvs
+ lvm vgcreate "$vgroup" -y "${devices[@]}"
+ lvm vgs
+ lvm vgchange -ay "$vgroup"
+ lvm lvcreate -y -L 4M "$vgroup" -n mypart1
+ lvm lvcreate -y -L 32M "$vgroup" -n mypart2
+ lvm lvs
+ udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+ mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
+ udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+
+ # Mount mypart1 through by-label devlink
+ mkdir -p /tmp/mypart1-mount-point
+ mount /dev/disk/by-label/mylvpart1 /tmp/mypart1-mount-point
+ timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
+ # Extend the partition and check if the device and mount units are still active.
+ # See https://bugzilla.redhat.com/show_bug.cgi?id=2158628
+ # Note, the test below may be unstable with LVM2 without the following patch:
+ # https://github.com/lvmteam/lvm2/pull/105
+ # But, to reproduce the issue, udevd must start to process the first 'change' uevent
+ # earlier than extending the volume has been finished, and in most case, the extension
+ # is hopefully fast.
+ lvm lvextend -y --size 8M "/dev/$vgroup/mypart1"
+ udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
+ timeout 30 bash -c "until systemctl -q is-active '/dev/$vgroup/mypart1'; do sleep .2; done"
+ timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
+ # Umount the partition, otherwise the underlying device unit will stay in
+ # the inactive state and not be collected, and helper_check_device_units() will fail.
+ systemctl show /tmp/mypart1-mount-point
+ umount /tmp/mypart1-mount-point
+
+ # Rename partitions (see issue #24518)
+ lvm lvrename "/dev/$vgroup/mypart1" renamed1
+ lvm lvrename "/dev/$vgroup/mypart2" renamed2
+ udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+ udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+
+ # Rename them back
+ lvm lvrename "/dev/$vgroup/renamed1" mypart1
+ lvm lvrename "/dev/$vgroup/renamed2" mypart2
+ udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
+ udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+
+ # Do not "unready" suspended encrypted devices w/o superblock info
+ # See:
+ # - https://github.com/systemd/systemd/pull/24177
+ # - https://bugzilla.redhat.com/show_bug.cgi?id=1985288
+ dd if=/dev/urandom of=/etc/lvm_keyfile bs=64 count=1 iflag=fullblock
+ chmod 0600 /etc/lvm_keyfile
+ # Intentionally use weaker cipher-related settings, since we don't care
+ # about security here as it's a throwaway LUKS partition
+ cryptsetup luksFormat -q --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
+ "/dev/$vgroup/mypart2" /etc/lvm_keyfile
+ # Mount the LUKS partition & create a filesystem on it
+ mkdir -p /tmp/lvmluksmnt
+ cryptsetup open --key-file=/etc/lvm_keyfile "/dev/$vgroup/mypart2" "lvmluksmap"
+ udevadm wait --settle --timeout="$timeout" "/dev/mapper/lvmluksmap"
+ mkfs.ext4 -L lvmluksfs "/dev/mapper/lvmluksmap"
+ udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/lvmluksfs"
+ # Make systemd "interested" in the mount by adding it to /etc/fstab
+ echo "/dev/disk/by-label/lvmluksfs /tmp/lvmluksmnt ext4 defaults 0 2" >>/etc/fstab
+ systemctl daemon-reload
+ mount "/tmp/lvmluksmnt"
+ mountpoint "/tmp/lvmluksmnt"
+ # Temporarily suspend the LUKS device and trigger udev - basically what `cryptsetup resize`
+ # does but in a more deterministic way suitable for a test/reproducer
+ for _ in {0..5}; do
+ dmsetup suspend "/dev/mapper/lvmluksmap"
+ udevadm trigger -v --settle "/dev/mapper/lvmluksmap"
+ dmsetup resume "/dev/mapper/lvmluksmap"
+ # The mount should survive this sequence of events
+ mountpoint "/tmp/lvmluksmnt"
+ done
+ # Cleanup
+ umount "/tmp/lvmluksmnt"
+ cryptsetup close "/dev/mapper/lvmluksmap"
+ sed -i "/lvmluksfs/d" "/etc/fstab"
+ systemctl daemon-reload
+
+ # Disable the VG and check symlinks...
+ lvm vgchange -an "$vgroup"
+ udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1"
+ helper_check_device_symlinks "/dev/disk"
+ helper_check_device_units
+
+ # reenable the VG and check the symlinks again if all LVs are properly activated
+ lvm vgchange -ay "$vgroup"
+ udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+
+ # Same as above, but now with more "stress"
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ iterations=10
+ else
+ iterations=50
+ fi
+
+ for ((i = 1; i <= iterations; i++)); do
+ lvm vgchange -an "$vgroup"
+ lvm vgchange -ay "$vgroup"
+
+ if ((i % 5 == 0)); then
+ udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+ fi
+ done
+
+ # Remove the first LV
+ lvm lvremove -y "$vgroup/mypart1"
+ udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1"
+ udevadm wait --timeout=0 "/dev/$vgroup/mypart2"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+
+ # Create & remove LVs in a loop, i.e. with more "stress"
+ if [[ -v ASAN_OPTIONS ]]; then
+ iterations=8
+ partitions=16
+ elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ iterations=8
+ partitions=8
+ else
+ iterations=16
+ partitions=16
+ fi
+
+ for ((i = 1; i <= iterations; i++)); do
+ # 1) Create some logical volumes
+ for ((part = 0; part < partitions; part++)); do
+ lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part"
+ done
+
+ # 2) Immediately remove them
+ lvm lvremove -y $(seq -f "$vgroup/looppart%g" 0 "$((partitions - 1))")
+
+ # 3) On every 4th iteration settle udev and check if all partitions are
+ # indeed gone, and if all symlinks are still valid
+ if ((i % 4 == 0)); then
+ for ((part = 0; part < partitions; part++)); do
+ udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/looppart$part"
+ done
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+ fi
+ done
+}
+
+testcase_btrfs_basic() {
+ local dev_stub i label mpoint uuid
+ local devices=(
+ /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3}
+ )
+
+ ls -l "${devices[@]}"
+
+ echo "Single device: default settings"
+ uuid="deadbeef-dead-dead-beef-000000000000"
+ label="btrfs_root"
+ udevadm lock --device="${devices[0]}" mkfs.btrfs -f -L "$label" -U "$uuid" "${devices[0]}"
+ udevadm wait --settle --timeout=30 "${devices[0]}" "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+ btrfs filesystem show
+ helper_check_device_symlinks
+ helper_check_device_units
+
+ echo "Multiple devices: using partitions, data: single, metadata: raid1"
+ uuid="deadbeef-dead-dead-beef-000000000001"
+ label="btrfs_mpart"
+ udevadm lock --device="${devices[0]}" sfdisk --wipe=always "${devices[0]}" <<EOF
+label: gpt
+
+name="diskpart1", size=85M
+name="diskpart2", size=85M
+name="diskpart3", size=85M
+name="diskpart4", size=85M
+EOF
+ udevadm wait --settle --timeout=30 /dev/disk/by-partlabel/diskpart{1..4}
+ udevadm lock --device="${devices[0]}" mkfs.btrfs -f -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4}
+ udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+ btrfs filesystem show
+ helper_check_device_symlinks
+ helper_check_device_units
+ wipefs -a -f "${devices[0]}"
+ udevadm wait --settle --timeout=30 --removed /dev/disk/by-partlabel/diskpart{1..4}
+
+ echo "Multiple devices: using disks, data: raid10, metadata: raid10, mixed mode"
+ uuid="deadbeef-dead-dead-beef-000000000002"
+ label="btrfs_mdisk"
+ udevadm lock \
+ --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs0 \
+ --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs1 \
+ --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs2 \
+ --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs3 \
+ mkfs.btrfs -f -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}"
+ udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+ btrfs filesystem show
+ helper_check_device_symlinks
+ helper_check_device_units
+
+ echo "Multiple devices: using LUKS encrypted disks, data: raid1, metadata: raid1, mixed mode"
+ uuid="deadbeef-dead-dead-beef-000000000003"
+ label="btrfs_mencdisk"
+ mpoint="/btrfs_enc$RANDOM"
+ mkdir "$mpoint"
+ # Create a key-file
+ dd if=/dev/urandom of=/etc/btrfs_keyfile bs=64 count=1 iflag=fullblock
+ chmod 0600 /etc/btrfs_keyfile
+ # Encrypt each device and add it to /etc/crypttab, so it can be mounted
+ # automagically later
+ : >/etc/crypttab
+ for ((i = 0; i < ${#devices[@]}; i++)); do
+ # Intentionally use weaker cipher-related settings, since we don't care
+ # about security here as it's a throwaway LUKS partition
+ cryptsetup luksFormat -q \
+ --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
+ --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile
+ udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i" "/dev/disk/by-label/encdisk$i"
+ # Add the device into /etc/crypttab, reload systemd, and then activate
+ # the device so we can create a filesystem on it later
+ echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks" >>/etc/crypttab
+ systemctl daemon-reload
+ systemctl start "systemd-cryptsetup@encbtrfs$i"
+ done
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Check if we have all necessary DM devices
+ ls -l /dev/mapper/encbtrfs{0..3}
+ # Create a multi-device btrfs filesystem on the LUKS devices
+ udevadm lock \
+ --device=/dev/mapper/encbtrfs0 \
+ --device=/dev/mapper/encbtrfs1 \
+ --device=/dev/mapper/encbtrfs2 \
+ --device=/dev/mapper/encbtrfs3 \
+ mkfs.btrfs -f -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3}
+ udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+ btrfs filesystem show
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Mount it and write some data to it we can compare later
+ mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint"
+ echo "hello there" >"$mpoint/test"
+ # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise)
+ umount "$mpoint"
+ systemctl stop systemd-cryptsetup@encbtrfs{0..3}
+ udevadm wait --settle --timeout=30 --removed "/dev/disk/by-uuid/$uuid"
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Add the mount point to /etc/fstab and check if the device can be put together
+ # automagically. The source device is the DM name of the first LUKS device
+ # (from /etc/crypttab). We have to specify all LUKS devices manually, as
+ # registering the necessary devices is usually initrd's job (via btrfs device scan)
+ dev_stub="/dev/mapper/encbtrfs"
+ echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab
+ # Tell systemd about the new mount
+ systemctl daemon-reload
+ # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab
+ systemctl restart cryptsetup.target
+ # Start the corresponding mount unit and check if the btrfs device was reconstructed
+ # correctly
+ systemctl start "${mpoint##*/}.mount"
+ udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+ btrfs filesystem show
+ helper_check_device_symlinks
+ helper_check_device_units
+ grep "hello there" "$mpoint/test"
+ # Cleanup
+ systemctl stop "${mpoint##*/}.mount"
+ systemctl stop systemd-cryptsetup@encbtrfs{0..3}
+ sed -i "/${mpoint##*/}/d" /etc/fstab
+ : >/etc/crypttab
+ rm -fr "$mpoint"
+ systemctl daemon-reload
+ udevadm settle
+}
+
+testcase_iscsi_lvm() {
+ local dev i label link lun_id mpoint target_name uuid
+ local target_ip="127.0.0.1"
+ local target_port="3260"
+ local vgroup="iscsi_lvm$RANDOM"
+ local expected_symlinks=()
+ local devices=(
+ /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3}
+ )
+
+ ls -l "${devices[@]}"
+
+ # Start the target daemon
+ systemctl start tgtd
+ systemctl status tgtd
+
+ echo "iSCSI LUNs backed by devices"
+ # See RFC3721 and RFC7143
+ target_name="iqn.2021-09.com.example:iscsi.test"
+ # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
+ # backed by a device
+ tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name"
+ for ((i = 0; i < ${#devices[@]}; i++)); do
+ # lun-0 is reserved by iSCSI
+ lun_id="$((i + 1))"
+ tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}"
+ tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id"
+ expected_symlinks+=(
+ "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id"
+ )
+ done
+ tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
+ # Configure the iSCSI initiator
+ iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
+ iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Cleanup
+ iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
+ tgtadm --lld iscsi --op delete --mode target --tid=1
+
+ echo "iSCSI LUNs backed by files + LVM"
+ # Note: we use files here to "trick" LVM the disks are indeed on a different
+ # host, so it doesn't automagically detect another path to the backing
+ # device once we disconnect the iSCSI devices
+ target_name="iqn.2021-09.com.example:iscsi.lvm.test"
+ mpoint="$(mktemp -d /iscsi_storeXXX)"
+ expected_symlinks=()
+ # Use the first device as it's configured with larger capacity
+ mkfs.ext4 -L iscsi_store "${devices[0]}"
+ udevadm wait --settle --timeout=30 "${devices[0]}"
+ mount "${devices[0]}" "$mpoint"
+ for i in {1..4}; do
+ dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32
+ done
+ # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
+ # backed by a file
+ tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name"
+ # lun-0 is reserved by iSCSI
+ for i in {1..4}; do
+ tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img"
+ tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i"
+ expected_symlinks+=(
+ "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i"
+ )
+ done
+ tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL
+ # Configure the iSCSI initiator
+ iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
+ iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Add all iSCSI devices into a LVM volume group, create two logical volumes,
+ # and check if necessary symlinks exist (and are valid)
+ lvm pvcreate -y "${expected_symlinks[@]}"
+ lvm pvs
+ lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}"
+ lvm vgs
+ lvm vgchange -ay "$vgroup"
+ lvm lvcreate -y -L 4M "$vgroup" -n mypart1
+ lvm lvcreate -y -L 8M "$vgroup" -n mypart2
+ lvm lvs
+ udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+ mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
+ udevadm wait --settle --timeout=30 "/dev/disk/by-label/mylvpart1"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+ # Disconnect the iSCSI devices and check all the symlinks
+ iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
+ # "Reset" the DM state, since we yanked the backing storage from under the LVM,
+ # so the currently active VGs/LVs are invalid
+ dmsetup remove_all --deferred
+ # The LVM and iSCSI related symlinks should be gone
+ udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}"
+ helper_check_device_symlinks "/dev/disk"
+ helper_check_device_units
+ # Reconnect the iSCSI devices and check if everything get detected correctly
+ iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
+ iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
+ helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+ helper_check_device_units
+ # Cleanup
+ iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
+ tgtadm --lld iscsi --op delete --mode target --tid=2
+ umount "$mpoint"
+ rm -rf "$mpoint"
+}
+
+testcase_long_sysfs_path() {
+ local cursor link logfile mpoint
+ local expected_symlinks=(
+ "/dev/disk/by-label/data_vol"
+ "/dev/disk/by-label/swap_vol"
+ "/dev/disk/by-partlabel/test_swap"
+ "/dev/disk/by-partlabel/test_part"
+ "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
+ "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
+ "/dev/disk/by-uuid/deadbeef-dead-dead-beef-222222222222"
+ )
+
+ # Create a cursor file to skip messages generated by udevd in initrd, as it
+ # might not be the same up-to-date version as we currently run (hence generating
+ # messages we check for later and making the test fail)
+ cursor="$(mktemp)"
+ journalctl --cursor-file="${cursor:?}" -n0 -q
+
+ # Make sure the test device is connected and show its "wonderful" path
+ stat /sys/block/vda
+ readlink -f /sys/block/vda/dev
+
+ dev="/dev/vda"
+ sfdisk "${dev:?}" <<EOF
+label: gpt
+
+name="test_swap", size=32M
+uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
+EOF
+ udevadm settle
+ mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
+ mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${dev}2"
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+
+ # Try to mount the data partition manually (using its label)
+ mpoint="$(mktemp -d /logsysfsXXX)"
+ mount LABEL=data_vol "$mpoint"
+ touch "$mpoint/test"
+ umount "$mpoint"
+ # Do the same, but with UUID and using fstab
+ echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
+ systemctl daemon-reload
+ mount "$mpoint"
+ timeout 30 bash -c "until systemctl -q is-active '$mpoint'; do sleep .2; done"
+ test -e "$mpoint/test"
+ umount "$mpoint"
+
+ # Test out the swap partition
+ swapon -v -L swap_vol
+ swapoff -v -L swap_vol
+
+ udevadm settle
+
+ logfile="$(mktemp)"
+ # Check state of affairs after https://github.com/systemd/systemd/pull/22759
+ # Note: can't use `--cursor-file` here, since we don't want to update the cursor
+ # after using it
+ [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]]
+ [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic --grep "Unit name .*vda.?\.device\" too long, falling back to hashed unit name" | wc -l)" -gt 0 ]]
+ # Check if the respective "hashed" units exist and are active (plugged)
+ systemctl status --no-pager "$(readlink -f /sys/block/vda/vda1)"
+ systemctl status --no-pager "$(readlink -f /sys/block/vda/vda2)"
+ # Make sure we don't unnecessarily spam the log
+ { journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile"
+ [[ "$(wc -l <"$logfile")" -lt 10 ]]
+
+ : >/etc/fstab
+ rm -fr "${cursor:?}" "${logfile:?}" "${mpoint:?}"
+}
+
+testcase_mdadm_basic() {
+ local i part_name raid_name raid_dev uuid
+ local expected_symlinks=()
+ local devices=(
+ /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..4}
+ )
+
+ ls -l "${devices[@]}"
+
+ echo "Mirror raid (RAID 1)"
+ raid_name="mdmirror"
+ raid_dev="/dev/md/$raid_name"
+ part_name="${raid_name}_part"
+ uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001"
+ expected_symlinks=(
+ "$raid_dev"
+ "/dev/disk/by-id/md-name-H:$raid_name"
+ "/dev/disk/by-id/md-uuid-$uuid"
+ "/dev/disk/by-label/$part_name" # ext4 partition
+ )
+ # Create a simple RAID 1 with an ext4 filesystem
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2
+ udevadm wait --settle --timeout=30 "$raid_dev"
+ mkfs.ext4 -L "$part_name" "$raid_dev"
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ for i in {0..9}; do
+ echo "Disassemble - reassemble loop, iteration #$i"
+ mdadm -v --stop "$raid_dev"
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ mdadm --assemble "$raid_dev" --name "$raid_name" -v
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ done
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Cleanup
+ mdadm -v --stop "$raid_dev"
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+
+ echo "Parity raid (RAID 5)"
+ raid_name="mdparity"
+ raid_dev="/dev/md/$raid_name"
+ part_name="${raid_name}_part"
+ uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000101"
+ expected_symlinks=(
+ "$raid_dev"
+ "/dev/disk/by-id/md-name-H:$raid_name"
+ "/dev/disk/by-id/md-uuid-$uuid"
+ "/dev/disk/by-label/$part_name" # ext4 partition
+ )
+ # Create a simple RAID 5 with an ext4 filesystem
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..2} -v -f --level=5 --raid-devices=3
+ udevadm wait --settle --timeout=30 "$raid_dev"
+ mkfs.ext4 -L "$part_name" "$raid_dev"
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ for i in {0..9}; do
+ echo "Disassemble - reassemble loop, iteration #$i"
+ mdadm -v --stop "$raid_dev"
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ mdadm --assemble "$raid_dev" --name "$raid_name" -v
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ done
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Cleanup
+ mdadm -v --stop "$raid_dev"
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ helper_check_device_units
+
+ echo "Mirror + parity raid (RAID 10) + multiple partitions"
+ raid_name="mdmirpar"
+ raid_dev="/dev/md/$raid_name"
+ part_name="${raid_name}_part"
+ uuid="aaaaaaaa:bbbbbbbb:cccccccc:00001010"
+ expected_symlinks=(
+ "$raid_dev"
+ "/dev/disk/by-id/md-name-H:$raid_name"
+ "/dev/disk/by-id/md-uuid-$uuid"
+ "/dev/disk/by-label/$part_name" # ext4 partition
+ # Partitions
+ "${raid_dev}1"
+ "${raid_dev}2"
+ "${raid_dev}3"
+ "/dev/disk/by-id/md-name-H:$raid_name-part1"
+ "/dev/disk/by-id/md-name-H:$raid_name-part2"
+ "/dev/disk/by-id/md-name-H:$raid_name-part3"
+ "/dev/disk/by-id/md-uuid-$uuid-part1"
+ "/dev/disk/by-id/md-uuid-$uuid-part2"
+ "/dev/disk/by-id/md-uuid-$uuid-part3"
+ )
+ # Create a simple RAID 10 with an ext4 filesystem
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..3} -v -f --level=10 --raid-devices=4
+ udevadm wait --settle --timeout=30 "$raid_dev"
+ # Partition the raid device
+ # Here, 'udevadm lock' is meaningless, as udevd does not lock MD devices.
+ sfdisk --wipe=always "$raid_dev" <<EOF
+label: gpt
+
+uuid="deadbeef-dead-dead-beef-111111111111", name="mdpart1", size=8M
+uuid="deadbeef-dead-dead-beef-222222222222", name="mdpart2", size=32M
+uuid="deadbeef-dead-dead-beef-333333333333", name="mdpart3", size=16M
+EOF
+ udevadm wait --settle --timeout=30 "/dev/disk/by-id/md-uuid-$uuid-part2"
+ mkfs.ext4 -L "$part_name" "/dev/disk/by-id/md-uuid-$uuid-part2"
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ for i in {0..9}; do
+ echo "Disassemble - reassemble loop, iteration #$i"
+ mdadm -v --stop "$raid_dev"
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ mdadm --assemble "$raid_dev" --name "$raid_name" -v
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ done
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Cleanup
+ mdadm -v --stop "$raid_dev"
+ # Check if all expected symlinks were removed after the cleanup
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ helper_check_device_units
+}
+
+testcase_mdadm_lvm() {
+ local part_name raid_name raid_dev uuid vgroup
+ local expected_symlinks=()
+ local devices=(
+ /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..4}
+ )
+
+ ls -l "${devices[@]}"
+
+ raid_name="mdlvm"
+ raid_dev="/dev/md/$raid_name"
+ part_name="${raid_name}_part"
+ vgroup="${raid_name}_vg"
+ uuid="aaaaaaaa:bbbbbbbb:ffffffff:00001010"
+ expected_symlinks=(
+ "$raid_dev"
+ "/dev/$vgroup/mypart1" # LVM partition
+ "/dev/$vgroup/mypart2" # LVM partition
+ "/dev/disk/by-id/md-name-H:$raid_name"
+ "/dev/disk/by-id/md-uuid-$uuid"
+ "/dev/disk/by-label/$part_name" # ext4 partition
+ )
+ # Create a RAID 10 with LVM + ext4
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..3} -v -f --level=10 --raid-devices=4
+ udevadm wait --settle --timeout=30 "$raid_dev"
+ # Create an LVM on the MD
+ lvm pvcreate -y "$raid_dev"
+ lvm pvs
+ lvm vgcreate "$vgroup" -y "$raid_dev"
+ lvm vgs
+ lvm vgchange -ay "$vgroup"
+ lvm lvcreate -y -L 4M "$vgroup" -n mypart1
+ lvm lvcreate -y -L 8M "$vgroup" -n mypart2
+ lvm lvs
+ udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+ mkfs.ext4 -L "$part_name" "/dev/$vgroup/mypart2"
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ # Disassemble the array
+ lvm vgchange -an "$vgroup"
+ mdadm -v --stop "$raid_dev"
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Reassemble it and check if all required symlinks exist
+ mdadm --assemble "$raid_dev" --name "$raid_name" -v
+ udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+ helper_check_device_symlinks
+ helper_check_device_units
+ # Cleanup
+ lvm vgchange -an "$vgroup"
+ mdadm -v --stop "$raid_dev"
+ # Check if all expected symlinks were removed after the cleanup
+ udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+ helper_check_device_units
+}
+
+udevadm settle
+udevadm control --log-level debug
+lsblk -a
+
+echo "Check if all symlinks under /dev/disk/ are valid (pre-test)"
+helper_check_device_symlinks
+
+# TEST_FUNCTION_NAME is passed on the kernel command line via systemd.setenv=
+# in the respective test.sh file
+if ! command -v "${TEST_FUNCTION_NAME:?}"; then
+ echo >&2 "Missing verification handler for test case '$TEST_FUNCTION_NAME'"
+ exit 1
+fi
+
+echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME"
+"$TEST_FUNCTION_NAME"
+udevadm settle
+
+echo "Check if all symlinks under /dev/disk/ are valid (post-test)"
+helper_check_device_symlinks
+
+udevadm control --log-level info
+
+systemctl status systemd-udevd
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-65-ANALYZE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemctl log-level debug
+export SYSTEMD_LOG_LEVEL=debug
+
+# Sanity checks
+#
+# We can't really test time, critical-chain and plot verbs here, as
+# the testsuite service is a part of the boot transaction, so let's assume
+# they fail
+systemd-analyze || :
+systemd-analyze time || :
+systemd-analyze critical-chain || :
+# blame
+systemd-analyze blame
+systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame
+(! systemd-analyze blame --global)
+# plot
+systemd-analyze plot >/dev/null || :
+systemd-analyze plot --json=pretty >/dev/null || :
+systemd-analyze plot --json=short >/dev/null || :
+systemd-analyze plot --json=off >/dev/null || :
+systemd-analyze plot --json=pretty --no-legend >/dev/null || :
+systemd-analyze plot --json=short --no-legend >/dev/null || :
+systemd-analyze plot --json=off --no-legend >/dev/null || :
+systemd-analyze plot --table >/dev/null || :
+systemd-analyze plot --table --no-legend >/dev/null || :
+(! systemd-analyze plot --global)
+# legacy/deprecated options (moved to systemctl, but still usable from analyze)
+systemd-analyze log-level
+systemd-analyze log-level "$(systemctl log-level)"
+systemd-analyze get-log-level
+systemd-analyze set-log-level "$(systemctl log-level)"
+systemd-analyze log-target
+systemd-analyze log-target "$(systemctl log-target)"
+systemd-analyze get-log-target
+systemd-analyze set-log-target "$(systemctl log-target)"
+systemd-analyze service-watchdogs
+systemd-analyze service-watchdogs "$(systemctl service-watchdogs)"
+# dot
+systemd-analyze dot >/dev/null
+systemd-analyze dot systemd-journald.service >/dev/null
+systemd-analyze dot systemd-journald.service systemd-logind.service >/dev/null
+systemd-analyze dot --from-pattern="*" --from-pattern="*.service" systemd-journald.service >/dev/null
+systemd-analyze dot --to-pattern="*" --to-pattern="*.service" systemd-journald.service >/dev/null
+systemd-analyze dot --from-pattern="*.service" --to-pattern="*.service" systemd-journald.service >/dev/null
+systemd-analyze dot --order systemd-journald.service systemd-logind.service >/dev/null
+systemd-analyze dot --require systemd-journald.service systemd-logind.service >/dev/null
+systemd-analyze dot "systemd-*.service" >/dev/null
+(! systemd-analyze dot systemd-journald.service systemd-logind.service "*" bbb ccc)
+(! systemd-analyze dot --global systemd-journald.service)
+# dump
+# this should be rate limited to 10 calls in 10 minutes for unprivileged callers
+for _ in {1..10}; do
+ runas testuser systemd-analyze dump systemd-journald.service >/dev/null
+done
+(! runas testuser systemd-analyze dump >/dev/null)
+# still limited after a reload
+systemctl daemon-reload
+(! runas testuser systemd-analyze dump >/dev/null)
+# and a re-exec
+systemctl daemon-reexec
+(! runas testuser systemd-analyze dump >/dev/null)
+# privileged call, so should not be rate limited
+for _ in {1..10}; do
+ systemd-analyze dump systemd-journald.service >/dev/null
+done
+systemd-analyze dump >/dev/null
+systemd-analyze dump "*" >/dev/null
+systemd-analyze dump "*.socket" >/dev/null
+systemd-analyze dump "*.socket" "*.service" aaaaaaa ... >/dev/null
+systemd-analyze dump systemd-journald.service >/dev/null
+(! systemd-analyze dump "")
+(! systemd-analyze dump --global systemd-journald.service)
+# malloc
+systemd-analyze malloc >/dev/null
+(! systemd-analyze malloc --global)
+# unit-files
+systemd-analyze unit-files >/dev/null
+systemd-analyze unit-files systemd-journald.service >/dev/null
+systemd-analyze unit-files "*" >/dev/null
+systemd-analyze unit-files "*" aaaaaa "*.service" "*.target" >/dev/null
+systemd-analyze unit-files --user >/dev/null
+systemd-analyze unit-files --user "*" aaaaaa "*.service" "*.target" >/dev/null
+(! systemd-analyze unit-files --global)
+# unit-paths
+systemd-analyze unit-paths
+systemd-analyze unit-paths --user
+systemd-analyze unit-paths --global
+# exist-status
+systemd-analyze exit-status
+systemd-analyze exit-status STDOUT BPF
+systemd-analyze exit-status 0 1 {63..65}
+(! systemd-analyze exit-status STDOUT BPF "hello*")
+(! systemd-analyze exit-status --global)
+# capability
+systemd-analyze capability
+systemd-analyze capability cap_chown CAP_KILL
+systemd-analyze capability 0 1 {30..32}
+(! systemd-analyze capability cap_chown CAP_KILL "hello*")
+(! systemd-analyze capability --global)
+# condition
+mkdir -p /run/systemd/system
+UNIT_NAME="analyze-condition-$RANDOM.service"
+cat >"/run/systemd/system/$UNIT_NAME" <<EOF
+[Unit]
+AssertPathExists=/etc/os-release
+AssertEnvironment=!FOOBAR
+ConditionKernelVersion=>1.0
+ConditionPathExists=/etc/os-release
+
+[Service]
+ExecStart=true
+EOF
+systemctl daemon-reload
+systemd-analyze condition --unit="$UNIT_NAME"
+systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
+ 'ConditionKernelVersion = >=3.1' \
+ 'ConditionACPower=|false' \
+ 'ConditionArchitecture=|!arm' \
+ 'AssertPathExists=/etc/os-release'
+(! systemd-analyze condition 'ConditionArchitecture=|!arm' 'AssertXYZ=foo')
+(! systemd-analyze condition 'ConditionKernelVersion=<1.0')
+(! systemd-analyze condition 'AssertKernelVersion=<1.0')
+(! systemd-analyze condition --global 'ConditionKernelVersion = ! <4.0')
+# syscall-filter
+systemd-analyze syscall-filter >/dev/null
+systemd-analyze syscall-filter @chown @sync
+systemd-analyze syscall-filter @sync @sync @sync
+(! systemd-analyze syscall-filter @chown @sync @foobar)
+(! systemd-analyze syscall-filter --global)
+# filesystems (requires libbpf support)
+if systemctl --version | grep "+BPF_FRAMEWORK"; then
+ systemd-analyze filesystems >/dev/null
+ systemd-analyze filesystems @basic-api
+ systemd-analyze filesystems @basic-api @basic-api @basic-api
+ (! systemd-analyze filesystems @basic-api @basic-api @foobar @basic-api)
+ (! systemd-analyze filesystems --global @basic-api)
+fi
+# calendar
+systemd-analyze calendar '*-2-29 0:0:0'
+systemd-analyze calendar --iterations=5 '*-2-29 0:0:0'
+systemd-analyze calendar '*-* *:*:*'
+systemd-analyze calendar --iterations=5 '*-* *:*:*'
+systemd-analyze calendar --iterations=50 '*-* *:*:*'
+systemd-analyze calendar --iterations=0 '*-* *:*:*'
+systemd-analyze calendar --iterations=5 '01-01-22 01:00:00'
+systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*'
+(! systemd-analyze calendar --iterations=0 '*-* 99:*:*')
+(! systemd-analyze calendar --base-time=never '*-* *:*:*')
+(! systemd-analyze calendar 1)
+(! systemd-analyze calendar "")
+(! systemd-analyze calendar --global '*-2-29 0:0:0')
+# timestamp
+systemd-analyze timestamp now
+systemd-analyze timestamp -- -1
+systemd-analyze timestamp yesterday now tomorrow
+(! systemd-analyze timestamp yesterday never tomorrow)
+(! systemd-analyze timestamp 1)
+(! systemd-analyze timestamp '*-2-29 0:0:0')
+(! systemd-analyze timestamp "")
+(! systemd-analyze timestamp --global now)
+# timespan
+systemd-analyze timespan 1
+systemd-analyze timespan 1s 300s '1year 0.000001s'
+(! systemd-analyze timespan 1s 300s aaaaaa '1year 0.000001s')
+(! systemd-analyze timespan -- -1)
+(! systemd-analyze timespan '*-2-29 0:0:0')
+(! systemd-analyze timespan "")
+(! systemd-analyze timespan --global 1)
+# cat-config
+systemd-analyze cat-config systemd/system.conf >/dev/null
+systemd-analyze cat-config /etc/systemd/system.conf >/dev/null
+systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null
+systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
+systemd-analyze cat-config foo/bar
+systemd-analyze cat-config --tldr systemd/system.conf >/dev/null
+systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null
+systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null
+systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null
+systemd-analyze cat-config --tldr foo/bar
+(! systemd-analyze cat-config --global systemd/system.conf)
+# security
+systemd-analyze security
+systemd-analyze security --json=off
+systemd-analyze security --json=pretty | jq
+systemd-analyze security --json=short | jq
+(! systemd-analyze security --global)
+
+if [[ ! -v ASAN_OPTIONS ]]; then
+ # check that systemd-analyze cat-config paths work in a chroot
+ mkdir -p /tmp/root
+ mount --bind / /tmp/root
+ if mountpoint -q /usr; then
+ mount --bind /usr /tmp/root/usr
+ fi
+ systemd-analyze cat-config systemd/system-preset >/tmp/out1
+ chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
+ diff /tmp/out{1,2}
+fi
+
+# verify
+mkdir -p /tmp/img/usr/lib/systemd/system/
+mkdir -p /tmp/img/opt/
+
+touch /tmp/img/opt/script0.sh
+chmod +x /tmp/img/opt/script0.sh
+
+cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
+[Service]
+ExecStart = /opt/script0.sh
+EOF
+
+set +e
+# Default behaviour is to recurse through all dependencies when unit is loaded
+(! systemd-analyze verify --root=/tmp/img/ testfile.service)
+
+# As above, recurses through all dependencies when unit is loaded
+(! systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service)
+
+# Recurses through unit file and its direct dependencies when unit is loaded
+(! systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service)
+
+set -e
+
+# zero exit status since dependencies are ignored when unit is loaded
+systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service
+
+rm /tmp/img/usr/lib/systemd/system/testfile.service
+
+cat <<EOF >/tmp/testfile.service
+[Unit]
+foo = bar
+
+[Service]
+ExecStart = echo hello
+EOF
+
+cat <<EOF >/tmp/testfile2.service
+[Unit]
+Requires = testfile.service
+
+[Service]
+ExecStart = echo hello
+EOF
+
+# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
+systemd-analyze verify --recursive-errors=no /tmp/testfile2.service
+
+set +e
+# Non-zero exit status since all associated dependencies are recursively loaded when the unit file is loaded
+(! systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service)
+set -e
+
+rm /tmp/testfile.service
+rm /tmp/testfile2.service
+
+cat <<EOF >/tmp/sample.service
+[Unit]
+Description = A Sample Service
+
+[Service]
+ExecStart = echo hello
+Slice=support.slice
+EOF
+
+# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
+systemd-analyze verify --recursive-errors=no /tmp/sample.service
+
+cat <<EOF >/tmp/testfile.service
+[Service]
+ExecStart = echo hello
+DeviceAllow=/dev/sda
+EOF
+
+# Prevent regression from #13380 and #20859 where we can't verify hidden files
+cp /tmp/testfile.service /tmp/.testfile.service
+
+systemd-analyze verify /tmp/.testfile.service
+
+rm /tmp/.testfile.service
+
+# Alias a unit file's name on disk (see #20061)
+cp /tmp/testfile.service /tmp/testsrvc
+
+(! systemd-analyze verify /tmp/testsrvc)
+
+systemd-analyze verify /tmp/testsrvc:alias.service
+
+# Zero exit status since the value used for comparison determine exposure to security threats is by default 100
+systemd-analyze security --offline=true /tmp/testfile.service
+
+#The overall exposure level assigned to the unit is greater than the set threshold
+(! systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service)
+
+# Ensure we print the list of ACLs, see https://github.com/systemd/systemd/issues/23185
+systemd-analyze security --offline=true /tmp/testfile.service | grep -q -F "/dev/sda"
+
+rm /tmp/testfile.service
+
+cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
+[Service]
+ExecStart = echo hello
+PrivateNetwork = yes
+PrivateDevices = yes
+PrivateUsers = yes
+EOF
+
+# The new overall exposure level assigned to the unit is less than the set thresholds
+# Verifies that the --offline= option works with --root=
+systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service
+
+cat <<EOF >/tmp/foo@.service
+[Service]
+ExecStart=ls
+EOF
+
+cat <<EOF >/tmp/hoge@test.service
+[Service]
+ExecStart=ls
+EOF
+
+# issue #30357
+pushd /tmp
+systemd-analyze verify foo@bar.service
+systemd-analyze verify foo@.service
+systemd-analyze verify hoge@test.service
+(! systemd-analyze verify hoge@nonexist.service)
+(! systemd-analyze verify hoge@.service)
+popd
+pushd /
+systemd-analyze verify tmp/foo@bar.service
+systemd-analyze verify tmp/foo@.service
+systemd-analyze verify tmp/hoge@test.service
+(! systemd-analyze verify tmp/hoge@nonexist.service)
+(! systemd-analyze verify tmp/hoge@.service)
+popd
+pushd /usr
+systemd-analyze verify ../tmp/foo@bar.service
+systemd-analyze verify ../tmp/foo@.service
+systemd-analyze verify ../tmp/hoge@test.service
+(! systemd-analyze verify ../tmp/hoge@nonexist.service)
+(! systemd-analyze verify ../tmp/hoge@.service)
+popd
+systemd-analyze verify /tmp/foo@bar.service
+systemd-analyze verify /tmp/foo@.service
+systemd-analyze verify /tmp/hoge@test.service
+(! systemd-analyze verify /tmp/hoge@nonexist.service)
+(! systemd-analyze verify /tmp/hoge@.service)
+
+# test that all commands are verified.
+cat <<EOF >/tmp/multi-exec-start.service
+[Service]
+Type=oneshot
+ExecStart=true
+ExecStart=ls
+EOF
+systemd-analyze verify /tmp/multi-exec-start.service
+echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service
+(! systemd-analyze verify /tmp/multi-exec-start.service)
+
+# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
+# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
+# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
+# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is
+# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure
+# level for the unit file should decrease to account for that increased weight.
+cat <<EOF >/tmp/testfile.json
+{"UserOrDynamicUser":
+ {"description_bad": "Service runs as root user",
+ "weight": 0,
+ "range": 10
+ },
+"SupplementaryGroups":
+ {"description_good": "Service has no supplementary groups",
+ "description_bad": "Service runs with supplementary groups",
+ "description_na": "Service runs as root, option does not matter",
+ "weight": 200,
+ "range": 1
+ },
+"PrivateDevices":
+ {"description_good": "Service has no access to hardware devices",
+ "description_bad": "Service potentially has access to hardware devices",
+ "weight": 1000,
+ "range": 1
+ },
+"PrivateMounts":
+ {"description_good": "Service cannot install system mounts",
+ "description_bad": "Service may install system mounts",
+ "weight": 1000,
+ "range": 1
+ },
+"PrivateNetwork":
+ {"description_good": "Service doesn't have access to the host's network",
+ "description_bad": "Service has access to the host's network",
+ "weight": 6000,
+ "range": 1
+ },
+"PrivateTmp":
+ {"description_good": "Service has no access to other software's temporary files",
+ "description_bad": "Service has access to other software's temporary files",
+ "weight": 1000,
+ "range": 1
+ },
+"PrivateUsers":
+ {"description_good": "Service does not have access to other users",
+ "description_bad": "Service has access to other users",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectControlGroups":
+ {"description_good": "Service cannot modify the control group file system",
+ "description_bad": "Service may modify the control group file system",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectKernelModules":
+ {"description_good": "Service cannot load or read kernel modules",
+ "description_bad": "Service may load or read kernel modules",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectKernelTunables":
+ {"description_good": "Service cannot alter kernel tunables (/proc/sys, …)",
+ "description_bad": "Service may alter kernel tunables",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectKernelLogs":
+ {"description_good": "Service cannot read from or write to the kernel log ring buffer",
+ "description_bad": "Service may read from or write to the kernel log ring buffer",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectClock":
+ {"description_good": "Service cannot write to the hardware clock or system clock",
+ "description_bad": "Service may write to the hardware clock or system clock",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectHome":
+ {"weight": 1000,
+ "range": 10
+ },
+"ProtectHostname":
+ {"description_good": "Service cannot change system host/domainname",
+ "description_bad": "Service may change system host/domainname",
+ "weight": 50,
+ "range": 1
+ },
+"ProtectSystem":
+ {"weight": 1000,
+ "range": 10
+ },
+"RootDirectoryOrRootImage":
+ {"description_good": "Service has its own root directory/image",
+ "description_bad": "Service runs within the host's root directory",
+ "weight": 200,
+ "range": 1
+ },
+"LockPersonality":
+ {"description_good": "Service cannot change ABI personality",
+ "description_bad": "Service may change ABI personality",
+ "weight": 100,
+ "range": 1
+ },
+"MemoryDenyWriteExecute":
+ {"description_good": "Service cannot create writable executable memory mappings",
+ "description_bad": "Service may create writable executable memory mappings",
+ "weight": 100,
+ "range": 1
+ },
+"NoNewPrivileges":
+ {"description_good": "Service processes cannot acquire new privileges",
+ "description_bad": "Service processes may acquire new privileges",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_ADMIN":
+ {"description_good": "Service has no administrator privileges",
+ "description_bad": "Service has administrator privileges",
+ "weight": 1500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP":
+ {"description_good": "Service cannot change UID/GID identities/capabilities",
+ "description_bad": "Service may change UID/GID identities/capabilities",
+ "weight": 1500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_PTRACE":
+ {"description_good": "Service has no ptrace() debugging abilities",
+ "description_bad": "Service has ptrace() debugging abilities",
+ "weight": 1500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_TIME":
+ {"description_good": "Service processes cannot change the system clock",
+ "description_bad": "Service processes may change the system clock",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_NET_ADMIN":
+ {"description_good": "Service has no network configuration privileges",
+ "description_bad": "Service has network configuration privileges",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_RAWIO":
+ {"description_good": "Service has no raw I/O access",
+ "description_bad": "Service has raw I/O access",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_MODULE":
+ {"description_good": "Service cannot load kernel modules",
+ "description_bad": "Service may load kernel modules",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_AUDIT":
+ {"description_good": "Service has no audit subsystem access",
+ "description_bad": "Service has audit subsystem access",
+ "weight": 500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYSLOG":
+ {"description_good": "Service has no access to kernel logging",
+ "description_bad": "Service has access to kernel logging",
+ "weight": 500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE":
+ {"description_good": "Service has no privileges to change resource use parameters",
+ "description_bad": "Service has privileges to change resource use parameters",
+ "weight": 500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_MKNOD":
+ {"description_good": "Service cannot create device nodes",
+ "description_bad": "Service may create device nodes",
+ "weight": 500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP":
+ {"description_good": "Service cannot change file ownership/access mode/capabilities",
+ "description_bad": "Service may change file ownership/access mode/capabilities unrestricted",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER":
+ {"description_good": "Service cannot override UNIX file/IPC permission checks",
+ "description_bad": "Service may override UNIX file/IPC permission checks",
+ "weight": 1000,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_KILL":
+ {"description_good": "Service cannot send UNIX signals to arbitrary processes",
+ "description_bad": "Service may send UNIX signals to arbitrary processes",
+ "weight": 500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW":
+ {"description_good": "Service has no elevated networking privileges",
+ "description_bad": "Service has elevated networking privileges",
+ "weight": 500,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_BOOT":
+ {"description_good": "Service cannot issue reboot()",
+ "description_bad": "Service may issue reboot()",
+ "weight": 100,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_MAC":
+ {"description_good": "Service cannot adjust SMACK MAC",
+ "description_bad": "Service may adjust SMACK MAC",
+ "weight": 100,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE":
+ {"description_good": "Service cannot mark files immutable",
+ "description_bad": "Service may mark files immutable",
+ "weight": 75,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_IPC_LOCK":
+ {"description_good": "Service cannot lock memory into RAM",
+ "description_bad": "Service may lock memory into RAM",
+ "weight": 50,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_CHROOT":
+ {"description_good": "Service cannot issue chroot()",
+ "description_bad": "Service may issue chroot()",
+ "weight": 50,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_BLOCK_SUSPEND":
+ {"description_good": "Service cannot establish wake locks",
+ "description_bad": "Service may establish wake locks",
+ "weight": 25,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_WAKE_ALARM":
+ {"description_good": "Service cannot program timers that wake up the system",
+ "description_bad": "Service may program timers that wake up the system",
+ "weight": 25,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_LEASE":
+ {"description_good": "Service cannot create file leases",
+ "description_bad": "Service may create file leases",
+ "weight": 25,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG":
+ {"description_good": "Service cannot issue vhangup()",
+ "description_bad": "Service may issue vhangup()",
+ "weight": 25,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_SYS_PACCT":
+ {"description_good": "Service cannot use acct()",
+ "description_bad": "Service may use acct()",
+ "weight": 25,
+ "range": 1
+ },
+"CapabilityBoundingSet_CAP_BPF":
+ {"description_good": "Service may load BPF programs",
+ "description_bad": "Service may not load BPF programs",
+ "weight": 25,
+ "range": 1
+ },
+"UMask":
+ {"weight": 100,
+ "range": 10
+ },
+"KeyringMode":
+ {"description_good": "Service doesn't share key material with other services",
+ "description_bad": "Service shares key material with other service",
+ "weight": 1000,
+ "range": 1
+ },
+"ProtectProc":
+ {"description_good": "Service has restricted access to process tree(/proc hidepid=)",
+ "description_bad": "Service has full access to process tree(/proc hidepid=)",
+ "weight": 1000,
+ "range": 3
+ },
+"ProcSubset":
+ {"description_good": "Service has no access to non-process/proc files(/proc subset=)",
+ "description_bad": "Service has full access to non-process/proc files(/proc subset=)",
+ "weight": 10,
+ "range": 1
+ },
+"NotifyAccess":
+ {"description_good": "Service child processes cannot alter service state",
+ "description_bad": "Service child processes may alter service state",
+ "weight": 1000,
+ "range": 1
+ },
+"RemoveIPC":
+ {"description_good": "Service user cannot leave SysV IPC objects around",
+ "description_bad": "Service user may leave SysV IPC objects around",
+ "description_na": "Service runs as root, option does not apply",
+ "weight": 100,
+ "range": 1
+ },
+"Delegate":
+ {"description_good": "Service does not maintain its own delegated control group subtree",
+ "description_bad": "Service maintains its own delegated control group subtree",
+ "weight": 100,
+ "range": 1
+ },
+"RestrictRealtime":
+ {"description_good": "Service realtime scheduling access is restricted",
+ "description_bad": "Service may acquire realtime scheduling",
+ "weight": 500,
+ "range": 1
+ },
+"RestrictSUIDSGID":
+ {"description_good": "SUID/SGIDfilecreationbyserviceisrestricted",
+ "description_bad": "ServicemaycreateSUID/SGIDfiles",
+ "weight": 1000,
+ "range": 1
+ },
+"RestrictNamespaces_user":
+ {"description_good": "Servicecannotcreateusernamespaces",
+ "description_bad": "Servicemaycreateusernamespaces",
+ "weight": 1500,
+ "range": 1
+ },
+"RestrictNamespaces_mnt":
+ {"description_good": "Service cannot create file system namespaces",
+ "description_bad": "Service may create file system namespaces",
+ "weight": 500,
+ "range": 1
+ },
+"RestrictNamespaces_ipc":
+ {"description_good": "Service cannot create IPC namespaces",
+ "description_bad": "Service may create IPC namespaces",
+ "weight": 500,
+ "range": 1
+ },
+"RestrictNamespaces_pid":
+ {"description_good": "Service cannot create process namespaces",
+ "description_bad": "Service may create process namespaces",
+ "weight": 500,
+ "range": 1
+ },
+"RestrictNamespaces_cgroup":
+ {"description_good": "Service cannot create cgroup namespaces",
+ "description_bad": "Service may create cgroup namespaces",
+ "weight": 500,
+ "range": 1
+ },
+"RestrictNamespaces_net":
+ {"description_good": "Service cannot create network namespaces",
+ "description_bad": "Service may create network namespaces",
+ "weight": 500,
+ "range": 1
+ },
+"RestrictNamespaces_uts":
+ {"description_good": "Service cannot create hostname namespaces",
+ "description_bad": "Service may create hostname namespaces",
+ "weight": 100,
+ "range": 1
+ },
+"RestrictAddressFamilies_AF_INET_INET6":
+ {"description_good": "Service cannot allocate Internet sockets",
+ "description_bad": "Service may allocate Internet sockets",
+ "weight": 1500,
+ "range": 1
+ },
+"RestrictAddressFamilies_AF_UNIX":
+ {"description_good": "Service cannot allocate local sockets",
+ "description_bad": "Service may allocate local sockets",
+ "weight": 25,
+ "range": 1
+ },
+"RestrictAddressFamilies_AF_NETLINK":
+ {"description_good": "Service cannot allocate netlink sockets",
+ "description_bad": "Service may allocate netlink sockets",
+ "weight": 200,
+ "range": 1
+ },
+"RestrictAddressFamilies_AF_PACKET":
+ {"description_good": "Service cannot allocate packet sockets",
+ "description_bad": "Service may allocate packet sockets",
+ "weight": 1000,
+ "range": 1
+ },
+"RestrictAddressFamilies_OTHER":
+ {"description_good": "Service cannot allocate exotic sockets",
+ "description_bad": "Service may allocate exotic sockets",
+ "weight": 1250,
+ "range": 1
+ },
+"SystemCallArchitectures":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_swap":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_obsolete":
+ {"weight": 250,
+ "range": 10
+ },
+"SystemCallFilter_clock":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_cpu_emulation":
+ {"weight": 250,
+ "range": 10
+ },
+"SystemCallFilter_debug":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_mount":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_module":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_raw_io":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_reboot":
+ {"weight": 1000,
+ "range": 10
+ },
+"SystemCallFilter_privileged":
+ {"weight": 700,
+ "range": 10
+ },
+"SystemCallFilter_resources":
+ {"weight": 700,
+ "range": 10
+ },
+"IPAddressDeny":
+ {"weight": 1000,
+ "range": 10
+ },
+"DeviceAllow":
+ {"weight": 1000,
+ "range": 10
+ },
+"AmbientCapabilities":
+ {"description_good": "Service process does not receive ambient capabilities",
+ "description_bad": "Service process receives ambient capabilities",
+ "weight": 500,
+ "range": 1
+ },
+"INVALID_ID":
+ {"weight": 1000,
+ "range": 10
+ }
+}
+EOF
+
+# Reads in custom security requirements from the parsed .json file and uses these for comparison
+systemd-analyze security --threshold=90 --offline=true \
+ --security-policy=/tmp/testfile.json \
+ --root=/tmp/img/ testfile.service
+
+# The strict profile adds a lot of sanboxing options
+systemd-analyze security --threshold=25 --offline=true \
+ --security-policy=/tmp/testfile.json \
+ --profile=strict \
+ --root=/tmp/img/ testfile.service
+
+# The trusted profile doesn't add any sanboxing options
+(! systemd-analyze security --threshold=25 --offline=true \
+ --security-policy=/tmp/testfile.json \
+ --profile=/usr/lib/systemd/portable/profile/trusted/service.conf \
+ --root=/tmp/img/ testfile.service)
+
+(! systemd-analyze security --threshold=50 --offline=true \
+ --security-policy=/tmp/testfile.json \
+ --root=/tmp/img/ testfile.service)
+
+rm /tmp/img/usr/lib/systemd/system/testfile.service
+
+if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
+ systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
+fi
+
+systemd-analyze --threshold=90 security systemd-journald.service
+
+# issue 23663
+check() {(
+ set +x
+ output=$(systemd-analyze security --offline="${2?}" "${3?}" | grep -F 'SystemCallFilter=')
+ assert_in "System call ${1?} list" "$output"
+ assert_in "[+✓] SystemCallFilter=~@swap" "$output"
+ assert_in "[+✓] SystemCallFilter=~@resources" "$output"
+ assert_in "[+✓] SystemCallFilter=~@reboot" "$output"
+ assert_in "[+✓] SystemCallFilter=~@raw-io" "$output"
+ assert_in "[-✗] SystemCallFilter=~@privileged" "$output"
+ assert_in "[+✓] SystemCallFilter=~@obsolete" "$output"
+ assert_in "[+✓] SystemCallFilter=~@mount" "$output"
+ assert_in "[+✓] SystemCallFilter=~@module" "$output"
+ assert_in "[+✓] SystemCallFilter=~@debug" "$output"
+ assert_in "[+✓] SystemCallFilter=~@cpu-emulation" "$output"
+ assert_in "[-✗] SystemCallFilter=~@clock" "$output"
+)}
+
+export -n SYSTEMD_LOG_LEVEL
+
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/allow-list.service <<EOF
+[Service]
+ExecStart=false
+SystemCallFilter=@system-service
+SystemCallFilter=~@resources:ENOANO @privileged
+SystemCallFilter=@clock
+EOF
+
+cat >/run/systemd/system/deny-list.service <<EOF
+[Service]
+ExecStart=false
+SystemCallFilter=~@known
+SystemCallFilter=@system-service
+SystemCallFilter=~@resources:ENOANO @privileged
+SystemCallFilter=@clock
+EOF
+
+systemctl daemon-reload
+
+check allow yes /run/systemd/system/allow-list.service
+check allow no allow-list.service
+check deny yes /run/systemd/system/deny-list.service
+check deny no deny-list.service
+
+output=$(systemd-run -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
+name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
+
+check allow yes /run/systemd/transient/"$name"
+check allow no "$name"
+
+output=$(systemd-run -p "SystemCallFilter=~@known" -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
+name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
+
+check deny yes /run/systemd/transient/"$name"
+check deny no "$name"
+
+# Let's also test the "image-policy" verb
+
+systemd-analyze image-policy '*' 2>&1 | grep -q -F "Long form: =verity+signed+encrypted+unprotected+unused+absent"
+systemd-analyze image-policy '-' 2>&1 | grep -q -F "Long form: =unused+absent"
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -F "Long form: usr=verity:home=encrypted:=unused+absent"
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^home \+encrypted \+'
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr \+verity \+'
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^root \+ignore \+'
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr-verity \+unprotected \+'
+
+(! systemd-analyze image-policy 'doedel')
+
+# Output is very hard to predict, but let's run it for coverage anyway
+systemd-analyze pcrs
+systemd-analyze pcrs --json=pretty
+systemd-analyze pcrs 14 7 0 ima
+
+systemd-analyze architectures
+systemd-analyze architectures --json=pretty
+systemd-analyze architectures x86
+systemd-analyze architectures x86-64
+systemd-analyze architectures native
+systemd-analyze architectures uname
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Service that uses device isolation
+
+[Service]
+DevicePolicy=strict
+DeviceAllow=/dev/null r
+StandardOutput=file:/tmp/TEST-66-DEVICE-ISOLATION.serviceresults
+ExecStartPre=rm -f /tmp/TEST-66-DEVICE-ISOLATION.serviceresults
+ExecStart=bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+RESULTS_FILE=/tmp/TEST-66-DEVICE-ISOLATION.serviceresults
+
+systemd-analyze log-level debug
+
+systemctl start TEST-66-DEVICE-ISOLATION-device-isolation.service
+
+sleep 5
+grep -q "Operation not permitted" "$RESULTS_FILE"
+
+systemctl daemon-reload
+systemctl daemon-reexec
+
+systemctl stop TEST-66-DEVICE-ISOLATION-device-isolation.service
+
+grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-66-DEVICEISOLATION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-67-INTEGRITY
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -euxo pipefail
+
+export DM_NAME="integrity_test"
+export FULL_DM_DEV_NAME="/dev/mapper/${DM_NAME}"
+export FS_UUID="01234567-ffff-eeee-eeee-0123456789ab"
+export GEN="/var/run/systemd/generator"
+
+image_dir=""
+
+cleanup()
+{
+ if [ -z "${image_dir}" ]; then
+ return
+ fi
+
+ if [ -f "${image_dir}/image" ]; then
+ if [ -e "${FULL_DM_DEV_NAME}" ]; then
+ integritysetup close "${DM_NAME}"
+ fi
+ losetup -d "${loop}"
+ fi
+
+ rm -rf "${image_dir}"
+}
+
+trap cleanup EXIT
+
+build_integrity_tab()
+{
+cat <<EOF >"/etc/integritytab"
+${DM_NAME} ${loop} - integrity-algorithm=$1
+EOF
+}
+
+image_dir="$(mktemp -d -t -p / integrity.tmp.XXXXXX)"
+if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
+ echo "mktemp under / failed"
+ exit 1
+fi
+
+dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1
+dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1
+loop="$(losetup --show -f "${image_dir}/image")"
+
+if [[ ! -e ${loop} ]]; then
+ echo "Loopback device created not found!"
+ exit 1
+fi
+
+# Do one iteration with a separate data device, to test those branches
+separate_data=1
+
+for algorithm in crc32c crc32 sha1 sha256
+do
+ if [ "${separate_data}" -eq 1 ]; then
+ data_option="--data-device=${image_dir}/data"
+ else
+ data_option=""
+ fi
+ integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1
+ integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1
+ mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1
+
+ # Give userspace time to handle udev events for new FS showing up ...
+ udevadm settle
+
+ integritysetup close "${DM_NAME}" || exit 1
+
+ # create integritytab, generate units, start service
+ if [ "${separate_data}" -eq 1 ]; then
+ data_option=",data-device=${image_dir}/data"
+ else
+ data_option=""
+ fi
+ build_integrity_tab "${algorithm}${data_option}"
+
+ # Cause the generator to re-run
+ systemctl daemon-reload || exit 1
+
+ # Check for existence of unit files...
+ if [[ ! -e "/run/systemd/generator/systemd-integritysetup@${DM_NAME}.service" ]]; then
+ echo "Service file does not exist!"
+ exit 1
+ fi
+
+ # Make sure we are in a consistent state, e.g. not already active before we start
+ systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
+ systemctl start systemd-integritysetup@"${DM_NAME}".service || exit 1
+ # Reset the start-limit counters, as we're going to restart the service a couple of times
+ systemctl reset-failed systemd-integritysetup@"${DM_NAME}".service
+
+ # Check the signature on the FS to ensure we can retrieve it and that is matches
+ if [ -e "${FULL_DM_DEV_NAME}" ]; then
+ # If a separate device is used for the metadata storage, then blkid will return one of the loop devices
+ if [ "${separate_data}" -eq 1 ]; then
+ dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')"
+ else
+ dev_name="${FULL_DM_DEV_NAME}"
+ fi
+ if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then
+ echo "Failed to locate FS with matching UUID!"
+ exit 1
+ fi
+ else
+ echo "Failed to bring up integrity device!"
+ exit 1
+ fi
+
+ systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
+
+ if [ -e "${FULL_DM_DEV_NAME}" ]; then
+ echo "Expecting ${FULL_DM_DEV_NAME} to not exist after stopping unit!"
+ exit 1
+ fi
+
+ separate_data=0
+done
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-68-PROPAGATE-EXIT-STATUS
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# Wait for a service to enter a state within a timeout period, if it doesn't
+# enter the desired state within the timeout period then this function will
+# exit the test case with a non zero exit code.
+wait_on_state_or_fail() {
+ service=$1
+ expected_state=$2
+ timeout=$3
+
+ state=$(systemctl show "$service" --property=ActiveState --value)
+ while [ "$state" != "$expected_state" ]; do
+ if [ "$timeout" = "0" ]; then
+ systemd-analyze log-level info
+ exit 1
+ fi
+ timeout=$((timeout - 1))
+ sleep 1
+ state=$(systemctl show "$service" --property=ActiveState --value)
+ done
+}
+
+systemd-analyze log-level debug
+
+cat >/run/systemd/system/testservice-failure-68.service <<EOF
+[Unit]
+OnFailure=testservice-failure-exit-handler-68.service
+
+[Service]
+ExecStart=sh -c "exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-failure-68-template.service <<EOF
+[Unit]
+OnFailure=testservice-failure-exit-handler-68-template@%n.service
+
+[Service]
+ExecStart=sh -c "exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-success-68.service <<EOF
+[Unit]
+OnSuccess=testservice-success-exit-handler-68.service
+
+[Service]
+ExecStart=sh -c "exit 0"
+EOF
+
+cat >/run/systemd/system/testservice-success-68-template.service <<EOF
+[Unit]
+OnSuccess=testservice-success-exit-handler-68-template@%n.service
+
+[Service]
+ExecStart=sh -c "exit 0"
+EOF
+
+# Script to check that when an OnSuccess= dependency fires, the correct
+# MONITOR* env variables are passed.
+cat >/tmp/check_on_success.sh <<"EOF"
+#!/bin/sh
+
+set -ex
+env | sort
+if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then
+ echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'"
+ exit 1
+fi
+
+if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
+ echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
+ exit 1
+fi
+
+if [ "$MONITOR_EXIT_STATUS" != "0" ]; then
+ echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'"
+ exit 1
+fi
+
+if [ -z "$MONITOR_INVOCATION_ID" ]; then
+ echo "MONITOR_INVOCATION_ID unset"
+ exit 1
+fi
+
+if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] &&
+ [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] &&
+ [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then
+
+ echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'"
+ exit 1
+fi
+
+exit 0
+EOF
+chmod +x /tmp/check_on_success.sh
+
+cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF
+[Service]
+ExecStartPre=/tmp/check_on_success.sh
+ExecStart=/tmp/check_on_success.sh
+EOF
+
+cp /run/systemd/system/testservice-success-exit-handler-68.service \
+ /run/systemd/system/testservice-transient-success-exit-handler-68.service
+
+# Template version.
+cat >/run/systemd/system/testservice-success-exit-handler-68-template@.service <<EOF
+[Service]
+ExecStartPre=echo "triggered by %i"
+ExecStartPre=/tmp/check_on_success.sh
+ExecStart=/tmp/check_on_success.sh
+EOF
+
+# Script to check that when an OnFailure= dependency fires, the correct
+# MONITOR* env variables are passed.
+cat >/tmp/check_on_failure.sh <<"EOF"
+#!/bin/sh
+
+set -ex
+env | sort
+if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then
+ echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'"
+ exit 1
+fi
+
+if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
+ echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
+ exit 1
+fi
+
+if [ "$MONITOR_EXIT_STATUS" != "1" ]; then
+ echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'"
+ exit 1
+fi
+
+if [ -z "$MONITOR_INVOCATION_ID" ]; then
+ echo "MONITOR_INVOCATION_ID unset"
+ exit 1
+fi
+
+if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] &&
+ [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] &&
+ [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then
+
+ echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'"
+ exit 1
+fi
+
+exit 0
+EOF
+chmod +x /tmp/check_on_failure.sh
+
+
+cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF
+[Service]
+# repeat the check to make sure that values are set correctly on repeated invocations
+Type=oneshot
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
+EOF
+
+cp /run/systemd/system/testservice-failure-exit-handler-68.service \
+ /run/systemd/system/testservice-transient-failure-exit-handler-68.service
+
+# Template version.
+cat >/run/systemd/system/testservice-failure-exit-handler-68-template@.service <<EOF
+[Service]
+Type=oneshot
+ExecStartPre=echo "triggered by %i"
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
+EOF
+
+systemctl daemon-reload
+
+: "-------I----------------------------------------------------"
+systemctl start testservice-failure-68.service
+wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
+
+: "-------II---------------------------------------------------"
+systemctl start testservice-success-68.service
+wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
+
+# Test some transient units since these exit very quickly.
+: "-------III--------------------------------------------------"
+systemd-run --unit=testservice-transient-success-68 \
+ --property=OnSuccess=testservice-transient-success-exit-handler-68.service \
+ sh -c "exit 0"
+wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
+
+: "-------IIII-------------------------------------------------"
+systemd-run --unit=testservice-transient-failure-68 \
+ --property=OnFailure=testservice-transient-failure-exit-handler-68.service \
+ sh -c "exit 1"
+wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
+
+# Test template handlers too
+: "-------V---------------------------------------------------"
+systemctl start testservice-success-68-template.service
+wait_on_state_or_fail "testservice-success-exit-handler-68-template@testservice-success-68-template.service.service" "inactive" "10"
+
+: "-------VI----------------------------------------------------"
+systemctl start testservice-failure-68-template.service
+wait_on_state_or_fail "testservice-failure-exit-handler-68-template@testservice-failure-68-template.service.service" "inactive" "10"
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-69-SHUTDOWN
+
+[Service]
+Type=oneshot
+ExecStart=true
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+# Ensure that sandboxing doesn't stop creds from being accessible
+echo "test" > /tmp/testdata
+systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2
+# LoadCredentialEncrypted
+systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
+# SetCredentialEncrypted
+systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
+
+rm -f /tmp/testdata
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+cryptenroll_wipe_and_check() {(
+ set +o pipefail
+
+ : >/tmp/cryptenroll.out
+ systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out
+ grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out
+)}
+
+# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it
+if [[ "$(uname -m)" == "ppc64le" ]]; then
+ echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716"
+ exit 0
+fi
+
+export SYSTEMD_LOG_LEVEL=debug
+IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)"
+
+truncate -s 20M "$IMAGE"
+echo -n password >/tmp/password
+# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages
+chmod 0600 /tmp/password
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password
+
+# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff
+systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE"
+NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE"
+NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE"
+for _ in {0..9}; do
+ systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE"
+done
+PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
+# Do some basic checks before we start wiping stuff
+systemd-cryptenroll "$IMAGE"
+systemd-cryptenroll "$IMAGE" | grep password
+systemd-cryptenroll "$IMAGE" | grep recovery
+# Let's start wiping
+cryptenroll_wipe_and_check "$IMAGE" --wipe=empty
+(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty)
+cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0
+PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password
+systemd-cryptenroll "$IMAGE" | grep password
+(! systemd-cryptenroll "$IMAGE" | grep recovery)
+# We shouldn't be able to wipe all keyslots without enrolling a new key first
+(! systemd-cryptenroll "$IMAGE" --wipe=all)
+PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all
+# Check if the newly (and only) enrolled password works
+(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE")
+(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE")
+PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE"
+
+systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE"
+systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE"
+systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE"
+systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE"
+systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE"
+
+# Unlocking using TPM2
+PASSWORD=foo systemd-cryptenroll --tpm2-device=auto "$IMAGE"
+systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
+systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --wipe-slot=tpm2 "$IMAGE"
+
+# Add PIN to TPM2 enrollment
+NEWPIN=1234 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
+
+# Change PIN on TPM2 enrollment
+PIN=1234 NEWPIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
+PIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
+
+(! systemd-cryptenroll --fido2-with-client-pin=false)
+(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo)
+(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE")
+(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE")
+(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE")
+(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE")
+(! systemd-cryptenroll --recovery-key --password "$IMAGE")
+(! systemd-cryptenroll --password --recovery-key "$IMAGE")
+(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE")
+(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE")
+(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE")
+(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
+(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE")
+(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE")
+(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE")
+(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE")
+(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE")
+(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE")
+(! systemd-cryptenroll --wipe-slot "$IMAGE")
+(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE")
+(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
+
+rm -f "$IMAGE"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+cryptsetup_has_token_plugin_support() {
+ local plugin_path
+
+ plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)"
+ cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]]
+}
+
+tpm_check_failure_with_wrong_pin() {
+ local testIMAGE="${1:?}"
+ local badpin="${2:?}"
+ local goodpin="${3:?}"
+
+ # We need to be careful not to trigger DA lockout; allow 2 failures
+ tpm2_dictionarylockout -s -n 2
+ (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1)
+ # Verify the correct PIN works, to be sure the failure wasn't a DA lockout
+ PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1
+ systemd-cryptsetup detach test-volume
+ # Clear/reset the DA lockout counter
+ tpm2_dictionarylockout -c
+}
+
+at_exit() {
+ # Evict the TPM primary key that we persisted
+ if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then
+ tpm2_evictcontrol -c "$PERSISTENT_HANDLE"
+ fi
+}
+
+trap at_exit EXIT
+
+# Prepare a fresh disk image
+IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)"
+
+truncate -s 20M "$IMAGE"
+echo -n passphrase >/tmp/passphrase
+# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages
+chmod 0600 /tmp/passphrase
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
+
+# Unlocking via keyfile
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE"
+
+# Enroll unlock with default PCR policy
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Check with wrong PCR
+tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+
+# Enroll unlock with PCR+PIN policy
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
+PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout
+for _ in {0..3}; do
+ tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
+done
+
+# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
+if cryptsetup_has_token_plugin_support; then
+ PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1
+ systemd-cryptsetup detach test-volume
+
+ # Check failure with wrong PIN
+ for _ in {0..3}; do
+ tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
+ done
+else
+ echo 'cryptsetup has no LUKS2 token plugin support, skipping'
+fi
+
+# Check failure with wrong PCR (and correct PIN)
+tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+
+# Enroll unlock with PCR 0+7
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Check with wrong PCR 0
+tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+
+if tpm_has_pcr sha256 12; then
+ # Enroll using an explicit PCR value (that does match current PCR value)
+ systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+ EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
+ systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+ systemd-cryptsetup detach test-volume
+
+ # Same as above plus more PCRs without the value or alg specified
+ systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+ EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
+ systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+ systemd-cryptsetup detach test-volume
+
+ # Same as above plus more PCRs with hash alg specified but hash value not specified
+ systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+ EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
+ systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+ systemd-cryptsetup detach test-volume
+
+ # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value
+ systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+ tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
+ CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
+ (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+ tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE"
+ systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+ systemd-cryptsetup detach test-volume
+
+ # enroll TPM using device key instead of direct access, then verify unlock using TPM
+ tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
+ CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
+ systemd-analyze srk > /tmp/srk2.pub
+ cmp /tmp/srk.pub /tmp/srk2.pub
+ if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then
+ cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public
+ fi
+
+ # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
+ if openssl_supports_kdf SSKDF; then
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE"
+ systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+ systemd-cryptsetup detach test-volume
+ fi
+
+ rm -f /tmp/pcr.dat /tmp/srk.pub
+fi
+
+# Use default (0) seal key handle
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Use SRK seal key handle
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Test invalid ranges: pcr, nv, session, permanent
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE") # PCR
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent
+
+# Use non-SRK persistent seal key handle (by creating/persisting new key)
+PRIMARY=/tmp/primary.ctx
+tpm2_createprimary -c "$PRIMARY"
+PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle)
+PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}"
+tpm2_flushcontext -t
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
+if openssl_supports_kdf SSKDF; then
+ # Make sure that --tpm2-device-key= also works with systemd-repart
+ tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
+ mkdir /tmp/dditest
+ cat > /tmp/dditest/50-root.conf <<EOF
+[Partition]
+Type=root
+Format=ext4
+CopyFiles=/tmp/dditest:/
+Encrypt=tpm2
+EOF
+ PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=50M /tmp/dditest.raw --tpm2-pcrs=
+ DEVICE="$(systemd-dissect --attach /tmp/dditest.raw)"
+ udevadm wait --settle --timeout=10 "$DEVICE"
+ systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes
+ mkdir /tmp/dditest.mnt
+ mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt
+ cmp /tmp/dditest.mnt/50-root.conf /tmp/dditest/50-root.conf
+ umount /tmp/dditest.mnt
+ rmdir /tmp/dditest.mnt
+ rm /tmp/dditest.raw
+ rm /tmp/dditest/50-root.conf
+ rmdir /tmp/dditest
+fi
+
+rm -f "$IMAGE" "$PRIMARY"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+SD_MEASURE="/usr/lib/systemd/systemd-measure"
+
+if [[ ! -x "${SD_MEASURE:?}" ]]; then
+ echo "$SD_MEASURE not found, skipping the test"
+ exit 0
+fi
+
+IMAGE="$(mktemp /tmp/systemd-measure-XXX.image)"
+
+echo HALLO >/tmp/tpmdata1
+echo foobar >/tmp/tpmdata2
+
+cat >/tmp/result <<EOF
+11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
+11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
+11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
+11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: | cmp - /tmp/result
+
+cat >/tmp/result.json <<EOF
+{"sha1":[{"pcr":11,"hash":"5177e4ad69db92192c10e5f80402bf81bfec8a81"}],"sha256":[{"pcr":11,"hash":"37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13"}],"sha384":[{"pcr":11,"hash":"5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087"}],"sha512":[{"pcr":11,"hash":"961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b"}]}
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: -j | diff -u - /tmp/result.json
+
+cat >/tmp/result <<EOF
+11:sha1=6765ee305db063040c454d32697d922b3d4f232b
+11:sha256=21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699
+11:sha384=08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2
+11:sha512=65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo | cmp - /tmp/result
+
+cat >/tmp/result.json <<EOF
+{"sha1":[{"phase":"foo","pcr":11,"hash":"6765ee305db063040c454d32697d922b3d4f232b"}],"sha256":[{"phase":"foo","pcr":11,"hash":"21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699"}],"sha384":[{"phase":"foo","pcr":11,"hash":"08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2"}],"sha512":[{"phase":"foo","pcr":11,"hash":"65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0"}]}
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo -j | diff -u - /tmp/result.json
+
+rm /tmp/result /tmp/result.json
+
+if ! tpm_has_pcr sha1 11 || ! tpm_has_pcr sha256 11; then
+ echo "PCR sysfs files not found, skipping signed PCR policy tests"
+ exit 0
+fi
+
+# Generate key pair
+openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "/tmp/pcrsign-private.pem"
+openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
+
+MEASURE_BANKS=("--bank=sha256")
+# Check if SHA1 signatures are supported
+#
+# Some distros have started phasing out SHA1, so make sure the SHA1
+# signatures are supported before trying to use them.
+if echo hello | openssl dgst -sign /tmp/pcrsign-private.pem -sha1 >/dev/null; then
+ MEASURE_BANKS+=("--bank=sha1")
+fi
+
+# Sign current PCR state with it
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
+dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
+systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
+systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
+
+# Invalidate PCR, decrypting should fail now
+tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null)
+
+# Sign new PCR state, decrypting should work now.
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2"
+systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
+
+# Now, do the same, but with a cryptsetup binding
+truncate -s 20M "$IMAGE"
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
+# Ensure that an unrelated signature, when not requested, is not used
+touch /run/systemd/tpm2-pcr-signature.json
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE"
+# Reset and use the signature now
+rm -f /run/systemd/tpm2-pcr-signature.json
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE"
+
+# Check if we can activate that (without the token module stuff)
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2
+
+# Check if we can activate that (and a second time with the token module stuff enabled)
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2
+
+# After extending the PCR things should fail
+tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
+(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
+
+# But once we sign the current PCRs, we should be able to unlock again
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3"
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
+systemd-cryptsetup detach test-volume2
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
+systemd-cryptsetup detach test-volume2
+
+# Test --append mode and de-duplication. With the same parameters signing should not add a new entry
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4"
+cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4"
+
+# Sign one more phase, this should
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5"
+(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5")
+
+# Should still be good to unlock, given the old entry still exists
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1
+systemd-cryptsetup detach test-volume2
+
+# Adding both signatures once more should not change anything, due to the deduplication
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6"
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7"
+cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7"
+
+rm -f "$IMAGE"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
+
+if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then
+ echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests"
+ exit 0
+fi
+
+at_exit() {
+ if [[ $? -ne 0 ]]; then
+ # Dump the event log on fail, to make debugging a bit easier
+ jq --seq --slurp </run/log/systemd/tpm2-measure.log
+ fi
+}
+
+trap at_exit EXIT
+
+# Temporarily override sd-pcrextend's sanity checks
+export SYSTEMD_FORCE_MEASURE=1
+
+"$SD_PCREXTEND" --help
+"$SD_PCREXTEND" --version
+"$SD_PCREXTEND" foo
+"$SD_PCREXTEND" --machine-id
+"$SD_PCREXTEND" --tpm2-device=list
+"$SD_PCREXTEND" --tpm2-device=auto foo
+"$SD_PCREXTEND" --tpm2-device=/dev/tpm0 foo
+"$SD_PCREXTEND" --bank=sha256 foo
+"$SD_PCREXTEND" --bank=sha256 --bank=sha256 foo
+"$SD_PCREXTEND" --graceful foo
+"$SD_PCREXTEND" --pcr=15 foo
+"$SD_PCREXTEND" --file-system=/
+"$SD_PCREXTEND" --file-system=/tmp --file-system=/
+"$SD_PCREXTEND" --file-system=/tmp --file-system=/ --pcr=15 --pcr=11
+
+if tpm_has_pcr sha1 11; then
+ "$SD_PCREXTEND" --bank=sha1 --pcr=11 foo
+fi
+
+(! "$SD_PCREXTEND")
+(! "$SD_PCREXTEND" "")
+(! "$SD_PCREXTEND" foo bar)
+(! "$SD_PCREXTEND" --bank= foo)
+(! "$SD_PCREXTEND" --tpm2-device= foo)
+(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
+(! "$SD_PCREXTEND" --pcr= foo)
+(! "$SD_PCREXTEND" --pcr=-1 foo)
+(! "$SD_PCREXTEND" --pcr=1024 foo)
+(! "$SD_PCREXTEND" --foo=bar)
+
+unset SYSTEMD_FORCE_MEASURE
+
+# Note: since we're reading the TPM event log as json-seq, the same rules apply to the output
+# as well, i.e. each record is prefixed by RS (0x1E, 036) and suffixed by LF (0x0A, 012).
+# LF is usually eaten by bash, but RS needs special handling.
+
+# Save the number of events in the current event log, so we can skip them when
+# checking changes caused by following tests
+RECORD_COUNT="$(jq --seq --slurp '. | length' </run/log/systemd/tpm2-measure.log | tr -d '\036')"
+
+# Let's measure the machine ID
+tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
+mv /etc/machine-id /etc/machine-id.save
+echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
+SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id
+mv /etc/machine-id.save /etc/machine-id
+tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
+
+# And check it matches expectations
+diff /tmp/newpcr15 \
+ <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
+
+# Check that the event log record was properly written
+test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')"
+DIGEST_EXPECTED="$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)"
+DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
+test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
+
+RECORD_COUNT=$((RECORD_COUNT + 1))
+# And similar for the boot phase measurement into PCR 11
+tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
+# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...)
+mkdir -p /run/systemd/system/systemd-pcrextend.socket.d
+cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf <<EOF
+[Unit]
+# Turn off all conditions */
+ConditionSecurity=
+EOF
+systemctl daemon-reload
+systemctl restart systemd-pcrextend.socket
+varlinkctl call /run/systemd/io.systemd.PCRExtend io.systemd.PCRExtend.Extend '{"pcr":11,"text":"foobar"}'
+tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
+
+diff /tmp/newpcr11 \
+ <(cat /tmp/oldpcr11 <(echo -n "foobar" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
+
+# Check the event log for the 2nd new record since $RECORD_COUNT
+test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e11')"
+DIGEST_EXPECTED="$(echo -n "foobar" | openssl dgst -hex -sha256 -r)"
+DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
+test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
+
+# Measure a file system into PCR 15
+tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
+SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --file-system=/
+# Put together the "file system word" we just sent to the TPM
+# file-system:MOUNTPOINT:TYPE:UUID:LABEL:PART_ENTRY_UUID:PART_ENTRY_TYPE:PART_ENTRY_NAME
+ROOT_DEVICE="$(findmnt -n -o SOURCE /)"
+FS_WORD="$(lsblk -n -o MOUNTPOINT,FSTYPE,UUID,LABEL,PARTUUID,PARTTYPE,PARTLABEL "$ROOT_DEVICE" | sed -r 's/[ ]+/:/g')"
+tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
+
+# And check if it matches with the current PCR 15 state
+diff /tmp/newpcr15 \
+ <(cat /tmp/oldpcr15 <(echo -n "file-system:$FS_WORD" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
+
+rm -f /tmp/oldpcr{11,15} /tmp/newpcr{11,15}
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+export PAGER=
+SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
+SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
+
+if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] ; then
+ echo "$SD_PCREXTEND or $SD_PCRLOCK not found, skipping pcrlock tests"
+ exit 0
+fi
+
+at_exit() {
+ if [[ $? -ne 0 ]]; then
+ # Dump the event log on fail, to make debugging a bit easier
+ [[ -e /run/log/systemd/tpm2-measure.log ]] && jq --seq --slurp </run/log/systemd/tpm2-measure.log
+ fi
+
+ return 0
+}
+
+trap at_exit EXIT
+
+# Temporarily override sd-pcrextend's sanity checks
+export SYSTEMD_FORCE_MEASURE=1
+
+# The PCRs we are going to lock to. We exclude the various PCRs we touched
+# above where no event log record was written, because we cannot analyze
+# things without event log. We include debug PCR 16, see below.
+PCRS="1+2+3+4+5+16"
+
+# Remove the old measurement log, as it contains all kinds of nonsense from the
+# previous test, which will fail our consistency checks. Removing the file also
+# means we'll fail consistency check, but at least we'll fail them consistently
+# (as the PCR values simply won't match the log).
+rm -f /run/log/systemd/tpm2-measure.log
+
+# Ensure a truncated log doesn't crash pcrlock
+echo -n -e \\x1e >/tmp/borked
+set +e
+SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty
+ret=$?
+set -e
+# If it crashes the exit code will be 149
+test $ret -eq 1
+
+SYSTEMD_COLORS=256 "$SD_PCRLOCK"
+"$SD_PCRLOCK" cel --no-pager --json=pretty
+"$SD_PCRLOCK" log --pcr="$PCRS"
+"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"
+"$SD_PCRLOCK" list-components
+"$SD_PCRLOCK" list-components --location=250-
+"$SD_PCRLOCK" list-components --location=250-:350-
+"$SD_PCRLOCK" lock-firmware-config
+"$SD_PCRLOCK" lock-gpt
+"$SD_PCRLOCK" lock-machine-id
+"$SD_PCRLOCK" lock-file-system
+"$SD_PCRLOCK" lock-file-system /
+"$SD_PCRLOCK" predict --pcr="$PCRS"
+"$SD_PCRLOCK" predict --pcr="0x1+0x3+4"
+"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS"
+
+SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
+if [[ -n "$SD_STUB" ]]; then
+ "$SD_PCRLOCK" lock-pe "$SD_STUB"
+ "$SD_PCRLOCK" lock-pe <"$SD_STUB"
+ "$SD_PCRLOCK" lock-uki "$SD_STUB"
+ "$SD_PCRLOCK" lock-uki <"$SD_STUB"
+fi
+
+PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
+# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
+
+img="/tmp/pcrlock.img"
+truncate -s 20M "$img"
+echo -n hoho >/tmp/pcrlockpwd
+chmod 0600 /tmp/pcrlockpwd
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd
+
+systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --wipe-slot=tpm2 "$img"
+systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
+systemd-cryptsetup detach pcrlock
+
+# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail
+"$SD_PCREXTEND" --pcr=16 test70
+
+"$SD_PCRLOCK" cel --json=pretty
+
+(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless )
+
+# Now add a component for it, rebuild policy and it should work (we'll rebuild
+# once like that, but don't provide the recovery pin. This should fail, since
+# the PCR is hosed after all. But then we'll use recovery pin, and it should
+# work.
+echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
+(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
+PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
+
+systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
+systemd-cryptsetup detach pcrlock
+
+# And now let's do it the clean way, and generate the right policy ahead of time.
+echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+# the next one should be skipped because redundant
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+# but this one should not be skipped, even if redundant, because we force it
+"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
+
+"$SD_PCREXTEND" --pcr=16 test70-take-two
+
+"$SD_PCRLOCK" cel --json=pretty
+
+systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
+systemd-cryptsetup detach pcrlock
+
+# Now use the root fs support, i.e. make the tool write a copy of the pcrlock
+# file as service credential to some temporary dir and remove the local copy, so that
+# it has to use the credential version.
+mkdir /tmp/fakexbootldr
+SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
+mv /var/lib/systemd/pcrlock.json /var/lib/systemd/pcrlock.json.gone
+
+systemd-creds decrypt /tmp/fakexbootldr/loader/credentials/pcrlock.*.cred
+
+SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY=/tmp/fakexbootldr/loader/credentials systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,headless
+systemd-cryptsetup detach pcrlock
+
+mv /var/lib/systemd/pcrlock.json.gone /var/lib/systemd/pcrlock.json
+SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" remove-policy
+
+"$SD_PCRLOCK" unlock-firmware-config
+"$SD_PCRLOCK" unlock-gpt
+"$SD_PCRLOCK" unlock-machine-id
+"$SD_PCRLOCK" unlock-file-system
+"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock
+"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock
+
+(! "$SD_PCRLOCK" "")
+(! "$SD_PCRLOCK" predict --pcr=-1)
+(! "$SD_PCRLOCK" predict --pcr=foo)
+(! "$SD_PCRLOCK" predict --pcr=1+1)
+(! "$SD_PCRLOCK" predict --pcr=1+++++1)
+(! "$SD_PCRLOCK" make-policy --nv-index=0)
+(! "$SD_PCRLOCK" make-policy --nv-index=foo)
+(! "$SD_PCRLOCK" list-components --location=:)
+(! "$SD_PCRLOCK" lock-gpt "")
+(! "$SD_PCRLOCK" lock-gpt /dev/sr0)
+(! "$SD_PCRLOCK" lock-pe /dev/full)
+(! "$SD_PCRLOCK" lock-pe /bin/true)
+(! "$SD_PCRLOCK" lock-uki /dev/full)
+(! "$SD_PCRLOCK" lock-uki /bin/true)
+(! "$SD_PCRLOCK" lock-file-system "")
+
+# Exercise Varlink API a bit (but first turn off condition)
+
+mkdir -p /run/systemd/system/systemd-pcrlock.socket.d
+cat > /run/systemd/system/systemd-pcrlock.socket.d/50-no-condition.conf <<EOF
+[Unit]
+# Turn off all conditions
+ConditionSecurity=
+EOF
+
+systemctl daemon-reload
+systemctl restart systemd-pcrlock.socket
+
+varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.RemovePolicy '{}'
+varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.MakePolicy '{}'
+varlinkctl call --collect --json=pretty /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.ReadEventLog '{}'
+
+rm "$img" /tmp/pcrlockpwd
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-70-TPM2
+Wants=tpm2.target
+After=tpm2.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
+
+if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then
+ echo "$SD_TPM2SETUP not found, skipping the test"
+ exit 0
+fi
+
+"$SD_TPM2SETUP" --help
+"$SD_TPM2SETUP" --version
+"$SD_TPM2SETUP" --tpm2-device=list
+"$SD_TPM2SETUP" --tpm2-device=auto
+"$SD_TPM2SETUP" --tpm2-device=/dev/tpm0
+"$SD_TPM2SETUP" --early=yes
+"$SD_TPM2SETUP" --early=yes
+"$SD_TPM2SETUP" --early=no
+"$SD_TPM2SETUP" --early=no
+
+(! "$SD_TPM2SETUP" "")
+(! "$SD_TPM2SETUP" --tpm2-device=)
+(! "$SD_TPM2SETUP" --tpm2-device=/dev/null)
+(! "$SD_TPM2SETUP" --foo=bar)
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-71-HOSTNAME
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+restore_hostname() {
+ if [[ -e /tmp/hostname.bak ]]; then
+ mv /tmp/hostname.bak /etc/hostname
+ else
+ rm -f /etc/hostname
+ fi
+}
+
+testcase_hostname() {
+ local orig=
+
+ if [[ -f /etc/hostname ]]; then
+ cp /etc/hostname /tmp/hostname.bak
+ orig=$(cat /etc/hostname)
+ fi
+
+ trap restore_hostname RETURN
+
+ # should activate daemon and work
+ if [[ -n "$orig" ]]; then
+ assert_in "Static hostname: $orig" "$(hostnamectl)"
+ fi
+ assert_in "Kernel: $(uname -s) $(uname -r)" "$(hostnamectl)"
+
+ # change hostname
+ assert_rc 0 hostnamectl set-hostname testhost
+ assert_eq "$(cat /etc/hostname)" "testhost"
+ assert_in "Static hostname: testhost" "$(hostnamectl)"
+
+ if [[ -n "$orig" ]]; then
+ # reset to original
+ assert_rc 0 hostnamectl set-hostname "$orig"
+ assert_eq "$(cat /etc/hostname)" "$orig"
+ assert_in "Static hostname: $orig" "$(hostnamectl)"
+ fi
+}
+
+restore_machine_info() {
+ if [[ -e /tmp/machine-info.bak ]]; then
+ mv /tmp/machine-info.bak /etc/machine-info
+ else
+ rm -f /etc/machine-info
+ fi
+}
+
+get_chassis() (
+ # shellcheck source=/dev/null
+ . /etc/machine-info
+
+ echo "$CHASSIS"
+)
+
+stop_hostnamed() {
+ systemctl stop systemd-hostnamed.service
+ # Reset trigger limit. This might fail if the unit was unloaded already, so ignore any errors.
+ systemctl reset-failed systemd-hostnamed || :
+}
+
+testcase_chassis() {
+ local i
+
+ if [[ -f /etc/machine-info ]]; then
+ cp /etc/machine-info /tmp/machine-info.bak
+ fi
+
+ trap restore_machine_info RETURN
+
+ # Invalid chassis type is refused
+ assert_rc 1 hostnamectl chassis hoge
+
+ # Valid chassis types
+ for i in vm container desktop laptop convertible server tablet handset watch embedded; do
+ hostnamectl chassis "$i"
+ assert_eq "$(hostnamectl chassis)" "$i"
+ assert_eq "$(get_chassis)" "$i"
+ done
+
+ stop_hostnamed
+ rm -f /etc/machine-info
+
+ # fallback chassis type
+ if systemd-detect-virt --quiet --container; then
+ assert_eq "$(hostnamectl chassis)" container
+ elif systemd-detect-virt --quiet --vm; then
+ assert_eq "$(hostnamectl chassis)" vm
+ fi
+}
+
+restore_sysfs_dmi() {
+ umount /sys/class/dmi/id
+ rm -rf /run/systemd/system/systemd-hostnamed.service.d
+ systemctl daemon-reload
+ stop_hostnamed
+}
+
+testcase_firmware_date() {
+ # No DMI on s390x or ppc
+ if [[ ! -d /sys/class/dmi/id ]]; then
+ echo "/sys/class/dmi/id not found, skipping firmware date tests."
+ return 0
+ fi
+
+ trap restore_sysfs_dmi RETURN
+
+ # Ignore /sys being mounted as tmpfs
+ mkdir -p /run/systemd/system/systemd-hostnamed.service.d/
+ cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
+[Service]
+Environment="SYSTEMD_DEVICE_VERIFY_SYSFS=0"
+Environment="SYSTEMD_HOSTNAME_FORCE_DMI=1"
+EOF
+ systemctl daemon-reload
+
+ mount -t tmpfs none /sys/class/dmi/id
+ echo '1' >/sys/class/dmi/id/uevent
+
+ echo '09/08/2000' >/sys/class/dmi/id/bios_date
+ stop_hostnamed
+ assert_in '2000-09-08' "$(hostnamectl)"
+
+ echo '2022' >/sys/class/dmi/id/bios_date
+ stop_hostnamed
+ assert_not_in 'Firmware Date' "$(hostnamectl)"
+
+ echo 'garbage' >/sys/class/dmi/id/bios_date
+ stop_hostnamed
+ assert_not_in 'Firmware Date' "$(hostnamectl)"
+}
+
+testcase_nss-myhostname() {
+ local database host i
+
+ HOSTNAME="$(hostnamectl hostname)"
+
+ # Set up a dummy network for _gateway and _outbound labels
+ ip link add foo type dummy
+ ip link set up dev foo
+ ip addr add 10.0.0.2/24 dev foo
+ for i in {128..150}; do
+ ip addr add "10.0.0.$i/24" dev foo
+ done
+ ip route add 10.0.0.1 dev foo
+ ip route add default via 10.0.0.1 dev foo
+
+ # Note: `getent hosts` probes gethostbyname2(), whereas `getent ahosts` probes gethostbyname3()
+ # and gethostbyname4() (through getaddrinfo() -> gaih_inet() -> get_nss_addresses())
+ getent hosts -s myhostname
+ getent ahosts -s myhostname
+
+ # With IPv6 disabled
+ sysctl -w net.ipv6.conf.all.disable_ipv6=1
+ # Everything under .localhost and .localhost.localdomain should resolve to localhost
+ for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
+ run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname "$host"
+ run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahosts -s myhostname "$host"
+ run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahostsv4 -s myhostname "$host"
+ (! getent ahostsv6 -s myhostname localhost)
+ done
+ for i in 2 {128..150}; do
+ run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
+ run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
+ run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
+ run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "10.0.0.$i"
+ run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
+ run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
+ done
+ for database in hosts ahosts ahostsv4 ahostsv6; do
+ (! getent "$database" -s myhostname ::1)
+ done
+ (! getent ahostsv6 -s myhostname "$HOSTNAME")
+ run_and_grep -n "^fe80:[^ ]+\s+STREAM$" getent ahosts -s myhostname "$HOSTNAME"
+
+ # With IPv6 enabled
+ sysctl -w net.ipv6.conf.all.disable_ipv6=0
+ # Everything under .localhost and .localhost.localdomain should resolve to localhost
+ for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
+ run_and_grep "^::1\s+localhost$" getent hosts -s myhostname "$host"
+ run_and_grep "^::1\s+STREAM" getent ahosts -s myhostname "$host"
+ run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
+ run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahostsv4 -s myhostname "$host"
+ run_and_grep -n "^::1\s+STREAM" getent ahostsv4 -s myhostname "$host"
+ run_and_grep "^::1\s+STREAM" getent ahostsv6 -s myhostname "$host"
+ run_and_grep -n "^127\.0\.0\.1\s+STREAM" getent ahostsv6 -s myhostname "$host"
+ done
+ for i in 2 {128..150}; do
+ run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
+ run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
+ run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
+ run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
+ done
+ run_and_grep "^fe80:[^ ]+\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
+ run_and_grep "^fe80:[^ ]+\s+STREAM" getent ahosts -s myhostname "$HOSTNAME"
+ run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname 127.0.0.1
+ run_and_grep "^127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahosts -s myhostname 127.0.0.1
+ run_and_grep "^::ffff:127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahostsv6 -s myhostname 127.0.0.1
+ run_and_grep "^127\.0\.0\.2\s+$HOSTNAME$" getent hosts -s myhostname 127.0.0.2
+ run_and_grep "^::1\s+localhost $HOSTNAME$" getent hosts -s myhostname ::1
+ run_and_grep "^::1\s+STREAM\s+::1$" getent ahosts -s myhostname ::1
+ (! getent ahostsv4 -s myhostname ::1)
+
+ # _gateway
+ for host in _gateway{,.} 10.0.0.1; do
+ run_and_grep "^10\.0\.0\.1\s+_gateway$" getent hosts -s myhostname "$host"
+ run_and_grep "^10\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
+ done
+
+ # _outbound
+ for host in _outbound{,.} 10.0.0.2; do
+ run_and_grep "^10\.0\.0\.2\s+" getent hosts -s myhostname "$host"
+ run_and_grep "^10\.0\.0\.2\s+STREAM" getent ahosts -s myhostname "$host"
+ done
+
+ # Non-existent records
+ for database in hosts ahosts ahostsv4 ahostsv6; do
+ (! getent "$database" -s myhostname this.should.not.exist)
+ done
+ (! getent hosts -s myhostname 10.254.254.1)
+ (! getent hosts -s myhostname fd00:dead:beef:cafe::1)
+}
+
+test_varlink() {
+ A="$(mktemp -u)"
+ B="$(mktemp -u)"
+ varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A"
+ hostnamectl --json=short > "$B"
+ cmp "$A" "$B"
+}
+
+run_testcases
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-72-SYSUPDATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+SYSUPDATE=/lib/systemd/systemd-sysupdate
+SECTOR_SIZES=(512 4096)
+WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
+BACKING_FILE="$WORKDIR/joined.raw"
+export SYSTEMD_ESP_PATH="$WORKDIR/esp"
+export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr"
+export SYSTEMD_PAGER=cat
+export SYSTEMD_LOG_LEVEL=debug
+
+if [[ ! -x "$SYSUPDATE" ]]; then
+ echo "no systemd-sysupdate" >/skipped
+ exit 77
+fi
+
+# Loopback devices may not be supported. They are used because sfdisk cannot
+# change the sector size of a file, and we want to test both 512 and 4096 byte
+# sectors. If loopback devices are not supported, we can only test one sector
+# size, and the underlying device is likely to have a sector size of 512 bytes.
+if [[ ! -e /dev/loop-control ]]; then
+ echo "No loopback device support"
+ SECTOR_SIZES=(512)
+fi
+
+at_exit() {
+ set +e
+
+ losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
+ losetup --detach "$loop_dev"
+ done
+
+ rm -rf "$WORKDIR"
+}
+
+trap at_exit EXIT
+
+new_version() {
+ local sector_size="${1:?}"
+ local version="${2:?}"
+
+ # Create a pair of random partition payloads, and compress one
+ dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048
+ dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048
+ gzip -k -f "$WORKDIR/source/part2-$version.raw"
+
+ # Create a random "UKI" payload
+ echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
+
+ # Create a random extra payload
+ echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
+
+ # Create tarball of a directory
+ mkdir -p "$WORKDIR/source/dir-$version"
+ echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt"
+ echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt"
+ tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" .
+
+ (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
+}
+
+update_now() {
+ # Update to newest version. First there should be an update ready, then we
+ # do the update, and then there should not be any ready anymore
+
+ "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new
+ "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update
+ (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new)
+}
+
+verify_version() {
+ local block_device="${1:?}"
+ local sector_size="${2:?}"
+ local version="${3:?}"
+ local part1_number="${4:?}"
+ local part2_number="${5:?}"
+ local gpt_reserved_sectors part1_offset part2_offset
+
+ gpt_reserved_sectors=$((1024 * 1024 / sector_size))
+ part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors))
+ part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors))
+
+ # Check the partitions
+ dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw"
+ dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw"
+
+ # Check the UKI
+ cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi"
+ test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")"
+
+ # Check the extra efi
+ cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
+
+ # Check the directories
+ cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt"
+ cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
+}
+
+for sector_size in "${SECTOR_SIZES[@]}"; do
+ # Disk size of:
+ # - 1MB for GPT
+ # - 4 partitions of 2048 sectors each
+ # - 1MB for backup GPT
+ disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2))
+ rm -f "$BACKING_FILE"
+ truncate -s "$disk_size" "$BACKING_FILE"
+
+ if [[ -e /dev/loop-control ]]; then
+ blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
+ else
+ blockdev="$BACKING_FILE"
+ fi
+
+ sfdisk "$blockdev" <<EOF
+label: gpt
+unit: sectors
+sector-size: $sector_size
+
+size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
+size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
+size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
+size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
+EOF
+
+ for d in "dirs" "defs"; do
+ rm -rf "${WORKDIR:?}/$d"
+ mkdir -p "$WORKDIR/$d"
+ done
+
+ cat >"$WORKDIR/defs/01-first.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=part1-@v.raw
+
+[Target]
+Type=partition
+Path=$blockdev
+MatchPattern=part1-@v
+MatchPartitionType=root-x86-64
+EOF
+
+ cat >"$WORKDIR/defs/02-second.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=part2-@v.raw.gz
+
+[Target]
+Type=partition
+Path=$blockdev
+MatchPattern=part2-@v
+MatchPartitionType=root-x86-64-verity
+EOF
+
+ cat >"$WORKDIR/defs/03-third.conf" <<EOF
+[Source]
+Type=directory
+Path=$WORKDIR/source
+MatchPattern=dir-@v
+
+[Target]
+Type=directory
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
+MatchPattern=dir-@v
+InstancesMax=3
+EOF
+
+ cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=uki-@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=uki_@v+@l-@d.efi \
+ uki_@v+@l.efi \
+ uki_@v.efi
+Mode=0444
+TriesLeft=3
+TriesDone=0
+InstancesMax=2
+EOF
+
+ cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=uki-extra-@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=uki_@v.efi.extra.d/extra.addon.efi
+Mode=0444
+InstancesMax=2
+EOF
+
+ rm -rf "${WORKDIR:?}"/{esp,xbootldr,source}
+ mkdir -p "$WORKDIR"/{source,esp/EFI/Linux,xbootldr/EFI/Linux}
+
+ # Install initial version and verify
+ new_version "$sector_size" v1
+ update_now
+ verify_version "$blockdev" "$sector_size" v1 1 3
+
+ # Create second version, update and verify that it is added
+ new_version "$sector_size" v2
+ update_now
+ verify_version "$blockdev" "$sector_size" v2 2 4
+
+ # Create third version, update and verify it replaced the first version
+ new_version "$sector_size" v3
+ update_now
+ verify_version "$blockdev" "$sector_size" v3 1 3
+ test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
+ test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
+ test ! -d "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d"
+
+ # Create fourth version, and update through a file:// URL. This should be
+ # almost as good as testing HTTP, but is simpler for us to set up. file:// is
+ # abstracted in curl for us, and since our main goal is to test our own code
+ # (and not curl) this test should be quite good even if not comprehensive. This
+ # will test the SHA256SUMS logic at least (we turn off GPG validation though,
+ # see above)
+ new_version "$sector_size" v4
+
+ cat >"$WORKDIR/defs/02-second.conf" <<EOF
+[Source]
+Type=url-file
+Path=file://$WORKDIR/source
+MatchPattern=part2-@v.raw.gz
+
+[Target]
+Type=partition
+Path=$blockdev
+MatchPattern=part2-@v
+MatchPartitionType=root-x86-64-verity
+EOF
+
+ cat >"$WORKDIR/defs/03-third.conf" <<EOF
+[Source]
+Type=url-tar
+Path=file://$WORKDIR/source
+MatchPattern=dir-@v.tar.gz
+
+[Target]
+Type=directory
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
+MatchPattern=dir-@v
+InstancesMax=3
+EOF
+
+ update_now
+ verify_version "$blockdev" "$sector_size" v4 2 4
+
+ # Cleanup
+ [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
+ rm "$BACKING_FILE"
+done
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-73-LOCALE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+enable_debug() {
+ mkdir -p /run/systemd/system/systemd-localed.service.d
+ cat >>/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+ mkdir -p /run/systemd/system/systemd-vconsole-setup.service.d
+ cat >>/run/systemd/system/systemd-vconsole-setup.service.d/override.conf <<EOF
+[Unit]
+StartLimitIntervalSec=0
+
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+ systemctl daemon-reload
+}
+
+testcase_locale() {
+ local i output
+
+ if [[ -f /etc/locale.conf ]]; then
+ cp /etc/locale.conf /tmp/locale.conf.bak
+ fi
+
+ # Debian/Ubuntu specific file
+ if [[ -f /etc/default/locale ]]; then
+ cp /etc/default/locale /tmp/default-locale.bak
+ fi
+
+ if [[ -f /etc/locale.gen ]]; then
+ cp /etc/locale.gen /tmp/locale.gen.bak
+ fi
+
+ # remove locale.conf to make /etc/default/locale used by Debian/Ubuntu
+ rm -f /etc/locale.conf
+ # also remove /etc/default/locale
+ rm -f /etc/default/locale
+ # and create /etc/default to make /etc/default/locale created by localed
+ mkdir -p /etc/default
+
+ trap restore_locale RETURN
+ # Ensure at least one UTF-8 locale exists.
+ generate_locale en_US.UTF-8
+
+ # create invalid locale
+ mkdir -p /usr/lib/locale/xx_XX.UTF-8
+ assert_not_in "xx_XX.UTF-8" "$(localectl list-locales)"
+
+ if [[ -z "$(localectl list-locales)" ]]; then
+ echo "No locale installed, skipping test."
+ return
+ fi
+
+ # start with a known default environment and make sure to also give a
+ # default value to LC_CTYPE= since we're about to also set/unset it. We
+ # also reload PID1 configuration to make sure that PID1 environment itself
+ # is updated as it's not always been the case.
+ assert_rc 0 localectl set-locale "LANG=en_US.UTF-8" "LC_CTYPE=C"
+ systemctl daemon-reload
+ output=$(localectl)
+ assert_in "System Locale: LANG=en_US.UTF-8" "$output"
+ assert_in "LC_CTYPE=C" "$output"
+ output=$(systemctl show-environment)
+ assert_in "LANG=en_US.UTF-8" "$output"
+ assert_in "LC_CTYPE=C" "$output"
+
+ # warn when kernel command line has locale settings
+ output=$(SYSTEMD_PROC_CMDLINE="locale.LANG=C.UTF-8 locale.LC_CTYPE=ja_JP.UTF-8" localectl 2>&1)
+ assert_in "Warning:" "$output"
+ assert_in "Command Line: LANG=C.UTF-8" "$output"
+ assert_in "LC_CTYPE=ja_JP.UTF-8" "$output"
+ assert_in "System Locale:" "$output"
+
+ # change locale
+ for i in $(localectl list-locales); do
+ assert_rc 0 localectl set-locale "LANG=C" "LC_CTYPE=$i"
+ if [[ -f /etc/default/locale ]]; then
+ assert_eq "$(cat /etc/default/locale)" "LANG=C
+LC_CTYPE=$i"
+ else
+ assert_eq "$(cat /etc/locale.conf)" "LANG=C
+LC_CTYPE=$i"
+ fi
+ output=$(localectl)
+ assert_in "System Locale: LANG=C" "$output"
+ assert_in "LC_CTYPE=$i" "$output"
+ output=$(systemctl show-environment)
+ assert_in "LANG=C" "$output"
+ assert_in "LC_CTYPE=$i" "$output"
+
+ assert_rc 0 localectl set-locale "$i"
+ if [[ -f /etc/default/locale ]]; then
+ assert_eq "$(cat /etc/default/locale)" "LANG=$i"
+ else
+ assert_eq "$(cat /etc/locale.conf)" "LANG=$i"
+ fi
+ output=$(localectl)
+ assert_in "System Locale: LANG=$i" "$output"
+ assert_not_in "LC_CTYPE=" "$output"
+ output=$(systemctl show-environment)
+ assert_in "LANG=$i" "$output"
+ assert_not_in "LC_CTYPE=" "$output"
+ done
+
+ # test if localed auto-runs locale-gen
+ if command -v locale-gen >/dev/null 2>&1 &&
+ ! localectl list-locales | grep -F "de_DE.UTF-8"; then
+
+ # clear previous locale
+ systemctl stop systemd-localed.service
+ rm -f /etc/locale.conf /etc/default/locale
+
+ # change locale
+ assert_rc 0 localectl set-locale de_DE.UTF-8
+ if [[ -f /etc/default/locale ]]; then
+ assert_eq "$(cat /etc/default/locale)" "LANG=de_DE.UTF-8"
+ else
+ assert_eq "$(cat /etc/locale.conf)" "LANG=de_DE.UTF-8"
+ fi
+ assert_in "System Locale: LANG=de_DE.UTF-8" "$(localectl)"
+ assert_in "LANG=de_DE.UTF-8" "$(systemctl show-environment)"
+
+ # ensure tested locale exists and works now
+ assert_in "de_DE.UTF-8" "$(localectl list-locales)"
+ fi
+}
+
+backup_keymap() {
+ if [[ -f /etc/vconsole.conf ]]; then
+ cp /etc/vconsole.conf /tmp/vconsole.conf.bak
+ fi
+
+ if [[ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]]; then
+ cp /etc/X11/xorg.conf.d/00-keyboard.conf /tmp/00-keyboard.conf.bak
+ fi
+
+ # Debian/Ubuntu specific file
+ if [[ -f /etc/default/keyboard ]]; then
+ cp /etc/default/keyboard /tmp/default-keyboard.bak
+ fi
+
+ mkdir -p /etc/default
+}
+
+restore_keymap() {
+ if [[ -f /tmp/vconsole.conf.bak ]]; then
+ mv /tmp/vconsole.conf.bak /etc/vconsole.conf
+ else
+ rm -f /etc/vconsole.conf
+ fi
+
+ if [[ -f /tmp/00-keyboard.conf.bak ]]; then
+ mv /tmp/00-keyboard.conf.bak /etc/X11/xorg.conf.d/00-keyboard.conf
+ else
+ rm -f /etc/X11/xorg.conf.d/00-keyboard.conf
+ fi
+
+ if [[ -f /tmp/default-keyboard.bak ]]; then
+ mv /tmp/default-keyboard.bak /etc/default/keyboard
+ else
+ rm -f /etc/default/keyboard
+ rmdir --ignore-fail-on-non-empty /etc/default
+ fi
+}
+
+wait_vconsole_setup() {
+ local i ss
+ for i in {1..20}; do
+ (( i > 1 )) && sleep 0.5
+ ss="$(systemctl --property SubState --value show systemd-vconsole-setup.service)"
+ if [[ "$ss" == "exited" || "$ss" == "dead" || "$ss" == "condition" ]]; then
+ return 0
+ elif [[ "$ss" == "failed" ]]; then
+ echo "WARNING: systemd-vconsole-setup.service failed, ignoring." >&2
+ systemctl reset-failed systemd-vconsole-setup.service
+ return 0
+ fi
+ done
+
+ systemctl status systemd-vconsole-setup.service
+ return 1
+}
+
+testcase_vc_keymap() {
+ local i output vc
+
+ if [[ -z "$(localectl list-keymaps)" ]]; then
+ echo "No vconsole keymap installed, skipping test."
+ return
+ fi
+
+ backup_keymap
+ trap restore_keymap RETURN
+
+ # should activate daemon and work
+ assert_in "VC Keymap:" "$(localectl)"
+
+ for i in $(localectl list-keymaps); do
+ # clear previous conversion from VC -> X11 keymap
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+ # set VC keymap
+ assert_rc 0 localectl set-keymap "$i"
+ output=$(localectl)
+
+ # check VC keymap
+ vc=$(cat /etc/vconsole.conf)
+ assert_in "KEYMAP=$i" "$vc"
+ assert_in "VC Keymap: $i" "$output"
+
+ # check VC -> X11 keymap conversion
+ if [[ "$i" == "us" ]]; then
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: pc105\+inet" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+ assert_in "XKBLAYOUT=us" "$vc"
+ assert_in "XKBMODEL=pc105\+inet" "$vc"
+ assert_not_in "XKBVARIANT" "$vc"
+ assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+ elif [[ "$i" == "us-acentos" ]]; then
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: pc105" "$output"
+ assert_in "X11 Variant: intl" "$output"
+ assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+ assert_in "XKBLAYOUT=us" "$vc"
+ assert_in "XKBMODEL=pc105" "$vc"
+ assert_in "XKBVARIANT=intl" "$vc"
+ assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+ elif [[ "$i" =~ ^us-.* ]]; then
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: microsoftpro" "$output"
+ assert_in "X11 Variant:" "$output"
+ assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+ assert_in "XKBLAYOUT=us" "$vc"
+ assert_in "XKBMODEL=microsoftpro" "$vc"
+ assert_in "XKBVARIANT=" "$vc"
+ assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+ fi
+ done
+
+ # gets along without config file
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/vconsole.conf
+ assert_in "VC Keymap: .unset." "$(localectl)"
+}
+
+testcase_x11_keymap() {
+ local output
+
+ if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+ echo "No x11 keymap installed, skipping test."
+ return
+ fi
+
+ backup_keymap
+ trap restore_keymap RETURN
+
+ # should activate daemon and work
+ assert_in "X11 Layout:" "$(localectl)"
+
+ # set x11 keymap (layout, model, variant, options)
+ assert_rc 0 localectl set-x11-keymap us pc105+inet intl terminate:ctrl_alt_bksp
+
+ if [[ -f /etc/default/keyboard ]]; then
+ assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
+XKBMODEL=pc105+inet
+XKBVARIANT=intl
+XKBOPTIONS=terminate:ctrl_alt_bksp"
+ else
+ output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+ assert_in 'Option "XkbLayout" "us"' "$output"
+ assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
+ assert_in 'Option "XkbVariant" "intl"' "$output"
+ assert_in 'Option "XkbOptions" "terminate:ctrl_alt_bksp"' "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in 'XKBLAYOUT=us' "$output"
+ assert_in 'XKBMODEL=pc105\+inet' "$output"
+ assert_in 'XKBVARIANT=intl' "$output"
+ assert_in 'XKBOPTIONS=terminate:ctrl_alt_bksp' "$output"
+ fi
+
+ output=$(localectl)
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: pc105\+inet" "$output"
+ assert_in "X11 Variant: intl" "$output"
+ assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+ # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
+ rm -f /etc/default/keyboard
+
+ # set x11 keymap (layout, model, variant)
+ assert_rc 0 localectl set-x11-keymap us pc105+inet intl
+
+ if [[ -f /etc/default/keyboard ]]; then
+ assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
+XKBMODEL=pc105+inet
+XKBVARIANT=intl"
+ else
+ output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+ assert_in 'Option "XkbLayout" "us"' "$output"
+ assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
+ assert_in 'Option "XkbVariant" "intl"' "$output"
+ assert_not_in 'Option "XkbOptions"' "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in 'XKBLAYOUT=us' "$output"
+ assert_in 'XKBMODEL=pc105\+inet' "$output"
+ assert_in 'XKBVARIANT=intl' "$output"
+ assert_not_in 'XKBOPTIONS' "$output"
+ fi
+
+ output=$(localectl)
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: pc105\+inet" "$output"
+ assert_in "X11 Variant: intl" "$output"
+ assert_not_in "X11 Options:" "$output"
+
+ # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
+ rm -f /etc/default/keyboard
+
+ # set x11 keymap (layout, model)
+ assert_rc 0 localectl set-x11-keymap us pc105+inet
+
+ if [[ -f /etc/default/keyboard ]]; then
+ assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
+XKBMODEL=pc105+inet"
+ else
+ output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+ assert_in 'Option "XkbLayout" "us"' "$output"
+ assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
+ assert_not_in 'Option "XkbVariant"' "$output"
+ assert_not_in 'Option "XkbOptions"' "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in 'XKBLAYOUT=us' "$output"
+ assert_in 'XKBMODEL=pc105\+inet' "$output"
+ assert_not_in 'XKBVARIANT' "$output"
+ assert_not_in 'XKBOPTIONS' "$output"
+ fi
+
+ output=$(localectl)
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: pc105\+inet" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_not_in "X11 Options:" "$output"
+
+ # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
+ rm -f /etc/default/keyboard
+
+ # set x11 keymap (layout)
+ assert_rc 0 localectl set-x11-keymap us
+
+ if [[ -f /etc/default/keyboard ]]; then
+ assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
+ else
+ output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+ assert_in 'Option "XkbLayout" "us"' "$output"
+ assert_not_in 'Option "XkbModel"' "$output"
+ assert_not_in 'Option "XkbVariant"' "$output"
+ assert_not_in 'Option "XkbOptions"' "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in 'XKBLAYOUT=us' "$output"
+ assert_not_in 'XKBMODEL' "$output"
+ assert_not_in 'XKBVARIANT' "$output"
+ assert_not_in 'XKBOPTIONS' "$output"
+ fi
+
+ output=$(localectl)
+ assert_in "X11 Layout: us" "$output"
+ assert_not_in "X11 Model:" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_not_in "X11 Options:" "$output"
+
+ # gets along without config file
+ systemctl stop systemd-localed.service
+ rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+ output=$(localectl)
+ assert_in "X11 Layout: .unset." "$output"
+ assert_not_in "X11 Model:" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_not_in "X11 Options:" "$output"
+}
+
+testcase_convert() {
+ if [[ -z "$(localectl list-keymaps)" ]]; then
+ echo "No vconsole keymap installed, skipping test."
+ return
+ fi
+
+ if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+ echo "No x11 keymap installed, skipping test."
+ return
+ fi
+
+ backup_keymap
+ trap restore_keymap RETURN
+
+ # clear previous settings
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+ # set VC keymap without conversion
+ assert_rc 0 localectl --no-convert set-keymap us
+ output=$(localectl)
+
+ # check VC keymap
+ vc=$(cat /etc/vconsole.conf)
+ assert_in "KEYMAP=us" "$vc"
+ assert_in "VC Keymap: us" "$output"
+
+ # check VC -> X11 keymap conversion (nothing set)
+ assert_in "X11 Layout: .unset." "$output"
+ assert_not_in "X11 Model:" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_not_in "X11 Options:" "$output"
+
+ assert_not_in "XKBLAYOUT=" "$vc"
+ assert_not_in "XKBMODEL=" "$vc"
+ assert_not_in "XKBVARIANT=" "$vc"
+ assert_not_in "XKBOPTIONS=" "$vc"
+
+ # set VC keymap with conversion
+ assert_rc 0 localectl set-keymap us
+ output=$(localectl)
+
+ # check VC keymap
+ vc=$(cat /etc/vconsole.conf)
+ assert_in "KEYMAP=us" "$vc"
+ assert_in "VC Keymap: us" "$output"
+
+ # check VC -> X11 keymap conversion
+ assert_in "X11 Layout: us" "$output"
+ assert_in "X11 Model: pc105\+inet" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+ assert_in "XKBLAYOUT=us" "$vc"
+ assert_in "XKBMODEL=pc105\+inet" "$vc"
+ assert_not_in "XKBVARIANT" "$vc"
+ assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+
+ # clear previous settings
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+ # set x11 keymap (layout) without conversion
+ assert_rc 0 localectl --no-convert set-x11-keymap us
+
+ assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)"
+ assert_in "VC Keymap: .unset." "$(localectl)"
+
+ if [[ -f /etc/default/keyboard ]]; then
+ assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
+ else
+ output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+ assert_in 'Option "XkbLayout" "us"' "$output"
+ assert_not_in 'Option "XkbModel"' "$output"
+ assert_not_in 'Option "XkbVariant"' "$output"
+ assert_not_in 'Option "XkbOptions"' "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in 'XKBLAYOUT=us' "$output"
+ assert_not_in 'XKBMODEL=' "$output"
+ assert_not_in 'XKBVARIANT=' "$output"
+ assert_not_in 'XKBOPTIONS=' "$output"
+ fi
+
+ output=$(localectl)
+ assert_in "X11 Layout: us" "$output"
+ assert_not_in "X11 Model:" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_not_in "X11 Options:" "$output"
+
+ # set x11 keymap (layout, model) with conversion
+ assert_rc 0 localectl set-x11-keymap us
+
+ assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)"
+ assert_in "VC Keymap: us" "$(localectl)"
+
+ if [[ -f /etc/default/keyboard ]]; then
+ assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
+ else
+ output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+ assert_in 'Option "XkbLayout" "us"' "$output"
+ assert_not_in 'Option "XkbModel"' "$output"
+ assert_not_in 'Option "XkbVariant"' "$output"
+ assert_not_in 'Option "XkbOptions"' "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in 'XKBLAYOUT=us' "$output"
+ assert_not_in 'XKBMODEL=' "$output"
+ assert_not_in 'XKBVARIANT=' "$output"
+ assert_not_in 'XKBOPTIONS=' "$output"
+ fi
+
+ output=$(localectl)
+ assert_in "X11 Layout: us" "$output"
+ assert_not_in "X11 Model:" "$output"
+ assert_not_in "X11 Variant:" "$output"
+ assert_not_in "X11 Options:" "$output"
+}
+
+testcase_validate() {
+ if [[ -z "$(localectl list-keymaps)" ]]; then
+ echo "No vconsole keymap installed, skipping test."
+ return
+ fi
+
+ if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+ echo "No x11 keymap installed, skipping test."
+ return
+ fi
+
+ backup_keymap
+ trap restore_keymap RETURN
+
+ # clear previous settings
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+ # create invalid configs
+ cat >/etc/vconsole.conf <<EOF
+KEYMAP=foobar
+XKBLAYOUT=hogehoge
+EOF
+
+ # confirm that the invalid settings are not shown
+ output=$(localectl)
+ assert_in "VC Keymap: .unset." "$output"
+ if [[ "$output" =~ "X11 Layout: hogehoge" ]]; then
+ # Debian/Ubuntu build systemd without xkbcommon.
+ echo "systemd built without xkbcommon, skipping test."
+ return
+ fi
+ assert_in "X11 Layout: .unset." "$output"
+
+ # only update the virtual console keymap
+ assert_rc 0 localectl --no-convert set-keymap us
+
+ output=$(localectl)
+ assert_in "VC Keymap: us" "$output"
+ assert_in "X11 Layout: .unset." "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in "KEYMAP=us" "$output"
+ assert_not_in "XKBLAYOUT=" "$output"
+
+ # clear previous settings
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+ # create invalid configs
+ cat >/etc/vconsole.conf <<EOF
+KEYMAP=foobar
+XKBLAYOUT=hogehoge
+EOF
+
+ # confirm that the invalid settings are not shown
+ output=$(localectl)
+ assert_in "VC Keymap: .unset." "$output"
+ assert_in "X11 Layout: .unset." "$output"
+
+ # only update the X11 keyboard layout
+ assert_rc 0 localectl --no-convert set-x11-keymap us
+
+ output=$(localectl)
+ assert_in "VC Keymap: .unset." "$output"
+ assert_in "X11 Layout: us" "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_not_in "KEYMAP=" "$output"
+ assert_in "XKBLAYOUT=us" "$output"
+
+ # clear previous settings
+ systemctl stop systemd-localed.service
+ wait_vconsole_setup
+ rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+ # create invalid configs
+ cat >/etc/vconsole.conf <<EOF
+KEYMAP=foobar
+XKBLAYOUT=hogehoge
+EOF
+
+ # update the virtual console keymap with conversion
+ assert_rc 0 localectl set-keymap us
+
+ output=$(localectl)
+ assert_in "VC Keymap: us" "$output"
+ assert_in "X11 Layout: us" "$output"
+
+ output=$(cat /etc/vconsole.conf)
+ assert_in "KEYMAP=us" "$output"
+ assert_in "XKBLAYOUT=us" "$output"
+}
+
+locale_gen_cleanup() {
+ # Some running apps might keep the mount point busy, hence the lazy unmount
+ mountpoint -q /usr/lib/locale && umount --lazy /usr/lib/locale
+ [[ -e /tmp/locale.gen.bak ]] && mv -f /tmp/locale.gen.bak /etc/locale.gen
+
+ return 0
+}
+
+# Issue: https://github.com/systemd/systemd/pull/27179
+testcase_locale_gen_leading_space() {
+ if ! command -v locale-gen >/dev/null; then
+ echo "No locale-gen support, skipping test."
+ return 0
+ fi
+
+ [[ -e /etc/locale.gen ]] && cp -f /etc/locale.gen /tmp/locale.gen.bak
+ trap locale_gen_cleanup RETURN
+ # Overmount the existing locale-gen database with an empty directory
+ # to force it to regenerate locales
+ mount -t tmpfs tmpfs /usr/lib/locale
+
+ {
+ echo -e "en_US.UTF-8 UTF-8"
+ echo -e " en_US.UTF-8 UTF-8"
+ echo -e "\ten_US.UTF-8 UTF-8"
+ echo -e " \t en_US.UTF-8 UTF-8 \t"
+ } >/etc/locale.gen
+
+ localectl set-locale de_DE.UTF-8
+ localectl set-locale en_US.UTF-8
+}
+
+# Make sure the content of kbd-model-map is the one that the tests expect
+# regardless of the version installed on the distro where the testsuite is
+# running on.
+export SYSTEMD_KBD_MODEL_MAP=/usr/lib/systemd/tests/testdata/test-keymap-util/kbd-model-map
+
+enable_debug
+run_testcases
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+/usr/lib/systemd/systemd-battery-check --help
+/usr/lib/systemd/systemd-battery-check --version
+
+/usr/lib/systemd/systemd-battery-check || :
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt --quiet --container; then
+ echo "running on container, skipping."
+ exit 0
+fi
+
+if ! command -v bootctl >/dev/null; then
+ echo "bootctl not found, skipping."
+ exit 0
+fi
+
+if [[ ! -d /usr/lib/systemd/boot/efi ]]; then
+ echo "sd-boot is not installed, skipping."
+ exit 0
+fi
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+basic_tests() {
+ bootctl "$@" --help
+ bootctl "$@" --version
+
+ bootctl "$@" install --make-entry-directory=yes
+ bootctl "$@" remove --make-entry-directory=yes
+
+ bootctl "$@" install --all-architectures
+ bootctl "$@" remove --all-architectures
+
+ bootctl "$@" install --make-entry-directory=yes --all-architectures
+ bootctl "$@" remove --make-entry-directory=yes --all-architectures
+
+ bootctl "$@" install
+ (! bootctl "$@" update)
+ bootctl "$@" update --graceful
+
+ bootctl "$@" is-installed
+ bootctl "$@" is-installed --graceful
+ bootctl "$@" random-seed
+
+ bootctl "$@"
+ bootctl "$@" status
+ bootctl "$@" status --quiet
+ bootctl "$@" list
+ bootctl "$@" list --quiet
+ bootctl "$@" list --json=short
+ bootctl "$@" list --json=pretty
+
+ bootctl "$@" remove
+ (! bootctl "$@" is-installed)
+ (! bootctl "$@" is-installed --graceful)
+}
+
+testcase_bootctl_basic() {
+ assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$"
+ assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$"
+ bootctl --print-root-device
+
+ basic_tests
+}
+
+cleanup_image() (
+ set +e
+
+ if [[ -z "${IMAGE_DIR:-}" ]]; then
+ return 0
+ fi
+
+ umount "${IMAGE_DIR}/root"
+
+ if [[ -n "${LOOPDEV:-}" ]]; then
+ losetup -d "${LOOPDEV}"
+ unset LOOPDEV
+ fi
+
+ udevadm settle
+
+ rm -rf "${IMAGE_DIR}"
+ unset IMAGE_DIR
+
+ return 0
+)
+
+testcase_bootctl_image() {
+ IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+ trap cleanup_image RETURN
+
+ truncate -s 256m "${IMAGE_DIR}/image"
+
+ cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+ LOOPDEV="$(losetup --show -P -f "${IMAGE_DIR}/image")"
+ sfdisk "$LOOPDEV" <"${IMAGE_DIR}/partscript"
+
+ udevadm settle
+
+ mkfs.vfat -n esp "${LOOPDEV}p1"
+ mkfs.ext4 -L root "${LOOPDEV}p2"
+ mkfs.ext4 -L boot "${LOOPDEV}p3"
+
+ mkdir -p "${IMAGE_DIR}/root"
+ mount -t ext4 "${LOOPDEV}p2" "${IMAGE_DIR}/root"
+
+ mkdir -p "${IMAGE_DIR}/root/efi"
+ mkdir -p "${IMAGE_DIR}/root/boot"
+ mkdir -p "${IMAGE_DIR}/root/etc"
+ mkdir -p "${IMAGE_DIR}/root/usr/lib"
+ if [[ -f /usr/lib/os-release ]]; then
+ cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+ ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+ else
+ cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+ fi
+
+ umount "${IMAGE_DIR}/root"
+
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path)" "/run/systemd/mount-rootfs/efi"
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path --esp-path=/efi)" "/run/systemd/mount-rootfs/efi"
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path)" "/run/systemd/mount-rootfs/boot"
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path --boot-path=/boot)" "/run/systemd/mount-rootfs/boot"
+
+ # FIXME: This provides spurious result.
+ bootctl --image "${IMAGE_DIR}/image" --print-root-device || :
+
+ basic_tests --image "${IMAGE_DIR}/image"
+}
+
+cleanup_raid() (
+ set +e
+
+ if [[ -z "${IMAGE_DIR:-}" ]]; then
+ return 0
+ fi
+
+ systemd-umount "${IMAGE_DIR}/root/efi"
+ systemd-umount "${IMAGE_DIR}/root/boot"
+ systemd-umount "${IMAGE_DIR}/root"
+
+ mdadm --misc --stop /dev/md/raid-esp
+ mdadm --misc --stop /dev/md/raid-root
+
+ if [[ -n "${LOOPDEV1:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+ fi
+
+ if [[ -n "${LOOPDEV2:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+ fi
+
+ udevadm settle
+
+ if [[ -n "${LOOPDEV1:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+ losetup -d "${LOOPDEV1}"
+ unset LOOPDEV1
+ fi
+
+ if [[ -n "${LOOPDEV2:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+ losetup -d "${LOOPDEV2}"
+ unset LOOPDEV2
+ fi
+
+ udevadm settle
+
+ rm -rf "${IMAGE_DIR}"
+
+ return 0
+)
+
+testcase_bootctl_raid() {
+ if ! command -v mdadm >/dev/null; then
+ echo "mdadm not found, skipping."
+ return 0
+ fi
+
+ if ! command -v mkfs.btrfs >/dev/null; then
+ echo "mkfs.btrfs not found, skipping."
+ return 0
+ fi
+
+ IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+ trap cleanup_raid RETURN
+
+ truncate -s 256m "${IMAGE_DIR}/image1"
+ truncate -s 256m "${IMAGE_DIR}/image2"
+
+ cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+ LOOPDEV1="$(losetup --show -P -f "${IMAGE_DIR}/image1")"
+ LOOPDEV2="$(losetup --show -P -f "${IMAGE_DIR}/image2")"
+ sfdisk "$LOOPDEV1" <"${IMAGE_DIR}/partscript"
+ sfdisk "$LOOPDEV2" <"${IMAGE_DIR}/partscript"
+
+ udevadm settle
+
+ echo y | mdadm --create /dev/md/raid-esp --name "raid-esp" "${LOOPDEV1}p1" "${LOOPDEV2}p1" -v -f --level=1 --raid-devices=2
+ mkfs.vfat /dev/md/raid-esp
+ echo y | mdadm --create /dev/md/raid-root --name "raid-root" "${LOOPDEV1}p2" "${LOOPDEV2}p2" -v -f --level=1 --raid-devices=2
+ mkfs.ext4 /dev/md/raid-root
+ mkfs.btrfs -f -M -d raid1 -m raid1 -L "raid-boot" "${LOOPDEV1}p3" "${LOOPDEV2}p3"
+
+ mkdir -p "${IMAGE_DIR}/root"
+ mount -t ext4 /dev/md/raid-root "${IMAGE_DIR}/root"
+ mkdir -p "${IMAGE_DIR}/root/efi"
+ mount -t vfat /dev/md/raid-esp "${IMAGE_DIR}/root/efi"
+ mkdir -p "${IMAGE_DIR}/root/boot"
+ mount -t btrfs "${LOOPDEV1}p3" "${IMAGE_DIR}/root/boot"
+
+ mkdir -p "${IMAGE_DIR}/root/etc"
+ mkdir -p "${IMAGE_DIR}/root/usr/lib"
+ if [[ -f /usr/lib/os-release ]]; then
+ cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+ ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+ else
+ cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+ fi
+
+ # find_esp() does not support md RAID partition.
+ (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path)
+ (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)
+
+ # If the verification is relaxed, it accepts md RAID partition.
+ assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path)" "${IMAGE_DIR}/root/efi"
+ assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)" "${IMAGE_DIR}/root/efi"
+
+ # find_xbootldr() does not support btrfs RAID, and bootctl tries to fall back to use ESP.
+ # (but as in the above, the ESP verification is also failed in this case).
+ (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path)
+ (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)
+
+ # If the verification for ESP is relaxed, bootctl falls back to use ESP.
+ assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/efi"
+
+ # If the verification is relaxed, it accepts the xbootldr partition.
+ assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/boot"
+ assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)" "${IMAGE_DIR}/root/boot"
+
+ # FIXME: This provides spurious result.
+ bootctl --root "${IMAGE_DIR}/root" --print-root-device || :
+
+ SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
+}
+
+testcase_bootctl_varlink() {
+ varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}'
+
+ # We may have UEFI in the test environment.
+ # If we don't have UEFI then we can test whether bootctl's varlink API fails cleanly.
+ # If we do have UEFI then the rest of the clean fail tests should be skipped.
+ if ! (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported; then
+ return 0
+ fi
+ (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+ (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+}
+
+run_testcases
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+busctl --help
+busctl help
+busctl --version
+busctl
+busctl list --no-pager --allow-interactive-authorization=no
+busctl list
+busctl list --unique --show-machine --full
+# Pass the JSON output (-j) through jq to check if it's valid
+busctl list --acquired --activatable --no-legend -j | jq
+busctl status
+busctl status --machine=.host --augment-creds=no
+busctl status --user --machine=testuser@.host
+busctl status org.freedesktop.systemd1
+# Ignore the exit code here, since this runs during machine bootup, so busctl
+# might attempt to introspect a job that already finished and fail, i.e.:
+# Failed to introspect object /org/freedesktop/systemd1/job/335 of service org.freedesktop.systemd1: Unknown object '/org/freedesktop/systemd1/job/335'.
+busctl tree || :
+busctl tree org.freedesktop.login1
+busctl tree --list org.freedesktop.login1
+busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
+busctl introspect --watch-bind=yes --xml-interface org.freedesktop.systemd1 /org/freedesktop/LogControl1
+busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager
+
+busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ GetDefaultTarget
+# Pass both JSON outputs through jq to check if the response JSON is valid
+busctl call --json=pretty \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
+busctl call --json=short \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
+# Get all properties on the org.freedesktop.systemd1.Manager interface and dump
+# them as JSON to exercise the internal JSON transformations
+busctl call -j \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Properties \
+ GetAll s "org.freedesktop.systemd1.Manager" | jq -c
+busctl call --verbose --timeout=60 --expect-reply=yes \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"
+
+busctl emit /org/freedesktop/login1 org.freedesktop.login1.Manager \
+ PrepareForSleep b false
+busctl emit --auto-start=no --destination=systemd-logind.service \
+ /org/freedesktop/login1 org.freedesktop.login1.Manager \
+ PrepareForShutdown b false
+
+busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ Version
+busctl get-property --verbose \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ LogLevel LogTarget SystemState Version
+# Pass both JSON outputs through jq to check if the response JSON is valid
+busctl get-property --json=pretty \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ LogLevel LogTarget SystemState Version | jq
+busctl get-property --json=short \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ LogLevel LogTarget SystemState Version | jq
+
+# Set a property and check if it was indeed set
+busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ KExecWatchdogUSec t 666
+busctl get-property -j \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ KExecWatchdogUSec | jq -e '.data == 666'
+
+(! busctl status org.freedesktop.systemd2)
+(! busctl tree org.freedesktop.systemd2)
+(! busctl introspect org.freedesktop.systemd1)
+(! busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd2)
+(! busctl introspect org.freedesktop.systemd2 /org/freedesktop/systemd1)
+
+# Invalid method
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ ThisMethodDoesntExist)
+# Invalid signature
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ ListUnitsByNames ab 1 false)
+# Invalid arguments
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ GetUnitByPID u "hello")
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ -- ListUnitsByNames as -1 "systemd-journald.service")
+# Not enough arguments
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ ListUnitsByNames as 99 "systemd-journald.service")
+
+(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ NonexistentProperty)
+(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ Version NonexistentProperty Version)
+
+# Invalid property
+(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ NonexistentProperty t 666)
+# Invalid signature
+(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ KExecWatchdogUSec s 666)
+# Invalid argument
+(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ KExecWatchdogUSec t "foo")
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+at_exit() {
+ set +e
+ systemctl --no-block stop capsule@foobar.service
+ rm -rf /run/capsules/foobar
+ rm -rf /var/lib/capsules/foobar
+ rm -f /run/systemd/system/capsule@.service.d/99-asan.conf
+}
+
+trap at_exit EXIT
+
+# Appease ASan, since the capsule@.service uses DynamicUser=yes
+systemctl edit --runtime --stdin capsule@.service --drop-in=99-asan.conf <<EOF
+[Service]
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
+EOF
+
+(! test -f /run/capsules/foobar )
+(! test -f /var/lib/capsules/foobar )
+(! id -u c-foobar )
+
+systemctl start capsule@foobar.service
+
+test -d /run/capsules/foobar
+test -d /var/lib/capsules/foobar
+id -u c-foobar
+
+systemctl status capsule@foobar.service
+
+busctl -C foobar
+
+systemctl -C foobar
+
+systemd-run -C foobar -u sleepinfinity /bin/sleep infinity
+
+systemctl -C foobar status sleepinfinity
+
+systemctl -C foobar stop sleepinfinity
+
+(! systemctl clean capsule@foobar.service )
+
+systemctl stop capsule@foobar.service
+
+systemctl clean capsule@foobar.service --what=all
+
+(! test -f /run/capsules/foobar )
+(! test -f /var/lib/capsules/foobar )
+(! id -u c-foobar )
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-cgls
+systemd-cgls --all --full
+systemd-cgls -k
+systemd-cgls --xattr=yes
+systemd-cgls --xattr=no
+systemd-cgls --cgroup-id=yes
+systemd-cgls --cgroup-id=no
+
+systemd-cgls /system.slice/systemd-journald.service
+systemd-cgls /system.slice/systemd-journald.service /init.scope
+systemd-cgls /sys/fs/cgroup/system.slice/systemd-journald.service /init.scope
+[[ -d /sys/fs/cgroup/init.scope ]] && init_scope="init.scope" || init_scope="systemd/init.scope"
+(cd "/sys/fs/cgroup/$init_scope" && systemd-cgls)
+systemd-cgls --unit=systemd-journald.service
+# There's most likely no user session running, so we need to create one
+systemd-run --user --wait --pipe -M testuser@.host systemd-cgls --user-unit=app.slice
+
+(! systemd-cgls /foo/bar)
+(! systemd-cgls --unit=hello.world)
+(! systemd-cgls --user-unit=hello.world)
+(! systemd-cgls --xattr=foo)
+(! systemd-cgls --cgroup-id=foo)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Without tty attached cgtop should default to --iterations=1
+systemd-cgtop
+systemd-cgtop --iterations=1
+# Same as --iterations=1
+systemd-cgtop -1
+systemd-cgtop --delay=1ms
+systemd-cgtop --raw
+systemd-cgtop --batch
+systemd-cgtop --cpu=percentage
+systemd-cgtop --cpu=time
+systemd-cgtop -P
+systemd-cgtop -k
+systemd-cgtop --recursive=no -P
+systemd-cgtop --recursive=no -k
+systemd-cgtop --depth=0
+systemd-cgtop --depth=100
+
+for order in path tasks cpu memory io; do
+ systemd-cgtop --order="$order"
+done
+systemd-cgtop -p -t -c -m -i
+
+(! systemd-cgtop --cpu=foo)
+(! systemd-cgtop --order=foo)
+(! systemd-cgtop --depth=-1)
+(! systemd-cgtop --recursive=foo)
+(! systemd-cgtop --delay=1foo)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+ . "$(dirname "$0")"/util.sh
+
+# Make sure the binary name fits into 15 characters
+CORE_TEST_BIN="/tmp/test-dump"
+CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
+MAKE_DUMP_SCRIPT="/tmp/make-dump"
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+at_exit() {
+ rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
+}
+
+trap at_exit EXIT
+
+if systemd-detect-virt -cq; then
+ echo "Running in a container, skipping the systemd-coredump test..."
+ exit 0
+fi
+
+# To make all coredump entries stored in system.journal.
+journalctl --rotate
+
+# Check that we're the ones to receive coredumps
+sysctl kernel.core_pattern | grep systemd-coredump
+
+# Prepare "fake" binaries for coredumps, so we can properly exercise
+# the matching stuff too
+cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
+cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
+# Simple script that spawns given "fake" binary and then kills it with
+# given signal
+cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF
+#!/bin/bash -ex
+
+bin="${1:?}"
+sig="${2:?}"
+
+ulimit -c unlimited
+"$bin" infinity &
+pid=$!
+# Sync with the "fake" binary, so we kill it once it's fully forked off,
+# otherwise we might kill it during fork and kernel would then report
+# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN).
+# In this case, wait until the "fake" binary (sleep in this case) enters
+# the "interruptible sleep" state, at which point it should be ready
+# to be sacrificed.
+for _ in {0..9}; do
+ read -ra self_stat <"/proc/$pid/stat"
+ [[ "${self_stat[2]}" == S ]] && break
+ sleep .5
+done
+kill -s "$sig" "$pid"
+# This should always fail
+! wait "$pid"
+EOF
+chmod +x "$MAKE_DUMP_SCRIPT"
+
+# Privileged stuff
+[[ "$(id -u)" -eq 0 ]]
+# Trigger a couple of coredumps
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
+# In the tests we store the coredumps in journals, so let's generate a couple
+# with Storage=external as well
+mkdir -p /run/systemd/coredump.conf.d/
+printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
+rm -fv /run/systemd/coredump.conf.d/99-external.conf
+# Wait a bit for the coredumps to get processed
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+
+if cgroupfs_supports_user_xattrs; then
+ # Make sure we can forward crashes back to containers
+ CONTAINER="TEST-74-AUX-UTILS-container"
+
+ mkdir -p "/var/lib/machines/$CONTAINER"
+ mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
+ # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
+ # but we need the ASan-related overrides scattered across /etc
+ cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
+[Service]
+ExecStart=
+ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
+ --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
+EOF
+ systemctl daemon-reload
+
+ [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
+
+ machinectl start "$CONTAINER"
+ timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
+
+ [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
+ machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
+ systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT"
+ systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
+ # Wait a bit for the coredumps to get processed
+ timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"
+
+ rm -rf "/var/lib/machines/$CONTAINER"
+fi
+
+coredumpctl
+SYSTEMD_LOG_LEVEL=debug coredumpctl
+coredumpctl --help
+coredumpctl --version
+coredumpctl --no-pager --no-legend
+coredumpctl --all
+coredumpctl -1
+coredumpctl -n 1
+coredumpctl --reverse
+coredumpctl -F COREDUMP_EXE
+coredumpctl --json=short | jq
+coredumpctl --json=pretty | jq
+coredumpctl --json=off
+coredumpctl --root=/
+coredumpctl --directory=/var/log/journal
+coredumpctl --file="/var/log/journal/$(</etc/machine-id)"/*.journal
+coredumpctl --since=@0
+coredumpctl --since=yesterday --until=tomorrow
+# We should have a couple of externally stored coredumps
+coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
+grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
+rm -f /tmp/coredumpctl.out
+
+coredumpctl info
+coredumpctl info "$CORE_TEST_BIN"
+coredumpctl info /foo /bar/ /baz "$CORE_TEST_BIN"
+coredumpctl info "${CORE_TEST_BIN##*/}"
+coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}"
+coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN"
+coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN"
+
+coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN"
+SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN"
+coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"
+
+coredumpctl dump "$CORE_TEST_BIN" >/tmp/core.redirected
+test -s /tmp/core.redirected
+coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}"
+test -s /tmp/core.output
+rm -f /tmp/core.{output,redirected}
+
+# Unprivileged stuff
+# Related issue: https://github.com/systemd/systemd/issues/26912
+UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
+# Trigger a couple of coredumps as an unprivileged user
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
+# In the tests we store the coredumps in journals, so let's generate a couple
+# with Storage=external as well
+mkdir -p /run/systemd/coredump.conf.d/
+printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
+rm -fv /run/systemd/coredump.conf.d/99-external.conf
+# Wait a bit for the coredumps to get processed
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+
+# root should see coredumps from both binaries
+coredumpctl info "$CORE_TEST_UNPRIV_BIN"
+coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
+# The test user should see only their own coredumps
+"${UNPRIV_CMD[@]}" coredumpctl
+"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN"
+"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
+(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN")
+(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}")
+# We should have a couple of externally stored coredumps
+"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
+grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
+rm -f /tmp/coredumpctl.out
+
+"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN"
+"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}"
+
+"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected
+test -s /tmp/core.redirected
+"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}"
+test -s /tmp/core.output
+rm -f /tmp/core.{output,redirected}
+(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null)
+
+# --backtrace mode
+# Pass one of the existing journal coredump records to systemd-coredump and
+# use our PID as the source to make matching the coredump later easier
+# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME
+journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
+ /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
+# Wait a bit for the coredump to get processed
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
+coredumpctl info "$$"
+coredumpctl info COREDUMP_HOSTNAME="mymachine"
+
+# This used to cause a stack overflow
+systemd-run -t --property CoredumpFilter=all ls /tmp
+systemd-run -t --property CoredumpFilter=default ls /tmp
+
+(! coredumpctl --hello-world)
+(! coredumpctl -n 0)
+(! coredumpctl -n -1)
+(! coredumpctl --file=/dev/null)
+(! coredumpctl --since=0)
+(! coredumpctl --until='')
+(! coredumpctl --since=today --until=yesterday)
+(! coredumpctl --directory=/ --root=/)
+(! coredumpctl --json=foo)
+(! coredumpctl -F foo -F bar)
+(! coredumpctl list 0)
+(! coredumpctl list -- -1)
+(! coredumpctl list '')
+(! coredumpctl info /../.~=)
+(! coredumpctl info '')
+(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN")
+(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN")
+(! coredumpctl debug --debugger=/bin/false)
+(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+ rm -rfv /{run,etc}/systemd/system/delta-test*
+}
+
+trap at_exit EXIT
+
+# Create a couple of supporting units with overrides
+#
+# Extended unit
+cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF
+[Service]
+ExecStart=true
+EOF
+mkdir -p "/run/systemd/system/delta-test-unit-extended.service.d"
+cat >"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <<EOF
+[Unit]
+Description=Foo Bar
+[Service]
+ExecStartPre=true
+EOF
+# Masked unit
+cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service
+systemctl mask delta-test-unit-masked.service
+# Overridden unit
+cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-overridden.service
+cp -fv /run/systemd/system/delta-test-unit-overridden.service /etc/systemd/system/delta-test-unit-overridden.service
+echo "ExecStartPost=/bin/true" >>/etc/systemd/system/delta-test-unit-overridden.service
+# Overridden but equivalent unit
+ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-equivalent.service
+ln -sfv /run/systemd/system/delta-test-unit-extended.service /etc/systemd/system/delta-test-unit-equivalent.service
+# Redirected unit
+ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-redirected.service
+ln -sfv /run/systemd/system/delta-test-unit-overidden.service /etc/systemd/system/delta-test-unit-extended.service
+
+systemctl daemon-reload
+
+systemd-delta
+systemd-delta /run
+systemd-delta systemd/system
+systemd-delta /run systemd/system /run
+systemd-delta /run foo/bar hello/world systemd/system /run
+systemd-delta foo/bar
+systemd-delta --diff=true
+systemd-delta --diff=false
+
+for type in masked equivalent redirected overridden extended unchanged; do
+ systemd-delta --type="$type"
+ systemd-delta --type="$type" /run
+done
+systemd-delta --type=equivalent,redirected
+
+(! systemd-delta --diff=foo)
+(! systemd-delta --type=foo)
+(! systemd-delta --type=equivalent,redirected,foo)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Simple wrapper to check both escaping and unescaping of given strings
+# Arguments:
+# $1 - expected unescaped string
+# $2 - expected escaped string
+# $3 - optional arguments for systemd-escape
+check_escape() {
+ unescaped="${1?}"
+ escaped="${2?}"
+ shift 2
+
+ assert_eq "$(systemd-escape "$@" -- "$unescaped")" "$escaped"
+ assert_eq "$(systemd-escape "$@" --unescape -- "$escaped")" "$unescaped"
+}
+
+systemd-escape --help
+systemd-escape --version
+
+check_escape '' ''
+check_escape 'hello' 'hello'
+check_escape 'hello-world' 'hello\x2dworld'
+check_escape '-+ěščřž---🤔' '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94'
+check_escape '/this/is/a/path/a b c' '-this-is-a-path-a\x20b\x20c'
+
+# Multiple strings to escape/unescape
+assert_eq "$(systemd-escape 'hello-world' '/dev/loop1' 'template@🐍')" \
+ 'hello\x2dworld -dev-loop1 template\x40\xf0\x9f\x90\x8d'
+assert_eq "$(systemd-escape --unescape -- 'hello\x2dworld' '-dev-loop1' 'template\x40\xf0\x9f\x90\x8d')" \
+ 'hello-world /dev/loop1 template@🐍'
+
+# --suffix= is not compatible with --unescape
+assert_eq "$(systemd-escape --suffix=mount -- '-+ěščřž---🤔')" \
+ '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94.mount'
+assert_eq "$(systemd-escape --suffix=timer 'this has spaces')" \
+ 'this\x20has\x20spaces.timer'
+assert_eq "$(systemd-escape --suffix=service 'trailing-spaces ')" \
+ 'trailing\x2dspaces\x20\x20.service'
+assert_eq "$(systemd-escape --suffix=automount ' leading-spaces')" \
+ '\x20\x20\x20leading\x2dspaces.automount'
+
+# --template=
+check_escape 'hello' 'hello@hello.service' --template=hello@.service
+check_escape ' what - is _ love? 🤔 ¯\_(ツ)_/¯' \
+ 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service' \
+ --template=hello@.service
+check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
+ 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
+ --template=mount-my-stuff@.service
+check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
+ 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
+ --template=mount-my-stuff@.service --path
+
+# --instance (must be used with --unescape)
+assert_eq "$(systemd-escape --unescape --instance 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service')" \
+ ' what - is _ love? 🤔 ¯\_(ツ)_/¯'
+assert_eq "$(systemd-escape --unescape --instance 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
+ '/this/is/where/my/stuff/is/ with spaces though '
+assert_eq "$(systemd-escape --unescape --instance --path 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
+ '/this/is/where/my/stuff/is/ with spaces though '
+
+# --path, reversible cases
+check_escape / '-' --path
+check_escape '/hello/world' 'hello-world' --path
+check_escape '/mnt/smb/おにぎり' \
+ 'mnt-smb-\xe3\x81\x8a\xe3\x81\xab\xe3\x81\x8e\xe3\x82\x8a' \
+ --path
+
+# --path, non-reversible cases
+assert_eq "$(systemd-escape --path ///////////////)" '-'
+assert_eq "$(systemd-escape --path /..)" '-'
+assert_eq "$(systemd-escape --path /../.././../.././)" '-'
+assert_eq "$(systemd-escape --path /../.././../.././foo)" 'foo'
+
+# --mangle
+assert_eq "$(systemd-escape --mangle 'hello-world')" 'hello-world.service'
+assert_eq "$(systemd-escape --mangle '/mount/this')" 'mount-this.mount'
+assert_eq "$(systemd-escape --mangle 'my-service@ 🐱 ')" 'my-service@\x20\xf0\x9f\x90\xb1\x20.service'
+assert_eq "$(systemd-escape --mangle '/dev/disk/by-emoji/🍎')" 'dev-disk-by\x2demoji-\xf0\x9f\x8d\x8e.device'
+assert_eq "$(systemd-escape --mangle 'daily-existential-crisis .timer')" 'daily-existential-crisis\x20.timer'
+assert_eq "$(systemd-escape --mangle 'trailing-whitespace.mount ')" 'trailing-whitespace.mount\x20.service'
+
+(! systemd-escape)
+(! systemd-escape --suffix='' hello)
+(! systemd-escape --suffix=invalid hello)
+(! systemd-escape --suffix=mount --template=hello@.service hello)
+(! systemd-escape --suffix=mount --mangle)
+(! systemd-escape --template='')
+(! systemd-escape --template=@)
+(! systemd-escape --template='hello@.service' '')
+(! systemd-escape --unescape --template='hello@.service' '@hello.service')
+(! systemd-escape --unescape --template='hello@.service' 'hello@.service')
+(! systemd-escape --mangle --template=hello@.service hello)
+(! systemd-escape --instance 'hello@hello.service')
+(! systemd-escape --instance --template=hello@.service 'hello@hello.service')
+(! systemd-escape --unescape --instance --path 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')
+(! systemd-escape --path '/../hello/..')
+(! systemd-escape --path '.')
+(! systemd-escape --path '..')
+(! systemd-escape --path "$(set +x; printf '%0.sa' {0..256})")
+(! systemd-escape --unescape --path '')
+(! systemd-escape --mangle '')
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if ! command -v systemd-firstboot >/dev/null; then
+ echo "systemd-firstboot not found, skipping the test"
+ exit 0
+fi
+
+at_exit() {
+ if [[ -n "${ROOT:-}" ]]; then
+ ls -lR "$ROOT"
+ rm -fr "$ROOT"
+ fi
+
+ restore_locale
+}
+
+trap at_exit EXIT
+
+# Generated via `mkpasswd -m sha-512 -S foobarsalt password1`
+# shellcheck disable=SC2016
+ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41'
+# Generated via `mkpasswd -m sha-512 -S foobarsalt password2`
+# shellcheck disable=SC2016
+ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.'
+
+if [[ -f /etc/locale.conf ]]; then
+ cp /etc/locale.conf /tmp/locale.conf.bak
+fi
+
+# Debian/Ubuntu specific file
+if [[ -f /etc/default/locale ]]; then
+ cp /etc/default/locale /tmp/default-locale.bak
+fi
+
+if [[ -f /etc/locale.gen ]]; then
+ cp /etc/locale.gen /tmp/locale.gen.bak
+fi
+
+# Make sure at least two locales exist (C.UTF-8 and en_US.UTF-8) as systemd-firstboot --prompt-locale will
+# skip writing the locale if it detects only one is installed.
+generate_locale en_US.UTF-8
+
+# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make
+# sure we use the appropriate path for locale configuration.
+LOCALE_PATH="/etc/locale.conf"
+[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale"
+[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8
+
+# Create a minimal root so we don't modify the testbed
+ROOT=test-root
+mkdir -p "$ROOT/bin"
+# Dummy shell for --root-shell=
+touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
+
+systemd-firstboot --root="$ROOT" --locale=foo
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+rm -fv "$ROOT$LOCALE_PATH"
+systemd-firstboot --root="$ROOT" --locale-messages=foo
+grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH"
+rm -fv "$ROOT$LOCALE_PATH"
+systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+
+systemd-firstboot --root="$ROOT" --keymap=foo
+grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+
+systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin"
+
+systemd-firstboot --root="$ROOT" --hostname "foobar"
+grep -q "foobar" "$ROOT/etc/hostname"
+
+systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
+
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-password=foo
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+echo "foo" >root.passwd
+systemd-firstboot --root="$ROOT" --root-password-file=root.passwd
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd
+# Set the shell together with the password, as firstboot won't touch
+# /etc/passwd if it already exists
+systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell
+grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
+
+systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42"
+grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
+
+# Configs should not get overwritten if they exist unless --force is used
+systemd-firstboot --root="$ROOT" \
+ --locale=locale-overwrite \
+ --locale-messages=messages-overwrite \
+ --keymap=keymap-overwrite \
+ --timezone=CET \
+ --hostname=hostname-overwrite \
+ --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
+ --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
+ --root-shell=/bin/barshell \
+ --kernel-command-line="hello.world=0"
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
+grep -q "foobar" "$ROOT/etc/hostname"
+grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
+grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
+grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
+
+# The same thing, but now with --force
+systemd-firstboot --root="$ROOT" --force \
+ --locale=locale-overwrite \
+ --locale-messages=messages-overwrite \
+ --keymap=keymap-overwrite \
+ --timezone=CET \
+ --hostname=hostname-overwrite \
+ --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
+ --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
+ --root-shell=/bin/barshell \
+ --kernel-command-line="hello.world=0"
+grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH"
+grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf"
+readlink "$ROOT/etc/localtime" | grep -q "/CET$"
+grep -q "hostname-overwrite" "$ROOT/etc/hostname"
+grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id"
+grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow"
+grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline"
+
+# Test that --reset removes all files configured by firstboot.
+systemd-firstboot --root="$ROOT" --reset
+[[ ! -e "$ROOT/etc/locale.conf" ]]
+[[ ! -e "$ROOT/etc/vconsole.conf" ]]
+[[ ! -e "$ROOT/etc/localtime" ]]
+[[ ! -e "$ROOT/etc/hostname" ]]
+[[ ! -e "$ROOT/etc/machine-id" ]]
+[[ ! -e "$ROOT/etc/kernel/cmdline" ]]
+
+# --copy-* options
+rm -fr "$ROOT"
+mkdir "$ROOT"
+# Copy everything at once (--copy)
+systemd-firstboot --root="$ROOT" --copy
+diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
+diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
+diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
+[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
+rm -fr "$ROOT"
+mkdir "$ROOT"
+# Copy everything at once, but now by using separate switches
+systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell
+diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
+diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
+diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
+[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
+
+# --prompt-* options
+rm -fr "$ROOT"
+mkdir -p "$ROOT/bin"
+touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
+# Temporarily disable pipefail to avoid `echo: write error: Broken pipe
+set +o pipefail
+# We can do only limited testing here, since it's all an interactive stuff,
+# so --prompt and --prompt-root-password are skipped on purpose
+echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so
+# it can present them as a list to the user. As Debian does not ship/provide
+# compatible keymaps (from the kbd package), skip this test if the keymaps are
+# missing.
+if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then
+ echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
+ grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+fi
+echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
+echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
+grep -q "foobar" "$ROOT/etc/hostname"
+echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
+grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+# Existing files should not get overwritten
+echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
+grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+# Now without the welcome screen but with force
+echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no
+grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
+# Re-enable pipefail
+set -o pipefail
+
+# Assorted tests
+rm -fr "$ROOT"
+mkdir "$ROOT"
+
+systemd-firstboot --root="$ROOT" --setup-machine-id
+grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id"
+
+systemd-firstboot --root="$ROOT" --delete-root-password
+diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+
+(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell)
+(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
+(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-id128 --help
+systemd-id128 help
+systemd-id128 show
+systemd-id128 show --pretty | tail
+systemd-id128 show --value | tail
+systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709 # root-x86-64
+systemd-id128 show --pretty 4f68bce3e8cd4db196e7fbcaf984b709
+systemd-id128 show root-x86-64
+systemd-id128 show --pretty root-x86-64
+[[ "$(systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
+[[ "$(systemd-id128 show 4f68bce3-e8cd-4db1-96e7-fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
+
+systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+[[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]]
+
+systemd-id128 show -j
+systemd-id128 show --no-pager
+systemd-id128 show --json=short
+systemd-id128 show --no-legend
+systemd-id128 show --no-pager --no-legend
+systemd-id128 show root -P -u
+
+[[ "$(systemd-id128 new | wc -c)" -eq 33 ]]
+systemd-id128 new -p
+systemd-id128 new -u
+systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709
+
+systemd-id128 machine-id
+systemd-id128 machine-id --pretty
+systemd-id128 machine-id --uuid
+systemd-id128 machine-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+assert_eq "$(systemd-id128 machine-id)" "$(</etc/machine-id)"
+
+systemd-id128 boot-id
+systemd-id128 boot-id --pretty
+systemd-id128 boot-id --uuid
+systemd-id128 boot-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+assert_eq "$(systemd-id128 boot-id --uuid)" "$(</proc/sys/kernel/random/boot_id)"
+
+# shellcheck disable=SC2016
+systemd-run --wait --pipe bash -euxc '[[ $INVOCATION_ID == "$(systemd-id128 invocation-id)" ]]'
+
+(! systemd-id128)
+(! systemd-id128 new -a '')
+(! systemd-id128 new -a '0')
+(! systemd-id128 invocation-id -a 4f68bce3e8cd4db196e7fbcaf984b709)
+(! systemd-id128 show '')
+(! systemd-id128 show "$(set +x; printf '%0.s0' {0..64})")
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2064
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+root_mock() {
+ local root="${1:?}"
+
+ mkdir -p "$root"
+ # Put a tmpfs over the "root", so we're able to remount it as read-only
+ # when needed
+ mount -t tmpfs tmpfs "$root"
+ mkdir "$root/etc" "$root/run"
+}
+
+root_cleanup() {
+ local root="${1:?}"
+
+ umount --recursive "$root"
+ rm -fr "$root"
+}
+
+testcase_sanity() {
+ systemd-machine-id-setup
+ systemd-machine-id-setup --help
+ systemd-machine-id-setup --version
+ systemd-machine-id-setup --print
+ systemd-machine-id-setup --root= --print
+ systemd-machine-id-setup --root=/ --print
+
+ (! systemd-machine-id-setup "")
+ (! systemd-machine-id-setup --foo)
+}
+
+testcase_invalid() {
+ local root machine_id
+
+ root="$(mktemp -d)"
+ trap "root_cleanup $root" RETURN
+ root_mock "$root"
+
+ systemd-machine-id-setup --print --root "$root"
+ echo abc >>"$root/etc/machine-id"
+ machine_id="$(systemd-machine-id-setup --print --root "$root")"
+ diff <(echo "$machine_id") "$root/etc/machine-id"
+}
+
+testcase_transient() {
+ local root transient_id committed_id
+
+ root="$(mktemp -d)"
+ trap "root_cleanup $root" RETURN
+ root_mock "$root"
+
+ systemd-machine-id-setup --print --root "$root"
+ echo abc >>"$root/etc/machine-id"
+ mount -o remount,ro "$root"
+ mount -t tmpfs tmpfs "$root/run"
+ transient_id="$(systemd-machine-id-setup --print --root "$root")"
+ mount -o remount,rw "$root"
+ committed_id="$(systemd-machine-id-setup --print --commit --root "$root")"
+ [[ "$transient_id" == "$committed_id" ]]
+ diff "$root/etc/machine-id" "$root/run/machine-id"
+}
+
+# Check if we correctly processed the invalid machine ID we set up in the respective
+# test.sh file
+systemctl --state=failed --no-legend --no-pager >/failed
+test ! -s /failed
+
+run_testcases
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+MODULES_LOAD_BIN="/usr/lib/systemd/systemd-modules-load"
+CONFIG_FILE="/run/modules-load.d/99-test.conf"
+
+at_exit() {
+ rm -rfv "${CONFIG_FILE:?}"
+}
+
+trap at_exit EXIT
+
+if systemd-detect-virt -cq; then
+ echo "Running in a container, skipping the systemd-modules-load test..."
+ exit 0
+fi
+
+ORIG_MODULES_LOAD_CONFIG="$(systemd-analyze cat-config modules-load.d)"
+
+# Check if we have required kernel modules
+modprobe --all --resolve-alias dummy
+
+mkdir -p /run/modules-load.d/
+
+"$MODULES_LOAD_BIN"
+"$MODULES_LOAD_BIN" --help
+"$MODULES_LOAD_BIN" --version
+
+# Explicit config file
+modprobe -v --all --remove dummy
+printf "dummy" >"$CONFIG_FILE"
+"$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log
+grep -E "Inserted module .*dummy" /tmp/out.log
+
+# Implicit config file
+modprobe -v --all --remove dummy
+printf "dummy" >"$CONFIG_FILE"
+"$MODULES_LOAD_BIN" |& tee /tmp/out.log
+grep -E "Inserted module .*dummy" /tmp/out.log
+
+# Valid & invalid data mixed together
+modprobe -v --all --remove dummy
+cat >"$CONFIG_FILE" <<EOF
+
+dummy
+dummy
+dummy
+ dummy
+dummy
+ \\n\n\n\\\\\\
+
+dumm!@@123##2455
+# This is a comment
+$(printf "%.0sx" {0..4096})
+dummy
+dummy
+foo-bar-baz
+1
+"
+'
+EOF
+"$MODULES_LOAD_BIN" |& tee /tmp/out.log
+grep -E "^Inserted module .*dummy" /tmp/out.log
+grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
+(! grep -E "This is a comment" /tmp/out.log)
+# Each module should be loaded only once, even if specified multiple times
+[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
+[[ "$(grep -Ec "^Failed to find module" /tmp/out.log)" -eq 7 ]]
+
+# Command line arguments
+modprobe -v --all --remove dummy
+# Make sure we have no config files left over that might interfere with
+# following tests
+rm -fv "$CONFIG_FILE"
+[[ "$ORIG_MODULES_LOAD_CONFIG" == "$(systemd-analyze cat-config modules-load.d)" ]]
+CMDLINE="ro root= modules_load= modules_load=, / = modules_load=foo-bar-baz,dummy modules_load=dummy,dummy,dummy"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" "$MODULES_LOAD_BIN" |& tee /tmp/out.log
+grep -E "^Inserted module .*dummy" /tmp/out.log
+grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
+# Each module should be loaded only once, even if specified multiple times
+[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
+
+(! "$MODULES_LOAD_BIN" --nope)
+(! "$MODULES_LOAD_BIN" /foo/bar/baz)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# We're going to play around with block/loop devices, so bail out early
+# if we're running in nspawn
+if systemd-detect-virt --container >/dev/null; then
+ echo "Container detected, skipping the test"
+ exit 0
+fi
+
+at_exit() {
+ set +e
+
+ [[ -n "${LOOP:-}" ]] && losetup -d "$LOOP"
+ [[ -n "${WORK_DIR:-}" ]] && rm -fr "$WORK_DIR"
+}
+
+trap at_exit EXIT
+
+WORK_DIR="$(mktemp -d)"
+
+systemd-mount --list
+systemd-mount --list --full
+systemd-mount --list --no-legend
+systemd-mount --list --no-pager
+systemd-mount --list --quiet
+
+# Set up a simple block device for further tests
+dd if=/dev/zero of="$WORK_DIR/simple.img" bs=1M count=16
+mkfs.ext4 -L sd-mount-test "$WORK_DIR/simple.img"
+LOOP="$(losetup --show --find "$WORK_DIR/simple.img")"
+udevadm wait --timeout 60 --settle "$LOOP"
+mkdir "$WORK_DIR/mnt"
+mount "$LOOP" "$WORK_DIR/mnt"
+touch "$WORK_DIR/mnt/foo.bar"
+umount "$LOOP"
+(! mountpoint "$WORK_DIR/mnt")
+
+# Mount with both source and destination set
+systemd-mount "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$WORK_DIR/mnt"
+systemd-mount --list --full
+test -e "$WORK_DIR/mnt/foo.bar"
+systemd-umount "$WORK_DIR/mnt"
+# Same thing, but with explicitly specified filesystem and disabled filesystem check
+systemd-mount --type=ext4 --fsck=no --collect "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").mount"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemd-mount --umount "$LOOP"
+# Discover additional metadata (unit description should now contain filesystem label)
+systemd-mount --no-ask-password --discover "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Description "$WORK_DIR/mnt" | grep -q sd-mount-test
+systemd-umount "$WORK_DIR/mnt"
+# Set a unit description
+systemd-mount --description="Very Important Unit" "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Very Important Unit"
+systemd-umount "$WORK_DIR/mnt"
+# Set a property
+systemd-mount --property="Description=Foo Bar" "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Foo Bar"
+systemd-umount "$WORK_DIR/mnt"
+# Set mount options
+systemd-mount --options=ro,x-foo-bar "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Options "$WORK_DIR/mnt" | grep -Eq "(^ro|,ro)"
+systemctl show -P Options "$WORK_DIR/mnt" | grep -q "x-foo-bar"
+systemd-umount "$WORK_DIR/mnt"
+
+# Mount with only source set
+systemd-mount "$LOOP"
+systemctl status /run/media/system/sd-mount-test
+systemd-mount --list --full
+test -e /run/media/system/sd-mount-test/foo.bar
+systemd-umount LABEL=sd-mount-test
+
+# Automount
+systemd-mount --automount=yes "$LOOP" "$WORK_DIR/mnt"
+systemd-mount --list --full
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
+[[ "$(systemctl show -P ActiveState "$WORK_DIR/mnt")" == inactive ]]
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl status "$WORK_DIR/mnt"
+systemd-umount "$WORK_DIR/mnt"
+# Automount + automount-specific property
+systemd-mount -A --automount-property="Description=Bar Baz" "$LOOP" "$WORK_DIR/mnt"
+systemctl show -P Description "$(systemd-escape --path "$WORK_DIR/mnt").automount" | grep -q "Bar Baz"
+test -e "$WORK_DIR/mnt/foo.bar"
+# Call --umount via --machine=, first with a relative path (bad) and then with
+# an absolute one (good)
+(! systemd-umount --machine=.host "$(realpath --relative-to=. "$WORK_DIR/mnt")")
+systemd-umount --machine=.host "$WORK_DIR/mnt"
+
+# ext4 doesn't support uid=/gid=
+(! systemd-mount -t ext4 --owner=testuser "$LOOP" "$WORK_DIR/mnt")
+
+# Automount + --bind-device
+systemd-mount --automount=yes --bind-device --timeout-idle-sec=1 "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
+# Trigger the automount
+test -e "$WORK_DIR/mnt/foo.bar"
+# Wait until it's idle again
+sleep 1.5
+# Safety net for slower/overloaded systems
+timeout 10s bash -c "while systemctl is-active -q $WORK_DIR/mnt; do sleep .2; done"
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
+# Disassemble the underlying block device
+losetup -d "$LOOP"
+unset LOOP
+# The automount unit should disappear once the underlying blockdev is gone
+timeout 10s bash -c "while systemctl status '$(systemd-escape --path "$WORK_DIR/mnt".automount)'; do sleep .2; done"
+
+# Mount a disk image
+systemd-mount --discover "$WORK_DIR/simple.img"
+# We can access files in the image even if the loopback block device is not initialized by udevd.
+test -e /run/media/system/simple.img/foo.bar
+# systemd-mount --list and systemd-umount require the loopback block device is initialized by udevd.
+udevadm settle --timeout 30
+assert_in "/dev/loop.* ext4 +sd-mount-test" "$(systemd-mount --list --full)"
+systemd-umount "$WORK_DIR/simple.img"
+
+# --owner + vfat
+#
+# Create a vfat image, as ext4 doesn't support uid=/gid= fixating for all
+# files/directories
+dd if=/dev/zero of="$WORK_DIR/owner-vfat.img" bs=1M count=16
+mkfs.vfat -n owner-vfat "$WORK_DIR/owner-vfat.img"
+LOOP="$(losetup --show --find "$WORK_DIR/owner-vfat.img")"
+udevadm wait --timeout 60 --settle "$LOOP"
+# Mount it and check the UID/GID
+[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]]
+systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$WORK_DIR/mnt"
+[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "testuser:testuser" ]]
+touch "$WORK_DIR/mnt/hello"
+[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]]
+systemd-umount LABEL=owner-vfat
+
+# tmpfs
+mkdir -p "$WORK_DIR/mnt/foo/bar"
+systemd-mount --tmpfs "$WORK_DIR/mnt/foo"
+test ! -d "$WORK_DIR/mnt/foo/bar"
+touch "$WORK_DIR/mnt/foo/baz"
+systemd-umount "$WORK_DIR/mnt/foo"
+test -d "$WORK_DIR/mnt/foo/bar"
+test ! -e "$WORK_DIR/mnt/foo/baz"
+
+# overlay
+systemd-mount --type=overlay --options="lowerdir=/etc,upperdir=$WORK_DIR/upper,workdir=$WORK_DIR/work" /etc "$WORK_DIR/overlay"
+touch "$WORK_DIR/overlay/foo"
+test -e "$WORK_DIR/upper/foo"
+systemd-umount "$WORK_DIR/overlay"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+at_exit() {
+ rm -f /run/credstore/network.conf.50-testme
+ rm -f /run/credstore/network.network.50-testme
+ rm -f /run/systemd/networkd.conf.d/50-testme.conf
+ rm -f /run/systemd/network/50-testme.network
+ rm -f /run/systemd/system/systemd-network-generator.service.d/50-testme.conf
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/network.conf.50-testme <<EOF
+[Network]
+SpeedMeter=yes
+EOF
+
+cat > /run/credstore/network.network.50-testme <<EOF
+[Match]
+Property=IDONTEXIST
+EOF
+
+systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=network.conf.50-testme
+LoadCredential=network.network.50-testme
+EOF
+
+systemctl restart systemd-network-generator
+
+diff /run/credstore/network.conf.50-testme /run/systemd/networkd.conf.d/50-testme.conf
+diff /run/credstore/network.network.50-testme /run/systemd/network/50-testme.network
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ systemctl stop systemd-networkd
+
+ if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then
+ rm -fvr {/usr/lib,/etc,/run}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \
+ {/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
+ "new" "+4"
+ fi
+
+ rm -f /run/systemd/networkd.conf.d/10-hoge.conf
+}
+
+trap at_exit EXIT
+
+export NETWORK_NAME="10-networkctl-test-$RANDOM.network"
+export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev"
+export LINK_NAME="10-networkctl-test-$RANDOM.link"
+cat >"/usr/lib/systemd/network/$NETWORK_NAME" <<EOF
+[Match]
+Name=test
+EOF
+
+# Test files
+
+networkctl mask --runtime "donotexist.network"
+assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
+networkctl unmask "donotexist.network" # unmask should work even without --runtime
+[[ ! -e /run/systemd/network/donotexist.network ]]
+
+touch /usr/lib/systemd/network/donotexist.network
+(! networkctl unmask "donotexist.network")
+rm /usr/lib/systemd/network/donotexist.network
+
+networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
+
+cat >new <<EOF
+[Match]
+Name=test2
+EOF
+
+EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
+(! networkctl mask --runtime "$NETWORK_NAME")
+printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
+
+networkctl mask "$NETWORK_NAME"
+assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
+(! networkctl edit "$NETWORK_NAME")
+(! networkctl edit --runtime "$NETWORK_NAME")
+(! networkctl cat "$NETWORK_NAME")
+networkctl unmask "$NETWORK_NAME"
+
+EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
+printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
+
+(! networkctl mask "$NETWORK_NAME")
+(! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
+
+cat >"+4" <<EOF
+[Network]
+IPv6AcceptRA=no
+EOF
+
+EDITOR='cp' script -ec 'networkctl edit "$NETWORK_NAME" --drop-in test' /dev/null
+cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf"
+
+networkctl cat "$NETWORK_NAME" | grep '^# ' |
+ cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
+
+cat >"/usr/lib/systemd/network/$NETDEV_NAME" <<EOF
+[NetDev]
+Name=test2
+Kind=dummy
+EOF
+
+networkctl cat "$NETDEV_NAME"
+
+cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
+[Match]
+OriginalName=test2
+
+[Link]
+Alias=test_alias
+EOF
+
+SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null
+cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
+
+# Test links
+systemctl unmask systemd-networkd
+systemctl stop systemd-networkd
+(! networkctl cat @test2)
+
+systemctl start systemd-networkd
+SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
+networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
+
+EDITOR='cp' script -ec 'networkctl edit @test2 --drop-in test2.conf' /dev/null
+cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test2.conf"
+
+SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
+(! EDITOR='true' script -ec 'networkctl edit @test2 --runtime --drop-in test2.conf' /dev/null)
+
+ip_link="$(ip link show test2)"
+if systemctl --quiet is-active systemd-udevd; then
+ assert_in 'alias test_alias' "$ip_link"
+fi
+
+mkdir -p /run/systemd/networkd.conf.d
+cat >/run/systemd/networkd.conf.d/10-hoge.conf <<EOF
+# TEST DROP-IN FILE
+[Network]
+SpeedMeter=yes
+EOF
+
+assert_in '# TEST DROP-IN FILE' "$(networkctl cat)"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+USER_DIRS_CONF="/root/.config/user-dirs.dirs"
+
+at_exit() {
+ set +e
+
+ rm -fv "${USER_DIRS_CONF:?}"
+}
+
+trap at_exit EXIT
+
+# Check that we indeed run under root to make the rest of the test work
+[[ "$(id -u)" -eq 0 ]]
+
+# Create a custom user-dirs.dir file to exercise the xdg-user-dirs part
+# of sd-path/from_user_dir()
+mkdir -p "/root/.config"
+cat >"${USER_DIRS_CONF:?}" <<\EOF
+XDG_DESKTOP_DIR="$HOME/my-fancy-desktop"
+XDG_INVALID
+
+XDG_DOWNLOAD_DIR = "$HOME"
+XDG_TEMPLATES_DIR="/templates"
+# Invalid records
+XDG_TEMPLATES_DIR=/not-templates"
+XDG_TEMPLATES_DIR="/also-not-teplates
+XDG_TEMPLATES_DIR=""
+XDG_TEMPLATES_DIR="../"
+
+XDG_PUBLICSHARE_DIR="$HOME/cat-pictures"
+XDG_DOCUMENTS_DIR="$HOME/top/secret/documents"
+XDG_MUSIC_DIR="/tmp/vaporwave"
+XDG_PICTURES_DIR="$HOME/Pictures"
+XDG_VIDEOS_DIR="$HOME/🤔"
+EOF
+
+systemd-path --help
+systemd-path --version
+systemd-path
+systemd-path temporary system-binaries user binfmt
+
+assert_eq "$(systemd-path system-runtime)" "/run"
+assert_eq "$(systemd-path --suffix='' system-runtime)" "/run"
+assert_eq "$(systemd-path --suffix='🤔' system-runtime)" "/run/🤔"
+assert_eq "$(systemd-path --suffix=hello system-runtime)" "/run/hello"
+
+# Note for the stuff below: everything defaults to $HOME, only the desktop
+# directory defaults to $HOME/Desktop.
+#
+# Check the user-dirs.dir stuff from above
+assert_eq "$(systemd-path user)" "/root"
+assert_eq "$(systemd-path user-desktop)" "/root/my-fancy-desktop"
+assert_eq "$(systemd-path user-documents)" "/root/top/secret/documents"
+assert_eq "$(systemd-path user-download)" "/root"
+assert_eq "$(systemd-path user-music)" "/tmp/vaporwave"
+assert_eq "$(systemd-path user-pictures)" "/root/Pictures"
+assert_eq "$(systemd-path user-public)" "/root/cat-pictures"
+assert_eq "$(systemd-path user-templates)" "/templates"
+assert_eq "$(systemd-path user-videos)" "/root/🤔"
+
+# Remove the user-dirs.dir file and check the defaults
+rm -fv "$USER_DIRS_CONF"
+[[ ! -e "$USER_DIRS_CONF" ]]
+assert_eq "$(systemd-path user-desktop)" "/root/Desktop"
+for dir in "" documents download music pictures public templates videos; do
+ assert_eq "$(systemd-path "user${dir:+-$dir}")" "/root"
+done
+
+# sd-path should consider only absolute $HOME
+assert_eq "$(HOME=/hello-world systemd-path user)" "/hello-world"
+assert_eq "$(HOME=hello-world systemd-path user)" "/root"
+assert_eq "$(HOME=/hello systemd-path --suffix=world user)" "/hello/world"
+assert_eq "$(HOME=hello systemd-path --suffix=world user)" "/root/world"
+# Same with some other env variables
+assert_in "/my-config" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path search-configuration)"
+assert_in "/my-config/foo" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path --suffix=foo search-configuration)"
+assert_in "/my-home/.config/foo" "$(HOME=/my-home XDG_CONFIG_HOME=my-config systemd-path --suffix=foo search-configuration)"
+assert_not_in "my-config" "$(HOME=my-config XDG_CONFIG_HOME=my-config systemd-path search-configuration)"
+
+(! systemd-path '')
+(! systemd-path system-binaries 🤔 user)
+(! systemd-path --xyz)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl log-level info
+
+if systemd-detect-virt -cq; then
+ echo "Running in a container, skipping the systemd-pstore test..."
+ exit 0
+fi
+
+DUMMY_DMESG_0="$(mktemp)"
+cat >"$DUMMY_DMESG_0" <<\EOF
+6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124
+6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293
+6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728
+6,17162,5343126766065,-; loop0: p1 p2
+6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
+6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem.
+6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728
+6,17166,5343158073563,-; loop0: p1 p2
+6,17167,5343158074325,-; loop0: p1 p2
+6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
+6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem.
+6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728
+6,17171,5343158700439,-; loop0: p1 p2
+6,17172,5343158701120,-; loop0: p1 p2
+EOF
+
+DUMMY_DMESG_1="$(mktemp)"
+cat >"$DUMMY_DMESG_1" <<\EOF
+Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu.
+Příliš žluťoučký kůň úpěl ďábelské ódy.
+Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů.
+Vyciď křišťálový nůž, ó učiň úděsné líbivým!
+Loď čeří kýlem tůň obzvlášť v Grónské úžině
+Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům.
+Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk.
+Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso.
+Stróż pchnął kość w quiz gędźb vel fax myjń.
+Portez ce vieux whisky au juge blond qui fume!
+EOF
+
+file_count() { find "${1:?}" -type f | wc -l; }
+file_size() { wc -l <"${1:?}"; }
+random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; }
+
+# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
+# record id, a 64-bit number.
+#
+# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.
+
+prepare_efi_logs() {
+ local file="${1:?}"
+ local timestamp="${2:?}"
+ local chunk count filename
+
+ # For the EFI backend, the 3 least significant digits of record id encodes a
+ # "count" number, the next 2 least significant digits for the dmesg part
+ # (chunk) number, and the remaining digits as the timestamp. See
+ # linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write().
+ count="$(file_size "$file")"
+ chunk=0
+ # The sed in the process substitution below just reverses the file
+ while read -r line; do
+ filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")"
+ echo "$line" >"/sys/fs/pstore/$filename"
+ chunk=$((chunk + 1))
+ done < <(sed '1!G;h;$!d' "$file")
+
+ if [[ "$chunk" -eq 0 ]]; then
+ echo >&2 "No dmesg-efi files were created"
+ exit 1
+ fi
+}
+
+prepare_erst_logs() {
+ local file="${1:?}"
+ local start_id="${2:?}"
+ local id filename
+
+ # For the ERST backend, the record is a monotonically increasing number, seeded as
+ # a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer().
+ id="$start_id"
+ # The sed in the process substitution below just reverses the file
+ while read -r line; do
+ filename="$(printf "dmesg-erst-%0.16d" "$id")"
+ echo "$line" >"/sys/fs/pstore/$filename"
+ id=$((id + 1))
+ done < <(sed '1!G;h;$!d' "$file")
+
+ if [[ "$id" -eq "$start_id" ]]; then
+ echo >&2 "No dmesg-erst files were created"
+ exit 1
+ fi
+
+ # ID of the last dmesg file will be the ID of the erst subfolder
+ echo "$((id - 1))"
+}
+
+prepare_pstore_config() {
+ local storage="${1:?}"
+ local unlink="${2:?}"
+
+ systemctl stop systemd-pstore
+
+ rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/*
+
+ mkdir -p /run/systemd/pstore.conf.d
+ cat >/run/systemd/pstore.conf.d/99-test.conf <<EOF
+[PStore]
+Storage=$storage
+Unlink=$unlink
+EOF
+
+ systemd-analyze cat-config systemd/pstore.conf | grep "$storage"
+ systemd-analyze cat-config systemd/pstore.conf | grep "$unlink"
+}
+
+start_pstore() {
+ rm -f /tmp/journal.cursor
+ journalctl -q -n 0 --cursor-file=/tmp/journal.cursor
+ systemctl restart systemd-pstore
+ journalctl --sync
+}
+
+at_exit() {
+ set +e
+
+ mountpoint -q /sys/fs/pstore && umount /sys/fs/pstore
+ rm -fr /var/lib/systemd/pstore/*
+ rm -f /run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf
+ rm -f /run/systemd/pstore.conf.d/99-test.conf
+}
+
+trap at_exit EXIT
+
+# To avoid having to depend on the VM providing the pstore, let's simulate
+# it using a simple bind mount
+PSTORE_DIR="$(mktemp -d)"
+mount --bind "${PSTORE_DIR:?}" "/sys/fs/pstore"
+
+# Disable the start limit since we're going to restart the systemd-pstore
+# service quite a lot in a short time span
+mkdir -p /run/systemd/system/systemd-pstore.service.d
+cat >/run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf <<EOF
+[Unit]
+StartLimitInterval=0
+EOF
+systemctl daemon-reload
+
+# systemd-pstore is a no-op with Storage=none
+for unlink in yes no; do
+ : "Backend: N/A; Storage: none; Unlink: $unlink"
+ timestamp="$(random_efi_timestamp)"
+ prepare_pstore_config "none" "$unlink"
+ prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
+ old_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$old_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
+
+ : "Backend: EFI; Storage: external; Unlink: $unlink"
+ timestamp="$(random_efi_timestamp)"
+ prepare_pstore_config "external" "$unlink"
+ prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
+ [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+ # We always log to journal
+ diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+ filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "$DUMMY_DMESG_0")")"
+ diff "$DUMMY_DMESG_0" "$filename"
+
+ : "Backend: EFI; Storage: external; Unlink: $unlink; multiple dmesg files"
+ timestamps=()
+ timestamp="$(random_efi_timestamp)"
+ prepare_pstore_config "external" "$unlink"
+ for i in {0..6}; do
+ timestamp="$((timestamp + (i * 10)))"
+ timestamps+=("$timestamp")
+ # Create a name reference to one of the $DUMMY_DMESG_X variables
+ dmesg="DUMMY_DMESG_$((i % 2))"
+ prepare_efi_logs "${!dmesg}" "$timestamp"
+ # Add one "random" (non-dmesg) file as well
+ echo "hello world" >/sys/fs/pstore/foo.bar
+ [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+ filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "${!dmesg}")")"
+ diff "${!dmesg}" "$filename"
+ grep "hello world" "/var/lib/systemd/pstore/foo.bar"
+ done
+ # Check that we kept all previous records as well
+ for timestamp in "${timestamps[@]}"; do
+ [[ -d "/var/lib/systemd/pstore/$timestamp" ]]
+ [[ "$(file_count "/var/lib/systemd/pstore/$timestamp/")" -gt 0 ]]
+ done
+
+ : "Backend: EFI; Storage: journal; Unlink: $unlink"
+ timestamp="$(random_efi_timestamp)"
+ prepare_pstore_config "journal" "$unlink"
+ prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
+ [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
+ diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+
+ : "Backend: ERST; Storage: external; Unlink: $unlink"
+ prepare_pstore_config "external" "$unlink"
+ last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
+ [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+ # We always log to journal
+ diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+ filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
+ diff "$DUMMY_DMESG_0" "$filename"
+
+ : "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files"
+ last_ids=()
+ prepare_pstore_config "external" "$unlink"
+ for i in {0..9}; do
+ # Create a name reference to one of the $DUMMY_DMESG_X variables
+ dmesg="DUMMY_DMESG_$((i % 2))"
+ last_id="$(prepare_erst_logs "${!dmesg}" "$((i * 100))")"
+ last_ids+=("$last_id")
+ # Add one "random" (non-dmesg) file as well
+ echo "hello world" >/sys/fs/pstore/foo.bar
+ [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+ filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
+ diff "${!dmesg}" "$filename"
+ grep "hello world" "/var/lib/systemd/pstore/foo.bar"
+ done
+ # Check that we kept all previous records as well
+ for last_id in "${last_ids[@]}"; do
+ directory="$(printf "/var/lib/systemd/pstore/%0.16d" "$last_id")"
+ [[ -d "$directory" ]]
+ [[ "$(file_count "$directory")" -gt 0 ]]
+ done
+
+ : "Backend: ERST; Storage: journal; Unlink: $unlink"
+ prepare_pstore_config "journal" "$unlink"
+ last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
+ [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+ start_pstore
+ [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+ [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
+ diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-run --help --no-pager
+systemd-run --version
+systemd-run --no-ask-password true
+systemd-run --no-block --collect true
+
+export PARENT_FOO=bar
+touch /tmp/public-marker
+
+: "Transient service (system daemon)"
+systemd-run --wait --pipe \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --system \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice=foo \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice=foo.slice \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice-inherit \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice-inherit --slice=foo \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.service$ ]]'
+# We should not inherit caller's environment
+systemd-run --wait --pipe bash -xec '[[ -z "$PARENT_FOO" ]]'
+systemd-run --wait --pipe bash -xec '[[ "$PWD" == / && -n "$INVOCATION_ID" ]]'
+systemd-run --wait --pipe \
+ --send-sighup \
+ --working-directory="" \
+ --working-directory=/tmp \
+ bash -xec '[[ "$PWD" == /tmp ]]'
+systemd-run --wait --pipe --same-dir bash -xec "[[ \"\$PWD\" == $PWD ]]"
+systemd-run --wait --pipe \
+ --property=LimitCORE=1M:2M \
+ --property=LimitCORE=16M:32M \
+ --property=PrivateTmp=yes \
+ bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
+systemd-run --wait --pipe \
+ --uid=testuser \
+ bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
+systemd-run --wait --pipe \
+ --gid=testuser \
+ bash -xec '[[ "$(id -nu)" == root && "$(id -ng)" == testuser ]]'
+systemd-run --wait --pipe \
+ --uid=testuser \
+ --gid=root \
+ bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == root ]]'
+systemd-run --wait --pipe --expand-environment=no \
+ --nice=10 \
+ bash -xec 'read -r -a SELF_STAT </proc/self/stat && [[ "${SELF_STAT[18]}" -eq 10 ]]'
+systemd-run --wait --pipe \
+ --setenv=ENV_HELLO="nope" \
+ --setenv=ENV_HELLO="env world" \
+ --setenv=EMPTY= \
+ --setenv=PARENT_FOO \
+ --property=Environment="ALSO_HELLO='also world'" \
+ bash -xec '[[ "$ENV_HELLO" == "env world" && -z "$EMPTY" && "$PARENT_FOO" == bar && "$ALSO_HELLO" == "also world" ]]'
+
+UNIT="service-0-$RANDOM"
+systemd-run --remain-after-exit --unit="$UNIT" \
+ --service-type=simple \
+ --service-type=oneshot \
+ true
+systemctl cat "$UNIT"
+grep -q "^Type=oneshot" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT"
+(! systemctl cat "$UNIT")
+
+: "Transient service (user daemon)"
+systemd-run --wait --pipe --user --machine=testuser@ \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/.+/run-.+\.service$ ]]'
+systemd-run --wait --pipe --user --machine=testuser@ \
+ bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
+systemd-run --wait --pipe --user --machine=testuser@ \
+ bash -xec '[[ "$PWD" == /home/testuser && -n "$INVOCATION_ID" ]]'
+
+# PrivateTmp=yes implies PrivateUsers=yes for user manager, so skip this if we
+# don't have unprivileged user namespaces.
+if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -ne 1 ]]; then
+ systemd-run --wait --pipe --user --machine=testuser@ \
+ --property=LimitCORE=1M:2M \
+ --property=LimitCORE=16M:32M \
+ --property=PrivateTmp=yes \
+ bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
+fi
+
+: "Transient scope (system daemon)"
+systemd-run --scope \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --system \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice=foo \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice=foo.slice \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice-inherit \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice-inherit --slice=foo \
+ bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.scope$ ]]'
+# We should inherit caller's environment
+systemd-run --scope bash -xec '[[ "$PARENT_FOO" == bar ]]'
+systemd-run --scope \
+ --property=RuntimeMaxSec=10 \
+ --property=RuntimeMaxSec=infinity \
+ true
+
+: "Transient scope (user daemon)"
+# FIXME: https://github.com/systemd/systemd/issues/27883
+#systemd-run --scope --user --machine=testuser@ \
+# bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/run-.+\.scope$ ]]'
+# We should inherit caller's environment
+#systemd-run --scope --user --machine=testuser@ bash -xec '[[ "$PARENT_FOO" == bar ]]'
+
+: "Transient timer unit"
+UNIT="timer-0-$RANDOM"
+systemd-run --remain-after-exit \
+ --unit="$UNIT" \
+ --timer-property=OnUnitInactiveSec=16h \
+ true
+systemctl cat "$UNIT.service" "$UNIT.timer"
+grep -q "^OnUnitInactiveSec=16h$" "/run/systemd/transient/$UNIT.timer"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT.timer" "$UNIT.service" || :
+
+UNIT="timer-1-$RANDOM"
+systemd-run --remain-after-exit \
+ --unit="$UNIT" \
+ --on-active=10 \
+ --on-active=30s \
+ --on-boot=1s \
+ --on-startup=2m \
+ --on-unit-active=3h20m \
+ --on-unit-inactive="5d 4m 32s" \
+ --on-calendar="mon,fri *-1/2-1,3 *:30:45" \
+ --on-clock-change \
+ --on-clock-change \
+ --on-timezone-change \
+ --timer-property=After=systemd-journald.service \
+ --description="Hello world" \
+ --description="My Fancy Timer" \
+ true
+systemctl cat "$UNIT.service" "$UNIT.timer"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.timer"
+grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnActiveSec=10s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnActiveSec=30s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnBootSec=1s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnStartupSec=2min$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnUnitActiveSec=3h 20min$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnUnitInactiveSec=5d 4min 32s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnCalendar=mon,fri \*\-1/2\-1,3 \*:30:45$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnClockChange=yes$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnTimezoneChange=yes$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.service"
+grep -q "^RemainAfterExit=yes$" "/run/systemd/transient/$UNIT.service"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+(! grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.service")
+systemctl stop "$UNIT.timer" "$UNIT.service" || :
+
+: "Transient path unit"
+UNIT="path-0-$RANDOM"
+systemd-run --remain-after-exit \
+ --unit="$UNIT" \
+ --path-property=PathExists=/tmp \
+ --path-property=PathExists=/tmp/foo \
+ --path-property=PathChanged=/root/bar \
+ true
+systemctl cat "$UNIT.service" "$UNIT.path"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.path"
+grep -q "^PathExists=/tmp$" "/run/systemd/transient/$UNIT.path"
+grep -q "^PathExists=/tmp/foo$" "/run/systemd/transient/$UNIT.path"
+grep -q "^PathChanged=/root/bar$" "/run/systemd/transient/$UNIT.path"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT.path" "$UNIT.service" || :
+
+: "Transient socket unit"
+UNIT="socket-0-$RANDOM"
+systemd-run --remain-after-exit \
+ --unit="$UNIT" \
+ --socket-property=ListenFIFO=/tmp/socket.fifo \
+ --socket-property=SocketMode=0666 \
+ --socket-property=SocketMode=0644 \
+ true
+systemctl cat "$UNIT.service" "$UNIT.socket"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.socket"
+grep -q "^ListenFIFO=/tmp/socket.fifo$" "/run/systemd/transient/$UNIT.socket"
+grep -q "^SocketMode=0666$" "/run/systemd/transient/$UNIT.socket"
+grep -q "^SocketMode=0644$" "/run/systemd/transient/$UNIT.socket"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT.socket" "$UNIT.service" || :
+
+: "Interactive options"
+SHELL=/bin/true systemd-run --shell
+SHELL=/bin/true systemd-run --scope --shell
+systemd-run --wait --pty true
+systemd-run --wait --machine=.host --pty true
+(! SHELL=/bin/false systemd-run --quiet --shell)
+
+(! systemd-run)
+(! systemd-run "")
+(! systemd-run --foo=bar)
+(! systemd-run --wait --pipe --slice=foo.service true)
+
+for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} property service-type setenv; do
+ (! systemd-run "--$opt=" true)
+ (! systemd-run "--$opt=''" true)
+done
+
+# Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc
+BOOT_ID="$(</proc/sys/kernel/random/boot_id)"
+UNIT_BOOT_ID="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id)"
+assert_eq "$BOOT_ID" "$UNIT_BOOT_ID"
+
+TMP_KVER="/tmp/version.$RANDOM"
+KVER="$(</proc/version).piff"
+echo "$KVER" >"$TMP_KVER"
+mount --bind "$TMP_KVER" /proc/version
+UNIT_KVER="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)"
+assert_eq "$KVER" "$UNIT_KVER"
+umount /proc/version
+rm -f "$TMP_KVER"
+
+if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; then
+ # Check that invoking the tool under the run0 alias name works
+ run0 ls /
+ assert_eq "$(run0 echo foo)" "foo"
+ # Check if we set some expected environment variables
+ for arg in "" "--user=root" "--user=testuser"; do
+ assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
+ assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
+ assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+ done
+ # Let's chain a couple of run0 calls together, for fun
+ readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
+ assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER"
+fi
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-74-AUX-UTILS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ systemctl stop per-source-limit.socket
+ rm -f /run/systemd/system/per-source-limit{@.service,.socket} /run/foo.conn{1..4}
+ systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+cat >/run/systemd/system/per-source-limit.socket <<EOF
+[Socket]
+ListenStream=/run/per-source-limit.sk
+MaxConnectionsPerSource=2
+Accept=yes
+EOF
+
+cat >/run/systemd/system/per-source-limit@.service <<EOF
+[Unit]
+BindsTo=per-source-limit.socket
+After=per-source-limit.socket
+
+[Service]
+ExecStartPre=echo waldo
+ExecStart=sleep infinity
+StandardOutput=socket
+EOF
+
+systemctl daemon-reload
+systemctl start per-source-limit.socket
+systemctl status per-source-limit.socket
+
+# So these two should take up the first two connection slots
+socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn1 &
+J1="$!"
+socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn2 &
+J2="$!"
+
+waitfor() {
+ local file="${1:?}"
+
+ for _ in {0..20}; do
+ if grep -q waldo "$file"; then
+ return 0
+ fi
+
+ sleep .5
+ done
+
+ echo >&2 "Timeout while waiting for the expected output"
+ return 1
+}
+
+# Wait until the word "waldo" shows in the output files
+waitfor /tmp/foo.conn1
+waitfor /tmp/foo.conn2
+
+# The next connection should fail, because the limit is hit
+socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn3 &
+J3="$!"
+
+# But this one should work, because done under a different UID
+setpriv --reuid=1 socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn4 &
+J4="$!"
+
+waitfor /tmp/foo.conn4
+
+# The third job should fail quickly, wait for it
+wait "$J3"
+
+# The other jobs will hang forever, since we run "sleep infinity" on the server side. Let's kill the jobs now.
+kill "$J1"
+kill "$J2"
+kill "$J4"
+
+# The 3rd connection should not have seen "waldo", since it should have been refused too early
+(! grep -q "waldo" /tmp/foo.conn3 )
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if ! command -v ssh &> /dev/null || ! command -v sshd &> /dev/null ; then
+ echo "ssh/sshd not found, skipping test." >&2
+ exit 0
+fi
+
+systemctl -q is-active sshd-unix-local.socket
+
+if test -e /dev/vsock ; then
+ systemctl -q is-active sshd-vsock.socket
+fi
+
+if test -d /run/host/unix-export ; then
+ systemctl -q is-active sshd-unix-export.socket
+fi
+
+# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
+if [[ -v ASAN_OPTIONS ]] ; then
+ exit 0
+fi
+
+ROOTID=$(mktemp -u)
+
+removesshid() {
+ rm -f "$ROOTID" "$ROOTID".pub
+}
+
+ssh-keygen -N '' -C '' -t rsa -f "$ROOTID"
+
+mkdir -p 0700 /root/.ssh
+# Add a newline in case authorized_keys wasn't terminated correctly.
+echo >>/root/.ssh/authorized_keys
+cat "$ROOTID".pub >>/root/.ssh/authorized_keys
+
+# set root pw to "foo", just to set it to something valid
+# shellcheck disable=SC2016
+usermod -p '$5$AAy6BYJ6rzz.QELv$6LpVEU3/RQmVz.svHu/33qoJWWWzZuJ3DM2fo9JgcUD' root
+usermod -U root
+
+mkdir -p /etc/ssh
+test -f /etc/ssh/ssh_host_rsa_key || ssh-keygen -t rsa -C '' -N '' -f /etc/ssh/ssh_host_rsa_key
+echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
+echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
+
+test -f /etc/ssh/ssh_config || echo 'Include /etc/ssh/ssh_config.d/*.conf' > /etc/ssh/ssh_config
+
+# ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that are aware of distros use
+mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
+
+ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" .host cat /etc/machine-id | cmp - /etc/machine-id
+ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" unix/run/ssh-unix-local/socket cat /etc/machine-id | cmp - /etc/machine-id
+
+modprobe vsock_loopback ||:
+if test -e /dev/vsock -a -d /sys/module/vsock_loopback ; then
+ ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" vsock/1 cat /etc/machine-id | cmp - /etc/machine-id
+fi
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+varlinkctl --help
+varlinkctl help --no-pager
+varlinkctl --version
+varlinkctl --json=help
+
+# TODO: abstract namespace sockets (@...)
+# Path to a socket
+varlinkctl info /run/systemd/journal/io.systemd.journal
+varlinkctl info /run/systemd/../systemd/../../run/systemd/journal/io.systemd.journal
+varlinkctl info "./$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)"
+varlinkctl info unix:/run/systemd/journal/io.systemd.journal
+varlinkctl info --json=off /run/systemd/journal/io.systemd.journal
+varlinkctl info --json=pretty /run/systemd/journal/io.systemd.journal | jq .
+varlinkctl info --json=short /run/systemd/journal/io.systemd.journal | jq .
+varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq .
+
+varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal
+varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq .
+
+varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal
+varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
+
+if command -v userdbctl >/dev/null; then
+ systemctl start systemd-userdbd
+ varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'
+ varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq .
+ varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
+ varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | jq --seq .
+ varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
+ (! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .)
+fi
+
+IDL_FILE="$(mktemp)"
+varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal | tee "${IDL_FILE:?}"
+varlinkctl validate-idl "$IDL_FILE"
+varlinkctl validate-idl "$IDL_FILE"
+cat /bin/sh >"$IDL_FILE"
+(! varlinkctl validate-idl "$IDL_FILE")
+
+if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
+ # Path to an executable
+ varlinkctl info /usr/lib/systemd/systemd-pcrextend
+ varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend
+ varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend
+ varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
+fi
+
+# SSH transport
+SSHBINDIR="$(mktemp -d)"
+
+rm_rf_sshbindir() {
+ rm -rf "$SSHBINDIR"
+}
+
+trap rm_rf_sshbindir EXIT
+
+# Create a fake "ssh" binary that validates everything works as expected
+cat > "$SSHBINDIR"/ssh <<'EOF'
+#!/bin/sh
+
+set -xe
+
+test "$1" = "-W"
+test "$2" = "/run/systemd/journal/io.systemd.journal"
+test "$3" = "foobar"
+
+exec socat - UNIX-CONNECT:/run/systemd/journal/io.systemd.journal
+EOF
+
+chmod +x "$SSHBINDIR"/ssh
+
+SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.systemd.journal
+
+# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage
+find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do
+ varlinkctl info "$socket"
+
+ varlinkctl list-interfaces "$socket" | while read -r interface; do
+ varlinkctl introspect "$socket" "$interface"
+ done
+done
+
+(! varlinkctl)
+(! varlinkctl "")
+(! varlinkctl info)
+(! varlinkctl info "")
+(! varlinkctl info /run/systemd/notify)
+(! varlinkctl info /run/systemd/private)
+# Relative paths must begin with ./
+(! varlinkctl info "$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)")
+(! varlinkctl info unix:)
+(! varlinkctl info unix:"")
+(! varlinkctl info exec:)
+(! varlinkctl info exec:"")
+(! varlinkctl list-interfaces)
+(! varlinkctl list-interfaces "")
+(! varlinkctl introspect)
+(! varlinkctl introspect /run/systemd/journal/io.systemd.journal)
+(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "")
+(! varlinkctl introspect "" "")
+(! varlinkctl call)
+(! varlinkctl call "")
+(! varlinkctl call "" "")
+(! varlinkctl call "" "" "")
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord </dev/null)
+(! varlinkctl validate-idl "")
+(! varlinkctl validate-idl </dev/null)
+
+varlinkctl info /run/systemd/io.systemd.Hostname
+varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
+varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+ set +e
+ rm -rf /var/lib/machines/mymachine.raw.v
+ rm -rf /var/lib/machines/mytree.v
+ rm -rf /var/lib/machines/testroot.v
+ umount -l /tmp/dotvroot
+ rmdir /tmp/dotvroot
+}
+
+trap at_exit EXIT
+
+mkdir -p /var/lib/machines/mymachine.raw.v
+
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
+
+mkdir -p /var/lib/machines/mytree.v
+
+mkdir /var/lib/machines/mytree.v/mytree_33.4
+mkdir /var/lib/machines/mytree.v/mytree_33.5
+mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
+mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
+mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
+
+ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ "$ARCH" = "x86-64" ] ; then
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
+
+ systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+elif [ "$ARCH" = "arm64" ] ; then
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+ systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+else
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+ systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+fi
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
+
+if [ "$ARCH" != "arm64" ] ; then
+ test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+ test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+else
+ test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+ test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+fi
+
+(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
+
+mkdir /tmp/dotvroot
+mount --bind / /tmp/dotvroot
+
+mkdir /var/lib/machines/testroot.v
+mkdir /var/lib/machines/testroot.v/testroot_32
+ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
+mkdir /var/lib/machines/testroot.v/testroot_34
+
+ls -l /var/lib/machines/testroot.v
+
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+find /var/lib/machines/testroot.v/testroot_34
+rm -rf /var/lib/machines/testroot.v/testroot_34
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
+systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
+
+rm /var/lib/machines/testroot.v/testroot_33
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+rm -rf /var/lib/machines/testroot.v/testroot_32
+(! systemd-vpick /var/lib/machines/testroot.v)
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-75-RESOLVED
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: ts=4 sw=4 tw=0 et:
+
+# TODO:
+# - IPv6-only stack
+# - mDNS
+# - LLMNR
+# - DoT/DoH
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# We need at least Knot 3.0 which support (among others) the ds-push directive
+if ! knotc -c /usr/lib/systemd/tests/testdata/knot-data/knot.conf conf-check; then
+ echo "This test requires at least Knot 3.0. skipping..." | tee --append /skipped
+ exit 77
+fi
+
+RUN_OUT="$(mktemp)"
+
+run() {
+ "$@" |& tee "$RUN_OUT"
+}
+
+run_delv() {
+ # Since [0] delv no longer loads /etc/(bind/)bind.keys by default, so we
+ # have to do that explicitly for each invocation
+ run delv -a /etc/bind.keys "$@"
+}
+
+disable_ipv6() {
+ sysctl -w net.ipv6.conf.all.disable_ipv6=1
+}
+
+enable_ipv6() {
+ sysctl -w net.ipv6.conf.all.disable_ipv6=0
+ networkctl reconfigure dns0
+ /usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=dns0:routable --timeout=30
+}
+
+monitor_check_rr() (
+ set +x
+ set +o pipefail
+ local since="${1:?}"
+ local match="${2:?}"
+
+ # Wait until the first mention of the specified log message is
+ # displayed. We turn off pipefail for this, since we don't care about the
+ # lhs of this pipe expression, we only care about the rhs' result to be
+ # clean
+ timeout -v 30s journalctl -u resolvectl-monitor.service --since "$since" -f --full | grep -m1 "$match"
+)
+
+restart_resolved() {
+ systemctl stop systemd-resolved.service
+ (! systemctl is-failed systemd-resolved.service)
+ # Reset the restart counter since we call this method a bunch of times
+ # and can occasionally hit the default rate limit
+ systemctl reset-failed systemd-resolved.service
+ systemctl start systemd-resolved.service
+ systemctl service-log-level systemd-resolved.service debug
+}
+
+# Test for resolvectl, resolvconf
+systemctl unmask systemd-resolved.service
+systemctl enable --now systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+ip link add hoge type dummy
+ip link add hoge.foo type dummy
+resolvectl dns hoge 10.0.0.1 10.0.0.2
+resolvectl dns hoge.foo 10.0.0.3 10.0.0.4
+assert_in '10.0.0.1 10.0.0.2' "$(resolvectl dns hoge)"
+assert_in '10.0.0.3 10.0.0.4' "$(resolvectl dns hoge.foo)"
+resolvectl dns hoge 10.0.1.1 10.0.1.2
+resolvectl dns hoge.foo 10.0.1.3 10.0.1.4
+assert_in '10.0.1.1 10.0.1.2' "$(resolvectl dns hoge)"
+assert_in '10.0.1.3 10.0.1.4' "$(resolvectl dns hoge.foo)"
+if ! RESOLVCONF=$(command -v resolvconf 2>/dev/null); then
+ TMPDIR=$(mktemp -d -p /tmp resolvconf-tests.XXXXXX)
+ RESOLVCONF="$TMPDIR"/resolvconf
+ ln -s "$(command -v resolvectl 2>/dev/null)" "$RESOLVCONF"
+fi
+echo nameserver 10.0.2.1 10.0.2.2 | "$RESOLVCONF" -a hoge
+echo nameserver 10.0.2.3 10.0.2.4 | "$RESOLVCONF" -a hoge.foo
+assert_in '10.0.2.1 10.0.2.2' "$(resolvectl dns hoge)"
+assert_in '10.0.2.3 10.0.2.4' "$(resolvectl dns hoge.foo)"
+echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35
+echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp
+assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
+assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
+
+# Tests for _localdnsstub and _localdnsproxy
+assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)"
+assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)"
+assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)"
+assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)"
+
+assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)"
+assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)"
+assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)"
+assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
+
+# Tests for mDNS and LLMNR settings
+mkdir -p /run/systemd/resolved.conf.d
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=no"
+ echo "LLMNR=no"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+restart_resolved
+# make sure networkd is not running.
+systemctl stop systemd-networkd.service
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# Tests that reloading works
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=yes"
+ echo "LLMNR=yes"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl reload systemd-resolved.service
+# defaults to yes (both the global and per-link settings are yes)
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to resolve
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=resolve"
+ echo "LLMNR=resolve"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl reload systemd-resolved.service
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to no
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=no"
+ echo "LLMNR=no"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl reload systemd-resolved.service
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+
+# Cleanup
+rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf
+ip link del hoge
+ip link del hoge.foo
+
+### SETUP ###
+# Configure network
+hostnamectl hostname ns1.unsigned.test
+cat >>/etc/hosts <<EOF
+10.0.0.1 ns1.unsigned.test
+fd00:dead:beef:cafe::1 ns1.unsigned.test
+
+127.128.0.5 localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5
+EOF
+
+mkdir -p /run/systemd/network
+cat >/run/systemd/network/10-dns0.netdev <<EOF
+[NetDev]
+Name=dns0
+Kind=dummy
+EOF
+cat >/run/systemd/network/10-dns0.network <<EOF
+[Match]
+Name=dns0
+
+[Network]
+IPv6AcceptRA=no
+Address=10.0.0.1/24
+Address=fd00:dead:beef:cafe::1/64
+DNSSEC=allow-downgrade
+DNS=10.0.0.1
+DNS=fd00:dead:beef:cafe::1
+EOF
+cat >/run/systemd/network/10-dns1.netdev <<EOF
+[NetDev]
+Name=dns1
+Kind=dummy
+EOF
+cat >/run/systemd/network/10-dns1.network <<EOF
+[Match]
+Name=dns1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.99.0.1/24
+DNSSEC=no
+EOF
+systemctl edit --stdin --full --runtime --force "resolved-dummy-server.service" <<EOF
+[Service]
+Type=notify
+Environment=SYSTEMD_LOG_LEVEL=debug
+ExecStart=/usr/lib/systemd/tests/unit-tests/manual/test-resolved-dummy-server 10.99.0.1:53
+EOF
+
+DNS_ADDRESSES=(
+ "10.0.0.1"
+ "fd00:dead:beef:cafe::1"
+)
+
+mkdir -p /run/systemd/resolved.conf.d
+{
+ echo "[Resolve]"
+ echo "FallbackDNS="
+ echo "DNSSEC=allow-downgrade"
+ echo "DNSOverTLS=opportunistic"
+} >/run/systemd/resolved.conf.d/test.conf
+ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
+# Override the default NTA list, which turns off DNSSEC validation for (among
+# others) the test. domain
+mkdir -p "/etc/dnssec-trust-anchors.d/"
+echo local >/etc/dnssec-trust-anchors.d/local.negative
+
+# Copy over our knot configuration
+mkdir -p /var/lib/knot/zones/ /etc/knot/
+cp -rfv /usr/lib/systemd/tests/testdata/knot-data/zones/* /var/lib/knot/zones/
+cp -fv /usr/lib/systemd/tests/testdata/knot-data/knot.conf /etc/knot/knot.conf
+chgrp -R knot /etc/knot/ /var/lib/knot/
+chmod -R ug+rwX /var/lib/knot/
+chmod -R g+r /etc/knot/
+
+# Sign the root zone
+keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
+# Create a trust anchor for resolved with our root zone
+keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive
+# Create a bind-compatible trust anchor (for delv)
+# Note: the trust-anchors directive is relatively new, so use the original
+# managed-keys one until it's widespread enough
+{
+ echo 'managed-keys {'
+ keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g'
+ echo '};'
+} >/etc/bind.keys
+# Create an /etc/bind/bind.keys symlink, which is used by delv on Ubuntu
+mkdir -p /etc/bind
+ln -svf /etc/bind.keys /etc/bind/bind.keys
+
+# Start the services
+systemctl unmask systemd-networkd
+systemctl restart systemd-networkd
+/usr/lib/systemd/systemd-networkd-wait-online --interface=dns1:routable --timeout=60
+systemctl reload systemd-resolved
+systemctl start resolved-dummy-server
+
+# Create knot's runtime dir, since from certain version it's provided only by
+# the package and not created by tmpfiles/systemd
+if [[ ! -d /run/knot ]]; then
+ mkdir -p /run/knot
+ chown -R knot:knot /run/knot
+fi
+systemctl start knot
+# Wait a bit for the keys to propagate
+sleep 4
+
+systemctl status resolved-dummy-server
+networkctl status
+resolvectl status
+resolvectl log-level debug
+
+# Start monitoring queries
+systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor
+systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short
+
+# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones
+# that are forwarded using the `dnsproxy` module. Until the issue is resolved,
+# let's fall back to pre-processing the `zone-check` output a bit before checking it
+#
+# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913
+run knotc zone-check || :
+sed -i '/forwarded.test./d' "$RUN_OUT"
+[[ ! -s "$RUN_OUT" ]]
+# We need to manually propagate the DS records of onlinesign.test. to the parent
+# zone, since they're generated online
+knotc zone-begin test.
+if knotc zone-get test. onlinesign.test. ds | grep .; then
+ # Drop any old DS records, if present (e.g. on test re-run)
+ knotc zone-unset test. onlinesign.test. ds
+fi
+# Propagate the new DS records
+while read -ra line; do
+ knotc zone-set test. "${line[0]}" 600 "${line[@]:1}"
+done < <(keymgr onlinesign.test. ds)
+knotc zone-commit test.
+
+knotc reload
+sleep 2
+
+### SETUP END ###
+
+: "--- nss-resolve/nss-myhostname tests"
+# Sanity check
+TIMESTAMP=$(date '+%F %T')
+# Issue: https://github.com/systemd/systemd/issues/23951
+# With IPv6 enabled
+run getent -s resolve ahosts ns1.unsigned.test
+grep -qE "^fd00:dead:beef:cafe::1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
+monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN AAAA fd00:dead:beef:cafe::1"
+# With IPv6 disabled
+# Issue: https://github.com/systemd/systemd/issues/23951
+disable_ipv6
+run getent -s resolve ahosts ns1.unsigned.test
+grep -qE "^10\.0\.0\.1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
+(! grep -qE "fd00:dead:beef:cafe::1" "$RUN_OUT")
+monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN A 10.0.0.1"
+enable_ipv6
+
+# Issue: https://github.com/systemd/systemd/issues/18812
+# PR: https://github.com/systemd/systemd/pull/18896
+# Follow-up issue: https://github.com/systemd/systemd/issues/23152
+# Follow-up PR: https://github.com/systemd/systemd/pull/23161
+# With IPv6 enabled
+run getent -s resolve ahosts localhost
+grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
+run getent -s myhostname ahosts localhost
+grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
+# With IPv6 disabled
+disable_ipv6
+run getent -s resolve ahosts localhost
+grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
+(! grep -qE "::1" "$RUN_OUT")
+run getent -s myhostname ahosts localhost
+grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
+enable_ipv6
+
+# Issue: https://github.com/systemd/systemd/issues/25088
+run getent -s resolve hosts 127.128.0.5
+grep -qEx '127\.128\.0\.5\s+localhost5(\s+localhost5?\.localdomain[45]?){4}' "$RUN_OUT"
+[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
+
+# Issue: https://github.com/systemd/systemd/issues/20158
+run dig +noall +answer +additional localhost5.
+grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
+[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
+run dig +noall +answer +additional localhost5.localdomain4.
+grep -qEx 'localhost5\.localdomain4\.\s+0\s+IN\s+CNAME\s+localhost5\.' "$RUN_OUT"
+grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
+[ "$(wc -l <"$RUN_OUT")" -eq 2 ]
+
+: "--- Basic resolved tests ---"
+# Issue: https://github.com/systemd/systemd/issues/22229
+# PR: https://github.com/systemd/systemd/pull/22231
+FILTERED_NAMES=(
+ "0.in-addr.arpa"
+ "255.255.255.255.in-addr.arpa"
+ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"
+ "hello.invalid"
+ "hello.alt"
+)
+
+for name in "${FILTERED_NAMES[@]}"; do
+ (! run host "$name")
+ grep -qF "NXDOMAIN" "$RUN_OUT"
+done
+
+# Follow-up
+# Issue: https://github.com/systemd/systemd/issues/22401
+# PR: https://github.com/systemd/systemd/pull/22414
+run dig +noall +authority +comments SRV .
+grep -qF "status: NOERROR" "$RUN_OUT"
+grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT"
+
+run resolvectl query -t SVCB svcb.test
+grep -qF 'alpn="dot"' "$RUN_OUT"
+grep -qF "ipv4hint=10.0.0.1" "$RUN_OUT"
+
+run resolvectl query -t HTTPS https.test
+grep -qF 'alpn="h2,h3"' "$RUN_OUT"
+
+: "--- ZONE: unsigned.test. ---"
+run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA
+grep -qF "10.0.0.101" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
+run resolvectl query unsigned.test
+grep -qF "10.0.0.10" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
+grep -qF "authenticated: no" "$RUN_OUT"
+run dig @ns1.unsigned.test +short MX unsigned.test
+grep -qF "15 mail.unsigned.test." "$RUN_OUT"
+run resolvectl query --legend=no -t MX unsigned.test
+grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT"
+
+
+: "--- ZONE: signed.test (static DNSSEC) ---"
+# Check the trust chain (with and without systemd-resolved in between
+# Issue: https://github.com/systemd/systemd/issues/22002
+# PR: https://github.com/systemd/systemd/pull/23289
+run_delv @ns1.unsigned.test signed.test
+grep -qF "; fully validated" "$RUN_OUT"
+run_delv signed.test
+grep -qF "; fully validated" "$RUN_OUT"
+
+for addr in "${DNS_ADDRESSES[@]}"; do
+ run_delv "@$addr" -t A mail.signed.test
+ grep -qF "; fully validated" "$RUN_OUT"
+ run_delv "@$addr" -t AAAA mail.signed.test
+ grep -qF "; fully validated" "$RUN_OUT"
+done
+run resolvectl query mail.signed.test
+grep -qF "10.0.0.11" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::11" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+run dig +short signed.test
+grep -qF "10.0.0.10" "$RUN_OUT"
+run resolvectl query signed.test
+grep -qF "signed.test: 10.0.0.10" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+run dig @ns1.unsigned.test +short MX signed.test
+grep -qF "10 mail.signed.test." "$RUN_OUT"
+run resolvectl query --legend=no -t MX signed.test
+grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT"
+# Check a non-existent domain
+run dig +dnssec this.does.not.exist.signed.test
+grep -qF "status: NXDOMAIN" "$RUN_OUT"
+# Check a wildcard record
+run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test
+grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+# Check SRV support
+run resolvectl service _mysvc._tcp signed.test
+grep -qF "myservice.signed.test:1234" "$RUN_OUT"
+grep -qF "10.0.0.20" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+# Test service resolve over Varlink
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}'
+grep -qF '"services":[{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test","canonicalName":"myservice.signed.test","addresses":[{"ifindex":' "$RUN_OUT"
+grep -qF '"family":10,"address":[253,0,222,173,190,239,202,254,0,0,0,0,0,0,0,23]' "$RUN_OUT"
+grep -qF '"family":2,"address":[10,0,0,20]' "$RUN_OUT"
+grep -qF '}]}],"txt":["This is TXT for myservice"],"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"},"flags":' "$RUN_OUT"
+
+# without name
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test"}'
+# without txt (SD_RESOLVE_NO_TXT)
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":64}'
+(! grep -qF '"txt"' "$RUN_OUT")
+# without address (SD_RESOLVE_NO_ADDRESS)
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":128}'
+(! grep -qF '"addresses"' "$RUN_OUT")
+# without txt and address
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":192}'
+(! grep -qF '"txt"' "$RUN_OUT")
+(! grep -qF '"addresses"' "$RUN_OUT")
+
+(! run resolvectl service _invalidsvc._udp signed.test)
+grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT"
+run resolvectl service _untrustedsvc._udp signed.test
+grep -qF "myservice.untrusted.test:1111" "$RUN_OUT"
+grep -qF "10.0.0.123" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+# Check OPENPGPKEY support
+run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
+grep -qF "; fully validated" "$RUN_OUT"
+run resolvectl openpgp mr.smith@signed.test
+grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+# Check zone transfers (AXFR/IXFR)
+# Note: since resolved doesn't support zone transfers, let's just make sure it
+# simply refuses such requests without choking on them
+# See: https://github.com/systemd/systemd/pull/30809#issuecomment-1880102804
+run dig @ns1.unsigned.test AXFR signed.test
+grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
+run dig AXFR signed.test
+grep -qF "; Transfer failed" "$RUN_OUT"
+run dig @ns1.unsigned.test IXFR=43 signed.test
+grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
+run dig IXFR=43 signed.test
+grep -qF "; Transfer failed" "$RUN_OUT"
+
+# DNSSEC validation with multiple records of the same type for the same name
+# Issue: https://github.com/systemd/systemd/issues/22002
+# PR: https://github.com/systemd/systemd/pull/23289
+check_domain() {
+ local domain="${1:?}"
+ local record="${2:?}"
+ local message="${3:?}"
+ local addr
+
+ for addr in "${DNS_ADDRESSES[@]}"; do
+ run_delv "@$addr" -t "$record" "$domain"
+ grep -qF "$message" "$RUN_OUT"
+ done
+
+ run_delv -t "$record" "$domain"
+ grep -qF "$message" "$RUN_OUT"
+
+ run resolvectl query "$domain"
+ grep -qF "authenticated: yes" "$RUN_OUT"
+}
+
+check_domain "dupe.signed.test" "A" "; fully validated"
+check_domain "dupe.signed.test" "AAAA" "; negative response, fully validated"
+check_domain "dupe-ipv6.signed.test" "AAAA" "; fully validated"
+check_domain "dupe-ipv6.signed.test" "A" "; negative response, fully validated"
+check_domain "dupe-mixed.signed.test" "A" "; fully validated"
+check_domain "dupe-mixed.signed.test" "AAAA" "; fully validated"
+
+# Test resolution of CNAME chains
+TIMESTAMP=$(date '+%F %T')
+run resolvectl query -t A cname-chain.signed.test
+grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+monitor_check_rr "$TIMESTAMP" "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14"
+
+# Non-existing RR + CNAME chain
+#run dig +dnssec AAAA cname-chain.signed.test
+#grep -qF "status: NOERROR" "$RUN_OUT"
+#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
+
+
+: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---"
+# Check the trust chain (with and without systemd-resolved in between
+# Issue: https://github.com/systemd/systemd/issues/22002
+# PR: https://github.com/systemd/systemd/pull/23289
+run_delv @ns1.unsigned.test sub.onlinesign.test
+grep -qF "; fully validated" "$RUN_OUT"
+run_delv sub.onlinesign.test
+grep -qF "; fully validated" "$RUN_OUT"
+
+run dig +short sub.onlinesign.test
+grep -qF "10.0.0.133" "$RUN_OUT"
+run resolvectl query sub.onlinesign.test
+grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+run dig @ns1.unsigned.test +short TXT onlinesign.test
+grep -qF '"hello from onlinesign"' "$RUN_OUT"
+run resolvectl query --legend=no -t TXT onlinesign.test
+grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
+
+for addr in "${DNS_ADDRESSES[@]}"; do
+ run_delv "@$addr" -t A dual.onlinesign.test
+ grep -qF "10.0.0.135" "$RUN_OUT"
+ run_delv "@$addr" -t AAAA dual.onlinesign.test
+ grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
+ run_delv "@$addr" -t ANY ipv6.onlinesign.test
+ grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
+done
+run resolvectl query dual.onlinesign.test
+grep -qF "10.0.0.135" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+run resolvectl query ipv6.onlinesign.test
+grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+# Check a non-existent domain
+# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the
+# different response than with "standard" DNSSEC
+run dig +dnssec this.does.not.exist.onlinesign.test
+grep -qF "status: NOERROR" "$RUN_OUT"
+grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT"
+# Check a wildcard record
+run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test
+grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+# Resolve via dbus method
+TIMESTAMP=$(date '+%F %T')
+run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0
+grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT"
+monitor_check_rr "$TIMESTAMP" "secondsub.onlinesign.test IN A 10.0.0.134"
+
+
+: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
+# Issue: https://github.com/systemd/systemd/issues/23955
+# FIXME
+resolvectl flush-caches
+#run dig +short untrusted.test A untrusted.test AAAA
+#grep -qF "10.0.0.121" "$RUN_OUT"
+#grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
+run resolvectl query untrusted.test
+grep -qF "untrusted.test:" "$RUN_OUT"
+grep -qF "10.0.0.121" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
+grep -qF "authenticated: no" "$RUN_OUT"
+run resolvectl service _mysvc._tcp untrusted.test
+grep -qF "myservice.untrusted.test:1234" "$RUN_OUT"
+grep -qF "10.0.0.123" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
+
+# Issue: https://github.com/systemd/systemd/issues/19472
+# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN
+# FIXME: re-enable once the issue is resolved
+#run dig +dnssec AAAA untrusted.test
+#grep -qF "status: NOERROR" "$RUN_OUT"
+#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
+## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL
+#run dig +dnssec this.does.not.exist.untrusted.test
+#grep -qF "status: NXDOMAIN" "$RUN_OUT"
+
+: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---"
+JOURNAL_CURSOR="$(mktemp)"
+journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR"
+
+# See "test-resolved-dummy-server.c" for the server part
+(! run resolvectl query nope.forwarded.test)
+grep -qF "nope.forwarded.test" "$RUN_OUT"
+grep -qF "not found" "$RUN_OUT"
+
+# SERVFAIL + EDE code 6: DNSSEC Bogus
+(! run resolvectl query edns-bogus-dnssec.forwarded.test)
+grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT"
+# Same thing, but over Varlink
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT"
+grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed."
+
+# SERVFAIL + EDE code 16: Censored + extra text
+(! run resolvectl query edns-extra-text.forwarded.test)
+grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)"
+
+# SERVFAIL + EDE code 0: Other + extra text
+(! run resolvectl query edns-code-zero.forwarded.test)
+grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)"
+
+# SERVFAIL + invalid EDE code
+(! run resolvectl query edns-invalid-code.forwarded.test)
+grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)"
+
+# SERVFAIL + invalid EDE code + extra text
+(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test)
+grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)"
+
+### Test resolvectl show-cache
+run resolvectl show-cache
+run resolvectl show-cache --json=short
+run resolvectl show-cache --json=pretty
+
+# Issue: https://github.com/systemd/systemd/issues/29580 (part #1)
+dig @127.0.0.54 signed.test
+
+systemctl stop resolvectl-monitor.service
+systemctl stop resolvectl-monitor-json.service
+
+# Issue: https://github.com/systemd/systemd/issues/29580 (part #2)
+#
+# Check for any warnings regarding malformed messages
+(! journalctl -u resolvectl-monitor.service -u reseolvectl-monitor-json.service -p warning --grep malformed)
+# Verify that all queries recorded by `resolvectl monitor --json` produced a valid JSON
+# with expected fields
+journalctl -p info -o cat _SYSTEMD_UNIT="resolvectl-monitor-json.service" | while read -r line; do
+ # Check that both "question" and "answer" fields are arrays
+ #
+ # The expression is slightly more complicated due to the fact that the "answer" field is optional,
+ # so we need to select it only if it's present, otherwise the type == "array" check would fail
+ echo "$line" | jq -e '[. | .question, (select(has("answer")) | .answer) | type == "array"] | all'
+done
+
+# Test serve stale feature and NFTSet= if nftables is installed
+if command -v nft >/dev/null; then
+ ### Test without serve stale feature ###
+ NFT_FILTER_NAME=dns_port_filter
+
+ drop_dns_outbound_traffic() {
+ nft add table inet $NFT_FILTER_NAME
+ nft add chain inet $NFT_FILTER_NAME output \{ type filter hook output priority 0 \; \}
+ nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 udp dport 53 drop
+ nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 tcp dport 53 drop
+ nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 udp dport 53 drop
+ nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 tcp dport 53 drop
+ }
+
+ run dig stale1.unsigned.test -t A
+ grep -qE "NOERROR" "$RUN_OUT"
+ sleep 2
+ drop_dns_outbound_traffic
+ set +e
+ # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up
+ # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617
+ run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
+ set -eux
+ grep -qE "no servers could be reached" "$RUN_OUT"
+ nft flush ruleset
+
+ ### Test TIMEOUT with serve stale feature ###
+
+ mkdir -p /run/systemd/resolved.conf.d
+ {
+ echo "[Resolve]"
+ echo "StaleRetentionSec=1d"
+ } >/run/systemd/resolved.conf.d/test.conf
+ ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
+ systemctl reload systemd-resolved.service
+
+ run dig stale1.unsigned.test -t A
+ grep -qE "NOERROR" "$RUN_OUT"
+ sleep 2
+ drop_dns_outbound_traffic
+ # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above)
+ run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
+ grep -qE "NOERROR" "$RUN_OUT"
+ grep -qE "10.0.0.112" "$RUN_OUT"
+
+ nft flush ruleset
+
+ ### Test NXDOMAIN with serve stale feature ###
+ # NXDOMAIN response should replace the cache with NXDOMAIN response
+ run dig stale1.unsigned.test -t A
+ grep -qE "NOERROR" "$RUN_OUT"
+ # Delete stale1 record from zone
+ knotc zone-begin unsigned.test
+ knotc zone-unset unsigned.test stale1 A
+ knotc zone-commit unsigned.test
+ knotc reload
+ sleep 2
+ run dig stale1.unsigned.test -t A
+ grep -qE "NXDOMAIN" "$RUN_OUT"
+
+ nft flush ruleset
+
+ ### NFTSet= test
+ nft add table inet sd_test
+ nft add set inet sd_test c '{ type cgroupsv2; }'
+ nft add set inet sd_test u '{ typeof meta skuid; }'
+ nft add set inet sd_test g '{ typeof meta skgid; }'
+
+ # service
+ systemd-run --unit test-nft.service --service-type=exec -p DynamicUser=yes \
+ -p 'NFTSet=cgroup:inet:sd_test:c user:inet:sd_test:u group:inet:sd_test:g' sleep 10000
+ run nft list set inet sd_test c
+ grep -qF "test-nft.service" "$RUN_OUT"
+ uid=$(getent passwd test-nft | cut -d':' -f3)
+ run nft list set inet sd_test u
+ grep -qF "$uid" "$RUN_OUT"
+ gid=$(getent passwd test-nft | cut -d':' -f4)
+ run nft list set inet sd_test g
+ grep -qF "$gid" "$RUN_OUT"
+ systemctl stop test-nft.service
+
+ # scope
+ run systemd-run --scope -u test-nft.scope -p 'NFTSet=cgroup:inet:sd_test:c' nft list set inet sd_test c
+ grep -qF "test-nft.scope" "$RUN_OUT"
+
+ mkdir -p /run/systemd/system
+ # socket
+ {
+ echo "[Socket]"
+ echo "ListenStream=12345"
+ echo "BindToDevice=lo"
+ echo "NFTSet=cgroup:inet:sd_test:c"
+ } >/run/systemd/system/test-nft.socket
+ {
+ echo "[Service]"
+ echo "ExecStart=/usr/bin/sleep 10000"
+ } >/run/systemd/system/test-nft.service
+ systemctl daemon-reload
+ systemctl start test-nft.socket
+ systemctl status test-nft.socket
+ run nft list set inet sd_test c
+ grep -qF "test-nft.socket" "$RUN_OUT"
+ systemctl stop test-nft.socket
+ rm -f /run/systemd/system/test-nft.{socket,service}
+
+ # slice
+ mkdir /run/systemd/system/system.slice.d
+ {
+ echo "[Slice]"
+ echo "NFTSet=cgroup:inet:sd_test:c"
+ } >/run/systemd/system/system.slice.d/00-test-nft.conf
+ systemctl daemon-reload
+ run nft list set inet sd_test c
+ grep -qF "system.slice" "$RUN_OUT"
+ rm -rf /run/systemd/system/system.slice.d
+
+ nft flush ruleset
+else
+ echo "nftables is not installed. Skipped serve stale feature and NFTSet= tests."
+fi
+
+### Test resolvectl show-server-state ###
+run resolvectl show-server-state
+grep -qF "10.0.0.1" "$RUN_OUT"
+grep -qF "Interface" "$RUN_OUT"
+
+run resolvectl show-server-state --json=short
+grep -qF "10.0.0.1" "$RUN_OUT"
+grep -qF "Interface" "$RUN_OUT"
+
+run resolvectl show-server-state --json=pretty
+grep -qF "10.0.0.1" "$RUN_OUT"
+grep -qF "Interface" "$RUN_OUT"
+
+### Test resolvectl statistics ###
+run resolvectl statistics
+grep -qF "Transactions" "$RUN_OUT"
+grep -qF "Cache" "$RUN_OUT"
+grep -qF "Failure Transactions" "$RUN_OUT"
+grep -qF "DNSSEC Verdicts" "$RUN_OUT"
+
+run resolvectl statistics --json=short
+grep -qF "transactions" "$RUN_OUT"
+grep -qF "cache" "$RUN_OUT"
+grep -qF "dnssec" "$RUN_OUT"
+
+run resolvectl statistics --json=pretty
+grep -qF "transactions" "$RUN_OUT"
+grep -qF "cache" "$RUN_OUT"
+grep -qF "dnssec" "$RUN_OUT"
+
+### Test resolvectl reset-statistics ###
+run resolvectl reset-statistics
+
+run resolvectl reset-statistics --json=pretty
+
+run resolvectl reset-statistics --json=short
+
+test "$(resolvectl --json=short query -t AAAA localhost)" == '{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}'
+test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]}'
+
+# Test ResolveRecord RR resolving via Varlink
+test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}'
+test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}'
+
+# Ensure that reloading keeps the manually configured address
+{
+ echo "[Resolve]"
+ echo "DNS=8.8.8.8"
+} >/run/systemd/resolved.conf.d/reload.conf
+resolvectl dns dns0 1.1.1.1
+systemctl reload systemd-resolved.service
+resolvectl status
+resolvectl dns dns0 | grep -qF "1.1.1.1"
+# For some reason piping this last command to grep fails with:
+# 'resolvectl[1378]: Failed to print table: Broken pipe'
+# so use an intermediate file in /tmp/
+resolvectl >/tmp/output
+grep -qF "DNS Servers: 8.8.8.8" /tmp/output
+
+# Check if resolved exits cleanly.
+restart_resolved
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-76-SYSCTL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+echo "foo.bar=42" >/tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+echo "-foo.foo=42" >/tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+if ! systemd-detect-virt --quiet --container; then
+ ip link add hoge type dummy
+ udevadm wait /sys/class/net/hoge
+
+ cat >/tmp/foo.conf <<EOF
+net.ipv4.conf.*.drop_gratuitous_arp=1
+net.ipv4.*.*.bootp_relay=1
+net.ipv4.aaa.*.disable_policy=1
+EOF
+
+ echo 0 >/proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
+ echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay
+ echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy
+
+ assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
+ assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
+ assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
+ assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
+fi
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-78-SIGQUEUE
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then
+ echo "env tool too old, can't block signals, skipping test." >&2
+ echo OK >/testok
+ exit 0
+fi
+
+systemd-analyze log-level debug
+
+UNIT="test-sigqueue-$RANDOM.service"
+
+systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity
+
+systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
+
+# We simply check that six signals are queued now. There's no easy way to check
+# from shell which ones those are, hence we don't check that.
+P=$(systemctl show -P MainPID "$UNIT")
+
+test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6
+
+systemctl stop $UNIT
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-79-MEMPRESS
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+MemoryAccounting=1
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# We not just test if the file exists, but try to read from it, since if
+# CONFIG_PSI_DEFAULT_DISABLED is set in the kernel the file will exist and can
+# be opened, but any read()s will fail with EOPNOTSUPP, which we want to
+# detect.
+if ! cat /proc/pressure/memory >/dev/null ; then
+ echo "kernel too old, has no PSI." >&2
+ echo OK >/testok
+ exit 0
+fi
+
+systemd-analyze log-level debug
+
+CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-MEMPRESS.service -P ControlGroup)"
+test -d "$CGROUP"
+
+if ! test -f "$CGROUP"/memory.pressure ; then
+ echo "No memory accounting/PSI delegated via cgroup, can't test." >&2
+ echo OK >/testok
+ exit 0
+fi
+
+UNIT="test-mempress-$RANDOM.service"
+SCRIPT="/tmp/mempress-$RANDOM.sh"
+
+cat >"$SCRIPT" <<'EOF'
+#!/bin/bash
+
+set -ex
+
+export
+id
+
+test -n "$MEMORY_PRESSURE_WATCH"
+test "$MEMORY_PRESSURE_WATCH" != /dev/null
+test -w "$MEMORY_PRESSURE_WATCH"
+
+ls -al "$MEMORY_PRESSURE_WATCH"
+
+EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)"
+
+test "$EXPECTED" = "$MEMORY_PRESSURE_WRITE"
+
+EOF
+
+chmod +x "$SCRIPT"
+
+systemd-run -u "$UNIT" -p Type=exec -p ProtectControlGroups=1 -p DynamicUser=1 -p MemoryPressureWatch=on -p MemoryPressureThresholdSec=123ms -p BindPaths=$SCRIPT --wait "$SCRIPT"
+
+rm "$SCRIPT"
+
+systemd-analyze log-level info
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-80-NOTIFYACCESS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+mkfifo /tmp/syncfifo1 /tmp/syncfifo2
+
+sync_in() {
+ read -r x < /tmp/syncfifo1
+ test "$x" = "$1"
+}
+
+sync_out() {
+ echo "$1" > /tmp/syncfifo2
+}
+
+export SYSTEMD_LOG_LEVEL=debug
+
+systemctl --no-block start notify.service
+
+sync_in a
+
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
+assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts"
+
+sync_out b
+sync_in c
+
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main"
+assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process"
+assert_rc 3 systemctl --quiet is-active notify.service
+
+sync_out d
+sync_in e
+
+systemctl --quiet is-active notify.service
+assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK"
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none"
+
+systemctl stop notify.service
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
+
+rm /tmp/syncfifo1 /tmp/syncfifo2
+
+# Now test basic fdstore behaviour
+
+MYSCRIPT="/tmp/myscript$RANDOM.sh"
+cat >> "$MYSCRIPT" <<'EOF'
+#!/usr/bin/env bash
+set -eux
+set -o pipefail
+test "$FDSTORE" -eq 7
+N="/tmp/$RANDOM"
+echo $RANDOM > "$N"
+systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N"
+rm "$N"
+systemd-notify --ready
+exec sleep infinity
+EOF
+
+chmod +x "$MYSCRIPT"
+
+MYUNIT="myunit$RANDOM.service"
+systemd-run -u "$MYUNIT" -p Type=notify -p FileDescriptorStoreMax=7 "$MYSCRIPT"
+
+test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2
+systemd-analyze fdstore "$MYUNIT" --json=short
+systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]'
+
+systemctl stop "$MYUNIT"
+rm "$MYSCRIPT"
+
+systemd-analyze log-level debug
+
+# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service)
+systemctl start fdstore-pin.target
+
+assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes
+assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
+
+# The file descriptor store should survive service restarts
+systemctl restart fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
+
+# It should not survive the service stop plus a later start (unless pinned)
+systemctl stop fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
+
+systemctl start fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
+
+systemctl stop fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
+
+systemctl clean fdstore-pin.service --what=fdstore
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-debug-generator"
+OUT_DIR="$(mktemp -d /tmp/debug-generator.XXX)"
+
+at_exit() {
+ rm -frv "${OUT_DIR:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+# Potential FIXME:
+# - debug-generator should gracefully handle duplicated mask/wants
+# - also, handle gracefully empty mask/wants
+ARGS=(
+ "systemd.mask=masked-no-suffix"
+ "systemd.mask=masked.service"
+ "systemd.mask=masked.socket"
+ "systemd.wants=wanted-no-suffix"
+ "systemd.wants=wanted.service"
+ "systemd.wants=wanted.mount"
+ "rd.systemd.mask=masked-initrd.service"
+ "rd.systemd.wants=wanted-initrd.service"
+)
+
+# Regular (non-initrd) scenario
+#
+: "debug-shell: regular"
+CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
+link_eq "$OUT_DIR/early/masked.service" /dev/null
+link_eq "$OUT_DIR/early/masked.socket" /dev/null
+link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
+link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service
+link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
+# Following stuff should be ignored, as it's prefixed with rd.
+test ! -h "$OUT_DIR/early/masked-initrd.service"
+test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service"
+test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service"
+test ! -d "$OUT_DIR/early/initrd.target.wants"
+
+# Let's re-run the generator with systemd.debug_shell that should be honored
+: "debug-shell: regular + systemd.debug_shell"
+CMDLINE="$CMDLINE systemd.debug_shell"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+
+# Same thing, but with custom tty
+: "debug-shell: regular + systemd.debug_shell=/dev/tty666"
+CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
+
+# Same thing, but with custom tty using systemd.default_debug_tty
+: "debug-shell: regular + systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
+CMDLINE="$CMDLINE systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
+
+# Now override the default target via systemd.unit=
+: "debug-shell: regular + systemd.unit="
+CMDLINE="$CMDLINE systemd.unit=my-fancy.target"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
+link_eq "$OUT_DIR/early/masked.service" /dev/null
+link_eq "$OUT_DIR/early/masked.socket" /dev/null
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+test ! -d "$OUT_DIR/early/default.target.wants"
+
+
+# Initrd scenario
+: "debug-shell: initrd"
+CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell"
+SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
+link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
+# The non-initrd stuff (i.e. without the rd. suffix) should be ignored in
+# this case
+test ! -h "$OUT_DIR/early/masked-no-suffix.service"
+test ! -h "$OUT_DIR/early/masked.service"
+test ! -h "$OUT_DIR/early/masked.socket"
+test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service"
+test ! -d "$OUT_DIR/early/default.target.wants"
+
+# Again, but with rd.systemd.debug_shell
+: "debug-shell: initrd + rd.systemd.debug_shell"
+CMDLINE="$CMDLINE rd.systemd.debug_shell"
+SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+
+# Override the default target
+: "debug-shell: initrd + rd.systemd.unit"
+CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target"
+SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
+link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
+test ! -d "$OUT_DIR/early/initrd.target.wants"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator"
+CONFIG_FILE="/run/environment.d/99-test.conf"
+OUT_FILE="$(mktemp)"
+
+at_exit() {
+ set +e
+ rm -frv "${CONFIG_FILE:?}" "${OUT_FILE:?}"
+ systemctl -M testuser@.host --user daemon-reload
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+mkdir -p /run/environment.d/
+
+cat >"$CONFIG_FILE" <<EOF
+
+\t\n\t
+3
+=
+ =
+INVALID
+ALSO_INVALID=
+EMPTY_INVALID=""
+3_INVALID=foo
+xxxx xx xxxxxx
+# This is a comment
+$(printf "%.0sx" {0..4096})=
+SIMPLE=foo
+REF=\$SIMPLE
+ALSO_REF=\${SIMPLE}
+DEFAULT="\${NONEXISTENT:-default value}"
+ALTERNATE="\${SIMPLE:+alternate value}"
+LIST=foo,bar,baz
+SIMPLE=redefined
+UNASSIGNED=\$FOO_BAR_BAZ
+VERY_LONG="very $(printf "%.0sx" {0..4096})= long string"
+EOF
+
+# Source env assignments from a file and check them - do this in a subshell
+# to not pollute the test environment
+check_environment() {(
+ # shellcheck source=/dev/null
+ source "${1:?}"
+
+ [[ "$SIMPLE" == "redefined" ]]
+ [[ "$REF" == "foo" ]]
+ [[ "$ALSO_REF" == "foo" ]]
+ [[ "$DEFAULT" == "default value" ]]
+ [[ "$ALTERNATE" == "alternate value" ]]
+ [[ "$LIST" == "foo,bar,baz" ]]
+ [[ "$VERY_LONG" =~ ^very\ ]]
+ [[ "$VERY_LONG" =~ \ long\ string$ ]]
+ [[ -z "$UNASSIGNED" ]]
+ [[ ! -v INVALID ]]
+ [[ ! -v ALSO_INVALID ]]
+ [[ ! -v EMPTY_INVALID ]]
+ [[ ! -v 3_INVALID ]]
+)}
+
+# Check the output by directly calling the generator
+"$GENERATOR_BIN" | tee "$OUT_FILE"
+check_environment "$OUT_FILE"
+: >"$OUT_FILE"
+
+# Check if the generator is correctly called in a user session
+systemctl -M testuser@.host --user daemon-reload
+systemctl -M testuser@.host --user show-environment | tee "$OUT_FILE"
+check_environment "$OUT_FILE"
+
+(! "$GENERATOR_BIN" foo)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235,SC2233
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator"
+NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$"
+OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)"
+FSTAB="$(mktemp)"
+
+at_exit() {
+ mountpoint -q /proc/cmdline && umount /proc/cmdline
+ rm -fr "${OUT_DIR:?}" "${FSTAB:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+FSTAB_GENERAL=(
+ # Valid entries
+ "/dev/test2 /nofail ext4 nofail 0 0"
+ "/dev/test3 /regular btrfs defaults 0 0"
+ "/dev/test4 /x-systemd.requires xfs x-systemd.requires=foo.service 0 0"
+ "/dev/test5 /x-systemd.before-after xfs x-systemd.before=foo.service,x-systemd.after=bar.mount 0 0"
+ "/dev/test6 /x-systemd.wanted-required-by xfs x-systemd.wanted-by=foo.service,x-systemd.required-by=bar.device 0 0"
+ "/dev/test7 /x-systemd.requires-mounts-for xfs x-systemd.requires-mounts-for=/foo/bar/baz 0 0"
+ "/dev/test8 /x-systemd.automount-idle-timeout vfat x-systemd.automount,x-systemd.idle-timeout=50s 0 0"
+ "/dev/test9 /x-systemd.makefs xfs x-systemd.makefs 0 0"
+ "/dev/test10 /x-systemd.growfs xfs x-systemd.growfs 0 0"
+ "/dev/test11 /_netdev ext4 defaults,_netdev 0 0"
+ "/dev/test12 /_rwonly ext4 x-systemd.rw-only 0 0"
+ "/dev/test13 /chaos1 zfs x-systemd.rw-only,x-systemd.requires=hello.service,x-systemd.after=my.device 0 0"
+ "/dev/test14 /chaos2 zfs x.systemd.wanted-by=foo.service,x-systemd.growfs,x-systemd.makefs 0 0"
+ "/dev/test15 /fstype/auto auto defaults 0 0"
+ "/dev/test16 /fsck/me ext4 defaults 0 1"
+ "/dev/test17 /also/fsck/me ext4 defaults,x-systemd.requires-mounts-for=/var/lib/foo 0 99"
+ "/dev/test18 /swap swap defaults 0 0"
+ "/dev/test19 /swap/makefs swap defaults,x-systemd.makefs 0 0"
+ "/dev/test20 /var xfs defaults,x-systemd.device-timeout=1h 0 0"
+ "/dev/test21 /usr ext4 defaults 0 1"
+ "/dev/test22 /initrd/mount ext2 defaults,x-systemd.rw-only,x-initrd.mount 0 1"
+ "/dev/test23 /initrd/mount/nofail ext3 defaults,nofail,x-initrd.mount 0 1"
+ "/dev/test24 /initrd/mount/deps ext4 x-initrd.mount,x-systemd.before=early.service,x-systemd.after=late.service 0 1"
+
+ # Incomplete, but valid entries
+ "/dev/incomplete1 /incomplete1"
+ "/dev/incomplete2 /incomplete2 ext4"
+ "/dev/incomplete3 /incomplete3 ext4 defaults"
+ "/dev/incomplete4 /incomplete4 ext4 defaults 0"
+
+ # Remote filesystems
+ "/dev/remote1 /nfs nfs bg 0 0"
+ "/dev/remote2 /nfs4 nfs4 bg 0 0"
+ "bar.tld:/store /remote/storage nfs ro,x-systemd.wanted-by=store.service 0 0"
+ "user@host.tld:/remote/dir /remote/top-secret sshfs rw,x-systemd.before=naughty.service 0 0"
+ "foo.tld:/hello /hello/world ceph defaults 0 0"
+ "//192.168.0.1/storage /cifs-storage cifs automount,nofail 0 0"
+)
+
+FSTAB_GENERAL_ROOT=(
+ # rootfs with bunch of options we should ignore and fsck enabled
+ "/dev/test1 / ext4 noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1"
+ "${FSTAB_GENERAL[@]}"
+)
+
+FSTAB_MINIMAL=(
+ "/dev/loop1 /foo/bar ext3 defaults 0 0"
+)
+
+FSTAB_DUPLICATE=(
+ "/dev/dup1 / ext4 defaults 0 1"
+ "/dev/dup2 / ext4 defaults,x-systemd.requires=foo.mount 0 2"
+)
+
+FSTAB_INVALID=(
+ # Ignored entries
+ "/dev/ignored1 /sys/fs/cgroup/foo ext4 defaults 0 0"
+ "/dev/ignored2 /sys/fs/selinux ext4 defaults 0 0"
+ "/dev/ignored3 /dev/console ext4 defaults 0 0"
+ "/dev/ignored4 /proc/kmsg ext4 defaults 0 0"
+ "/dev/ignored5 /proc/sys ext4 defaults 0 0"
+ "/dev/ignored6 /proc/sys/kernel/random/boot_id ext4 defaults 0 0"
+ "/dev/ignored7 /run/host ext4 defaults 0 0"
+ "/dev/ignored8 /run/host/foo ext4 defaults 0 0"
+ "/dev/ignored9 /autofs autofs defaults 0 0"
+ "/dev/invalid1 not-a-path ext4 defaults 0 0"
+ ""
+ "/dev/invalid1"
+ " "
+ "\\"
+ "$"
+)
+
+check_fstab_mount_units() {
+ local what where fstype opts passno unit
+ local item opt split_options filtered_options supp service device arg
+ local array_name="${1:?}"
+ local out_dir="${2:?}/normal"
+ # Get a reference to the array from its name
+ local -n fstab_entries="$array_name"
+
+ # Running the checks in a container is pretty much useless, since we don't
+ # generate any mounts, but don't skip the whole test to test the "skip"
+ # paths as well
+ in_container && return 0
+
+ for item in "${fstab_entries[@]}"; do
+ # Don't use a pipe here, as it would make the variables out of scope
+ read -r what where fstype opts _ passno <<< "$item"
+
+ # Skip non-initrd mounts in initrd
+ if in_initrd_host && ! [[ "$opts" =~ x-initrd.mount ]]; then
+ continue
+ fi
+
+ if [[ "$fstype" == swap ]]; then
+ unit="$(systemd-escape --suffix=swap --path "${what:?}")"
+ cat "$out_dir/$unit"
+
+ grep -qE "^What=$what$" "$out_dir/$unit"
+ if [[ "$opts" != defaults ]]; then
+ grep -qE "^Options=$opts$" "$out_dir/$unit"
+ fi
+
+ if [[ "$opts" =~ x-systemd.makefs ]]; then
+ service="$(systemd-escape --template=systemd-mkswap@.service --path "$what")"
+ test -e "$out_dir/$service"
+ fi
+
+ continue
+ fi
+
+ # If we're parsing host's fstab in initrd, prefix all mount targets
+ # with /sysroot
+ in_initrd_host && where="/sysroot${where:?}"
+ unit="$(systemd-escape --suffix=mount --path "${where:?}")"
+ cat "$out_dir/$unit"
+
+ # Check the general stuff
+ grep -qE "^What=$what$" "$out_dir/$unit"
+ grep -qE "^Where=$where$" "$out_dir/$unit"
+ if [[ -n "$fstype" ]] && [[ "$fstype" != auto ]]; then
+ grep -qE "^Type=$fstype$" "$out_dir/$unit"
+ fi
+ if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then
+ # Some options are not propagated to the generated unit
+ if [[ "$where" == / || "$where" == /usr ]]; then
+ filtered_options="$(opt_filter "$opts" "(noauto|nofail|x-systemd.(wanted-by=|required-by=|automount|device-timeout=))")"
+ else
+ filtered_options="$(opt_filter "$opts" "^x-systemd.device-timeout=")"
+ fi
+
+ if [[ "${filtered_options[*]}" != defaults ]]; then
+ grep -qE "^Options=.*$filtered_options.*$" "$out_dir/$unit"
+ fi
+ fi
+
+ if ! [[ "$opts" =~ (noauto|x-systemd.(wanted-by=|required-by=|automount)) ]]; then
+ # We don't create the Requires=/Wants= symlinks for noauto/automount mounts
+ # and for mounts that use x-systemd.wanted-by=/required-by=
+ if in_initrd_host; then
+ if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
+ link_eq "$out_dir/initrd-fs.target.requires/$unit" "../$unit"
+ else
+ link_eq "$out_dir/initrd-fs.target.wants/$unit" "../$unit"
+ fi
+ elif [[ "$fstype" =~ $NETWORK_FS_RX || "$opts" =~ _netdev ]]; then
+ # Units with network filesystems should have a Requires= dependency
+ # on the remote-fs.target, unless they use nofail or are an nfs "bg"
+ # mounts, in which case the dependency is downgraded to Wants=
+ if [[ "$opts" =~ nofail ]] || [[ "$fstype" =~ ^(nfs|nfs4) && "$opts" =~ bg ]]; then
+ link_eq "$out_dir/remote-fs.target.wants/$unit" "../$unit"
+ else
+ link_eq "$out_dir/remote-fs.target.requires/$unit" "../$unit"
+ fi
+ else
+ # Similarly, local filesystems should have a Requires= dependency on
+ # the local-fs.target, unless they use nofail, in which case the
+ # dependency is downgraded to Wants=. Rootfs is a special case,
+ # since we always ignore nofail there
+ if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
+ link_eq "$out_dir/local-fs.target.requires/$unit" "../$unit"
+ else
+ link_eq "$out_dir/local-fs.target.wants/$unit" "../$unit"
+ fi
+ fi
+ fi
+
+ if [[ "${passno:=0}" -ne 0 ]]; then
+ # Generate systemd-fsck@.service dependencies, if applicable
+ if in_initrd && [[ "$where" == / || "$where" == /usr ]]; then
+ continue
+ fi
+
+ if [[ "$where" == / ]]; then
+ link_endswith "$out_dir/local-fs.target.wants/systemd-fsck-root.service" "/lib/systemd/system/systemd-fsck-root.service"
+ else
+ service="$(systemd-escape --template=systemd-fsck@.service --path "$what")"
+ grep -qE "^After=$service$" "$out_dir/$unit"
+ if [[ "$where" == /usr ]]; then
+ grep -qE "^Wants=$service$" "$out_dir/$unit"
+ else
+ grep -qE "^Requires=$service$" "$out_dir/$unit"
+ fi
+ fi
+ fi
+
+ # Check various x-systemd options
+ #
+ # First, split them into an array to make splitting them even further
+ # easier
+ IFS="," read -ra split_options <<< "$opts"
+ # and process them one by one.
+ #
+ # Note: the "machinery" below might (and probably does) miss some
+ # combinations of supported options, so tread carefully
+ for opt in "${split_options[@]}"; do
+ if [[ "$opt" =~ ^x-systemd.requires= ]]; then
+ service="$(opt_get_arg "$opt")"
+ grep -qE "^Requires=$service$" "$out_dir/$unit"
+ grep -qE "^After=$service$" "$out_dir/$unit"
+ elif [[ "$opt" =~ ^x-systemd.before= ]]; then
+ service="$(opt_get_arg "$opt")"
+ grep -qE "^Before=$service$" "$out_dir/$unit"
+ elif [[ "$opt" =~ ^x-systemd.after= ]]; then
+ service="$(opt_get_arg "$opt")"
+ grep -qE "^After=$service$" "$out_dir/$unit"
+ elif [[ "$opt" =~ ^x-systemd.wanted-by= ]]; then
+ service="$(opt_get_arg "$opt")"
+ if [[ "$where" == / ]]; then
+ # This option is ignored for rootfs mounts
+ (! link_eq "$out_dir/$service.wants/$unit" "../$unit")
+ else
+ link_eq "$out_dir/$service.wants/$unit" "../$unit"
+ fi
+ elif [[ "$opt" =~ ^x-systemd.required-by= ]]; then
+ service="$(opt_get_arg "$opt")"
+ if [[ "$where" == / ]]; then
+ # This option is ignored for rootfs mounts
+ (! link_eq "$out_dir/$service.requires/$unit" "../$unit")
+ else
+ link_eq "$out_dir/$service.requires/$unit" "../$unit"
+ fi
+ elif [[ "$opt" =~ ^x-systemd.requires-mounts-for= ]]; then
+ arg="$(opt_get_arg "$opt")"
+ grep -qE "^RequiresMountsFor=$arg$" "$out_dir/$unit"
+ elif [[ "$opt" == x-systemd.device-bound ]]; then
+ # This is implied for fstab mounts
+ :
+ elif [[ "$opt" == x-systemd.automount ]]; then
+ # The $unit should have an accompanying automount unit
+ supp="$(systemd-escape --suffix=automount --path "$where")"
+ if [[ "$where" == / ]]; then
+ # This option is ignored for rootfs mounts
+ test ! -e "$out_dir/$supp"
+ (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp")
+ else
+ test -e "$out_dir/$supp"
+ link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp"
+ fi
+ elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then
+ # The timeout applies to the automount unit, not the original
+ # mount one
+ arg="$(opt_get_arg "$opt")"
+ supp="$(systemd-escape --suffix=automount --path "$where")"
+ grep -qE "^TimeoutIdleSec=$arg$" "$out_dir/$supp"
+ elif [[ "$opt" =~ ^x-systemd.device-timeout= ]]; then
+ arg="$(opt_get_arg "$opt")"
+ device="$(systemd-escape --suffix=device --path "$what")"
+ grep -qE "^JobRunningTimeoutSec=$arg$" "$out_dir/${device}.d/50-device-timeout.conf"
+ elif [[ "$opt" == x-systemd.makefs ]]; then
+ service="$(systemd-escape --template=systemd-makefs@.service --path "$what")"
+ test -e "$out_dir/$service"
+ link_eq "$out_dir/${unit}.requires/$service" "../$service"
+ elif [[ "$opt" == x-systemd.rw-only ]]; then
+ grep -qE "^ReadWriteOnly=yes$" "$out_dir/$unit"
+ elif [[ "$opt" == x-systemd.growfs ]]; then
+ service="$(systemd-escape --template=systemd-growfs@.service --path "$where")"
+ link_endswith "$out_dir/${unit}.wants/$service" "/lib/systemd/system/systemd-growfs@.service"
+ elif [[ "$opt" == bg ]] && [[ "$fstype" =~ ^(nfs|nfs4)$ ]]; then
+ # We "convert" nfs bg mounts to fg, so we can do the job-control
+ # ourselves
+ grep -qE "^Options=.*\bx-systemd.mount-timeout=infinity\b" "$out_dir/$unit"
+ grep -qE "^Options=.*\bfg\b.*" "$out_dir/$unit"
+ elif [[ "$opt" =~ ^x-systemd\. ]]; then
+ echo >&2 "Unhandled mount option: $opt"
+ exit 1
+ fi
+ done
+ done
+}
+
+: "fstab-generator: regular"
+printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
+
+# Skip the rest when running in a container, as it makes little sense to check
+# initrd-related stuff there and fstab-generator might have a bit strange
+# behavior during certain tests, like https://github.com/systemd/systemd/issues/27156
+if in_container; then
+ echo "Running in a container, skipping the rest of the fstab-generator tests..."
+ exit 0
+fi
+
+# In this mode we treat the entries as "regular" ones
+: "fstab-generator: initrd - initrd fstab"
+printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null check_fstab_mount_units FSTAB_GENERAL "$OUT_DIR"
+
+# In this mode we prefix the mount target with /sysroot and ignore all mounts
+# that don't have the x-initrd.mount flag
+: "fstab-generator: initrd - host fstab"
+printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
+
+# Check the default stuff that we (almost) always create in initrd
+: "fstab-generator: initrd default"
+SYSTEMD_PROC_CMDLINE="root=/dev/sda2" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+test -e "$OUT_DIR/normal/sysroot.mount"
+test -e "$OUT_DIR/normal/systemd-fsck-root.service"
+link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
+link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
+
+: "fstab-generator: run as systemd-sysroot-fstab-check in initrd"
+ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check
+(! /tmp/systemd-sysroot-fstab-check foo)
+(! SYSTEMD_IN_INITRD=0 /tmp/systemd-sysroot-fstab-check)
+printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
+SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB="$FSTAB" /tmp/systemd-sysroot-fstab-check
+
+: "fstab-generator: duplicate"
+printf "%s\n" "${FSTAB_DUPLICATE[@]}" >"$FSTAB"
+cat "$FSTAB"
+(! SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR")
+
+: "fstab-generator: invalid"
+printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB"
+cat "$FSTAB"
+# Don't care about the exit code here
+SYSTEMD_PROC_CMDLINE="" SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || :
+# No mounts should get created here
+[[ "$(find "$OUT_DIR" -name "*.mount" | wc -l)" -eq 0 ]]
+
+: "fstab-generator: kernel args - fstab=0"
+printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+(! SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
+
+: "fstab-generator: kernel args - rd.fstab=0"
+printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
+
+: "fstab-generator: kernel args - systemd.swap=0"
+printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+# No swap units should get created here
+[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]]
+
+# Possible TODO
+# - combine the rootfs & usrfs arguments and mix them with fstab entries
+# - systemd.volatile=
+: "fstab-generator: kernel args - root= + rootfstype= + rootflags="
+# shellcheck disable=SC2034
+EXPECTED_FSTAB=(
+ "/dev/disk/by-label/rootfs / ext4 noexec,ro 0 1"
+)
+CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function
+# we're parsing host's fstab, but it's all on the kernel cmdline instead
+SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR"
+
+# This is a very basic sanity test that involves manual checks, since adding it
+# to the check_fstab_mount_units() function would make it way too complex
+# (yet another possible TODO)
+: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags="
+CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount"
+# The general idea here is to mount the device to /sysusr/usr and then
+# bind-mount /sysusr/usr to /sysroot/usr
+grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount"
+grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount"
+grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount"
+grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount"
+link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount"
+grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
+grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
+grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount"
+link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+# Disable history expansion so we don't have to escape ! in strings below
+set +o histexpand
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator"
+OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)"
+
+at_exit() {
+ rm -frv "${OUT_DIR:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+if in_container; then
+ # Do a limited test in a container, as writing to /dev is usually restrited
+ : "getty-generator: \$container_ttys env (container)"
+ # In a container we allow only /dev/pts/* ptys
+ PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+
+ # console-getty.service is always pulled in in containers
+ link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service"
+ link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service"
+ test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
+
+ exit 0
+fi
+
+DUMMY_ACTIVE_CONSOLES=(
+ "hvc99"
+ "xvc99"
+ "hvsi99"
+ "sclp_line99"
+ "ttysclp99"
+ "3270!tty99"
+ "dummy99"
+)
+DUMMY_INACTIVE_CONSOLES=(
+ "inactive99"
+ "xvc199"
+)
+DUMMY_CONSOLES=(
+ "${DUMMY_ACTIVE_CONSOLES[@]}"
+ "${DUMMY_INACTIVE_CONSOLES[@]}"
+)
+# Create a bunch of dummy consoles
+for console in "${DUMMY_CONSOLES[@]}"; do
+ mknod "/dev/$console" c 4 0
+done
+# Sneak in one "not-a-tty" console
+touch /dev/notatty99
+# Temporarily replace /sys/class/tty/console/active with our list of dummy
+# consoles so getty-generator can process them
+echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles
+mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active
+
+: "getty-generator: no arguments"
+# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
+done
+for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+done
+
+: "getty-generator: systemd.getty_auto=0 on kernel cmdline"
+SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment"
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+# Cleanup
+umount /sys/class/tty/console/active --lazy
+rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator"
+OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)"
+
+at_exit() {
+ rm -frv "${OUT_DIR:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+check_kernel_cmdline_target() {
+ local out_dir="${1:?}/normal"
+
+ cat "$out_dir/kernel-command-line.target"
+ grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
+ grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
+
+ link_eq "$out_dir/default.target" "kernel-command-line.target"
+}
+
+: "run-generator: empty cmdline"
+SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+: "run-generator: single command"
+CMDLINE="systemd.run='echo hello world'"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_kernel_cmdline_target "$OUT_DIR"
+UNIT="$OUT_DIR/normal/kernel-command-line.service"
+cat "$UNIT"
+systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
+grep -qE "^SuccessAction=exit$" "$UNIT"
+grep -qE "^FailureAction=exit$" "$UNIT"
+grep -qE "^ExecStart=echo hello world$" "$UNIT"
+
+: "run-generator: multiple commands + success/failure actions"
+ARGS=(
+ # These should be ignored
+ "systemd.run"
+ "systemd.run_success_action"
+ "systemd.run_failure_action"
+
+ # Set actions which we will overwrite later
+ "systemd.run_success_action="
+ "systemd.run_failure_action="
+
+ "systemd.run=/bin/false"
+ "systemd.run="
+ "systemd.run=/bin/true"
+ "systemd.run='echo this is a long string'"
+
+ "systemd.run_success_action=reboot"
+ "systemd.run_failure_action=poweroff-force"
+)
+CMDLINE="${ARGS[*]}"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_kernel_cmdline_target "$OUT_DIR"
+UNIT="$OUT_DIR/normal/kernel-command-line.service"
+cat "$UNIT"
+systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
+grep -qE "^SuccessAction=reboot$" "$UNIT"
+grep -qE "^FailureAction=poweroff-force$" "$UNIT"
+grep -qE "^ExecStart=/bin/false$" "$UNIT"
+grep -qE "^ExecStart=$" "$UNIT"
+grep -qE "^ExecStart=/bin/true$" "$UNIT"
+grep -qE "^ExecStart=echo this is a long string$" "$UNIT"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-81-GENERATORS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator"
+OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)"
+
+at_exit() {
+ rm -frv "${OUT_DIR:?}" /system-update
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+rm -f /system-update
+
+: "system-update-generator: no /system-update flag"
+run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+: "system-update-generator: with /system-update flag"
+touch /system-update
+run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
+
+: "system-update-generator: kernel cmdline warnings"
+# We should warn if the default target is overridden on the kernel cmdline
+# by a runlevel or systemd.unit=, but still generate the symlink
+SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log
+link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
+grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log
+grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-82-SOFTREBOOT
+DefaultDependencies=no
+After=basic.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+FileDescriptorStoreMax=3
+NotifyAccess=all
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ # Since the soft-reboot drops the enqueued end.service, we won't shutdown
+ # the test VM if the test fails and have to wait for the watchdog to kill
+ # us (which may take quite a long time). Let's just forcibly kill the machine
+ # instead to save CI resources.
+ if [[ $? -ne 0 ]]; then
+ echo >&2 "Test failed, shutting down the machine..."
+ systemctl poweroff -ff
+ fi
+}
+
+trap at_exit EXIT
+
+systemd-analyze log-level debug
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then
+ echo "This is the fourth boot!"
+ systemd-notify --status="Fourth Boot"
+
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3
+
+ rm /run/TEST-82-SOFTREBOOT.touch3
+ mount
+ rmdir /original-root /run/nextroot
+
+ # Check that the fdstore entry still exists
+ test "$LISTEN_FDS" -eq 3
+ read -r x <&5
+ test "$x" = "oinkoink"
+
+ # Check that the surviving services are still around
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
+
+ # Check journals
+ journalctl -o short-monotonic --no-hostname --grep '(will soft-reboot|KILL|corrupt)'
+ assert_eq "$(journalctl -q -o short-monotonic -u systemd-journald.service --grep 'corrupt')" ""
+
+ # All succeeded, exit cleanly now
+
+elif [ -f /run/TEST-82-SOFTREBOOT.touch2 ]; then
+ echo "This is the third boot!"
+ systemd-notify --status="Third Boot"
+
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2
+
+ rm /run/TEST-82-SOFTREBOOT.touch2
+
+ # Check that the fdstore entry still exists
+ test "$LISTEN_FDS" -eq 2
+ read -r x <&4
+ test "$x" = "miaumiau"
+
+ # Upload another entry
+ T="/dev/shm/fdstore.$RANDOM"
+ echo "oinkoink" >"$T"
+ systemd-notify --fd=3 --pid=parent 3<"$T"
+ rm "$T"
+
+ # Check that the surviving services are still around
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
+
+ # Test that we really are in the new overlayfs root fs
+ read -r x </lower
+ test "$x" = "miep"
+ cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
+ grep -q MARKER=1 /etc/os-release
+
+ # Switch back to the original root, away from the overlayfs
+ mount --bind /original-root /run/nextroot
+ mount
+
+ # Restart the unit that is not supposed to survive
+ systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity
+
+ # Now issue the soft reboot. We should be right back soon.
+ touch /run/TEST-82-SOFTREBOOT.touch3
+ systemctl --no-block soft-reboot
+
+ # Now block until the soft-boot killing spree kills us
+ exec sleep infinity
+
+elif [ -f /run/TEST-82-SOFTREBOOT.touch ]; then
+ echo "This is the second boot!"
+ systemd-notify --status="Second Boot"
+
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1
+
+ # Clean up what we created earlier
+ rm /run/TEST-82-SOFTREBOOT.touch
+
+ # Check that the fdstore entry still exists
+ test "$LISTEN_FDS" -eq 1
+ read -r x <&3
+ test "$x" = "wuffwuff"
+
+ # Check that we got a PrepareForShutdownWithMetadata signal with the right type
+ cat /run/TEST-82-SOFTREBOOT.signal
+ test "$(jq -r '.payload.data[1].type.data' </run/TEST-82-SOFTREBOOT.signal)" = "soft-reboot"
+
+ # Check that the system credentials survived the soft reboot.
+ test "$(systemd-creds cat --system kernelcmdlinecred)" = "uff"
+
+ # Upload another entry
+ T="/dev/shm/fdstore.$RANDOM"
+ echo "miaumiau" >"$T"
+ systemd-notify --fd=3 --pid=parent 3<"$T"
+ rm "$T"
+
+ # Check that the surviving services are still around
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
+ test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
+
+ # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs)
+ mkdir -p /run/nextroot /tmp/nextroot-lower /original-root
+ mount -t tmpfs tmpfs /tmp/nextroot-lower
+ echo miep >/tmp/nextroot-lower/lower
+
+ # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate
+ # directory across soft reboots. Try to cover corner cases by truncating it.
+ mkdir -p /tmp/nextroot-lower/usr/lib
+ grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release
+ echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release
+ cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
+ (! grep -q MARKER=1 /etc/os-release)
+
+ mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro
+
+ # Bind our current root into the target so that we later can return to it
+ mount --bind / /run/nextroot/original-root
+
+ # Restart the unit that is not supposed to survive
+ systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity
+
+ # Now ensure there are no naming clashes and a bunch of transient units all succeed
+ for _ in $(seq 1 25); do
+ systemd-run --wait true
+ done
+
+ # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should
+ # automatically do a softreboot instead of normal reboot.
+ touch /run/TEST-82-SOFTREBOOT.touch2
+ systemctl --no-block reboot
+
+ # Now block until the soft-boot killing spree kills us
+ exec sleep infinity
+else
+ # This is the first boot
+ systemd-notify --status="First Boot"
+
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0
+
+ # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly
+ T="/dev/shm/fdstore.$RANDOM"
+ echo "wuffwuff" >"$T"
+ systemd-notify --fd=3 --pid=parent 3<"$T"
+ rm "$T"
+
+ survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh"
+ cat >"$survive_sigterm" <<EOF
+#!/bin/bash
+trap "" TERM
+systemd-notify --ready
+rm "$survive_sigterm"
+exec sleep infinity
+EOF
+ chmod +x "$survive_sigterm"
+
+ survive_argv="/dev/shm/survive-argv-$RANDOM.sh"
+ cat >"$survive_argv" <<EOF
+#!/bin/bash
+systemd-notify --ready
+rm "$survive_argv"
+exec -a @sleep sleep infinity
+EOF
+ chmod +x "$survive_argv"
+ # This sets DefaultDependencies=no so that they remain running until the very end, and
+ # IgnoreOnIsolate=yes so that they aren't stopped via the "testsuite.target" isolation we do on next boot,
+ # and will be killed by the final sigterm/sigkill spree.
+ systemd-run --collect --service-type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=TEST-82-SOFTREBOOT-nosurvive-sigterm.service "$survive_sigterm"
+ systemd-run --collect --service-type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity
+
+ # Ensure that the unit doesn't get deactivated by dependencies on the source file. Given it's a verity
+ # image that is already open, even if the tmpfs with the image goes away, the file will be pinned by the
+ # kernel and will keep working.
+ cp /usr/share/minimal_0.* /tmp/
+
+ # Configure these transient units to survive the soft reboot - they will not conflict with shutdown.target
+ # and it will be ignored on the isolate that happens in the next boot. The first will use argv[0][0] =
+ # '@', and the second will use SurviveFinalKillSignal=yes. Both should survive.
+ systemd-run --service-type=notify --unit=TEST-82-SOFTREBOOT-survive-argv.service \
+ --property SurviveFinalKillSignal=no \
+ --property IgnoreOnIsolate=yes \
+ --property DefaultDependencies=no \
+ --property After=basic.target \
+ --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+ --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+ "$survive_argv"
+ systemd-run --service-type=exec --unit=TEST-82-SOFTREBOOT-survive.service \
+ --property TemporaryFileSystem="/run /tmp /var" \
+ --property RootImage=/tmp/minimal_0.raw \
+ --property BindReadOnlyPaths=/dev/log \
+ --property BindReadOnlyPaths=/run/systemd/journal/socket \
+ --property BindReadOnlyPaths=/run/systemd/journal/stdout \
+ --property SurviveFinalKillSignal=yes \
+ --property IgnoreOnIsolate=yes \
+ --property DefaultDependencies=no \
+ --property After=basic.target \
+ --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+ --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+ sleep infinity
+
+ # Check that we can set up an inhibitor, and that busctl monitor sees the
+ # PrepareForShutdownWithMetadata signal and that it says 'soft-reboot'.
+ systemd-run --unit busctl.service --service-type=exec --property StandardOutput=file:/run/TEST-82-SOFTREBOOT.signal \
+ busctl monitor --json=pretty --match 'sender=org.freedesktop.login1,path=/org/freedesktop/login1,interface=org.freedesktop.login1.Manager,member=PrepareForShutdownWithMetadata,type=signal'
+ systemd-run --unit inhibit.service --service-type=exec \
+ systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
+ sleep infinity
+
+ # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus
+ # being restarted and the "unique" bus IDs not being unique across restarts
+ for _ in $(seq 1 25); do
+ # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is
+ # used instead of the bus ID)
+ systemd-run --wait false || true
+ done
+
+ # Now issue the soft reboot. We should be right back soon.
+ touch /run/TEST-82-SOFTREBOOT.touch
+ systemctl --no-block --check-inhibitors=yes soft-reboot
+
+ # Now block until the soft-boot killing spree kills us
+ exec sleep infinity
+fi
+
+systemd-analyze log-level info
+
+touch /testok
+systemctl --no-block exit 123
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-83-BTRFS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FSTYPE="$(stat --file-system --format "%T" /)"
+
+if [[ "$FSTYPE" != "btrfs" ]]; then
+ echo "Root filesystem is $FSTYPE instead of btrfs, skipping"
+ exit 77
+fi
+
+TEST_BTRFS_OFFSET=/usr/lib/systemd/tests/unit-tests/manual/test-btrfs-physical-offset
+
+SWAPFILE=/var/tmp/swapfile
+
+btrfs filesystem mkswapfile -s 10m "$SWAPFILE"
+sync -f "$SWAPFILE"
+
+offset_btrfs_progs="$(btrfs inspect-internal map-swapfile -r "$SWAPFILE")"
+echo "btrfs-progs: $offset_btrfs_progs"
+
+offset_btrfs_util="$("$TEST_BTRFS_OFFSET" "$SWAPFILE")"
+echo "btrfs-util: $offset_btrfs_util"
+
+(( offset_btrfs_progs == offset_btrfs_util ))
+
+rm -f "$SWAPFILE"
+
+/usr/lib/systemd/tests/unit-tests/manual/test-btrfs
+
+touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-84-STORAGETM
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+modprobe -v nvmet-tcp
+modprobe -v nvme-tcp
+
+systemctl start sys-kernel-config.mount
+
+dd if=/dev/urandom of=/var/tmp/storagetm.test bs=1024 count=10240
+
+NVME_UUID="$(cat /proc/sys/kernel/random/uuid)"
+systemd-run -u teststoragetm.service -p Type=notify -p "Environment=SYSTEMD_NVME_UUID=${NVME_UUID:?}" /usr/lib/systemd/systemd-storagetm /var/tmp/storagetm.test --nqn=quux
+NVME_DEVICE="/dev/disk/by-id/nvme-uuid.${NVME_UUID:?}"
+
+nvme connect-all -t tcp -a 127.0.0.1 -s 16858 --hostid="$(cat /proc/sys/kernel/random/uuid)"
+udevadm wait --settle "$NVME_DEVICE"
+
+dd if="$NVME_DEVICE" bs=1024 | cmp /var/tmp/storagetm.test -
+
+nvme disconnect-all
+systemctl stop teststoragetm.service
+rm /var/tmp/storagetm.test
+
+touch /testok
_show_summary
}
-# Run all subtests (i.e. files named as testsuite-<testid>.<subtest_name>.sh)
+# Run all subtests (i.e. files named as $TESTNAME.<subtest_name>.sh)
run_subtests() {
local subtests=("${0%.sh}".*.sh)
local subtest
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-01-BASIC
-After=multi-user.target
-Wants=systemd-resolved.service systemd-networkd.service
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check if the colored --version output behaves correctly
-SYSTEMD_COLORS=256 systemctl --version
-
-# Check if we properly differentiate between a full systemd setup and a "light"
-# version of it that's done during daemon-reexec
-#
-# See: https://github.com/systemd/systemd/issues/27106
-if systemd-detect-virt -q --container; then
- # We initialize /run/systemd/container only during a full setup
- test -e /run/systemd/container
- cp -afv /run/systemd/container /tmp/container
- rm -fv /run/systemd/container
- systemctl daemon-reexec
- test ! -e /run/systemd/container
- cp -afv /tmp/container /run/systemd/container
-else
- # We bring the loopback netdev up only during a full setup, so it should
- # not get brought back up during reexec if we disable it beforehand
- [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]]
- ip link set lo down
- [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
- systemctl daemon-reexec
- [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
- ip link set lo up
-
- # We also disable coredumps only during a full setup
- sysctl -w kernel.core_pattern=dont-overwrite-me
- systemctl daemon-reexec
- diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern)
-fi
-
-# Collect failed units & do one daemon-reload to a basic sanity check
-systemctl --state=failed --no-legend --no-pager | tee /failed
-test ! -s /failed
-systemctl daemon-reload
-
-# Check that the early setup is actually skipped on reexec.
-# If the early setup is done more than once, then several timestamps,
-# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT
-# of systemd-analyze blame. See issue #27187.
-systemd-analyze blame
-
-# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
-# See issue #27163.
-# shellcheck disable=SC2034
-if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then
- for _ in {0..10}; do
- systemctl daemon-reexec &
- pid_reexec=$!
- # shellcheck disable=SC2034
- for _ in {0..10}; do
- SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
- done
- wait "$pid_reexec"
- done
-fi
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-02-UNITTESTS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if ! systemd-detect-virt -qc && [[ "${TEST_CMDLINE_NEWLINE:-}" != bar ]]; then
- cat /proc/cmdline
- echo >&2 "Expected TEST_CMDLINE_NEWLINE=bar from the kernel command line"
- exit 1
-fi
-
-# If we're running with TEST_PREFER_NSPAWN=1 limit the set of tests we run
-# in QEMU to only those that can't run in a container to avoid running
-# the same tests again in a, most likely, very slow environment
-if ! systemd-detect-virt -qc && [[ "${TEST_PREFER_NSPAWN:-0}" -ne 0 ]]; then
- TESTS_GLOB="test-loop-block"
-else
- TESTS_GLOB=${TESTS_GLOB:-test-*}
-fi
-
-NPROC=$(nproc)
-MAX_QUEUE_SIZE=${NPROC:-2}
-
-# Reset state
-rm -fv /failed /skipped /testok
-touch /lock
-
-if ! systemd-detect-virt -qc; then
- # Make sure ping works for unprivileged users (for test-bpf-firewall)
- sysctl net.ipv4.ping_group_range="0 2147483647"
-fi
-
-# Check & report test results
-# Arguments:
-# $1: test path
-# $2: test exit code
-run_test() {
- if [[ $# -ne 1 ]]; then
- echo >&2 "run_test: missing arguments"
- exit 1
- fi
-
- local test="$1"
- local name="${test##*/}"
-
- echo "Executing test $name as unit $name.service"
-
- systemd-run --quiet --property Delegate=1 --unit="$name" --wait "$test" && ret=0 || ret=$?
-
- exec {LOCK_FD}> /lock
- flock --exclusive ${LOCK_FD}
-
- if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then
- echo "$name failed with $ret"
- echo "$name" >>/failed-tests
- {
- echo "--- $name begin ---"
- journalctl --unit="$name" --no-hostname -o short-monotonic
- echo "--- $name end ---"
- } >>/failed
- elif [[ $ret == 77 || $ret == 127 ]]; then
- echo "$name skipped"
- echo "$name" >>/skipped-tests
- {
- echo "--- $name begin ---"
- journalctl --unit="$name" --no-hostname -o short-monotonic
- echo "--- $name end ---"
- } >>/skipped
- else
- echo "$name OK"
- echo "$name" >>/testok
- fi
-
- exec {LOCK_FD}<&-
-}
-
-export -f run_test
-
-find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}" -print0 |
- xargs -0 -I {} --max-procs="$MAX_QUEUE_SIZE" bash -ec "run_test {}"
-
-# Test logs are sometimes lost, as the system shuts down immediately after
-journalctl --sync
-
-test ! -s /failed
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-03-JOBS
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Simple test for that daemon-reexec works in container.
-# See: https://github.com/systemd/systemd/pull/23883
-systemctl daemon-reexec
-
-# Test merging of a --job-mode=ignore-dependencies job into a previously
-# installed job.
-
-systemctl start --no-block hello-after-sleep.target
-
-systemctl list-jobs >/root/list-jobs.txt
-until grep 'sleep\.service.*running' /root/list-jobs.txt; do
- systemctl list-jobs >/root/list-jobs.txt
-done
-
-grep 'hello\.service.*waiting' /root/list-jobs.txt
-
-# This is supposed to finish quickly, not wait for sleep to finish.
-START_SEC=$(date -u '+%s')
-systemctl start --job-mode=ignore-dependencies hello
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-
-test "$ELAPSED" -lt 3
-
-# sleep should still be running, hello not.
-systemctl list-jobs >/root/list-jobs.txt
-grep 'sleep\.service.*running' /root/list-jobs.txt
-grep 'hello\.service' /root/list-jobs.txt && exit 1
-systemctl stop sleep.service hello-after-sleep.target
-
-# Some basic testing that --show-transaction does something useful
-(! systemctl is-active systemd-importd)
-systemctl -T start systemd-importd
-systemctl is-active systemd-importd
-systemctl --show-transaction stop systemd-importd
-(! systemctl is-active systemd-importd)
-
-# Test for a crash when enqueuing a JOB_NOP when other job already exists
-systemctl start --no-block hello-after-sleep.target
-# hello.service should still be waiting, so these try-restarts will collapse
-# into NOPs.
-systemctl try-restart --job-mode=fail hello.service
-systemctl try-restart hello.service
-systemctl stop hello.service sleep.service hello-after-sleep.target
-
-# TODO: add more job queueing/merging tests here.
-
-# Test that restart propagates to activating units
-systemctl -T --no-block start always-activating.service
-systemctl list-jobs | grep 'always-activating.service'
-ACTIVATING_ID_PRE=$(systemctl show -P InvocationID always-activating.service)
-systemctl -T start always-activating.socket # Wait for the socket to come up
-systemctl -T restart always-activating.socket
-ACTIVATING_ID_POST=$(systemctl show -P InvocationID always-activating.service)
-[ "$ACTIVATING_ID_PRE" != "$ACTIVATING_ID_POST" ] || exit 1
-
-# Test for irreversible jobs
-systemctl start unstoppable.service
-
-# This is expected to fail with 'job cancelled'
-systemctl stop unstoppable.service && exit 1
-# But this should succeed
-systemctl stop --job-mode=replace-irreversibly unstoppable.service
-
-# We're going to shutdown soon. Let's see if it succeeds when
-# there's an active service that tries to be unstoppable.
-# Shutdown of the container/VM will hang if not.
-systemctl start unstoppable.service
-
-# Test waiting for a started units to terminate again
-cat <<EOF >/run/systemd/system/wait2.service
-[Unit]
-Description=Wait for 2 seconds
-[Service]
-ExecStart=sh -ec 'sleep 2'
-EOF
-cat <<EOF >/run/systemd/system/wait5fail.service
-[Unit]
-Description=Wait for 5 seconds and fail
-[Service]
-ExecStart=sh -ec 'sleep 5; false'
-EOF
-
-# wait2 succeeds
-START_SEC=$(date -u '+%s')
-systemctl start --wait wait2.service
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1
-
-# wait5fail fails, so systemctl should fail
-START_SEC=$(date -u '+%s')
-(! systemctl start --wait wait2.service wait5fail.service)
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
-
-# Test time-limited scopes
-START_SEC=$(date -u '+%s')
-set +e
-systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
-RESULT=$?
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
-[[ "$RESULT" -ne 0 ]] || exit 1
-
-# Test transactions with cycles
-# Provides coverage for issues like https://github.com/systemd/systemd/issues/26872
-for i in {0..19}; do
- cat >"/run/systemd/system/transaction-cycle$i.service" <<EOF
-[Unit]
-After=transaction-cycle$(((i + 1) % 20)).service
-Requires=transaction-cycle$(((i + 1) % 20)).service
-
-[Service]
-ExecStart=true
-EOF
-done
-systemctl daemon-reload
-for i in {0..19}; do
- systemctl start "transaction-cycle$i.service"
-done
-
-# Test PropagatesStopTo= when restart (issue #26839)
-systemctl start propagatestopto-and-pullin.target
-systemctl --quiet is-active propagatestopto-and-pullin.target
-
-systemctl restart propagatestopto-and-pullin.target
-systemctl --quiet is-active propagatestopto-and-pullin.target
-systemctl --quiet is-active sleep-infinity-simple.service
-
-systemctl start propagatestopto-only.target
-systemctl --quiet is-active propagatestopto-only.target
-systemctl --quiet is-active sleep-infinity-simple.service
-
-systemctl restart propagatestopto-only.target
-assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
-
-systemctl start propagatesstopto-indirect.target propagatestopto-and-pullin.target
-systemctl --quiet is-active propagatestopto-indirect.target
-systemctl --quiet is-active propagatestopto-and-pullin.target
-
-systemctl restart propagatestopto-indirect.target
-assert_rc 3 systemctl --quiet is-active propagatestopto-and-pullin.target
-assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
-
-# Test restart mode direct
-systemctl start succeeds-on-restart-restartdirect.target
-assert_rc 0 systemctl --quiet is-active succeeds-on-restart-restartdirect.target
-
-systemctl start fails-on-restart-restartdirect.target || :
-assert_rc 3 systemctl --quiet is-active fails-on-restart-restartdirect.target
-
-systemctl start succeeds-on-restart.target || :
-assert_rc 3 systemctl --quiet is-active succeeds-on-restart.target
-
-systemctl start fails-on-restart.target || :
-assert_rc 3 systemctl --quiet is-active fails-on-restart.target
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
- . "$(dirname "$0")"/util.sh
-
-add_logs_filtering_override() {
- local unit="${1:?}"
- local override_name="${2:?}"
- local log_filter="${3:-}"
-
- mkdir -p "/run/systemd/system/$unit.d/"
- echo -ne "[Service]\nLogFilterPatterns=$log_filter" >"/run/systemd/system/$unit.d/$override_name.conf"
- systemctl daemon-reload
-}
-
-run_service_and_fetch_logs() {
- local unit="${1:?}"
- local start
-
- start="$(date '+%Y-%m-%d %T.%6N')"
- systemctl start "$unit"
- journalctl --sync
- journalctl -q -u "$unit" -S "$start" -p notice
-}
-
-if cgroupfs_supports_user_xattrs; then
- # Accept all log messages
- add_logs_filtering_override "logs-filtering.service" "00-reset" ""
- [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- add_logs_filtering_override "logs-filtering.service" "01-allow-all" ".*"
- [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Discard all log messages
- add_logs_filtering_override "logs-filtering.service" "02-discard-all" "~.*"
- [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Accept all test messages
- add_logs_filtering_override "logs-filtering.service" "03-reset" ""
- [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Discard all test messages
- add_logs_filtering_override "logs-filtering.service" "04-discard-gg" "~.*gg.*"
- [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Deny filter takes precedence
- add_logs_filtering_override "logs-filtering.service" "05-allow-all-but-too-late" ".*"
- [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Use tilde in a deny pattern
- add_logs_filtering_override "logs-filtering.service" "06-reset" ""
- add_logs_filtering_override "logs-filtering.service" "07-prevent-tilde" "~~more~"
- [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Only allow a pattern that won't be matched
- add_logs_filtering_override "logs-filtering.service" "08-reset" ""
- add_logs_filtering_override "logs-filtering.service" "09-allow-only-non-existing" "non-existing string"
- [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- # Allow a pattern starting with a tilde
- add_logs_filtering_override "logs-filtering.service" "10-allow-with-escape-char" "\\\\x7emore~"
- [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- add_logs_filtering_override "logs-filtering.service" "11-reset" ""
- add_logs_filtering_override "logs-filtering.service" "12-allow-with-spaces" "foo bar"
- [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
- add_logs_filtering_override "delegated-cgroup-filtering.service" "00-allow-all" ".*"
- [[ -n $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
-
- add_logs_filtering_override "delegated-cgroup-filtering.service" "01-discard-hello" "~hello"
- [[ -z $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
-
- rm -rf /run/systemd/system/{logs-filtering,delegated-cgroup-filtering}.service.d
-fi
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# https://bugzilla.redhat.com/show_bug.cgi?id=2183546
-mkdir /run/systemd/system/systemd-journald.service.d
-MACHINE_ID="$(</etc/machine-id)"
-
-# Reset the start-limit counters, as we're going to restart journald a couple of times
-systemctl reset-failed systemd-journald.service
-
-for c in NONE XZ LZ4 ZSTD; do
- cat >/run/systemd/system/systemd-journald.service.d/compress.conf <<EOF
-[Service]
-Environment=SYSTEMD_JOURNAL_COMPRESS=${c}
-EOF
- systemctl daemon-reload
- systemctl restart systemd-journald.service
- journalctl --rotate
-
- ID="$(systemd-id128 new)"
- systemd-cat -t "$ID" /bin/bash -c "for ((i=0;i<100;i++)); do echo -n hoge with ${c}; done; echo"
- journalctl --sync
- timeout 10 bash -c "until SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /var/log/journal/$MACHINE_ID/system.journal 2>&1 | grep -q -F 'compress=${c}'; do sleep .5; done"
-
- # $SYSTEMD_JOURNAL_COMPRESS= also works for journal-remote
- if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
- for cc in NONE XZ LZ4 ZSTD; do
- rm -f /tmp/foo.journal
- SYSTEMD_JOURNAL_COMPRESS="${cc}" /usr/lib/systemd/systemd-journal-remote --split-mode=none -o /tmp/foo.journal --getter="journalctl -b -o export -t $ID"
- SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /tmp/foo.journal 2>&1 | grep -q -F "compress=${cc}"
- journalctl -t "$ID" -o cat --file /tmp/foo.journal | grep -q -F "hoge with ${c}"
- done
- fi
-done
-
-rm /run/systemd/system/systemd-journald.service.d/compress.conf
-systemctl daemon-reload
-systemctl restart systemd-journald.service
-systemctl reset-failed systemd-journald.service
-journalctl --rotate
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if systemd-detect-virt -cq; then
- echo "This test requires a VM, skipping the test" | tee --append /skipped
- exit 0
-fi
-
-if [[ ! -x /usr/lib/systemd/systemd-bsod ]]; then
- echo "systemd-bsod is not installed, skipping the test" | tee --append /skipped
- exit 0
-fi
-
-# shellcheck disable=SC2317
-at_exit() {
- local EC=$?
-
- if [[ $EC -ne 0 ]] && [[ -e /tmp/console.dump ]]; then
- cat /tmp/console.dump
- fi
-
- if mountpoint -q /var/log/journal; then
- # In order to preserve the journal from the just run test we need to do a little dance, as
- # --relinquish-var is not a "true" opposite of --flush, meaning that it won't move the existing
- # journal(s) from /var/log/ to /run/log/. To do that, let's rotate the journal first, so all
- # important bits are in the archived journal(s)...
- journalctl --rotate
- # ...then instruct sd-journald to write further entries to the runtime journal...
- journalctl --relinquish-var
- # ...make sure there are no outstanding writes to the persistent journal that might block us from
- # unmounting the tmpfs...
- journalctl --sync
- # ...move the archived journals to the runtime storage...
- mv -v "/var/log/journal/$(</etc/machine-id)"/system@*.journal "/run/log/journal/$(</etc/machine-id)/"
- # ...get rid of the tmpfs on /var/log/journal/...
- umount /var/log/journal
- # ...and finally flush everything to the "real" persistent journal, so we can collect it after the
- # test finishes.
- journalctl --flush
- fi
-
- return 0
-}
-
-vcs_dump_and_check() {
- local expected_message="${1:?}"
-
- # It might take a while before the systemd-bsod stuff appears on the VCS,
- # so try it a couple of times
- for _ in {0..9}; do
- setterm --term linux --dump --file /tmp/console.dump
- if grep -aq "Press any key to exit" /tmp/console.dump &&
- grep -aq "$expected_message" /tmp/console.dump &&
- grep -aq "The current boot has failed" /tmp/console.dump; then
-
- return 0
- fi
-
- sleep .5
- done
-
- return 1
-}
-
-# Since systemd-bsod always fetches only the first emergency message from the
-# current boot, let's temporarily overmount /var/log/journal with a tmpfs,
-# as we're going to wipe it multiple times, but we need to keep the original
-# journal intact for the other tests to work correctly.
-trap at_exit EXIT
-mount -t tmpfs tmpfs /var/log/journal
-systemctl restart systemd-journald
-
-systemctl stop systemd-bsod
-
-# Since we just wiped the journal, there should be no emergency messages and
-# systemd-bsod should be just a no-op
-timeout 10s /usr/lib/systemd/systemd-bsod
-setterm --term linux --dump --file /tmp/console.dump
-(! grep "The current boot has failed" /tmp/console.dump)
-
-# systemd-bsod should pick up emergency messages only with UID=0, so let's check
-# that as well
-systemd-run --user --machine testuser@ --wait --pipe systemd-cat -p emerg echo "User emergency message"
-systemd-cat -p emerg echo "Root emergency message"
-journalctl --sync
-# Set $SYSTEMD_COLORS so systemd-bsod also prints out the QR code
-SYSTEMD_COLORS=256 /usr/lib/systemd/systemd-bsod &
-PID=$!
-vcs_dump_and_check "Root emergency message"
-grep -aq "Scan the QR code" /tmp/console.dump
-# TODO: check if systemd-bsod exits on a key press (didn't figure this one out yet)
-kill $PID
-timeout 10 bash -c "while kill -0 $PID; do sleep .5; done"
-
-# Wipe the journal
-journalctl --vacuum-size=1 --rotate
-(! journalctl -q -b -p emerg --grep .)
-
-# Check the systemd-bsod.service as well
-# Note: the systemd-bsod.service unit has ConditionVirtualization=no, so let's
-# temporarily override it just for the test
-mkdir /run/systemd/system/systemd-bsod.service.d
-printf '[Unit]\nConditionVirtualization=\n' >/run/systemd/system/systemd-bsod.service.d/99-override.conf
-systemctl daemon-reload
-systemctl start systemd-bsod
-systemd-cat -p emerg echo "Service emergency message"
-vcs_dump_and_check "Service emergency message"
-systemctl status systemd-bsod
-systemctl stop systemd-bsod
-
-# Wipe the journal
-journalctl --vacuum-size=1 --rotate
-(! journalctl -q -b -p emerg --grep .)
-
-# Same as above, but make sure the service responds to signals even when there are
-# no "emerg" messages, see systemd/systemd#30084
-(! systemctl is-active systemd-bsod)
-systemctl start systemd-bsod
-timeout 5s bash -xec 'until systemctl is-active systemd-bsod; do sleep .5; done'
-timeout 5s systemctl stop systemd-bsod
-timeout 5s bash -xec 'while systemctl is-active systemd-bsod; do sleep .5; done'
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemctl enable --now systemd-journald@cat-test.socket
-
-systemd-cat --namespace cat-test env CAT_TEST_RESULT=1
-
-timeout 30 bash -c "until systemctl -q is-active systemd-journald@cat-test.service; do sleep .5; done"
-
-journalctl --namespace cat-test --grep "JOURNAL_STREAM="
-journalctl --namespace cat-test --grep "CAT_TEST_RESULT=1"
-
-systemctl disable --now systemd-journald@cat-test.socket
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-JOURNAL_DIR="$(mktemp -d)"
-REMOTE_OUT="$(mktemp -d)"
-# tar on C8S doesn't support the --zstd option
-unzstd --stdout "/usr/lib/systemd/tests/testdata/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
-while read -r file; do
- filename="${file##*/}"
- unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /usr/lib/systemd/tests/testdata/test-journals/corrupted/ -name "*.zst")
-# First, try each of them sequentially. Skip this part when running with plain
-# QEMU, as it is excruciatingly slow
-# Note: we care only about exit code 124 (timeout) and special bash exit codes
-# >124 (like signals)
-if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
- while read -r file; do
- timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]]
- timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]]
- timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]]
- timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]]
- timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]]
- if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
- timeout 10 /usr/lib/systemd/systemd-journal-remote \
- --getter="journalctl --file=$file --output=export" \
- --split-mode=none \
- --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
- timeout 10 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
- rm -f "$REMOTE_OUT"/*
- fi
- done < <(find "$JOURNAL_DIR" -type f)
-fi
-# And now all at once
-timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]]
-if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
- timeout 30 /usr/lib/systemd/systemd-journal-remote \
- --getter="journalctl --directory=$JOURNAL_DIR --output=export" \
- --split-mode=none \
- --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
- timeout 30 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
- rm -f "$REMOTE_OUT"/*
-fi
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Forward Secure Sealing
-
-if ! journalctl --version | grep -qF +GCRYPT; then
- echo "Built without gcrypt, skipping the FSS tests"
- exit 0
-fi
-
-journalctl --force --setup-keys --interval=2 |& tee /tmp/fss
-FSS_VKEY="$(sed -rn '/([a-f0-9]{6}\-){3}[a-f0-9]{6}\/[a-f0-9]+\-[a-f0-9]+/p' /tmp/fss)"
-[[ -n "$FSS_VKEY" ]]
-
-# Generate some buzz in the journal and wait until the FSS key is changed
-# at least once
-systemd-cat cat /etc/os-release
-sleep 4
-# Seal the journal
-journalctl --rotate
-# Verification should fail without a valid FSS key
-(! journalctl --verify)
-(! journalctl --verify --verify-key="")
-(! journalctl --verify --verify-key="000000-000000-000000-000000/00000000-00000")
-# FIXME: ignore --verify result until #27532 is resolved
-journalctl --verify --verify-key="$FSS_VKEY" || :
-
-# Sealing + systemd-journal-remote
-/usr/lib/systemd/systemd-journal-remote --getter="journalctl -n 5 -o export" \
- --split-mode=none \
- --seal=yes \
- --output=/tmp/sealed.journal
-(! journalctl --file=/tmp/sealed.journal --verify)
-(! journalctl --file=/tmp/sealed.journal --verify --verify-key="")
-(! journalctl --file=/tmp/sealed.journal --verify --verify-key="000000-000000-000000-000000/00000000-00000")
-# FIXME: ignore --verify result until #27532 is resolved
-journalctl --file=/tmp/sealed.journal --verify --verify-key="$FSS_VKEY" || :
-rm -f /tmp/sealed.journal
-
-# Return back to a journal without FSS
-rm -fv "/var/log/journal/$(</etc/machine-id)/fss"
-journalctl --rotate --vacuum-size=1
-# FIXME: ignore --verify result until #27532 is resolved
-journalctl --verify || :
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# test-journal-append corrupts the journal file by flipping a bit at a given offset and
-# following it by a write to check if we handle appending messages to corrupted journals
-# gracefully
-
-TEST_JOURNAL_APPEND=/usr/lib/systemd/tests/unit-tests/manual/test-journal-append
-
-[[ -x "$TEST_JOURNAL_APPEND" ]]
-
-# Corrupt the first ~1024 bytes, this should be pretty quick
-"$TEST_JOURNAL_APPEND" --sequential --start-offset=0 --iterations=350 --iteration-step=3
-
-# Skip most of the test when running without acceleration, as it's excruciatingly slow
-# (this shouldn't be an issue, as it should run in nspawn as well)
-if ! [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
- # Corrupt the beginning of every 1K block between 1K - 32K
- for ((i = 1024; i <= (32 * 1024); i += 1024)); do
- "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
- done
-
- # Corrupt the beginning of every 16K block between 32K - 128K
- for ((i = (32 * 1024); i <= (256 * 1024); i += (16 * 1024))); do
- "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
- done
-
- # Corrupt the beginning of every 128K block between 128K - 1M
- for ((i = (128 * 1024); i <= (1 * 1024 * 1024); i += (128 * 1024))); do
- "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
- done
-
- # And finally the beginning of every 1M block between 1M and 8M
- for ((i = (1 * 1024 * 1024); i < (8 * 1024 * 1024); i += (1 * 1024 * 1024))); do
- "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
- done
-
- if [[ "$(nproc)" -ge 2 ]]; then
- # Try to corrupt random bytes throughout the journal
- "$TEST_JOURNAL_APPEND" --iterations=25
- fi
-else
- "$TEST_JOURNAL_APPEND" --iterations=10
-fi
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-journalctl --rotate --vacuum-files=1
-# Nuke all archived journals, so we start with a clean slate
-rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
-rm -f "/var/log/journal/$(</etc/machine-id)"/user-*@*.journal
-journalctl --header | grep path
-
-# Make sure the user instance is active when we rotate journals
-loginctl enable-linger testuser
-systemd-run --unit user-sleep.service --user -M testuser@ sleep infinity
-
-for _ in {0..9}; do
- journalctl --rotate
- journalctl --sync
- SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
- (! journalctl -n0 -q |& grep corrupted)
-done
-
-systemctl stop --user -M testuser@ user-sleep.service
-loginctl disable-linger testuser
-
-journalctl --sync
-journalctl --rotate --vacuum-files=1
-# Nuke all archived journals, so we start with a clean slate
-rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
-rm -f "/var/log/journal/$(</etc/machine-id)/"user-*@*.journal
-journalctl --header | grep path
-
-for _ in {0..9}; do
- journalctl --rotate --vacuum-files=1
- journalctl --sync
- SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
- (! journalctl -n0 -q |& grep corrupted)
-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
- echo "Built without systemd-journal-gatewayd support, skipping the test"
- exit 0
-fi
-
-LOG_FILE="$(mktemp)"
-
-at_exit() {
- if [[ $? -ne 0 ]]; then
- # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
- # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
- # journal; things get very confusing otherwise.
- systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
- fi
-
- rm -f "$LOG_FILE"
-}
-
-trap at_exit EXIT
-
-TEST_MESSAGE="-= This is a test message $RANDOM =-"
-TEST_TAG="$(systemd-id128 new)"
-
-BEFORE_TIMESTAMP="$(date +%s)"
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-sleep 1
-journalctl --sync
-TEST_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
-BOOT_CURSOR="$(journalctl -q -b -n 0 --show-cursor | awk '{ print $3; }')"
-AFTER_TIMESTAMP="$(date +%s)"
-
-/usr/lib/systemd/systemd-journal-gatewayd --version
-/usr/lib/systemd/systemd-journal-gatewayd --help
-
-# Default configuration (HTTP, socket activated)
-systemctl start systemd-journal-gatewayd.socket
-
-# /browse
-# We should get redirected to /browse by default
-curl -LSfs http://localhost:19531 >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-(! curl -LSfs http://localhost:19531/foo/bar/baz)
-(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
-
-# /entries
-# Accept: text/plain should be the default
-curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
-jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
-jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
-curl -LSfs \
- --header "Accept: application/json" \
- --header "Range: entries=$BOOT_CURSOR:5:10" \
- http://localhost:19531/entries >"$LOG_FILE"
-jq -se "length == 10" "$LOG_FILE"
-# Check if the specified cursor refers to an existing entry and return just that entry
-curl -LSfs \
- --header "Accept: application/json" \
- --header "Range: entries=$TEST_CURSOR" \
- http://localhost:19531/entries?discrete >"$LOG_FILE"
-jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-# Check entry is present (resp. absent) when filtering by timestamp
-curl -LSfs \
- --header "Range: realtime=$BEFORE_TIMESTAMP:" \
- http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs \
- --header "Range: realtime=:$AFTER_TIMESTAMP" \
- http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs \
- --header "Accept: application/json" \
- --header "Range: realtime=:$BEFORE_TIMESTAMP" \
- http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 0" "$LOG_FILE"
-curl -LSfs \
- --header "Accept: application/json" \
- --header "Range: realtime=$AFTER_TIMESTAMP:" \
- http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 0" "$LOG_FILE"
-# Check positive and negative skip when filtering by timestamp
-echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-TEST2_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
-echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-sleep 1
-END_TIMESTAMP="$(date +%s)"
-curl -LSfs \
- --header "Accept: application/json" \
- --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
- http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
-curl -LSfs \
- --header "Accept: application/json" \
- --header "Range: realtime=$END_TIMESTAMP::-1:1" \
- http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
-
-# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
-# line is either empty or begins with data:
-curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
-awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
-# Same thing as journalctl --output=export
-mkdir /tmp/remote-journal
-curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
-/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
-journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
-rm -rf /tmp/remote-journal/*
-# Let's do the same thing again, but let systemd-journal-remote spawn curl itself
-/usr/lib/systemd/systemd-journal-remote --url=http://localhost:19531/entries \
- --output=/tmp/remote-journal/system.journal \
- --split-mode=none
-journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
-rm -rf /tmp/remote-journal
-
-# /machine
-curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
-jq . "$LOG_FILE"
-
-# /fields
-curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
-grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs http://localhost:19531/fields/_TRANSPORT
-(! curl -LSfs http://localhost:19531/fields)
-(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
-
-systemctl stop systemd-journal-gatewayd.{socket,service}
-
-if ! command -v openssl >/dev/null; then
- echo "openssl command not available, skipping the HTTPS tests"
- exit 0
-fi
-
-# Generate a self-signed certificate for systemd-journal-gatewayd
-#
-# Note: older OpenSSL requires a config file with some extra options, unfortunately
-cat >/tmp/openssl.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = localhost
-EOF
-openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
- -config /tmp/openssl.conf \
- -keyout /tmp/key.pem -out /tmp/cert.pem
-# Start HTTPS version of gatewayd via the systemd-socket-activate tool to give it some coverage as well
-systemd-socket-activate --listen=19531 -- \
- /usr/lib/systemd/systemd-journal-gatewayd \
- --cert=/tmp/cert.pem \
- --key=/tmp/key.pem \
- --file="/var/log/journal/*/*.journal" &
-GATEWAYD_PID=$!
-sleep 1
-
-# Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
-curl -LSfsk https://localhost:19531 >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
-jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
-jq . "$LOG_FILE"
-curl -LSfsk https://localhost:19531/fields/_TRANSPORT
-
-kill "$GATEWAYD_PID"
-
-# Test a couple of error scenarios
-GATEWAYD_FILE="$(mktemp /tmp/test-gatewayd-XXX.journal)"
-
-/usr/lib/systemd/systemd-journal-remote --output="$GATEWAYD_FILE" --getter="journalctl -n5 -o export"
-systemd-run --unit="test-gatewayd.service" --socket-property="ListenStream=19531" \
- /usr/lib/systemd/systemd-journal-gatewayd --file="$GATEWAYD_FILE"
-
-# Call an unsupported endpoint together with some garbage data - gatewayd should not send garbage in return
-# See: https://github.com/systemd/systemd/issues/9858
-OUT="$(mktemp)"
-for _ in {0..4}; do
- (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
- (! grep '[^[:print:]]' "$OUT")
-done
-(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
-(! grep '[^[:print:]]' "$OUT")
-rm -rf "$OUT"
-
-curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-# Nuke the file behind the /browse endpoint
-mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
-(! curl --fail-with-body -L http://localhost:19531/browse)
-mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
-curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-
-# Nuke the journal file
-mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
-(! curl --fail-with-body -L http://localhost:19531/fields/_PID)
-mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
-curl -LSfs http://localhost:19531/fields/_PID
-
-systemctl stop test-gatewayd.{socket,service}
-rm -f "$GATEWAYD_FILE"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-if [[ ! -x /usr/lib/systemd/systemd-journal-remote || ! -x /usr/lib/systemd/systemd-journal-upload ]]; then
- echo "Built without systemd-journal-remote/upload support, skipping the test"
- exit 0
-fi
-
-if ! command -v openssl >/dev/null; then
- echo "openssl command not available, skipping the tests"
- exit 0
-fi
-
-at_exit() {
- set +e
-
- systemctl stop systemd-journal-upload
- systemctl stop systemd-journal-remote.{socket,service}
- # Remove any remote journals on exit, so we don't try to export them together
- # with the local journals, causing a mess
- rm -rf /var/log/journal/remote
-}
-
-trap at_exit EXIT
-
-TEST_MESSAGE="-= This is a test message $RANDOM =-"
-TEST_TAG="$(systemd-id128 new)"
-
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-
-/usr/lib/systemd/systemd-journal-remote --version
-/usr/lib/systemd/systemd-journal-remote --help
-/usr/lib/systemd/systemd-journal-upload --version
-/usr/lib/systemd/systemd-journal-upload --help
-
-# Generate a self-signed certificate for systemd-journal-remote
-#
-# Note: older OpenSSL requires a config file with some extra options, unfortunately
-# Note2: /run here is used on purpose, since the systemd-journal-remote service uses PrivateTmp=yes
-mkdir -p /run/systemd/journal-remote-tls
-cat >/tmp/openssl.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = localhost
-EOF
-openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
- -config /tmp/openssl.conf \
- -keyout /run/systemd/journal-remote-tls/key.pem \
- -out /run/systemd/journal-remote-tls/cert.pem
-chown -R systemd-journal-remote /run/systemd/journal-remote-tls
-
-# Configure journal-upload to upload journals to journal-remote without client certificates
-mkdir -p /run/systemd/journal-{remote,upload}.conf.d
-cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
-[Remote]
-SplitMode=host
-ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
-ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
-TrustedCertificateFile=-
-EOF
-cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
-[Upload]
-URL=https://localhost:19532
-ServerKeyFile=-
-ServerCertificateFile=-
-TrustedCertificateFile=-
-EOF
-systemd-analyze cat-config systemd/journal-remote.conf
-systemd-analyze cat-config systemd/journal-upload.conf
-
-systemctl restart systemd-journal-remote.socket
-systemctl restart systemd-journal-upload
-timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
-systemctl status systemd-journal-{remote,upload}
-
-# It may take a bit until the whole journal is transferred
-timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
-
-systemctl stop systemd-journal-upload
-systemctl stop systemd-journal-remote.{socket,service}
-rm -rf /var/log/journal/remote/*
-
-# Now let's do the same, but with a full PKI setup
-#
-# journal-upload keeps the cursor of the last uploaded message, so let's send a fresh one
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-
-mkdir /run/systemd/remote-pki
-cat >/run/systemd/remote-pki/ca.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = Test CA
-
-[ v3_ca ]
-subjectKeyIdentifier = hash
-authorityKeyIdentifier = keyid:always,issuer:always
-basicConstraints = CA:true
-EOF
-cat >/run/systemd/remote-pki/client.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = Test Client
-EOF
-cat >/run/systemd/remote-pki/server.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = localhost
-EOF
-# Generate a dummy CA
-openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
- -extensions v3_ca \
- -config /run/systemd/remote-pki/ca.conf \
- -keyout /run/systemd/remote-pki/ca.key \
- -out /run/systemd/remote-pki/ca.crt
-openssl x509 -in /run/systemd/remote-pki/ca.crt -noout -text
-echo 01 >/run/systemd/remote-pki/ca.srl
-# Generate a client key and signing request
-openssl req -nodes -newkey rsa:2048 -sha256 \
- -config /run/systemd/remote-pki/client.conf \
- -keyout /run/systemd/remote-pki/client.key \
- -out /run/systemd/remote-pki/client.csr
-# Sign the request with the CA key
-openssl x509 -req -days 7 \
- -in /run/systemd/remote-pki/client.csr \
- -CA /run/systemd/remote-pki/ca.crt \
- -CAkey /run/systemd/remote-pki/ca.key \
- -out /run/systemd/remote-pki/client.crt
-# And do the same for the server
-openssl req -nodes -newkey rsa:2048 -sha256 \
- -config /run/systemd/remote-pki/server.conf \
- -keyout /run/systemd/remote-pki/server.key \
- -out /run/systemd/remote-pki/server.csr
-openssl x509 -req -days 7 \
- -in /run/systemd/remote-pki/server.csr \
- -CA /run/systemd/remote-pki/ca.crt \
- -CAkey /run/systemd/remote-pki/ca.key \
- -out /run/systemd/remote-pki/server.crt
-chown -R systemd-journal-remote:systemd-journal /run/systemd/remote-pki
-chmod -R g+rwX /run/systemd/remote-pki
-
-# Reconfigure journal-upload/journal remote with the new keys
-cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
-[Remote]
-SplitMode=host
-ServerKeyFile=/run/systemd/remote-pki/server.key
-ServerCertificateFile=/run/systemd/remote-pki/server.crt
-TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
-EOF
-cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
-[Upload]
-URL=https://localhost:19532
-ServerKeyFile=/run/systemd/remote-pki/client.key
-ServerCertificateFile=/run/systemd/remote-pki/client.crt
-TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
-EOF
-systemd-analyze cat-config systemd/journal-remote.conf
-systemd-analyze cat-config systemd/journal-upload.conf
-
-systemctl restart systemd-journal-remote.socket
-systemctl restart systemd-journal-upload
-timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
-systemctl status systemd-journal-{remote,upload}
-
-# It may take a bit until the whole journal is transferred
-timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
-
-systemctl stop systemd-journal-upload
-systemctl stop systemd-journal-remote.{socket,service}
-
-# Let's test if journal-remote refuses connection from journal-upload with invalid client certs
-#
-# We should end up with something like this:
-# systemd-journal-remote[726]: Client is not authorized
-# systemd-journal-upload[738]: Upload to https://localhost:19532/upload failed with code 401:
-# systemd[1]: systemd-journal-upload.service: Main process exited, code=exited, status=1/FAILURE
-# systemd[1]: systemd-journal-upload.service: Failed with result 'exit-code'.
-#
-cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
-[Upload]
-URL=https://localhost:19532
-ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
-ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
-TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
-EOF
-systemd-analyze cat-config systemd/journal-upload.conf
-mkdir -p /run/systemd/system/systemd-journal-upload.service.d
-cat >/run/systemd/system/systemd-journal-upload.service.d/99-test.conf <<EOF
-[Service]
-Restart=no
-EOF
-systemctl daemon-reload
-chgrp -R systemd-journal /run/systemd/journal-remote-tls
-chmod -R g+rwX /run/systemd/journal-remote-tls
-
-systemctl restart systemd-journal-upload
-timeout 10 bash -xec 'while [[ "$(systemctl show -P ActiveState systemd-journal-upload)" != failed ]]; do sleep 1; done'
-(! systemctl status systemd-journal-upload)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Rotation/flush test, see https://github.com/systemd/systemd/issues/19895
-journalctl --relinquish-var
-[[ "$(systemd-detect-virt -v)" == "qemu" ]] && ITERATIONS=10 || ITERATIONS=50
-for ((i = 0; i < ITERATIONS; i++)); do
- dd if=/dev/urandom bs=1M count=1 | base64 | systemd-cat
-done
-journalctl --rotate
-# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --rotate' via varlinkctl
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Rotate '{}'
-journalctl --flush
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.FlushToVar '{}'
-journalctl --sync
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
-journalctl --rotate --vacuum-size=8M
-
-# Reset the ratelimit buckets for the subsequent tests below.
-systemctl restart systemd-journald
-
-# Test stdout stream
-write_and_match() {
- local input="${1:?}"
- local expected="${2?}"
- local id
- shift 2
-
- id="$(systemd-id128 new)"
- echo -ne "$input" | systemd-cat -t "$id" "$@"
- journalctl --sync
- diff <(echo -ne "$expected") <(journalctl -b -o cat -t "$id")
-}
-# Skip empty lines
-write_and_match "\n\n\n" "" --level-prefix false
-write_and_match "<5>\n<6>\n<7>\n" "" --level-prefix true
-# Remove trailing spaces
-write_and_match "Trailing spaces \t \n" "Trailing spaces\n" --level-prefix false
-write_and_match "<5>Trailing spaces \t \n" "Trailing spaces\n" --level-prefix true
-# Don't remove leading spaces
-write_and_match " \t Leading spaces\n" " \t Leading spaces\n" --level-prefix false
-write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix true
-
-# --output-fields restricts output
-ID="$(systemd-id128 new)"
-echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false
-# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
-journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output
-[[ $(wc -l </tmp/output) -eq 9 ]]
-grep -q '^__CURSOR=' /tmp/output
-grep -q '^MESSAGE=foo$' /tmp/output
-grep -q '^PRIORITY=6$' /tmp/output
-(! grep '^FOO=' /tmp/output)
-(! grep '^SYSLOG_FACILITY=' /tmp/output)
-
-# --truncate shows only first line, skip under asan due to logger
-ID="$(systemd-id128 new)"
-echo -e 'HEAD\nTAIL\nTAIL' | systemd-cat -t "$ID"
-journalctl --sync
-journalctl -b -t "$ID" | grep -q HEAD
-journalctl -b -t "$ID" | grep -q TAIL
-journalctl -b -t "$ID" --truncate-newline | grep -q HEAD
-journalctl -b -t "$ID" --truncate-newline | grep -q -v TAIL
-
-# '-b all' negates earlier use of -b (-b and -m are otherwise exclusive)
-journalctl -b -1 -b all -m >/dev/null
-
-# -b always behaves like -b0
-journalctl -q -b-1 -b0 | head -1 >/tmp/expected
-journalctl -q -b-1 -b | head -1 >/tmp/output
-diff /tmp/expected /tmp/output
-# ... even when another option follows (both of these should fail due to -m)
-{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/tmp/expected
-{ journalctl -ball -b -m 2>&1 || :; } | head -1 >/tmp/output
-diff /tmp/expected /tmp/output
-
-# https://github.com/systemd/systemd/issues/13708
-ID=$(systemd-id128 new)
-systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' &
-PID=$!
-wait $PID
-journalctl --sync
-# We can drop this grep when https://github.com/systemd/systemd/issues/13937
-# has a fix.
-journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/tmp/output
-[[ $(wc -l </tmp/output) -eq 2 ]]
-grep -q "^_PID=$PID" /tmp/output
-grep -vq "^_PID=$PID" /tmp/output
-
-# https://github.com/systemd/systemd/issues/15654
-ID=$(systemd-id128 new)
-printf "This will\nusually fail\nand be truncated\n" >/tmp/expected
-systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;'
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/tmp/output
-diff /tmp/expected /tmp/output
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]]
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]]
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]]
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]]
-
-# test that LogLevelMax can also suppress logging about services, not only by services
-systemctl start silent-success
-journalctl --sync
-[[ -z "$(journalctl -b -q -u silent-success.service)" ]]
-
-# Test syslog identifiers exclusion
-systemctl start verbose-success.service
-journalctl --sync
-[[ -n "$(journalctl -b -q -u verbose-success.service -t systemd)" ]]
-[[ -n "$(journalctl -b -q -u verbose-success.service -t echo)" ]]
-[[ -n "$(journalctl -b -q -u verbose-success.service -T systemd)" ]]
-[[ -n "$(journalctl -b -q -u verbose-success.service -T echo)" ]]
-[[ -z "$(journalctl -b -q -u verbose-success.service -T echo -T '(echo)' -T sleep -T '(sleep)' -T systemd -T '(systemd)' -T systemd-executor)" ]]
-
-# Exercise the matching machinery
-SYSTEMD_LOG_LEVEL=debug journalctl -b -n 1 /dev/null /dev/zero /dev/null /dev/null /dev/null
-journalctl -b -n 1 /bin/true /bin/false
-journalctl -b -n 1 /bin/true + /bin/false
-journalctl -b -n 1 -r --unit "systemd*"
-
-systemd-run --user -M "testuser@.host" /bin/echo hello
-journalctl --sync
-journalctl -b -n 1 -r --user-unit "*"
-
-(! journalctl -b /dev/lets-hope-this-doesnt-exist)
-(! journalctl -b /dev/null /dev/zero /dev/this-also-shouldnt-exist)
-(! journalctl -b --unit "this-unit-should-not-exist*")
-
-# Facilities & priorities
-journalctl --facility help
-journalctl --facility help | grep -F 'kern'
-journalctl --facility help | grep -F 'mail'
-journalctl --facility kern -n 1
-journalctl --facility syslog --priority 0..3 -n 1
-journalctl --facility syslog --priority 3..0 -n 1
-journalctl --facility user --priority 0..0 -n 1
-journalctl --facility daemon --priority warning -n 1
-journalctl --facility daemon --priority warning..info -n 1
-journalctl --facility daemon --priority notice..crit -n 1
-journalctl --facility daemon --priority 5..crit -n 1
-
-# Assorted combinations
-journalctl -o help
-journalctl -o help | grep -F 'short'
-journalctl -o help | grep -F 'export'
-journalctl -q -n all -a | grep . >/dev/null
-journalctl -q --no-full | grep . >/dev/null
-journalctl -q --user --system | grep . >/dev/null
-journalctl --namespace "*" | grep . >/dev/null
-journalctl --namespace "" | grep . >/dev/null
-journalctl -q --namespace "+foo-bar-baz-$RANDOM" | grep . >/dev/null
-(! journalctl -q --namespace "foo-bar-baz-$RANDOM" | grep .)
-journalctl --root / | grep . >/dev/null
-journalctl --cursor "t=0;t=-1;t=0;t=0x0" | grep . >/dev/null
-journalctl --header | grep system.journal
-journalctl --field _EXE | grep . >/dev/null
-journalctl --no-hostname --utc --catalog | grep . >/dev/null
-# Exercise executable_is_script() and the related code, e.g. `journalctl -b /path/to/a/script.sh` should turn
-# into ((_EXE=/bin/bash AND _COMM=script.sh) AND _BOOT_ID=c002e3683ba14fa8b6c1e12878386514)
-journalctl -b "$(readlink -f "$0")" | grep . >/dev/null
-journalctl -b "$(systemd-id128 boot-id)" | grep . >/dev/null
-journalctl --since yesterday --reverse | grep . >/dev/null
-journalctl --machine .host | grep . >/dev/null
-# Log something that journald will forward to wall
-echo "Oh no!" | systemd-cat -t "emerg$RANDOM" -p emerg --stderr-priority emerg
-
-TAG="$(systemd-id128 new)"
-echo "Foo Bar Baz" | systemd-cat -t "$TAG"
-journalctl --sync
-# Relevant excerpt from journalctl(1):
-# If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case sensitive.
-# This can be overridden with the --case-sensitive option
-journalctl -e -t "$TAG" --grep "Foo Bar Baz"
-journalctl -e -t "$TAG" --grep "foo bar baz"
-(! journalctl -e -t "$TAG" --grep "foo Bar baz")
-journalctl -e -t "$TAG" --case-sensitive=false --grep "foo Bar baz"
-
-(! journalctl --facility hopefully-an-unknown-facility)
-(! journalctl --priority hello-world)
-(! journalctl --priority 0..128)
-(! journalctl --priority 0..systemd)
-
-# Other options
-journalctl --disk-usage
-journalctl --dmesg -n 1
-journalctl --fields
-journalctl --list-boots
-journalctl --update-catalog
-journalctl --list-catalog
-
-# Add new tests before here, the journald restarts below
-# may make tests flappy.
-
-# Don't lose streams on restart
-systemctl start forever-print-hola
-sleep 3
-systemctl restart systemd-journald
-sleep 3
-systemctl stop forever-print-hola
-[[ ! -f "/tmp/i-lose-my-logs" ]]
-
-# https://github.com/systemd/systemd/issues/4408
-rm -f /tmp/i-lose-my-logs
-systemctl start forever-print-hola
-sleep 3
-systemctl kill --signal=SIGKILL systemd-journald
-sleep 3
-[[ ! -f "/tmp/i-lose-my-logs" ]]
-systemctl stop forever-print-hola
-
-set +o pipefail
-# https://github.com/systemd/systemd/issues/15528
-journalctl --follow --file=/var/log/journal/*/* | head -n1 | grep .
-# https://github.com/systemd/systemd/issues/24565
-journalctl --follow --merge | head -n1 | grep .
-set -o pipefail
-
-# https://github.com/systemd/systemd/issues/26746
-rm -f /tmp/issue-26746-log /tmp/issue-26746-cursor
-ID="$(systemd-id128 new)"
-journalctl -t "$ID" --follow --cursor-file=/tmp/issue-26746-cursor | tee /tmp/issue-26746-log &
-systemd-cat -t "$ID" /bin/sh -c 'echo hogehoge'
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until [[ -f /tmp/issue-26746-log && "$(cat /tmp/issue-26746-log)" =~ hogehoge ]]; do sleep .5; done'
-pkill -TERM journalctl
-timeout 10 bash -c 'until test -f /tmp/issue-26746-cursor; do sleep .5; done'
-CURSOR_FROM_FILE="$(cat /tmp/issue-26746-cursor)"
-CURSOR_FROM_JOURNAL="$(journalctl -t "$ID" --output=export MESSAGE=hogehoge | sed -n -e '/__CURSOR=/ { s/__CURSOR=//; p }')"
-test "$CURSOR_FROM_FILE" = "$CURSOR_FROM_JOURNAL"
-
-# Check that the seqnum field at least superficially works
-systemd-cat echo "ya"
-journalctl --sync
-SEQNUM1=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
-systemd-cat echo "yo"
-journalctl --sync
-SEQNUM2=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
-test "$SEQNUM2" -gt "$SEQNUM1"
-
-# Test for journals without RTC
-# See: https://github.com/systemd/systemd/issues/662
-JOURNAL_DIR="$(mktemp -d)"
-while read -r file; do
- filename="${file##*/}"
- unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /usr/lib/systemd/tests/testdata/test-journals/no-rtc -name "*.zst")
-
-journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1
-diff -u /tmp/lb1 - <<'EOF'
-[{"index":-3,"boot_id":"5ea5fc4f82a14186b5332a788ef9435e","first_entry":1666569600994371,"last_entry":1666584266223608},{"index":-2,"boot_id":"bea6864f21ad4c9594c04a99d89948b0","first_entry":1666569601005945,"last_entry":1666584347230411},{"index":-1,"boot_id":"4c708e1fd0744336be16f3931aa861fb","first_entry":1666569601017222,"last_entry":1666584354649355},{"index":0,"boot_id":"35e8501129134edd9df5267c49f744a4","first_entry":1666569601009823,"last_entry":1666584438086856}]
-EOF
-rm -rf "$JOURNAL_DIR" /tmp/lb1
-
-# Check that using --after-cursor/--cursor-file= together with journal filters doesn't
-# skip over entries matched by the filter
-# See: https://github.com/systemd/systemd/issues/30288
-UNIT_NAME="test-cursor-$RANDOM.service"
-CURSOR_FILE="$(mktemp)"
-# Generate some messages we can match against
-journalctl --cursor-file="$CURSOR_FILE" -n1
-systemd-run --unit="$UNIT_NAME" --wait --service-type=exec bash -ec "sleep 2; set -x; echo hello; echo world; set +x; sleep 2"
-journalctl --sync
-# --after-cursor= + --unit=
-# The format of the "Starting ..." message depends on StatusUnitFormat=, so match only the beginning
-# which should be enough in this case
-[[ "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" _PID=1 )" =~ ^Starting\ ]]
-# There should be no such messages before the cursor
-[[ -z "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" --reverse)" ]]
-# --cursor-file= + a journal filter
-diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UNIT_NAME") - <<EOF
-+ echo hello
-hello
-+ echo world
-world
-+ set +x
-EOF
-rm -f "$CURSOR_FILE"
-
-# Check that --until works with --after-cursor and --lines/-n
-# See: https://github.com/systemd/systemd/issues/31776
-CURSOR_FILE="$(mktemp)"
-journalctl -q -n 0 --cursor-file="$CURSOR_FILE"
-TIMESTAMP="$(journalctl -q -n 1 --cursor="$(<"$CURSOR_FILE")" --output=short-unix | cut -d ' ' -f 1 | cut -d '.' -f 1)"
-[[ -z "$(journalctl -q -n 10 --after-cursor="$(<"$CURSOR_FILE")" --until "@$((TIMESTAMP - 3))")" ]]
-rm -f "$CURSOR_FILE"
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-04-JOURNAL
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-pre=test05
-cat >/run/systemd/system/"$pre"alpha.slice <<EOF
-[Slice]
-MemoryMax=40M
-MemoryHigh=40M
-TasksMax=400
-EOF
-
-cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
-[Slice]
-MemoryMax=10M
-MemoryHigh=10M
-TasksMax=100
-EOF
-
-cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
-[Slice]
-MemoryMax=20M
-MemoryHigh=20M
-TasksMax=200
-EOF
-
-systemctl daemon-reload
-
-srv=probe.service
-slc0="$pre"alpha.slice
-slc="$pre"alpha-beta-gamma.slice
-
-systemd-run --unit "$srv" --slice "$slc" \
- -p MemoryMax=5M \
- -p MemoryHigh=5M \
- -p TasksMax=50 \
- sleep inf
-
-# Compare with inequality because test can run in a constrained container
-assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
-
-systemctl stop "$srv"
-
-systemd-run --unit "$srv" --slice "$slc" \
- sleep inf
-
-assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
-assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
-assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
-
-systemctl set-property "$slc0" \
- MemoryMax=5M \
- MemoryHigh=5M \
- TasksMax=50
-
-assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
-
-systemctl stop "$srv"
-
-rm -f /run/systemd/system/"$pre"* || :
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-P=/run/systemd/system.conf.d
-mkdir $P
-
-cat >$P/rlimits.conf <<EOF
-[Manager]
-DefaultLimitNOFILE=10000:16384
-EOF
-
-systemctl daemon-reload
-
-[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
-[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
-
-[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
-[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
-
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-05-LIMITS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-06-SELINUX
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-. /etc/os-release
-if ! [[ "$ID" =~ centos|fedora ]]; then
- echo "Skipping because only CentOS and Fedora support SELinux tests" >>/skipped
- exit 77
-fi
-
-# Note: ATTOW the following checks should work with both Fedora and upstream reference policy
-# (with or without MCS/MLS)
-
-sestatus
-
-# We should end up in permissive mode
-[[ "$(getenforce)" == "Permissive" ]]
-
-# Check PID 1's context
-PID1_CONTEXT="$(ps -h -o label 1)"
-[[ "$PID1_CONTEXT" =~ ^system_u:system_r:init_t(:s0)?$ ]]
-# The same label should be attached to all PID 1's journal messages
-journalctl -q -b -p info -n 5 --grep . _SELINUX_CONTEXT="$PID1_CONTEXT"
-
-# Check context on a couple of arbitrarily-selected files/directories
-[[ "$(stat --printf %C /run/systemd/journal/)" =~ ^system_u:object_r:(syslogd_runtime_t|syslogd_var_run_t)(:s0)?$ ]]
-[[ "$(stat --printf %C /run/systemd/notify)" =~ ^system_u:object_r:(init_runtime_t|init_var_run_t)(:s0)?$ ]]
-[[ "$(stat --printf %C /run/systemd/sessions/)" =~ ^system_u:object_r:(systemd_sessions_runtime_t|systemd_logind_sessions_t)(:s0)?$ ]]
-
-# Check if our SELinux-related functionality works
-#
-# Since the SELinux policies vary wildly, use a context from some existing file
-# as our test context
-CONTEXT="$(stat -c %C /proc/sys/kernel/core_pattern)"
-
-[[ "$(systemd-run --wait --pipe -p SELinuxContext="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
-(! systemd-run --wait --pipe -p SELinuxContext="foo:bar:baz" cat /proc/self/attr/current)
-(! systemd-run --wait --pipe -p ConditionSecurity='selinux' false)
-systemd-run --wait --pipe -p ConditionSecurity='!selinux' false
-
-NSPAWN_ARGS=(systemd-nspawn -q --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id)
-[[ "$("${NSPAWN_ARGS[@]}" cat /proc/self/attr/current | tr -d '\0')" != "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" --selinux-context="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" stat --printf %C /run)" != "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" stat --printf %C /run)" == "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" --tmpfs=/tmp stat --printf %C /tmp)" == "$CONTEXT" ]]
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-export SYSTEMD_PAGER=cat
-
-if ! grep -q pidfd_open /proc/kallsyms; then
- echo "pidfds not available, skipping the test..."
- exit 0
-fi
-
-systemd-run --unit test-aux-scope.service \
- --service-type notify -p Slice=aux.slice -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
- /usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
-kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
-
-timeout 30s bash -xec 'until systemctl is-active test-aux-scope.scope; do sleep 1; done'
-
-systemctl status test-aux-scope.service
-# shellcheck disable=SC2009
-test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
-
-systemctl status test-aux-scope.scope
-# shellcheck disable=SC2009
-test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
-
-test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
-test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
-test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
-test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
-
-systemctl stop test-aux-scope.scope
-systemctl stop test-aux-scope.service
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Make sure the unit's exec context matches its configuration
-# See: https://github.com/systemd/systemd/pull/29552
-
-# Even though hidepid= was introduced in kernel 3.3, we support only
-# the post 5.8 implementation that allows us to apply the option per-instance,
-# instead of the whole namespace. To distinguish between these two implementations
-# lets check if we can mount procfs with a named value (e.g. hidepid=off), since
-# support for this was introduced in the same commit as the per-instance stuff
-proc_supports_option() {
- local option="${1:?}"
- local proc_tmp ec
-
- proc_tmp="$(mktemp -d)"
- mount -t proc -o "$option" proc "$proc_tmp" && ec=0 || ec=$?
- mountpoint -q "$proc_tmp" && umount -q "$proc_tmp"
- rm -rf "$proc_tmp"
-
- return $ec
-}
-
-# In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d
-# dropin in /etc. This dropin has, unfortunately, higher priority than
-# the transient stuff from systemd-run. Let's just skip the following tests
-# in that case instead of complicating the test setup even more */
-if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
- if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
- boot_path="$(bootctl --print-boot-path)"
- esp_path="$(bootctl --print-esp-path)"
-
- # If the mount points are handled by automount units, make sure we trigger
- # them before proceeding further
- ls -l "$boot_path" "$esp_path"
- fi
-
- systemd-run --wait --pipe -p ProtectSystem=yes \
- bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
- systemd-run --wait --pipe -p ProtectSystem=full \
- bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
- systemd-run --wait --pipe -p ProtectSystem=strict \
- bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
- systemd-run --wait --pipe -p ProtectSystem=no \
- bash -xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc"
-
- MARK="$(mktemp /root/.exec-context.XXX)"
- systemd-run --wait --pipe -p ProtectHome=yes \
- bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK"
- systemd-run --wait --pipe -p ProtectHome=read-only \
- bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK"
- systemd-run --wait --pipe -p ProtectHome=tmpfs \
- bash -xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK"
- systemd-run --wait --pipe -p ProtectHome=no \
- bash -xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK"
- rm -f "$MARK"
-fi
-
-if proc_supports_option "hidepid=off"; then
- systemd-run --wait --pipe -p ProtectProc=noaccess -p User=testuser \
- bash -xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm'
- systemd-run --wait --pipe -p ProtectProc=invisible -p User=testuser \
- bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
- systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser \
- bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
- systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser -p AmbientCapabilities=CAP_SYS_PTRACE \
- bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
- systemd-run --wait --pipe -p ProtectProc=default -p User=testuser \
- bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
-fi
-
-if proc_supports_option "subset=pid"; then
- systemd-run --wait --pipe -p ProcSubset=pid -p User=testuser \
- bash -xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo"
- systemd-run --wait --pipe -p ProcSubset=all -p User=testuser \
- bash -xec "test -r /proc/1/comm; test -r /proc/cpuinfo"
-fi
-
-if ! systemd-detect-virt -cq; then
- systemd-run --wait --pipe -p ProtectKernelLogs=yes -p User=testuser \
- bash -xec "test ! -r /dev/kmsg"
- systemd-run --wait --pipe -p ProtectKernelLogs=no -p User=testuser \
- bash -xec "test -r /dev/kmsg"
-fi
-
-systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
- bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
-systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
- bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
-# Make sure we properly serialize/deserialize paths with spaces
-# See: https://github.com/systemd/systemd/issues/30747
-touch "/tmp/test file with spaces"
-systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \
- bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'"
-systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \
- bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'"
-
-# Check if we correctly serialize, deserialize, and set directives that
-# have more complex internal handling
-if ! systemd-detect-virt -cq; then
- # Funny detail: this originally used the underlying rootfs device, but that,
- # for some reason, caused "divide error" in kernel, followed by a kernel panic
- TEMPFILE="$(mktemp)"
- LODEV="$(losetup --show -f "$TEMPFILE")"
- ROOT_DEV_MAJ_MIN="$(lsblk -nro MAJ:MIN "$LODEV")"
- EXPECTED_IO_MAX="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000"
- EXPECTED_IO_LATENCY="$ROOT_DEV_MAJ_MIN target=69000"
- SERVICE_NAME="test-io-directives-$RANDOM.service"
- CGROUP_PATH="/sys/fs/cgroup/system.slice/$SERVICE_NAME"
-
- # IO*=
- ARGUMENTS=(
- # Throw in a couple of invalid entries just to test things out
- -p IOReadBandwidthMax="/foo/bar 1M"
- -p IOReadBandwidthMax="/foo/baz 1M"
- -p IOReadBandwidthMax="$LODEV 1M"
- -p IOReadBandwidthMax="$LODEV 1K"
- -p IOWriteBandwidthMax="$LODEV 1G"
- -p IOWriteBandwidthMax="$LODEV 1T"
- -p IOReadIOPSMax="$LODEV 2G"
- -p IOWriteIOPSMax="$LODEV 4K"
- -p IODeviceLatencyTargetSec="$LODEV 666ms"
- -p IODeviceLatencyTargetSec="/foo/bar 69ms"
- -p IODeviceLatencyTargetSec="$LODEV 69ms"
- -p IOReadBandwidthMax="/foo/bar 1M"
- -p IOReadBandwidthMax="/foo/baz 1M"
- # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks
- -p IODeviceWeight="$LODEV 999"
- -p IODeviceWeight="/foo/bar 999"
- )
-
- systemctl set-property system.slice IOAccounting=yes
- # io.latency not available by default on Debian stable
- if [[ -e /sys/fs/cgroup/system.slice/io.latency ]]; then
- systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
- bash -xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency"
- fi
-
- # CPUScheduling=
- ARGUMENTS=(
- -p CPUSchedulingPolicy=rr # ID: 2
- -p CPUSchedulingPolicy=fifo # ID: 1
- -p CPUSchedulingPriority=5 # Actual prio: 94 (99 - prio)
- -p CPUSchedulingPriority=10 # Actual prio: 89 (99 - prio)
- )
-
- systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
- bash -xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched'
-
- # Device*=
- ARGUMENTS=(
- -p DevicePolicy=closed
- -p DevicePolicy=strict
- -p DeviceAllow="char-mem rm" # Allow read & mknod for /dev/{null,zero,...}
- -p DeviceAllow="$LODEV rw"
- -p DeviceAllow="$LODEV w" # Allow write for the loop
- # Everything else should be disallowed per the strict policy
- )
-
- systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
- bash -xec "test -r /dev/null; test ! -w /dev/null; test ! -r $LODEV; test -w $LODEV; test ! -r /dev/tty; test ! -w /dev/tty"
-
- if ! systemctl --version | grep -qF -- "-BPF_FRAMEWORK"; then
- # SocketBind*=
- ARGUMENTS=(
- -p SocketBindAllow=
- -p SocketBindAllow=1234
- -p SocketBindAllow=ipv4:udp:any
- -p SocketBindAllow=ipv6:6666
- # Everything but the last assignment is superfluous, but it still exercises
- # the parsing machinery
- -p SocketBindDeny=
- -p SocketBindDeny=1111
- -p SocketBindDeny=ipv4:1111
- -p SocketBindDeny=ipv4:any
- -p SocketBindDeny=ipv4:tcp:any
- -p SocketBindDeny=ipv4:udp:10000-11000
- -p SocketBindDeny=ipv6:1111
- -p SocketBindDeny=any
- )
-
- # We should fail with EPERM when trying to bind to a socket not on the allow list
- # (nc exits with 2 in that case)
- systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
- systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -l ::1 9999; exit 42'
- systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
- systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
- systemd-run --wait -p SuccessExitStatus="1 2" --pipe -p SocketBindDeny=any \
- bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
- # Consequently, we should succeed when binding to a socket on the allow list
- # and keep listening on it until we're killed by `timeout` (EC 124)
- systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1'
- systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1'
- systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -6 -l ::1 1234; exit 1'
- systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
- bash -xec 'timeout 1s nc -6 -l ::1 6666; exit 1'
- fi
-
- losetup -d "$LODEV"
- rm -f "$TEMPFILE"
-fi
-
-# {Cache,Configuration,Logs,Runtime,State}Directory=
-ARGUMENTS=(
- -p CacheDirectory="foo/bar/baz also\ with\ spaces"
- -p CacheDirectory="foo"
- -p CacheDirectory="context"
- -p CacheDirectoryMode="0123"
- -p CacheDirectoryMode="0666"
- -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon"
- -p ConfigurationDirectoryMode="0400"
- -p LogsDirectory="context/foo"
- -p LogsDirectory=""
- -p LogsDirectory="context/a/very/nested/logs/dir"
- -p RuntimeDirectory="context/with\ spaces"
- # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which
- # requires an additional level of escaping for the colon character
- -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces"
- -p RuntimeDirectoryPreserve=yes
- -p StateDirectory="context"
- -p StateDirectory="./././././././context context context"
- -p StateDirectoryMode="0000"
-)
-
-rm -rf /run/context
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]];
- [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]];
- [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
- [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]];
- [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]];
- [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
-test -d "/run/context/with spaces"
-test -s "/run/a symlink with : col:ons and spaces"
-rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
-
-# Limit*=
-#
-# Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan)
-# won't be able to mmap the shadow maps
-ARGUMENTS=(
- -p LimitCPU=15
- -p LimitCPU=10:15 # ulimit -t
- -p LimitFSIZE=96G # ulimit -f
- -p LimitDATA=8T:infinity
- -p LimitDATA=infinity # ulimit -d
- -p LimitSTACK=8M # ulimit -s
- -p LimitCORE=infinity
- -p LimitCORE=17M # ulimit -c
- -p LimitRSS=27G # ulimit -m
- -p LimitNOFILE=7:127 # ulimit -n
- -p LimitAS=infinity # ulimit -v
- -p LimitNPROC=1
- -p LimitNPROC=64:infinity # ulimit -u
- -p LimitMEMLOCK=37M # ulimit -l
- -p LimitLOCKS=19:1021 # ulimit -x
- -p LimitSIGPENDING=21 # ulimit -i
- -p LimitMSGQUEUE=666 # ulimit -q
- -p LimitNICE=4 # ulimit -e
- -p LimitRTPRIO=8 # ulimit -r
- -p LimitRTTIME=666666 # ulimit -R
-)
-# Do all the checks in one giant inline shell blob to avoid the overhead of spawning
-# a new service for each check
-#
-# Note: ulimit shows storage-related values in 1024-byte increments*
-# Note2: ulimit -R requires bash >= 5.1
-#
-# * in POSIX mode -c a -f options show values in 512-byte increments; let's hope
-# we never run in the POSIX mode
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024));
- : CPU; [[ $(ulimit -St) -eq 10 ]]; [[ $(ulimit -Ht) -eq 15 ]];
- : FSIZE; [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]];
- : DATA; [[ $(ulimit -Sd) == unlimited ]]; [[ $(ulimit -Hd) == unlimited ]];
- : STACK; [[ $(ulimit -Ss) -eq $((8 * MB)) ]]; [[ $(ulimit -Hs) -eq $((8 * MB)) ]];
- : CORE; [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]];
- : RSS; [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]];
- : NOFILE; [[ $(ulimit -Sn) -eq 7 ]]; [[ $(ulimit -Hn) -eq 127 ]];
- : AS; [[ $(ulimit -Sv) == unlimited ]]; [[ $(ulimit -Hv) == unlimited ]];
- : NPROC; [[ $(ulimit -Su) -eq 64 ]]; [[ $(ulimit -Hu) == unlimited ]];
- : MEMLOCK; [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]];
- : LOCKS; [[ $(ulimit -Sx) -eq 19 ]]; [[ $(ulimit -Hx) -eq 1021 ]];
- : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]]; [[ $(ulimit -Hi) -eq 21 ]];
- : MSGQUEUE; [[ $(ulimit -Sq) -eq 666 ]]; [[ $(ulimit -Hq) -eq 666 ]];
- : NICE; [[ $(ulimit -Se) -eq 4 ]]; [[ $(ulimit -He) -eq 4 ]];
- : RTPRIO; [[ $(ulimit -Sr) -eq 8 ]]; [[ $(ulimit -Hr) -eq 8 ]];
- ulimit -R || exit 0;
- : RTTIME; [[ $(ulimit -SR) -eq 666666 ]]; [[ $(ulimit -HR) -eq 666666 ]];'
-
-# RestrictFileSystems=
-#
-# Note: running instrumented binaries requires at least /proc to be accessible, so let's
-# skip the test when we're running under sanitizers
-#
-# Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files,
-# since gcov can't access the restricted filesystem (as expected)
-if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then
- ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)"
- systemd-run --wait --pipe -p RestrictFileSystems="" ls /
- systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls /
- (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc)
- (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /)
- systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc
- systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc
- systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup
-
- systemd-run --wait --pipe -p RestrictFileSystems="~" ls /
- systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /
- systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /
- (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /)
- (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc)
- (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc)
- (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc)
- (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys)
- systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /
- (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc)
- (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev)
- (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys)
-fi
-
-# Make sure we properly (de)serialize various string arrays, including whitespaces
-# See: https://github.com/systemd/systemd/issues/31214
-systemd-run --wait --pipe -p Environment="FOO='bar4 '" \
- bash -xec '[[ $FOO == "bar4 " ]]'
-systemd-run --wait --pipe -p Environment="FOO='bar4 ' BAR='\n\n'" \
- bash -xec "[[ \$FOO == 'bar4 ' && \$BAR == $'\n\n' ]]"
-systemd-run --wait --pipe -p Environment='FOO="bar4 \\ "' -p Environment="BAR='\n\t'" \
- bash -xec "[[ \$FOO == 'bar4 \\ ' && \$BAR == $'\n\t' ]]"
-TEST_ENV_FILE="/tmp/test-env-file-$RANDOM- "
-cat >"$TEST_ENV_FILE" <<EOF
-FOO="env file "
-BAR="
- "
-EOF
-systemd-run --wait --pipe cat "$TEST_ENV_FILE"
-systemd-run --wait --pipe -p ReadOnlyPaths="'$TEST_ENV_FILE'" \
- bash -xec '[[ ! -w "$TEST_ENV_FILE" ]]'
-systemd-run --wait --pipe -p PrivateTmp=yes -p BindReadOnlyPaths="'$TEST_ENV_FILE':'/tmp/bar- '" \
- bash -xec '[[ -e "/tmp/bar- " && ! -w "/tmp/bar- " ]]'
-systemd-run --wait --pipe -p EnvironmentFile="$TEST_ENV_FILE" \
- bash -xec "[[ \$FOO == 'env file ' && \$BAR == $'\n ' ]]"
-rm -f "$TEST_ENV_FILE"
-# manager_serialize()/manager_deserialize() uses similar machinery
-systemctl unset-environment FOO_WITH_SPACES
-systemctl set-environment FOO_WITH_SPACES="foo " FOO_WITH_TABS="foo\t\t\t"
-systemctl show-environment
-systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo '"
-systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
-systemctl daemon-reexec
-systemctl show-environment
-systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo '"
-systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
-
-# Ensure that clean-up codepaths work correctly if activation ultimately fails
-touch /run/not-a-directory
-mkdir /tmp/root
-touch /tmp/root/foo
-chmod +x /tmp/root/foo
-(! systemd-run --wait --pipe false)
-(! systemd-run --wait --pipe --unit "test-dynamicuser-fail" -p DynamicUser=yes -p WorkingDirectory=/nonexistent true)
-(! systemd-run --wait --pipe -p RuntimeDirectory=not-a-directory true)
-(! systemd-run --wait --pipe -p RootDirectory=/tmp/root this-shouldnt-exist)
-(! systemd-run --wait --pipe -p RootDirectory=/tmp/root /foo)
-(! systemd-run --wait --pipe --service-type=oneshot -p ExecStartPre=-/foo/bar/baz -p ExecStart=-/foo/bar/baz -p RootDirectory=/tmp/root -- "- foo")
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-setup_base_unit() {
- local unit_path="${1:?}"
- local log_file="${2:?}"
- local unit_name="${unit_path##*/}"
-
- cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-EOF
- systemctl daemon-reload
-
- systemctl --job-mode=replace --no-block start "$unit_name"
- # Wait until the unit leaves the "inactive" state
- timeout 5s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" == inactive ]]; do sleep .1; done"
- # Sleep for 1 second from the unit start to get well "into" the first (or second) ExecStart= directive
- sleep 1
-}
-
-check_output() {
- local unit_name="${1:?}"
- local log_file="${2:?}"
- local expected="${3?}"
- local unit_name="${unit_path##*/}"
-
- # Wait until the unit becomes inactive before checking the log
- timeout 10s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" != inactive ]]; do sleep .5; done"
-
- diff "$log_file" <(echo -ne "$expected")
-}
-
-testcase_no_change() {
- local unit_path log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-no-change-XXX.service)"
- log_file="$(mktemp)"
-
- setup_base_unit "$unit_path" "$log_file"
-
- # Simple sanity test without any reordering shenanignans, to check if the base unit works as expected.
- check_output "$unit_path" "$log_file" "foo\n"
-
- rm -f "$unit_path" "$log_file"
-}
-
-testcase_swapped() {
- local unit_path log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-swapped-XXX.service)"
- log_file="$(mktemp)"
-
- setup_base_unit "$unit_path" "$log_file"
-
- # Swap the two ExecStart= lines.
- #
- # Since we should be in the first "sleep" of the base unit, after replacing the unit with the following
- # one we should continue running from the respective "ExecStart=sleep 3" line, which is now the last
- # one, resulting no output in the final log file.
- cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo foo >>$log_file"
-ExecStart=sleep 3
-EOF
- systemctl daemon-reload
-
- check_output "$unit_path" "$log_file" ""
-
- rm -f "$unit_path" "$log_file"
-}
-
-testcase_added_before() {
- local unit_path log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-added-before-XXX.service)"
- log_file="$(mktemp)"
-
- setup_base_unit "$unit_path" "$log_file"
-
- # Add one new ExecStart= before the existing ones.
- #
- # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
- # bar" one will have no effect and we should end up with the same output as in the previous case.
- cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo bar >>$log_file"
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-EOF
- systemctl daemon-reload
-
- check_output "$unit_path" "$log_file" "foo\n"
-
- rm -f "$unit_path" "$log_file"
-}
-
-testcase_added_after() {
- local unit_path log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-added-after-XXX.service)"
- log_file="$(mktemp)"
-
- setup_base_unit "$unit_path" "$log_file"
-
- # Add an ExecStart= line after the existing ones.
- #
- # Same case as above, except the newly added ExecStart= should get executed, as it was added after the
- # "sleep 3" statement.
- cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-ExecStart=bash -c "echo bar >>$log_file"
-EOF
- systemctl daemon-reload
-
- check_output "$unit_path" "$log_file" "foo\nbar\n"
-
- rm -f "$unit_path" "$log_file"
-}
-
-testcase_interleaved() {
- local unit_path log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-interleaved-XXX.service)"
- log_file="$(mktemp)"
-
- setup_base_unit "$unit_path" "$log_file"
-
- # Combination of the two previous cases.
- cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo baz >>$log_file"
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-ExecStart=bash -c "echo bar >>$log_file"
-EOF
- systemctl daemon-reload
-
- check_output "$unit_path" "$log_file" "foo\nbar\n"
-
- rm -f "$unit_path" "$log_file"
-}
-
-testcase_removal() {
- local unit_path log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-removal-XXX.service)"
- log_file="$(mktemp)"
-
- setup_base_unit "$unit_path" "$log_file"
-
- # Remove the currently executed ExecStart= line.
- #
- # In this case we completely drop the currently executed "sleep 3" statement, so after reload systemd
- # should complain that the currently executed command vanished and simply finish executing the unit,
- # resulting in an empty log.
- cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo bar >>$log_file"
-ExecStart=bash -c "echo baz >>$log_file"
-EOF
- systemctl daemon-reload
-
- check_output "$unit_path" "$log_file" ""
-
- rm -f "$unit_path" "$log_file"
-}
-
-testcase_issue_6533() {
- local unit_path unit_name log_file
-
- unit_path="$(mktemp /run/systemd/system/test-deserialization-issue-6533-XXX.service)"
- unit_name="${unit_path##*/}"
- log_file="$(mktemp)"
-
- cat >"$unit_path" <<EOF
-[Service]
-Type=simple
-ExecStart=/bin/sleep 5
-EOF
- systemctl daemon-reload
-
- systemctl --job-mode=replace --no-block start "$unit_name"
- sleep 2
-
- # Make sure we try to execute the next command only for oneshot services, as for other types we allow
- # only one ExecStart= directive.
- #
- # See: https://github.com/systemd/systemd/issues/6533
- cat >"$unit_path" <<EOF
-[Service]
-Type=simple
-ExecStart=/bin/sleep 5
-ExecStart=bash -c "echo foo >>$log_file"
-EOF
- systemctl daemon-reload
-
- check_output "$unit_path" "$log_file" ""
- (! journalctl -b --grep "Freezing execution" _PID=1)
-}
-
-mkdir -p /run/systemd/system/
-run_testcases
-systemctl daemon-reload
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check that timestamps of a Type=notify service are consistent
-
-systemd-run --service-type notify --property NotifyAccess=all --unit notify.service --wait sh -c 'systemd-notify --ready; exit 1' || :
-
-start=$(systemctl show --property=ExecMainStartTimestampMonotonic --value notify.service)
-handoff=$(systemctl show --property=ExecMainHandoffTimestampMonotonic --value notify.service)
-active=$(systemctl show --property=ActiveEnterTimestampMonotonic --value notify.service)
-exit=$(systemctl show --property=ExecMainExitTimestampMonotonic --value notify.service)
-
-[[ $start -le $handoff ]]
-[[ $handoff -le $active ]]
-[[ $active -le $exit ]]
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test that KillMode=mixed does not leave left over processes with ExecStopPost=
-# Issue: https://github.com/systemd/systemd/issues/14566
-
-if [[ -n "${ASAN_OPTIONS:-}" ]]; then
- # Temporarily skip this test when running with sanitizers due to a deadlock
- # See: https://bugzilla.redhat.com/show_bug.cgi?id=2098125
- echo "Sanitizers detected, skipping the test..."
- exit 0
-fi
-
-systemctl start issue14566-repro
-sleep 4
-systemctl status issue14566-repro
-
-leaked_pid=$(cat /leakedtestpid)
-
-systemctl stop issue14566-repro
-sleep 4
-
-# Leaked PID will still be around if we're buggy.
-# I personally prefer to see 42.
-ps -p "$leaked_pid" && exit 42
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test ExecCondition= does not restart on abnormal or failure
-# Issue: https://github.com/systemd/systemd/issues/16115
-
-systemctl start issue16115-repro-1
-systemctl start issue16115-repro-2
-systemctl start issue16115-repro-3
-sleep 5 # wait a bit in case there are restarts so we can count them below
-
-[[ "$(systemctl show issue16115-repro-1 -P NRestarts)" == "0" ]]
-[[ "$(systemctl show issue16115-repro-2 -P NRestarts)" == "0" ]]
-[[ "$(systemctl show issue16115-repro-3 -P NRestarts)" == "0" ]]
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Segmentation fault in timer_enter_waiting while masking a unit
-# Issue: https://github.com/systemd/systemd/issues/1981
-
-at_exit() {
- set +e
-
- systemctl stop my.timer my.service
- rm -f /run/systemd/system/my.{service,timer}
- systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-mkdir -p /run/systemd/system
-
-cat >/run/systemd/system/my.service <<\EOF
-[Service]
-Type=oneshot
-ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer'
-ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"'
-ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"'
-ExecStart=echo Timer runs me
-EOF
-
-cat >/run/systemd/system/my.timer <<EOF
-[Timer]
-OnBootSec=10s
-OnUnitInactiveSec=1h
-EOF
-
-systemctl unmask my.timer
-systemctl start my.timer
-
-mkdir -p /run/systemd/system/my.timer.d/
-cat >/run/systemd/system/my.timer.d/override.conf <<EOF
-[Timer]
-OnBootSec=10s
-OnUnitInactiveSec=1h
-EOF
-
-systemctl daemon-reload
-systemctl mask my.timer
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Don't start services every few ms if condition fails
-# Issue: https://github.com/systemd/systemd/issues/2467
-
-rm -f /tmp/nonexistent
-systemctl start issue2467.socket
-nc -i20 -w20 -U /run/test.ctl || :
-
-# TriggerLimitIntervalSec= by default is set to 2s. A "sleep 10" should give
-# systemd enough time even on slower machines, to reach the trigger limit.
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until [[ "$(systemctl show issue2467.socket -P ActiveState)" == failed ]]; do sleep .5; done'
-[[ "$(systemctl show issue2467.socket -P Result)" == trigger-limit-hit ]]
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check if the unit doesn't remain in active state after the main PID exits
-# Issue: https://github.com/systemd/systemd/issues/27953
-
-systemctl start issue27953.service
-timeout 10 sh -c 'while systemctl is-active issue27953.service; do sleep .5; done'
-[[ "$(systemctl show -P ExitType issue27953.service)" == main ]]
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check that socket FDs are not double closed on error: https://github.com/systemd/systemd/issues/30412
-
-mkdir -p /run/systemd/system
-
-rm -f /tmp/badbin
-touch /tmp/badbin
-chmod 744 /tmp/badbin
-
-cat >/run/systemd/system/badbin_assert.service <<EOF
-[Service]
-ExecStart=/tmp/badbin
-Restart=no
-EOF
-
-cat >/run/systemd/system/badbin_assert.socket <<EOF
-[Socket]
-ListenStream=@badbin_assert.socket
-FlushPending=yes
-EOF
-
-systemctl daemon-reload
-systemctl start badbin_assert.socket
-
-socat - ABSTRACT-CONNECT:badbin_assert.socket
-
-timeout 10 sh -c 'while systemctl is-active badbin_assert.service; do sleep .5; done'
-[[ "$(systemctl show -P ExecMainStatus badbin_assert.service)" == 203 ]]
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Service doesn't enter the "failed" state
-# Issue: https://github.com/systemd/systemd/issues/3166
-
-systemctl --no-block start issue3166-fail-on-restart.service
-active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
-while [[ "$active_state" == "activating" || "$active_state" =~ ^(in)?active$ ]]; do
- sleep .5
- active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
-done
-systemctl is-failed issue3166-fail-on-restart.service || exit 1
-[[ "$(systemctl show --value --property NRestarts issue3166-fail-on-restart.service)" -le 3 ]] || exit 1
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# SocketGroup lost on daemon-reload with unit moving away temporarily
-# Issue: https://github.com/systemd/systemd/issues/3171
-
-echo "g adm - - -" | systemd-sysusers -
-
-U=/run/systemd/system/issue-3171.socket
-cat >$U <<EOF
-[Unit]
-Description=Test 12 socket
-[Socket]
-Accept=yes
-ListenStream=/run/issue-3171.socket
-SocketGroup=adm
-SocketMode=0660
-EOF
-
-cat >/run/systemd/system/issue-3171@.service <<EOF
-[Unit]
-Description=Test service
-[Service]
-StandardInput=socket
-ExecStart=sh -x -c cat
-EOF
-
-systemctl start issue-3171.socket
-systemctl is-active issue-3171.socket
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
-echo A | nc -w1 -U /run/issue-3171.socket
-
-mv $U ${U}.disabled
-systemctl daemon-reload
-systemctl is-active issue-3171.socket
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
-echo B | nc -w1 -U /run/issue-3171.socket && exit 1
-
-mv ${U}.disabled $U
-systemctl daemon-reload
-systemctl is-active issue-3171.socket
-echo C | nc -w1 -U /run/issue-3171.socket && exit 1
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
-
-systemctl restart issue-3171.socket
-systemctl is-active issue-3171.socket
-echo D | nc -w1 -U /run/issue-3171.socket
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test changing the main PID
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# The main service PID should be the parent bash process
-MAINPID="${PPID:?}"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Start a test process inside of our own cgroup
-sleep infinity &
-INTERNALPID=$!
-disown
-
-# Start a test process outside of our own cgroup
-systemd-run -p DynamicUser=1 --unit=test-sleep.service /bin/sleep infinity
-EXTERNALPID="$(systemctl show -P MainPID test-sleep.service)"
-
-# Update our own main PID to the external test PID, this should work
-systemd-notify MAINPID="$EXTERNALPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$EXTERNALPID"
-
-# Update our own main PID to the internal test PID, this should work, too
-systemd-notify MAINPID=$INTERNALPID
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$INTERNALPID"
-
-# Update it back to our own PID, this should also work
-systemd-notify MAINPID="$MAINPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Try to set it to PID 1, which it should ignore, because that's the manager
-systemd-notify MAINPID=1
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Try to set it to PID 0, which is invalid and should be ignored
-systemd-notify MAINPID=0
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Try to set it to a valid but non-existing PID, which should be ignored. (Note
-# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
-# which means we can be pretty sure it doesn't exist by coincidence)
-systemd-notify MAINPID=1073741824
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
-systemd-notify --uid=1000 MAINPID="$EXTERNALPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
-systemd-notify --uid=1000 MAINPID="$INTERNALPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$INTERNALPID"
-
-# Update it back to our own PID, this should also work
-systemd-notify --uid=1000 MAINPID="$MAINPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-cat >/tmp/test-mainpid.sh <<\EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-# Create a number of children, and make one the main one
-sleep infinity &
-disown
-
-sleep infinity &
-MAINPID=$!
-disown
-
-sleep infinity &
-disown
-
-echo $MAINPID >/run/mainpidsh/pid
-EOF
-chmod +x /tmp/test-mainpid.sh
-
-systemd-run --unit=test-mainpidsh.service \
- -p StandardOutput=tty \
- -p StandardError=tty \
- -p Type=forking \
- -p RuntimeDirectory=mainpidsh \
- -p PIDFile=/run/mainpidsh/pid \
- /tmp/test-mainpid.sh
-test "$(systemctl show -P MainPID test-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)"
-
-cat >/tmp/test-mainpid2.sh <<\EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-# Create a number of children, and make one the main one
-sleep infinity &
-disown
-
-sleep infinity &
-MAINPID=$!
-disown
-
-sleep infinity &
-disown
-
-echo $MAINPID >/run/mainpidsh2/pid
-chown 1001:1001 /run/mainpidsh2/pid
-EOF
-chmod +x /tmp/test-mainpid2.sh
-
-systemd-run --unit=test-mainpidsh2.service \
- -p StandardOutput=tty \
- -p StandardError=tty \
- -p Type=forking \
- -p RuntimeDirectory=mainpidsh2 \
- -p PIDFile=/run/mainpidsh2/pid \
- /tmp/test-mainpid2.sh
-test "$(systemctl show -P MainPID test-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)"
-
-cat >/dev/shm/test-mainpid3.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-sleep infinity &
-disown
-
-sleep infinity &
-disown
-
-sleep infinity &
-disown
-
-# Let's try to play games, and link up a privileged PID file
-ln -s ../mainpidsh/pid /run/mainpidsh3/pid
-
-# Quick assertion that the link isn't dead
-test -f /run/mainpidsh3/pid
-EOF
-chmod 755 /dev/shm/test-mainpid3.sh
-
-# This has to fail, as we shouldn't accept the dangerous PID file, and then
-# inotify-wait on it to be corrected which we never do.
-(! systemd-run \
- --unit=test-mainpidsh3.service \
- -p StandardOutput=tty \
- -p StandardError=tty \
- -p Type=forking \
- -p RuntimeDirectory=mainpidsh3 \
- -p PIDFile=/run/mainpidsh3/pid \
- -p DynamicUser=1 \
- `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
- -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
- -p TimeoutStartSec=2s \
- /dev/shm/test-mainpid3.sh)
-
-# Test that this failed due to timeout, and not some other error
-test "$(systemctl show -P Result test-mainpidsh3.service)" = timeout
-
-# Test that scope units work
-systemd-run --scope --unit test-true.scope /bin/true
-test "$(systemctl show -P Result test-true.scope)" = success
-
-# Test that user scope units work as well
-
-systemctl start user@4711.service
-runas testuser systemd-run --scope --user --unit test-true.scope /bin/true
-test "$(systemctl show -P Result test-true.scope)" = success
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Don't send invalid characters over dbus if a mount contains them
-
-at_exit() {
- mountpoint -q /proc/1/mountinfo && umount /proc/1/mountinfo
- [[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
- rm -f /run/systemd/system/foo-*.mount
- systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-# Check invalid characters directly in /proc/mountinfo
-#
-# This is a bit tricky (and hacky), since we have to temporarily replace
-# PID 1's /proc/mountinfo, but we have to keep the original mounts intact,
-# otherwise systemd would unmount them on reload
-TMP_MOUNTINFO="$(mktemp)"
-
-cp /proc/1/mountinfo "$TMP_MOUNTINFO"
-# Add a mount entry with a "Unicode non-character" in it
-LANG="C.UTF-8" printf '69 1 252:2 / /foo/mountinfo rw,relatime shared:1 - cifs //foo\ufffebar rw,seclabel\n' >>"$TMP_MOUNTINFO"
-mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo
-systemctl daemon-reload
-# On affected versions this would throw an error:
-# Failed to get properties: Bad message
-systemctl status foo-mountinfo.mount
-
-umount /proc/1/mountinfo
-systemctl daemon-reload
-rm -f "$TMP_MOUNTINFO"
-
-# Check invalid characters in a mount unit
-#
-# systemd already handles this and refuses to load the invalid string, e.g.:
-# foo-fstab.mount:9: String is not UTF-8 clean, ignoring assignment: What=//localhost/foo���bar
-#
-# a) Unit generated from /etc/fstab
-[[ -e /etc/fstab ]] && cp -f /etc/fstab /tmp/fstab.bak
-
-LANG="C.UTF-8" printf '//localhost/foo\ufffebar /foo/fstab cifs defaults 0 0\n' >/etc/fstab
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-fstab.mount)" == bad ]]
-
-# b) Unit generated from /etc/fstab (but the invalid character is in options)
-LANG="C.UTF-8" printf '//localhost/foobar /foo/fstab/opt cifs nosuid,a\ufffeb,noexec 0 0\n' >/etc/fstab
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]]
-rm -f /etc/fstab
-
-[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
-systemctl daemon-reload
-
-# c) Mount unit
-mkdir -p /run/systemd/system
-LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foo\ufffebar\nWhere=/foo/unit\nType=cifs\nOptions=noexec\n' >/run/systemd/system/foo-unit.mount
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-unit.mount)" == bad ]]
-rm -f /run/systemd/system/foo-unit.mount
-
-# d) Mount unit (but the invalid character is in Options=)
-mkdir -p /run/systemd/system
-LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foobar\nWhere=/foo/unit/opt\nType=cifs\nOptions=noexec,a\ufffeb,nosuid\n' >/run/systemd/system/foo-unit-opt.mount
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-unit-opt.mount)" == bad ]]
-rm -f /run/systemd/system/foo-unit-opt.mount
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-analyze log-level debug
-
-cat >/run/systemd/system/floodme@.service <<EOF
-[Service]
-ExecStart=true
-EOF
-
-cat >/run/systemd/system/floodme.socket <<EOF
-[Socket]
-ListenStream=/tmp/floodme
-PollLimitIntervalSec=10s
-Accept=yes
-PollLimitBurst=3
-EOF
-
-systemctl daemon-reload
-systemctl start floodme.socket
-
-START=$(date +%s%N)
-
-# Trigger this 100 times in a flood
-for _ in {1..100}; do
- logger -u /tmp/floodme foo &
-done
-
-# Let some time pass
-sleep 5
-
-END=$(date +%s%N)
-
-PASSED=$((END-START))
-
-# Calculate (round up) how many trigger events could have happened in the passed time
-MAXCOUNT=$(((PASSED+10000000000)*3/10000000000))
-
-# We started 100 connection attempts, but only 3 should have gone through, as per limit
-test "$(systemctl show -P NAccepted floodme.socket)" -le "$MAXCOUNT"
-
-systemctl stop floodme.socket floodme@*.service
-
-rm /run/systemd/system/floodme@.service /run/systemd/system/floodme.socket /tmp/floodme
-
-systemctl daemon-reload
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-cat >/run/systemd/system/nonexistent-execstart-exit-status.service <<EOF
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=-/foo/bar/not-exist
-EOF
-
-systemctl start nonexistent-execstart-exit-status.service
-systemctl is-active nonexistent-execstart-exit-status.service
-assert_eq "$(systemctl show nonexistent-execstart-exit-status.service -P Result)" "success"
-(( $(systemctl show nonexistent-execstart-exit-status.service -P ExecMainStatus) > 0 ))
-
-systemctl stop nonexistent-execstart-exit-status.service
-rm /run/systemd/system/nonexistent-execstart-exit-status.service
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# For issue https://github.com/systemd/systemd/issues/29526
-systemd-run -p PrivateNetwork=yes --wait /bin/true
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-07-PID1
-
-[Service]
-Type=oneshot
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-NotifyAccess=all
-# Issue: https://github.com/systemd/systemd/issues/2691
-ExecStop=sh -c 'kill -SEGV $$$$'
-RemainAfterExit=yes
-TimeoutStopSec=270s
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# Issue: https://github.com/systemd/systemd/issues/2730
-# See TEST-07-PID1/test.sh for the first "half" of the test
-mountpoint /issue2730
-
-run_subtests
-
-touch /testok
-systemctl --no-block exit 123
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test PassFileDescriptorsToExec= option in socket units
-
-for u in pass-fds-to-exec-{no,yes}.socket; do
- systemctl start "$u"
- systemctl stop "$u"
-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Make sure that we never mistake a process starting but failing quickly for a process failing to start, with Type=exec.
-# See https://github.com/systemd/systemd/pull/30799
-
-seq 25 | xargs -n 1 -P 0 systemd-run -p Type=exec /bin/false
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-08-INITRD
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if systemd-detect-virt -qc; then
- echo >&2 "This test can't run in a container"
- exit 1
-fi
-
-# This test requires systemd to run in the initrd as well, which is not the case
-# for mkinitrd-based initrd (Ubuntu/Debian)
-if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
- echo "systemd didn't run in the initrd, skipping the test"
- touch /skipped
- exit 77
-fi
-
-# We should've created a mount under /run in initrd (see the other half of the test)
-# that should've survived the transition from initrd to the real system
-test -d /run/initrd-mount-target
-mountpoint /run/initrd-mount-target
-[[ -e /run/initrd-mount-target/hello-world ]]
-
-# Copy the prepared shutdown initrd to its intended location. Check the respective
-# test.sh file for details
-mkdir -p /run/initramfs
-cp -r /shutdown-initrd/* /run/initramfs/
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-get_first_boot_id() {
- journalctl -b "${1:?}" -o json -n +1 | jq -r '._BOOT_ID'
-}
-
-get_last_boot_id() {
- journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID'
-}
-
-get_first_timestamp() {
- journalctl -b "${1:?}" -o json -n +1 | jq -r '.__REALTIME_TIMESTAMP'
-}
-
-get_last_timestamp() {
- journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP'
-}
-
-# Issue: #29275, second part
-# Now let's check if the boot entries are in the correct/expected order
-index=0
-SYSTEMD_LOG_LEVEL=debug journalctl --list-boots
-journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' |
- while read -r offset boot_id first_ts last_ts; do
- : "Boot #$((++index)) ($offset) with ID $boot_id"
-
- # Try the "regular" (non-json) variants first, as they provide a helpful
- # error message if something is not right
- SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index"
- SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset"
- SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id"
-
- # Check the boot ID of the first entry
- entry_boot_id="$(get_first_boot_id "$index")"
- assert_eq "$entry_boot_id" "$boot_id"
- entry_boot_id="$(get_first_boot_id "$offset")"
- assert_eq "$entry_boot_id" "$boot_id"
- entry_boot_id="$(get_first_boot_id "$boot_id")"
- assert_eq "$entry_boot_id" "$boot_id"
-
- # Check the timestamp of the first entry
- entry_ts="$(get_first_timestamp "$index")"
- assert_eq "$entry_ts" "$first_ts"
- entry_ts="$(get_first_timestamp "$offset")"
- assert_eq "$entry_ts" "$first_ts"
- entry_ts="$(get_first_timestamp "$boot_id")"
- assert_eq "$entry_ts" "$first_ts"
-
- # Check the boot ID of the last entry
- entry_boot_id="$(get_last_boot_id "$index")"
- assert_eq "$entry_boot_id" "$boot_id"
- entry_boot_id="$(get_last_boot_id "$offset")"
- assert_eq "$entry_boot_id" "$boot_id"
- entry_boot_id="$(get_last_boot_id "$boot_id")"
- assert_eq "$entry_boot_id" "$boot_id"
-
- # Check the timestamp of the last entry
- if [[ "$offset" != "0" ]]; then
- entry_ts="$(get_last_timestamp "$index")"
- assert_eq "$entry_ts" "$last_ts"
- entry_ts="$(get_last_timestamp "$offset")"
- assert_eq "$entry_ts" "$last_ts"
- entry_ts="$(get_last_timestamp "$boot_id")"
- assert_eq "$entry_ts" "$last_ts"
- fi
- done
-
-verify_seqnum() {
- if [[ "$REBOOT_COUNT" -ne "$NUM_REBOOT" ]]; then
- return 0
- fi
-
- journalctl --flush
- journalctl --sync
-
- ls -lR /var/log/journal/
- ls -lR /run/log/journal/
-
- journalctl --system --header
-
- (! journalctl --system -q -o short-monotonic -u systemd-journald.service --grep 'Journal file uses a different sequence number ID, rotating')
-
- set +x
- previous_seqnum=0
- previous_seqnum_id=
- previous_boot_id=
- journalctl --system -q -o json | jq -r '[.__SEQNUM, .__SEQNUM_ID, ._BOOT_ID] | @tsv' |
- while read -r seqnum seqnum_id boot_id; do
-
- if [[ -n "$previous_seqnum_id" ]]; then
- if ! test "$seqnum" -gt "$previous_seqnum"; then
- echo "seqnum=$seqnum is not greater than previous_seqnum=$previous_seqnum"
- echo "seqnum_id=$seqnum_id, previous_seqnum_id=$previous_seqnum_id"
- echo "boot_id=$boot_id, previous_boot_id=$previous_boot_id"
- return 1
- fi
-
- assert_eq "$seqnum_id" "$previous_seqnum_id"
- fi
-
- previous_seqnum="$seqnum"
- previous_seqnum_id="$seqnum_id"
- previous_boot_id="$boot_id"
- done
- set -x
-
- return 0
-}
-
-verify_seqnum
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-09-REBOOT
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export NUM_REBOOT=4
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-cat echo "Reboot count: $REBOOT_COUNT"
-systemd-cat journalctl --list-boots
-
-run_subtests
-
-if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then
- systemctl_final reboot
-elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then
- assert_not_reached
-fi
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export PAGER=
-
-at_exit() {
- set +e
- umount -l -R /var/lib/confexts
- rm -f /var/tmp/importtest /var/tmp/importtest2 /var/tmp/importtest.tar.gz /var/tmp/importtest2.tar.gz
-}
-
-trap at_exit EXIT
-
-systemctl service-log-level systemd-importd debug
-
-# Mount tmpfs over /var/lib/confexts to not pollute the image
-mkdir -p /var/lib/confexts
-mount -t tmpfs tmpfs /var/lib/confexts -o mode=755
-
-importctl
-importctl --no-pager --help
-importctl --version
-importctl list-transfers
-importctl list-transfers --no-legend --no-ask-password
-importctl list-transfers -j
-importctl list-images
-importctl list-images --no-legend --no-ask-password
-importctl list-images -j
-
-(! importctl cancel-transfer 4711)
-
-dd if=/dev/urandom of=/var/tmp/importtest bs=4096 count=10
-
-importctl import-raw --class=confext /var/tmp/importtest
-cmp /var/tmp/importtest /var/lib/confexts/importtest.raw
-importctl export-raw --class=confext importtest /var/tmp/importtest2
-cmp /var/tmp/importtest /var/tmp/importtest2
-
-(! importctl pull-raw --class=confext file:///var/tmp/importtest)
-importctl pull-raw --verify=no --class=confext file:///var/tmp/importtest importtest3
-cmp /var/tmp/importtest /var/lib/confexts/importtest3.raw
-
-tar czf /var/tmp/importtest.tar.gz -C /var/tmp importtest
-
-importctl import-tar --class=confext /var/tmp/importtest.tar.gz importtest4
-cmp /var/tmp/importtest /var/lib/confexts/importtest4/importtest
-
-importctl export-tar --class=confext importtest4 /var/tmp/importtest2.tar.gz
-importctl import-tar --class=confext /var/tmp/importtest2.tar.gz importtest5
-cmp /var/tmp/importtest /var/lib/confexts/importtest5/importtest
-
-importctl import-fs --class=confext /var/lib/confexts/importtest5 importtest6
-cmp /var/tmp/importtest /var/lib/confexts/importtest6/importtest
-
-(! importctl pull-tar --class=confext file:///var/tmp/importtest.tar.gz importtest7)
-importctl pull-tar --class=confext --verify=no file:///var/tmp/importtest.tar.gz importtest7
-cmp /var/tmp/importtest /var/lib/confexts/importtest7/importtest
-
-importctl list-images
-importctl list-images -j
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export PAGER=
-
-at_exit() {
- set +e
-
- machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running
- mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
- [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT"
- rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-systemctl service-log-level systemd-machined debug
-systemctl service-log-level systemd-importd debug
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-# Create a couple of containers we can refer to in tests
-for i in {0..4}; do
- create_dummy_container "/var/lib/machines/container$i"
- machinectl start "container$i"
-done
-# Create one "long running" container with some basic signal handling
-create_dummy_container /var/lib/machines/long-running
-cat >/var/lib/machines/long-running/sbin/init <<\EOF
-#!/usr/bin/bash -x
-
-PID=0
-
-trap "touch /poweroff" RTMIN+4
-trap "touch /reboot" INT
-trap "touch /trap" TRAP
-trap 'kill $PID' EXIT
-
-# We need to wait for the sleep process asynchronously in order to allow
-# bash to process signals
-sleep infinity &
-PID=$!
-while :; do
- wait || :
-done
-EOF
-machinectl start long-running
-
-machinectl
-machinectl --no-pager --help
-machinectl --version
-machinectl list
-machinectl list --no-legend --no-ask-password
-
-machinectl status long-running long-running long-running
-machinectl status --full long-running
-machinectl status --quiet --lines=1 long-running
-machinectl status --lines=0 --max-addresses=0 long-running
-machinectl status --machine=testuser@.host long-running
-machinectl status --output=help long-running
-while read -r output; do
- machinectl status --output="$output" long-running
-done < <(machinectl --output=help)
-
-machinectl show
-machinectl show --all
-machinectl show --all --machine=root@
-machinectl show --all --machine=testuser@
-[[ "$(machinectl show --property=PoolPath --value)" == "/var/lib/machines" ]]
-machinectl show long-running
-machinectl show long-running long-running long-running --all
-[[ "$(machinectl show --property=RootDirectory --value long-running)" == "/var/lib/machines/long-running" ]]
-
-machinectl enable long-running
-test -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
-machinectl enable long-running long-running long-running container1
-machinectl disable long-running
-test ! -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
-machinectl disable long-running long-running long-running container1
-
-[[ "$(machinectl shell testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "" ]]
-[[ "$(machinectl shell --setenv=FOO=bar testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "bar" ]]
-
-[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
-# Equivalent to machinectl kill --signal=SIGRTMIN+4 --kill-whom=leader
-rm -f /var/lib/machines/long-running/poweroff
-machinectl poweroff long-running
-timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sleep .5; done"
-# Equivalent to machinectl kill --signal=SIGINT --kill-whom=leader
-rm -f /var/lib/machines/long-running/reboot
-machinectl reboot long-running
-timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done"
-# Skip machinectl terminate for now, as it doesn't play well with our "init"
-rm -f /var/lib/machines/long-running/trap
-machinectl kill --signal=SIGTRAP --kill-whom=leader long-running
-timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done"
-# Multiple machines at once
-machinectl poweroff long-running long-running long-running
-machinectl reboot long-running long-running long-running
-machinectl kill --signal=SIGTRAP --kill-whom=leader long-running long-running long-running
-# All used signals should've been caught by a handler
-[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
-
-cp /etc/machine-id /tmp/foo
-machinectl copy-to long-running /tmp/foo /root/foo
-test -f /var/lib/machines/long-running/root/foo
-machinectl copy-from long-running /root/foo /tmp/bar
-diff /tmp/foo /tmp/bar
-rm -f /tmp/{foo,bar}
-
-# machinectl bind is covered by testcase_check_machinectl_bind() in nspawn tests
-
-machinectl list-images
-machinectl list-images --no-legend
-machinectl image-status
-machinectl image-status container1
-machinectl image-status container1 container1 container{0..4}
-machinectl show-image
-machinectl show-image container1
-machinectl show-image container1 container1 container{0..4}
-
-machinectl clone container1 clone1
-machinectl show-image clone1
-machinectl rename clone1 clone2
-(! machinectl show-image clone1)
-machinectl show-image clone2
-if lsattr -d /var/lib/machines >/dev/null; then
- [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
- machinectl read-only clone2 yes
- [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]]
- machinectl read-only clone2 no
- [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
-fi
-machinectl remove clone2
-for i in {0..4}; do
- machinectl clone container1 "clone$i"
-done
-machinectl remove clone{0..4}
-for i in {0..4}; do
- machinectl clone container1 ".hidden$i"
-done
-machinectl list-images --all
-test -d /var/lib/machines/.hidden1
-machinectl clean
-test ! -d /var/lib/machines/.hidden1
-
-# Prepare a simple raw container
-mkdir -p /tmp/mnt
-dd if=/dev/zero of=/var/tmp/container.raw bs=1M count=256
-mkfs.ext4 /var/tmp/container.raw
-mount -o loop /var/tmp/container.raw /tmp/mnt
-cp -r /var/lib/machines/container1/* /tmp/mnt
-umount /tmp/mnt
-# Try to import it, run it, export it, and re-import it
-machinectl import-raw /var/tmp/container.raw container-raw
-[[ "$(machinectl show-image --property=Type --value container-raw)" == "raw" ]]
-machinectl start container-raw
-machinectl export-raw container-raw /var/tmp/container-export.raw
-machinectl import-raw /var/tmp/container-export.raw container-raw-reimport
-[[ "$(machinectl show-image --property=Type --value container-raw-reimport)" == "raw" ]]
-rm -f /var/tmp/container{,-export}.raw
-
-# Prepare a simple tar.gz container
-tar -pczf /var/tmp/container.tar.gz -C /var/lib/machines/container1 .
-# Try to import it, run it, export it, and re-import it
-machinectl import-tar /var/tmp/container.tar.gz container-tar
-[[ "$(machinectl show-image --property=Type --value container-tar)" =~ directory|subvolume ]]
-machinectl start container-tar
-machinectl export-tar container-tar /var/tmp/container-export.tar.gz
-machinectl import-tar /var/tmp/container-export.tar.gz container-tar-reimport
-[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" =~ directory|subvolume ]]
-rm -f /var/tmp/container{,-export}.tar.gz
-
-# Try to import a container directory & run it
-cp -r /var/lib/machines/container1 /var/tmp/container.dir
-machinectl import-fs /var/tmp/container.dir container-dir
-[[ "$(machinectl show-image --property=Type --value container-dir)" =~ directory|subvolume ]]
-machinectl start container-dir
-rm -fr /var/tmp/container.dir
-
-timeout 10 bash -c "until machinectl clean --all; do sleep .5; done"
-
-NSPAWN_FRAGMENT="machinectl-test-$RANDOM.nspawn"
-cat >"/var/lib/machines/$NSPAWN_FRAGMENT" <<EOF
-[Exec]
-Boot=true
-EOF
-machinectl cat "$NSPAWN_FRAGMENT"
-EDITOR=true script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
-test -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
-diff "/var/lib/machines/$NSPAWN_FRAGMENT" "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
-
-cat >/tmp/fragment.nspawn <<EOF
-[Exec]
-Boot=false
-EOF
-machinectl cat /tmp/fragment.nspawn
-EDITOR="cp /tmp/fragment.nspawn" script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
-diff /tmp/fragment.nspawn "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
-
-for opt in format lines machine max-addresses output setenv verify; do
- (! machinectl status "--$opt=" long-running)
- (! machinectl status "--$opt=-1" long-running)
- (! machinectl status "--$opt=''" long-running)
-done
-(! machinectl show "")
-(! machinectl enable)
-(! machinectl enable "")
-(! machinectl disable)
-(! machinectl disable "")
-(! machinectl read-only container1 "")
-(! machinectl read-only container1 foo)
-(! machinectl read-only container1 -- -1)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-export SYSTEMD_LOG_TARGET=journal
-
-# shellcheck disable=SC2317
-at_exit() {
- set +e
-
- mountpoint -q /var/lib/machines && umount /var/lib/machines
- [[ -n "${DEV:-}" ]] && rm -f "$DEV"
- [[ -n "${NETNS:-}" ]] && umount "$NETNS" && rm -f "$NETNS"
- [[ -n "${TMPDIR:-}" ]] && rm -fr "$TMPDIR"
- rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-# Setup a couple of dirs/devices for the OCI containers
-DEV="$(mktemp -u /dev/oci-dev-XXX)"
-mknod -m 666 "$DEV" b 42 42
-NETNS="$(mktemp /var/tmp/netns.XXX)"
-mount --bind /proc/self/ns/net "$NETNS"
-TMPDIR="$(mktemp -d)"
-touch "$TMPDIR/hello"
-OCI="$(mktemp -d /var/lib/machines/testsuite-13.oci-bundle.XXX)"
-create_dummy_container "$OCI/rootfs"
-mkdir -p "$OCI/rootfs/opt/var"
-mkdir -p "$OCI/rootfs/opt/readonly"
-
-# Let's start with a simple config
-cat >"$OCI/config.json" <<EOF
-{
- "ociVersion" : "1.0.0",
- "root" : {
- "path" : "rootfs"
- },
- "mounts" : [
- {
- "destination" : "/root",
- "type" : "tmpfs",
- "source" : "tmpfs"
- }
- ]
-}
-EOF
-systemd-nspawn --oci-bundle="$OCI" bash -xec 'mountpoint /root'
-
-# And now for something a bit more involved
-# Notes:
-# - the hooks are parsed & processed, but never executed
-# - set sysctl's are parsed but never used?
-# - same goes for arg_sysctl in nspawn.c
-cat >"$OCI/config.json" <<EOF
-{
- "ociVersion" : "1.0.0",
- "hostname" : "my-oci-container",
- "root" : {
- "path" : "rootfs",
- "readonly" : false
- },
- "mounts" : [
- {
- "destination" : "/root",
- "type" : "tmpfs",
- "source" : "tmpfs"
- },
- ${COVERAGE_BUILD_DIR:+"{ \"destination\" : \"$COVERAGE_BUILD_DIR\" },"}
- {
- "destination" : "/var",
- "type" : "none",
- "source" : "$TMPDIR",
- "options" : ["rbind", "rw"]
- }
- ],
- "process" : {
- "terminal" : false,
- "consoleSize" : {
- "height" : 25,
- "width" : 80
- },
- "user" : {
- "uid" : 0,
- "gid" : 0,
- "additionalGids" : [5, 6]
- },
- "env" : [
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "FOO=bar"
- ],
- "cwd" : "/root",
- "args" : [
- "bash",
- "-xe",
- "/entrypoint.sh"
- ],
- "noNewPrivileges" : true,
- "oomScoreAdj" : 20,
- "capabilities" : {
- "bounding" : [
- "CAP_AUDIT_WRITE",
- "CAP_KILL",
- "CAP_NET_BIND_SERVICE"
- ],
- "permitted" : [
- "CAP_AUDIT_WRITE",
- "CAP_KILL",
- "CAP_NET_BIND_SERVICE"
- ],
- "inheritable" : [
- "CAP_AUDIT_WRITE",
- "CAP_KILL",
- "CAP_NET_BIND_SERVICE"
- ],
- "effective" : [
- "CAP_AUDIT_WRITE",
- "CAP_KILL"
- ],
- "ambient" : [
- "CAP_NET_BIND_SERVICE"
- ]
- },
- "rlimits" : [
- {
- "type" : "RLIMIT_NOFILE",
- "soft" : 1024,
- "hard" : 1024
- },
- {
- "type" : "RLIMIT_RTPRIO",
- "soft" : 5,
- "hard" : 10
- }
- ]
- },
- "linux" : {
- "namespaces" : [
- {
- "type" : "mount"
- },
- {
- "type" : "network",
- "path" : "$NETNS"
- },
- {
- "type" : "pid"
- },
- {
- "type" : "uts"
- }
- ],
- "uidMappings" : [
- {
- "containerID" : 0,
- "hostID" : 1000,
- "size" : 100
- }
- ],
- "gidMappings" : [
- {
- "containerID" : 0,
- "hostID" : 1000,
- "size" : 100
- }
- ],
- "devices" : [
- {
- "type" : "c",
- "path" : "/dev/zero",
- "major" : 1,
- "minor" : 5,
- "fileMode" : 444
- },
- {
- "type" : "b",
- "path" : "$DEV",
- "major" : 4,
- "minor" : 2,
- "fileMode" : 666,
- "uid" : 0,
- "gid" : 0
- }
- ],
- "resources" : {
- "devices" : [
- {
- "allow" : false,
- "access" : "m"
- },
- {
- "allow" : true,
- "type" : "b",
- "major" : 4,
- "minor" : 2,
- "access" : "rwm"
- }
- ],
- "memory" : {
- "limit" : 134217728,
- "reservation" : 33554432,
- "swap" : 268435456
- },
- "cpu" : {
- "shares" : 1024,
- "quota" : 1000000,
- "period" : 500000,
- "cpus" : "0-7"
- },
- "blockIO" : {
- "weight" : 10,
- "weightDevice" : [
- {
- "major" : 4,
- "minor" : 2,
- "weight" : 500
- }
- ],
- "throttleReadBpsDevice" : [
- {
- "major" : 4,
- "minor" : 2,
- "rate" : 500
- }
- ],
- "throttleWriteBpsDevice" : [
- {
- "major" : 4,
- "minor" : 2,
- "rate" : 500
- }
- ],
- "throttleReadIOPSDevice" : [
- {
- "major" : 4,
- "minor" : 2,
- "rate" : 500
- }
- ],
- "throttleWriteIOPSDevice" : [
- {
- "major" : 4,
- "minor" : 2,
- "rate" : 500
- }
- ]
- },
- "pids" : {
- "limit" : 1024
- }
- },
- "sysctl" : {
- "kernel.domainname" : "foo.bar",
- "vm.swappiness" : "60"
- },
- "seccomp" : {
- "defaultAction" : "SCMP_ACT_ALLOW",
- "architectures" : [
- "SCMP_ARCH_ARM",
- "SCMP_ARCH_X86_64"
- ],
- "syscalls" : [
- {
- "names" : [
- "lchown",
- "chmod"
- ],
- "action" : "SCMP_ACT_ERRNO",
- "args" : [
- {
- "index" : 0,
- "value" : 1,
- "op" : "SCMP_CMP_NE"
- },
- {
- "index" : 1,
- "value" : 2,
- "valueTwo" : 3,
- "op" : "SCMP_CMP_MASKED_EQ"
- }
- ]
- }
- ]
- },
- "rootfsPropagation" : "shared",
- "maskedPaths" : [
- "/proc/kcore",
- "/root/nonexistent"
- ],
- "readonlyPaths" : [
- "/proc/sys",
- "/opt/readonly"
- ]
- },
- "hooks" : {
- "prestart" : [
- {
- "path" : "/bin/sh",
- "args" : [
- "-xec",
- "echo \$PRESTART_FOO >/prestart"
- ],
- "env" : [
- "PRESTART_FOO=prestart_bar",
- "ALSO_FOO=also_bar"
- ],
- "timeout" : 666
- },
- {
- "path" : "/bin/touch",
- "args" : [
- "/tmp/also-prestart"
- ]
- }
- ],
- "poststart" : [
- {
- "path" : "/bin/sh",
- "args" : [
- "touch",
- "/poststart"
- ]
- }
- ],
- "poststop" : [
- {
- "path" : "/bin/sh",
- "args" : [
- "touch",
- "/poststop"
- ]
- }
- ]
- },
- "annotations" : {
- "hello.world" : "1",
- "foo" : "bar"
- }
-}
-EOF
-# Create a simple "entrypoint" script that validates that the container
-# is created correctly according to the OCI config
-cat >"$OCI/rootfs/entrypoint.sh" <<EOF
-#!/usr/bin/bash -e
-
-# Mounts
-mountpoint /root
-mountpoint /var
-test -e /var/hello
-
-# Process
-[[ "\$PWD" == /root ]]
-[[ "\$FOO" == bar ]]
-
-# Process - rlimits
-[[ "\$(ulimit -S -n)" -eq 1024 ]]
-[[ "\$(ulimit -H -n)" -eq 1024 ]]
-[[ "\$(ulimit -S -r)" -eq 5 ]]
-[[ "\$(ulimit -H -r)" -eq 10 ]]
-[[ "\$(hostname)" == my-oci-container ]]
-
-# Linux - devices
-test -c /dev/zero
-test -b "$DEV"
-[[ "\$(stat -c '%t:%T' "$DEV")" == 4:2 ]]
-
-# Linux - maskedPaths
-test -e /proc/kcore
-cat /proc/kcore && exit 1
-test ! -e /root/nonexistent
-
-# Linux - readonlyPaths
-touch /opt/readonly/foo && exit 1
-
-exit 0
-EOF
-timeout 30 systemd-nspawn --oci-bundle="$OCI"
-
-# Test a couple of invalid configs
-INVALID_SNIPPETS=(
- # Invalid object
- '"foo" : { }'
- '"process" : { "foo" : [ ] }'
- # Non-absolute mount
- '"mounts" : [ { "destination" : "foo", "type" : "tmpfs", "source" : "tmpfs" } ]'
- # Invalid rlimit
- '"process" : { "rlimits" : [ { "type" : "RLIMIT_FOO", "soft" : 0, "hard" : 0 } ] }'
- # rlimit without RLIMIT_ prefix
- '"process" : { "rlimits" : [ { "type" : "CORE", "soft" : 0, "hard" : 0 } ] }'
- # Invalid env assignment
- '"process" : { "env" : [ "foo" ] }'
- '"process" : { "env" : [ "foo=bar", 1 ] }'
- # Invalid process args
- '"process" : { "args" : [ ] }'
- '"process" : { "args" : [ "" ] }'
- '"process" : { "args" : [ "foo", 1 ] }'
- # Invalid capabilities
- '"process" : { "capabilities" : { "bounding" : [ 1 ] } }'
- '"process" : { "capabilities" : { "bounding" : [ "FOO_BAR" ] } }'
- # Unsupported option (without JSON_PERMISSIVE)
- '"linux" : { "resources" : { "cpu" : { "realtimeRuntime" : 1 } } }'
- # Invalid namespace
- '"linux" : { "namespaces" : [ { "type" : "foo" } ] }'
- # Namespace path for a non-network namespace
- '"linux" : { "namespaces" : [ { "type" : "user", "path" : "/foo/bar" } ] }'
- # Duplicate namespace
- '"linux" : { "namespaces" : [ { "type" : "ipc" }, { "type" : "ipc" } ] }'
- # Invalid device type
- '"linux" : { "devices" : [ { "type" : "foo", "path" : "/dev/foo" } ] }'
- # Invalid cgroups path
- '"linux" : { "cgroupsPath" : "/foo/bar/baz" }'
- '"linux" : { "cgroupsPath" : "foo/bar/baz" }'
- # Invalid sysctl assignments
- '"linux" : { "sysctl" : { "vm.swappiness" : 60 } }'
- '"linux" : { "sysctl" : { "foo..bar" : "baz" } }'
- # Invalid seccomp assignments
- '"linux" : { "seccomp" : { } }'
- '"linux" : { "seccomp" : { "defaultAction" : 1 } }'
- '"linux" : { "seccomp" : { "defaultAction" : "foo" } }'
- '"linux" : { "seccomp" : { "defaultAction" : "SCMP_ACT_ALLOW", "syscalls" : [ { "action" : "SCMP_ACT_ERRNO", "names" : [ ] } ] } }'
- # Invalid masked paths
- '"linux" : { "maskedPaths" : [ "/foo", 1 ] }'
- '"linux" : { "maskedPaths" : [ "/foo", "bar" ] }'
- # Invalid read-only paths
- '"linux" : { "readonlyPaths" : [ "/foo", 1 ] }'
- '"linux" : { "readonlyPaths" : [ "/foo", "bar" ] }'
- # Invalid hooks
- '"hooks" : { "prestart" : [ { "path" : "/bin/sh", "timeout" : 0 } ] }'
- # Invalid annotations
- '"annotations" : { "" : "bar" }'
- '"annotations" : { "foo" : 1 }'
-)
-
-for snippet in "${INVALID_SNIPPETS[@]}"; do
- : "Snippet: $snippet"
- cat >"$OCI/config.json" <<EOF
-{
- "ociVersion" : "1.0.0",
- "root" : {
- "path" : "rootfs"
- },
- $snippet
-}
-EOF
- (! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
-done
-
-# Invalid OCI bundle version
-cat >"$OCI/config.json" <<EOF
-{
- "ociVersion" : "6.6.6",
- "root" : {
- "path" : "rootfs"
- }
-}
-EOF
-(! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-#
-# Notes on coverage: when collecting coverage we need the $BUILD_DIR present
-# and writable in the container as well. To do this in the least intrusive way,
-# two things are going on in the background (only when built with -Db_coverage=true):
-# 1) the systemd-nspawn@.service is copied to /etc/systemd/system/ with
-# --bind=$BUILD_DIR appended to the ExecStart= line
-# 2) each create_dummy_container() call also creates an .nspawn file in /run/systemd/nspawn/
-# with the last fragment from the path used as a name
-#
-# The first change is quite self-contained and applies only to containers run
-# with machinectl. The second one might cause some unexpected side-effects, namely:
-# - nspawn config (setting) files don't support dropins, so tests that test
-# the config files might need some tweaking (as seen below with
-# the $COVERAGE_BUILD_DIR shenanigans) since they overwrite the .nspawn file
-# - also a note - if /etc/systemd/nspawn/cont-name.nspawn exists, it takes
-# precedence and /run/systemd/nspawn/cont-name.nspawn won't be read even
-# if it exists
-# - also a note 2 - --bind= overrides any Bind= from a config file
-# - in some cases we don't create a test container using create_dummy_container(),
-# so in that case an explicit call to coverage_create_nspawn_dropin() is needed
-#
-# However, even after jumping through all these hooks, there still might (and is)
-# some "incorrectly" missing coverage, especially in the window between spawning
-# the inner child process and bind-mounting the coverage $BUILD_DIR
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-
-export SYSTEMD_LOG_LEVEL=debug
-export SYSTEMD_LOG_TARGET=journal
-
-at_exit() {
- set +e
-
- mountpoint -q /var/lib/machines && umount --recursive /var/lib/machines
- rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-# check cgroup-v2
-IS_CGROUPSV2_SUPPORTED=no
-mkdir -p /tmp/cgroup2
-if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
- IS_CGROUPSV2_SUPPORTED=yes
- umount /tmp/cgroup2
-fi
-rmdir /tmp/cgroup2
-
-# check cgroup namespaces
-IS_CGNS_SUPPORTED=no
-if [[ -f /proc/1/ns/cgroup ]]; then
- IS_CGNS_SUPPORTED=yes
-fi
-
-IS_USERNS_SUPPORTED=no
-# On some systems (e.g. CentOS 7) the default limit for user namespaces
-# is set to 0, which causes the following unshare syscall to fail, even
-# with enabled user namespaces support. By setting this value explicitly
-# we can ensure the user namespaces support to be detected correctly.
-sysctl -w user.max_user_namespaces=10000
-if unshare -U bash -c :; then
- IS_USERNS_SUPPORTED=yes
-fi
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-testcase_sanity() {
- local template root image uuid tmpdir
-
- tmpdir="$(mktemp -d)"
- template="$(mktemp -d /tmp/nspawn-template.XXX)"
- create_dummy_container "$template"
- # Create a simple image from the just created container template
- image="$(mktemp /var/lib/machines/testsuite-13.image-XXX.img)"
- dd if=/dev/zero of="$image" bs=1M count=256
- mkfs.ext4 "$image"
- mkdir -p /mnt
- mount -o loop "$image" /mnt
- cp -r "$template"/* /mnt/
- umount /mnt
-
- systemd-nspawn --help --no-pager
- systemd-nspawn --version
-
- # --template=
- root="$(mktemp -u -d /var/lib/machines/testsuite-13.sanity.XXX)"
- coverage_create_nspawn_dropin "$root"
- (! systemd-nspawn --directory="$root" bash -xec 'echo hello')
- # Initialize $root from $template (the $root directory must not exist, hence
- # the `mktemp -u` above)
- systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
- systemd-nspawn --directory="$root" bash -xec 'echo hello; touch /initialized'
- test -e "$root/initialized"
- # Check if the $root doesn't get re-initialized once it's not empty
- systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
- test -e "$root/initialized"
-
- systemd-nspawn --directory="$root" --ephemeral bash -xec 'touch /ephemeral'
- test ! -e "$root/ephemeral"
- (! systemd-nspawn --directory="$root" \
- --read-only \
- bash -xec 'touch /nope')
- test ! -e "$root/nope"
- systemd-nspawn --image="$image" bash -xec 'echo hello'
-
- # --volatile=
- touch "$root/usr/has-usr"
- # volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
- systemd-nspawn --directory="$root"\
- --volatile \
- bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
- test ! -e "$root/nope"
- test ! -e "$root/usr/read-only"
- systemd-nspawn --directory="$root"\
- --volatile=yes \
- bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
- test ! -e "$root/nope"
- test ! -e "$root/usr/read-only"
- # volatile=state: rootfs is read-only, /var/ is tmpfs
- systemd-nspawn --directory="$root" \
- --volatile=state \
- bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
- test ! -e "$root/read-only"
- test ! -e "$root/var/nope"
- # volatile=state: tmpfs overlay is mounted over rootfs
- systemd-nspawn --directory="$root" \
- --volatile=overlay \
- bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
- test ! -e "$root/nope"
- test ! -e "$root/var/also-nope"
- test ! -e "$root/usr/nope-too"
-
- # --machine=, --hostname=
- systemd-nspawn --directory="$root" \
- --machine="foo-bar.baz" \
- bash -xec '[[ $(hostname) == foo-bar.baz ]]'
- systemd-nspawn --directory="$root" \
- --hostname="hello.world.tld" \
- bash -xec '[[ $(hostname) == hello.world.tld ]]'
- systemd-nspawn --directory="$root" \
- --machine="foo-bar.baz" \
- --hostname="hello.world.tld" \
- bash -xec '[[ $(hostname) == hello.world.tld ]]'
-
- # --uuid=
- rm -f "$root/etc/machine-id"
- uuid="deadbeef-dead-dead-beef-000000000000"
- systemd-nspawn --directory="$root" \
- --uuid="$uuid" \
- bash -xec "[[ \$container_uuid == $uuid ]]"
-
- # --as-pid2
- systemd-nspawn --directory="$root" bash -xec '[[ $$ -eq 1 ]]'
- systemd-nspawn --directory="$root" --as-pid2 bash -xec '[[ $$ -eq 2 ]]'
-
- # --user=
- # "Fake" getent passwd's bare minimum, so we don't have to pull it in
- # with all the DSO shenanigans
- cat >"$root/bin/getent" <<\EOF
-#!/bin/bash
-
-if [[ $# -eq 0 ]]; then
- :
-elif [[ $1 == passwd ]]; then
- echo "testuser:x:1000:1000:testuser:/:/bin/sh"
-elif [[ $1 == initgroups ]]; then
- echo "testuser"
-fi
-EOF
- chmod +x "$root/bin/getent"
- systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]'
- systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]'
-
- # --settings= + .nspawn files
- mkdir -p /run/systemd/nspawn/
- uuid="deadbeef-dead-dead-beef-000000000000"
- echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn
- systemd-nspawn --directory="$root" \
- --machine=foo-bar \
- --settings=yes \
- bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
- systemd-nspawn --directory="$root" \
- --machine=foo-bar \
- --uuid="$uuid" \
- --settings=yes \
- bash -xec "[[ \$container_uuid == $uuid ]]"
- systemd-nspawn --directory="$root" \
- --machine=foo-bar \
- --uuid="$uuid" \
- --settings=override \
- bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
- systemd-nspawn --directory="$root" \
- --machine=foo-bar \
- --uuid="$uuid" \
- --settings=trusted \
- bash -xec "[[ \$container_uuid == $uuid ]]"
-
- # Mounts
- mkdir "$tmpdir"/{1,2,3}
- touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three"
- touch "$tmpdir/foo"
- # --bind=
- systemd-nspawn --directory="$root" \
- ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
- --bind="$tmpdir:/foo" \
- --bind="$tmpdir:/also-foo:noidmap,norbind" \
- bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar'
- test -e "$tmpdir/bar"
- # --bind-ro=
- systemd-nspawn --directory="$root" \
- --bind-ro="$tmpdir:/foo" \
- --bind-ro="$tmpdir:/bar:noidmap,norbind" \
- bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true'
- # --inaccessible=
- systemd-nspawn --directory="$root" \
- --inaccessible=/var \
- bash -xec 'touch /var/foo && exit 1; true'
- # --tmpfs=
- systemd-nspawn --directory="$root" \
- --tmpfs=/var:rw,nosuid,noexec \
- bash -xec 'touch /var/nope'
- test ! -e "$root/var/nope"
- # --overlay=
- systemd-nspawn --directory="$root" \
- --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
- bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo'
- test -e "$tmpdir/3/foo"
- # --overlay-ro=
- systemd-nspawn --directory="$root" \
- --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
- bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true'
- test ! -e "$tmpdir/3/nope"
- rm -fr "$tmpdir"
-
- # --port (sanity only)
- systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true
- systemd-nspawn --network-veth --directory="$root" --port=80:8080 true
- systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true
- systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true
- systemd-nspawn --network-veth --directory="$root" --port=udp:80 true
- systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true
- (! systemd-nspawn --network-veth --directory="$root" --port= true)
- (! systemd-nspawn --network-veth --directory="$root" --port=-1 true)
- (! systemd-nspawn --network-veth --directory="$root" --port=: true)
- (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true)
- (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true)
- (! systemd-nspawn --network-veth --directory="$root" --port=8080: true)
- # Exercise adding/removing ports from an interface
- systemd-nspawn --directory="$root" \
- --network-veth \
- --port=6667 \
- --port=80:8080 \
- --port=udp:53 \
- --port=tcp:22:2222 \
- bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24'
-
- # --load-credential=, --set-credential=
- echo "foo bar" >/tmp/cred.path
- systemd-nspawn --directory="$root" \
- --load-credential=cred.path:/tmp/cred.path \
- --set-credential="cred.set:hello world" \
- bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
- # Combine with --user to ensure creds are still readable
- systemd-nspawn --directory="$root" \
- --user=testuser \
- --no-new-privileges=yes \
- --load-credential=cred.path:/tmp/cred.path \
- --set-credential="cred.set:hello world" \
- bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
- rm -f /tmp/cred.path
-
- # Assorted tests
- systemd-nspawn --directory="$root" --suppress-sync=yes bash -xec 'echo hello'
- systemd-nspawn --capability=help
- systemd-nspawn --resolv-conf=help
- systemd-nspawn --timezone=help
-
- # Handling of invalid arguments
- opts=(
- bind
- bind-ro
- bind-user
- chdir
- console
- inaccessible
- kill-signal
- link-journal
- load-credential
- network-{interface,macvlan,ipvlan,veth-extra,bridge,zone}
- no-new-privileges
- oom-score-adjust
- overlay
- overlay-ro
- personality
- pivot-root
- port
- private-users
- private-users-ownership
- register
- resolv-conf
- rlimit
- root-hash
- root-hash-sig
- set-credential
- settings
- suppress-sync
- timezone
- tmpfs
- uuid
- )
- for opt in "${opts[@]}"; do
- (! systemd-nspawn "--$opt")
- [[ "$opt" == network-zone ]] && continue
- (! systemd-nspawn "--$opt=''")
- (! systemd-nspawn "--$opt=%\$š")
- done
- (! systemd-nspawn --volatile="")
- (! systemd-nspawn --volatile=-1)
- (! systemd-nspawn --rlimit==)
-}
-
-nspawn_settings_cleanup() {
- for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
- ip link del "$dev" || :
- done
-
- return 0
-}
-
-testcase_nspawn_settings() {
- local root container dev private_users wlan_names
-
- mkdir -p /run/systemd/nspawn
- root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)"
- container="$(basename "$root")"
- create_dummy_container "$root"
- rm -f "/etc/systemd/nspawn/$container.nspawn"
- mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
-
- # add virtual wlan interfaces
- if modprobe mac80211_hwsim radios=2; then
- wlan_names="wlan0 wlan1:wl-renamed1"
- fi
-
- for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
- ip link add "$dev" type dummy
- done
- udevadm settle
- ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
- ip link
- trap nspawn_settings_cleanup RETURN
-
- # Let's start with one huge config to test as much as we can at once
- cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
-[Exec]
-Boot=no
-Ephemeral=no
-ProcessTwo=no
-Parameters=bash /entrypoint.sh "foo bar" 'bar baz'
-Environment=FOO=bar
-Environment=BAZ="hello world"
-User=root
-WorkingDirectory=/tmp
-Capability=CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN
-DropCapability=CAP_AUDIT_CONTROL CAP_AUDIT_WRITE
-AmbientCapability=CAP_BPF CAP_CHOWN
-NoNewPrivileges=no
-MachineID=f28f129b51874b1280a89421ec4b4ad4
-PrivateUsers=no
-NotifyReady=no
-SystemCallFilter=@basic-io @chown
-SystemCallFilter=~ @clock
-LimitNOFILE=1024:2048
-LimitRTPRIO=8:16
-OOMScoreAdjust=32
-CPUAffinity=0,0-5,1-5
-Hostname=nspawn-settings
-ResolvConf=copy-host
-Timezone=delete
-LinkJournal=no
-SuppressSync=no
-
-[Files]
-ReadOnly=no
-Volatile=no
-TemporaryFileSystem=/tmp
-TemporaryFileSystem=/opt/tmp
-Inaccessible=/opt/inaccessible
-Inaccessible=/opt/also-inaccessible
-PrivateUsersOwnership=auto
-Overlay=+/var::/var
-${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
-
-[Network]
-Private=yes
-VirtualEthernet=yes
-VirtualEthernetExtra=my-fancy-veth1
-VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
-Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names:-}
-MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
-IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
-Zone=sd-zone0
-Port=80
-Port=81:8181
-Port=tcp:60
-Port=udp:60:61
-EOF
- cat >"$root/entrypoint.sh" <<\EOF
-#!/bin/bash
-set -ex
-
-env
-
-[[ "$1" == "foo bar" ]]
-[[ "$2" == "bar baz" ]]
-
-[[ "$USER" == root ]]
-[[ "$FOO" == bar ]]
-[[ "$BAZ" == "hello world" ]]
-[[ "$PWD" == /tmp ]]
-[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]]
-[[ "$(ulimit -S -n)" -eq 1024 ]]
-[[ "$(ulimit -H -n)" -eq 2048 ]]
-[[ "$(ulimit -S -r)" -eq 8 ]]
-[[ "$(ulimit -H -r)" -eq 16 ]]
-[[ "$(</proc/self/oom_score_adj)" -eq 32 ]]
-[[ "$(hostname)" == nspawn-settings ]]
-[[ -e /etc/resolv.conf ]]
-[[ ! -e /etc/localtime ]]
-
-mountpoint /tmp
-touch /tmp/foo
-mountpoint /opt/tmp
-touch /opt/tmp/foo
-touch /opt/inaccessible/foo && exit 1
-touch /opt/also-inaccessible/foo && exit 1
-mountpoint /var
-
-ip link
-ip link | grep host-only && exit 1
-ip link | grep host0@
-ip link | grep my-fancy-veth1@
-ip link | grep my-fancy-veth2@
-ip link | grep sd-shared1
-ip link | grep sd-renamed2
-ip link | grep sd-shared3
-ip link | grep sd-altname3
-ip link | grep sd-altname-tooooooooooooo-long
-ip link | grep mv-sd-macvlan1@
-ip link | grep my-macvlan2@
-ip link | grep iv-sd-ipvlan1@
-ip link | grep my-ipvlan2@
-EOF
- if [[ -n "${wlan_names:-}" ]]; then
- cat >>"$root/entrypoint.sh" <<\EOF
-ip link | grep wlan0
-ip link | grep wl-renamed1
-EOF
- fi
-
- timeout 30 systemd-nspawn --directory="$root"
-
- # And now for stuff that needs to run separately
- #
- # Note on the condition below: since our container tree is owned by root,
- # both "yes" and "identity" private users settings will behave the same
- # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already
- # exists there, so skip setting it in such case
- for private_users in "131072:65536" yes identity pick; do
- cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
-[Exec]
-Hostname=private-users
-PrivateUsers=$private_users
-
-[Files]
-PrivateUsersOwnership=auto
-BindUser=
-$([[ "$private_users" =~ (yes|identity) ]] || echo "BindUser=testuser")
-${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
-EOF
- cat "/run/systemd/nspawn/$container.nspawn"
- chown -R root:root "$root"
- systemd-nspawn --directory="$root" bash -xec '[[ "$(hostname)" == private-users ]]'
- done
-
- rm -fr "$root" "/run/systemd/nspawn/$container.nspawn"
-}
-
-bind_user_cleanup() {
- userdel --force --remove nspawn-bind-user-1
- userdel --force --remove nspawn-bind-user-2
-}
-
-testcase_bind_user() {
- local root
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX)"
- create_dummy_container "$root"
- useradd --create-home --user-group nspawn-bind-user-1
- useradd --create-home --user-group nspawn-bind-user-2
- trap bind_user_cleanup RETURN
- touch /home/nspawn-bind-user-1/foo
- touch /home/nspawn-bind-user-2/bar
- # Add a couple of POSIX ACLs to test the patch-uid stuff
- mkdir -p "$root/opt"
- setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' "$root/opt"
- setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' "$root/opt"
-
- systemd-nspawn --directory="$root" \
- --private-users=pick \
- --bind-user=nspawn-bind-user-1 \
- bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo'
-
- systemd-nspawn --directory="$root" \
- --private-users=pick \
- --private-users-ownership=chown \
- --bind-user=nspawn-bind-user-1 \
- --bind-user=nspawn-bind-user-2 \
- bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar'
- chown -R root:root "$root"
-
- # User/group name collision
- echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >"$root/etc/passwd"
- (! systemd-nspawn --directory="$root" \
- --private-users=pick \
- --bind-user=nspawn-bind-user-1 \
- --bind-user=nspawn-bind-user-2 \
- true)
- rm -f "$root/etc/passwd"
-
- echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group"
- (! systemd-nspawn --directory="$root" \
- --private-users=pick \
- --bind-user=nspawn-bind-user-1 \
- --bind-user=nspawn-bind-user-2 \
- true)
- rm -f "$root/etc/group"
-
- rm -fr "$root"
-}
-
-testcase_bind_tmp_path() {
- # https://github.com/systemd/systemd/issues/4789
- local root
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.bind-tmp-path.XXX)"
- create_dummy_container "$root"
- : >/tmp/bind
- systemd-nspawn --register=no \
- --directory="$root" \
- --bind=/tmp/bind \
- bash -c 'test -e /tmp/bind'
-
- rm -fr "$root" /tmp/bind
-}
-
-testcase_norbind() {
- # https://github.com/systemd/systemd/issues/13170
- local root
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)"
- mkdir -p /tmp/binddir/subdir
- echo -n "outer" >/tmp/binddir/subdir/file
- mount -t tmpfs tmpfs /tmp/binddir/subdir
- echo -n "inner" >/tmp/binddir/subdir/file
- create_dummy_container "$root"
-
- systemd-nspawn --register=no \
- --directory="$root" \
- --bind=/tmp/binddir:/mnt:norbind \
- bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi'
-
- umount /tmp/binddir/subdir
- rm -fr "$root" /tmp/binddir/
-}
-
-rootidmap_cleanup() {
- local dir="${1:?}"
-
- mountpoint -q "$dir/bind" && umount "$dir/bind"
- rm -fr "$dir"
-}
-
-testcase_rootidmap() {
- local root cmd permissions
- local owner=1000
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)"
- # Create ext4 image, as ext4 supports idmapped-mounts.
- mkdir -p /tmp/rootidmap/bind
- dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048
- mkfs.ext4 /tmp/rootidmap/ext4.img
- mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind
- trap "rootidmap_cleanup /tmp/rootidmap/" RETURN
-
- touch /tmp/rootidmap/bind/file
- chown -R "$owner:$owner" /tmp/rootidmap/bind
-
- create_dummy_container "$root"
- cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file'
- if ! SYSTEMD_LOG_TARGET=console \
- systemd-nspawn --register=no \
- --directory="$root" \
- --bind=/tmp/rootidmap/bind:/mnt:rootidmap \
- bash -c "$cmd" |& tee nspawn.out; then
- if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
- echo "idmapped mounts are not supported, skipping the test..."
- return 0
- fi
-
- return 1
- fi
-
- permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file)
- if [[ $permissions != "$owner:$owner" ]]; then
- echo "*** wrong permissions: $permissions"
- [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
- fi
-}
-
-owneridmap_cleanup() {
- local dir="${1:?}"
-
- mountpoint -q "$dir/bind" && umount "$dir/bind"
- rm -fr "$dir"
-}
-
-testcase_owneridmap() {
- local root cmd permissions
- local owner=1000
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.owneridmap-path.XXX)"
- # Create ext4 image, as ext4 supports idmapped-mounts.
- mkdir -p /tmp/owneridmap/bind
- dd if=/dev/zero of=/tmp/owneridmap/ext4.img bs=4k count=2048
- mkfs.ext4 /tmp/owneridmap/ext4.img
- mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind
- trap "owneridmap_cleanup /tmp/owneridmap/" RETURN
-
- touch /tmp/owneridmap/bind/file
- chown -R "$owner:$owner" /tmp/owneridmap/bind
-
- # Allow users to read and execute / in order to execute binaries
- chmod o+rx "$root"
-
- create_dummy_container "$root"
-
- # --user=
- # "Fake" getent passwd's bare minimum, so we don't have to pull it in
- # with all the DSO shenanigans
- cat >"$root/bin/getent" <<\EOF
-#!/bin/bash
-
-if [[ $# -eq 0 ]]; then
- :
-elif [[ $1 == passwd ]]; then
- echo "testuser:x:1010:1010:testuser:/:/bin/sh"
-elif [[ $1 == initgroups ]]; then
- echo "testuser"
-fi
-EOF
- chmod +x "$root/bin/getent"
-
- mkdir -p "$root/home/testuser"
- chown 1010:1010 "$root/home/testuser"
-
- cmd='PERMISSIONS=$(stat -c "%u:%g" /home/testuser/file); if [[ $PERMISSIONS != "1010:1010" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /home/testuser/other_file'
- if ! SYSTEMD_LOG_TARGET=console \
- systemd-nspawn --register=no \
- --directory="$root" \
- -U \
- --user=testuser \
- --bind=/tmp/owneridmap/bind:/home/testuser:owneridmap \
- ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
- /usr/bin/bash -c "$cmd" |& tee nspawn.out; then
- if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
- echo "idmapped mounts are not supported, skipping the test..."
- return 0
- fi
-
- return 1
- fi
-
- permissions=$(stat -c "%u:%g" /tmp/owneridmap/bind/other_file)
- if [[ $permissions != "$owner:$owner" ]]; then
- echo "*** wrong permissions: $permissions"
- [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
- fi
-}
-
-testcase_notification_socket() {
- # https://github.com/systemd/systemd/issues/4944
- local root
- local cmd='echo a | nc -U -u -w 1 /run/host/notify'
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.check_notification_socket.XXX)"
- create_dummy_container "$root"
-
- systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd"
- systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd"
-
- rm -fr "$root"
-}
-
-testcase_os_release() {
- local root entrypoint os_release_source
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)"
- create_dummy_container "$root"
- entrypoint="$root/entrypoint.sh"
- cat >"$entrypoint" <<\EOF
-#!/usr/bin/bash -ex
-
-. /tmp/os-release
-[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1
-[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1
-[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1
-[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1
-
-cd /tmp
-(cd /run/host && md5sum os-release) | md5sum -c
-EOF
- chmod +x "$entrypoint"
-
- os_release_source="/etc/os-release"
- if [[ ! -r "$os_release_source" ]]; then
- os_release_source="/usr/lib/os-release"
- elif [[ -L "$os_release_source" ]]; then
- # Ensure that /etc always wins if available
- cp --remove-destination -fv /usr/lib/os-release /etc/os-release
- echo MARKER=1 >>/etc/os-release
- fi
-
- systemd-nspawn --register=no \
- --directory="$root" \
- --bind="$os_release_source:/tmp/os-release" \
- "${entrypoint##"$root"}"
-
- if grep -q MARKER /etc/os-release; then
- ln -svrf /usr/lib/os-release /etc/os-release
- fi
-
- rm -fr "$root"
-}
-
-testcase_machinectl_bind() {
- local service_path service_name root container_name ec
- local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;'
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.machinectl-bind.XXX)"
- create_dummy_container "$root"
- container_name="$(basename "$root")"
-
- service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)"
- service_name="${service_path##*/}"
- cat >"$service_path" <<EOF
-[Service]
-Type=notify
-ExecStart=systemd-nspawn --directory="$root" --notify-ready=no /usr/bin/bash -xec "$cmd"
-EOF
-
- systemctl daemon-reload
- systemctl start "$service_name"
- touch /tmp/marker
- machinectl bind --mkdir "$container_name" /tmp/marker
-
- timeout 10 bash -c "while [[ '\$(systemctl show -P SubState $service_name)' == running ]]; do sleep .2; done"
- ec="$(systemctl show -P ExecMainStatus "$service_name")"
- systemctl stop "$service_name"
-
- rm -fr "$root" "$service_path"
-
- return "$ec"
-}
-
-testcase_selinux() {
- # Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976
- if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then
- echo >&2 "SELinux is not enabled, skipping SELinux-related tests"
- return 0
- fi
-
- local root
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)"
- create_dummy_container "$root"
- chcon -R -t container_t "$root"
-
- systemd-nspawn --register=no \
- --boot \
- --directory="$root" \
- --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \
- --selinux-context=system_u:system_r:container_t:s0:c0,c1
-
- rm -fr "$root"
-}
-
-testcase_ephemeral_config() {
- # https://github.com/systemd/systemd/issues/13297
- local root container_name
-
- root="$(mktemp -d /var/lib/machines/testsuite-13.ephemeral-config.XXX)"
- create_dummy_container "$root"
- container_name="$(basename "$root")"
-
- mkdir -p /run/systemd/nspawn/
- rm -f "/etc/systemd/nspawn/$container_name.nspawn"
- cat >"/run/systemd/nspawn/$container_name.nspawn" <<EOF
-[Files]
-${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
-BindReadOnly=/tmp/ephemeral-config
-EOF
- touch /tmp/ephemeral-config
-
- systemd-nspawn --register=no \
- --directory="$root" \
- --ephemeral \
- bash -x -c "test -f /tmp/ephemeral-config"
-
- systemd-nspawn --register=no \
- --directory="$root" \
- --ephemeral \
- --machine=foobar \
- bash -x -c "! test -f /tmp/ephemeral-config"
-
- rm -fr "$root" "/run/systemd/nspawn/$container_name.nspawn"
-}
-
-matrix_run_one() {
- local cgroupsv2="${1:?}"
- local use_cgns="${2:?}"
- local api_vfs_writable="${3:?}"
- local root
-
- if [[ "$cgroupsv2" == "yes" && "$IS_CGROUPSV2_SUPPORTED" == "no" ]]; then
- echo >&2 "Unified cgroup hierarchy is not supported, skipping..."
- return 0
- fi
-
- if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]]; then
- echo >&2 "CGroup namespaces are not supported, skipping..."
- return 0
- fi
-
- root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")"
- create_dummy_container "$root"
-
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --boot
-
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --private-network \
- --boot
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --private-users=pick \
- --boot; then
- [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1
- else
- [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --private-network \
- --private-users=pick \
- --boot; then
- [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1
- else
- [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1
- fi
-
- local netns_opt="--network-namespace-path=/proc/self/ns/net"
- local net_opt
- local net_opts=(
- "--network-bridge=lo"
- "--network-interface=lo"
- "--network-ipvlan=lo"
- "--network-macvlan=lo"
- "--network-veth"
- "--network-veth-extra=lo"
- "--network-zone=zone"
- )
-
- # --network-namespace-path and network-related options cannot be used together
- for net_opt in "${net_opts[@]}"; do
- echo "$netns_opt in combination with $net_opt should fail"
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --boot \
- "$netns_opt" \
- "$net_opt"; then
- echo >&2 "unexpected pass"
- return 1
- fi
- done
-
- # allow combination of --network-namespace-path and --private-network
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --boot \
- --private-network \
- "$netns_opt"
-
- # test --network-namespace-path works with a network namespace created by "ip netns"
- ip netns add nspawn_test
- netns_opt="--network-namespace-path=/run/netns/nspawn_test"
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
- systemd-nspawn --register=no \
- --directory="$root" \
- --network-namespace-path=/run/netns/nspawn_test \
- ip a | grep -v -E '^1: lo.*UP'
- ip netns del nspawn_test
-
- rm -fr "$root"
-
- return 0
-}
-
-testcase_check_os_release() {
- # https://github.com/systemd/systemd/issues/29185
- local base common_opts root
-
- base="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release_base.XXX)"
- root="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release.XXX)"
- create_dummy_container "$base"
- cp -d "$base"/{bin,sbin,lib,lib64} "$root/"
- common_opts=(
- --boot
- --register=no
- --directory="$root"
- --bind-ro="$base/usr:/usr"
- )
-
- # Empty /etc/ & /usr/
- (! systemd-nspawn "${common_opts[@]}")
- (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
- (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}")
- SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
-
- # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink
- ln -svrf "$root/etc/os-release" "$root/usr/os-release"
- (! systemd-nspawn "${common_opts[@]}")
- (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
- SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
-
- rm -fr "$root" "$base"
-}
-
-run_testcases
-
-for api_vfs_writable in yes no network; do
- matrix_run_one no no $api_vfs_writable
- matrix_run_one yes no $api_vfs_writable
- matrix_run_one no yes $api_vfs_writable
- matrix_run_one yes yes $api_vfs_writable
-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
- set +e
-
- machinectl kill --signal=KILL nss-mymachines-{noip,singleip,manyips}
- mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
- rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-# Create a bunch of containers that:
-# 1) Have no IP addresses assigned
-create_dummy_container /var/lib/machines/nss-mymachines-noip
-cat >/var/lib/machines/nss-mymachines-noip/sbin/init <<\EOF
-#!/usr/bin/bash -ex
-
-ip addr show dev ve-noip
-touch /initialized
-sleep infinity &
-# Run the sleep command asynchronously, so bash is able to process signals
-while :; do
- wait || :
-done
-EOF
-# 2) Have one IP address assigned (IPv4 only)
-create_dummy_container /var/lib/machines/nss-mymachines-singleip
-cat >/var/lib/machines/nss-mymachines-singleip/sbin/init <<\EOF
-#!/usr/bin/bash -ex
-
-ip addr add 10.1.0.2/24 dev ve-singleip
-ip addr show dev ve-singleip
-touch /initialized
-sleep infinity &
-while :; do
- wait || :
-done
-EOF
-# 3) Have bunch of IP addresses assigned (both IPv4 and IPv6)
-create_dummy_container /var/lib/machines/nss-mymachines-manyips
-cat >/var/lib/machines/nss-mymachines-manyips/sbin/init <<\EOF
-#!/usr/bin/bash -ex
-
-ip addr add 10.2.0.2/24 dev ve-manyips
-for i in {100..120}; do
- ip addr add 10.2.0.$i/24 dev ve-manyips
-done
-ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad
-ip addr show dev ve-manyips
-touch /initialized
-sleep infinity
-while :; do
- wait || :
-done
-EOF
-# Create the respective .nspawn config files
-mkdir -p /run/systemd/nspawn
-for container in noip singleip manyips; do
- cat >"/run/systemd/nspawn/nss-mymachines-$container.nspawn" <<EOF
-[Exec]
-Boot=yes
-
-[Network]
-VirtualEthernetExtra=ve-$container
-EOF
-done
-
-# Start the containers and wait until all of them are initialized
-machinectl start nss-mymachines-{noip,singleip,manyips}
-for container in nss-mymachines-{noip,singleip,manyips}; do
- timeout 30 bash -xec "while [[ ! -e /var/lib/machines/$container/initialized ]]; do sleep .5; done"
-done
-
-# We need to configure the dummy interfaces on the "outside" as well for `getent {ahosts4,ahosts6}` to work
-# properly. This is caused by getaddrinfo() calling _check_pf() that iterates through all interfaces and
-# notes if any of them has an IPv4/IPv6 - this is then used together with AF_INET/AF_INET6 to determine if we
-# can ever return a valid answer, and if we configured the container interfaces only in the container, we
-# would have no valid IPv4/IPv6 on the "outside" (as we don't set up any other netdev) which would make
-# getaddrinfo() return EAI_NONAME without ever asking nss-mymachines.
-ip addr add 10.1.0.1/24 dev ve-singleip
-ip addr add 10.2.0.1/24 dev ve-manyips
-ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad
-
-getent hosts -s mymachines
-getent ahosts -s mymachines
-
-# And finally check if we can resolve the containers via nss-mymachines
-for database in hosts ahosts{,v4,v6}; do
- (! getent "$database" -s mymachines nss-mymachines-noip)
-done
-
-run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-singleip
-run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-singleip
-run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-singleip
-run_and_grep "^::ffff:10\.1\.0\.2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-singleip
-
-run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-manyips
-run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
-run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
-for i in {100..120}; do
- run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
- run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-manyips
-done
-run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-manyips
-(! run_and_grep "^fd00:" getent ahostsv4 -s mymachines nss-mymachines-manyips)
-(! run_and_grep "^10\.2:" getent ahostsv6 -s mymachines nss-mymachines-manyips)
-
-# Multiple machines at once
-run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
-
-for database in hosts ahosts ahostsv4 ahostsv6; do
- (! getent "$database" -s mymachines foo-bar-baz)
-done
-
-# getgrid(), getgrnam(), getpwuid(), and getpwnam() are explicitly handled by nss-mymachines, so probe them
-# as well
-(! getent group -s mymachines foo 11)
-(! getent passwd -s mymachines foo 11)
-
-machinectl stop nss-mymachines-{noip,singleip,manyips}
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-13-NSPAWN
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-FSTYPE="$(stat --file-system --format "%T" /)"
-
-if [[ "$FSTYPE" == "fuseblk" ]]; then
- echo "Root filesystem is virtiofs, skipping"
- exit 77
-fi
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-15-DROPIN
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-clear_unit() {
- local unit_name="${1:?}"
- local base suffix
-
- systemctl stop "$unit_name" 2>/dev/null || :
- rm -f /{etc,run,usr/lib}/systemd/system/"$unit_name"
- rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".d
- rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".{wants,requires}
- if [[ $unit_name == *@* ]]; then
- base="${unit_name%@*}"
- suffix="${unit_name##*.}"
- systemctl stop "$base@"*."$suffix" 2>/dev/null || :
- rm -f /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix"
- rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".d
- rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".{wants,requires}
- fi
-}
-
-clear_units() {
- for u in "$@"; do
- clear_unit "$u"
- done
- systemctl daemon-reload
-}
-
-create_service() {
- local service_name="${1:?}"
- clear_units "${service_name}".service
-
- cat >/etc/systemd/system/"$service_name".service <<EOF
-[Unit]
-Description=$service_name unit
-
-[Service]
-ExecStart=sleep 100000
-EOF
- mkdir -p /{etc,run,usr/lib}/systemd/system/"$service_name".service.{d,wants,requires}
-}
-
-create_services() {
- for u in "$@"; do
- create_service "$u"
- done
-}
-
-check_ok() {
- x="$(systemctl show --value -p "${2:?}" "${1:?}")"
- case "$x" in
- *${3:?}*) return 0 ;;
- *) return 1 ;;
- esac
-}
-
-check_ko() {
- ! check_ok "$@"
-}
-
-testcase_basic_dropins() {
- echo "Testing basic dropins..."
-
- echo "*** test a wants b wants c"
- create_services test15-a test15-b test15-c
- ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
- ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/
- check_ok test15-a Wants test15-b.service
- check_ok test15-b Wants test15-c.service
-
- echo "*** test a wants,requires b"
- create_services test15-a test15-b test15-c
- ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
- ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/
- check_ok test15-a Wants test15-b.service
- check_ok test15-a Requires test15-b.service
-
- echo "*** test a wants nonexistent"
- create_service test15-a
- ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/
- check_ok test15-a Wants nonexistent.service
- systemctl start test15-a
- systemctl stop test15-a
-
- echo "*** test a requires nonexistent"
- ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/
- systemctl daemon-reload
- check_ok test15-a Requires nonexistent.service
-
- # 'b' is already loaded when 'c' pulls it in via a dropin.
- echo "*** test a,c require b"
- create_services test15-a test15-b test15-c
- ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
- ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
- systemctl start test15-a
- check_ok test15-c Requires test15-b.service
- systemctl stop test15-a test15-b
-
- # 'b' is already loaded when 'c' pulls it in via an alias dropin.
- echo "*** test a wants alias"
- create_services test15-a test15-b test15-c
- ln -sf test15-c.service /etc/systemd/system/test15-c1.service
- ln -sf ../test15-c.service /etc/systemd/system/test15-a.service.wants/
- ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/
- systemctl start test15-a
- check_ok test15-a Wants test15-c.service
- check_ok test15-b Wants test15-c.service
- systemctl stop test15-a test15-c
-
- echo "*** test service.d/ top level drop-in"
- create_services test15-a test15-b
- check_ko test15-a ExecCondition "/bin/echo a"
- check_ko test15-b ExecCondition "/bin/echo b"
- mkdir -p /run/systemd/system/service.d
- cat >/run/systemd/system/service.d/override.conf <<EOF
-[Service]
-ExecCondition=/bin/echo %n
-EOF
- systemctl daemon-reload
- check_ok test15-a ExecCondition "/bin/echo test15-a"
- check_ok test15-b ExecCondition "/bin/echo test15-b"
- rm -rf /run/systemd/system/service.d
-
- clear_units test15-{a,b,c,c1}.service
-}
-
-testcase_linked_units() {
- echo "Testing linked units..."
- echo "*** test linked unit (same basename)"
-
- create_service test15-a
- mv /etc/systemd/system/test15-a.service /
- ln -s /test15-a.service /etc/systemd/system/
- ln -s test15-a.service /etc/systemd/system/test15-b.service
-
- check_ok test15-a Names test15-a.service
- check_ok test15-a Names test15-b.service
-
- echo "*** test linked unit (cross basename)"
-
- mv /test15-a.service /test15-a@.scope
- ln -fs /test15-a@.scope /etc/systemd/system/test15-a.service
- systemctl daemon-reload
-
- check_ok test15-a Names test15-a.service
- check_ok test15-a Names test15-b.service
- check_ko test15-a Names test15-a@ # test15-a@.scope is the symlink target.
- # Make sure it is completely ignored.
-
- rm /test15-a@.scope
- clear_units test15-{a,b}.service
-}
-
-testcase_template_alias() {
- echo "Testing instance alias..."
- echo "*** forward"
-
- create_service test15-a@
- ln -s test15-a@inst.service /etc/systemd/system/test15-b@inst.service # alias
-
- check_ok test15-a@inst Names test15-a@inst.service
- check_ok test15-a@inst Names test15-b@inst.service
-
- check_ok test15-a@other Names test15-a@other.service
- check_ko test15-a@other Names test15-b@other.service
-
- echo "*** reverse"
-
- systemctl daemon-reload
-
- check_ok test15-b@inst Names test15-a@inst.service
- check_ok test15-b@inst Names test15-b@inst.service
-
- check_ko test15-b@other Names test15-a@other.service
- check_ok test15-b@other Names test15-b@other.service
-
- clear_units test15-{a,b}@.service
-}
-
-testcase_hierarchical_service_dropins() {
- echo "Testing hierarchical service dropins..."
- echo "*** test service.d/ top level drop-in"
- create_services a-b-c
- check_ko a-b-c ExecCondition "echo service.d"
- check_ko a-b-c ExecCondition "echo a-.service.d"
- check_ko a-b-c ExecCondition "echo a-b-.service.d"
- check_ko a-b-c ExecCondition "echo a-b-c.service.d"
-
- for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
- mkdir -p "/run/systemd/system/$dropin"
- cat >"/run/systemd/system/$dropin/override.conf" <<EOF
-[Service]
-ExecCondition=echo $dropin
-EOF
- systemctl daemon-reload
- check_ok a-b-c ExecCondition "echo $dropin"
-
- # Check that we can start a transient service in presence of the drop-ins
- systemd-run -u a-b-c2.service -p Description='sleepy' sleep infinity
-
- # The transient setting replaces the default
- check_ok a-b-c2.service Description "sleepy"
-
- # The override takes precedence for ExecCondition
- # (except the last iteration when it only applies to the other service)
- if [ "$dropin" != "a-b-c.service.d" ]; then
- check_ok a-b-c2.service ExecCondition "echo $dropin"
- fi
-
- # Check that things are the same after a reload
- systemctl daemon-reload
- check_ok a-b-c2.service Description "sleepy"
- if [ "$dropin" != "a-b-c.service.d" ]; then
- check_ok a-b-c2.service ExecCondition "echo $dropin"
- fi
-
- systemctl stop a-b-c2.service
- done
- for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
- rm -rf "/run/systemd/system/$dropin"
- done
-
- clear_units a-b-c.service
-}
-
-testcase_hierarchical_slice_dropins() {
- echo "Testing hierarchical slice dropins..."
- echo "*** test slice.d/ top level drop-in"
- # Slice units don't even need a fragment, so we test the defaults here
- check_ok a-b-c.slice Description "Slice /a/b/c"
- check_ok a-b-c.slice MemoryMax "infinity"
-
- # Test drop-ins
- for dropin in slice.d a-.slice.d a-b-.slice.d a-b-c.slice.d; do
- mkdir -p "/run/systemd/system/$dropin"
- cat >"/run/systemd/system/$dropin/override.conf" <<EOF
-[Slice]
-MemoryMax=1000000000
-EOF
- systemctl daemon-reload
- check_ok a-b-c.slice MemoryMax "1000000000"
-
- busctl call \
- org.freedesktop.systemd1 \
- /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager \
- StartTransientUnit 'ssa(sv)a(sa(sv))' \
- 'a-b-c.slice' 'replace' \
- 2 \
- 'Description' s 'slice too' \
- 'MemoryMax' t 1000000002 \
- 0
-
- # The override takes precedence for MemoryMax
- check_ok a-b-c.slice MemoryMax "1000000000"
- # The transient setting replaces the default
- check_ok a-b-c.slice Description "slice too"
-
- # Check that things are the same after a reload
- systemctl daemon-reload
- check_ok a-b-c.slice MemoryMax "1000000000"
- check_ok a-b-c.slice Description "slice too"
-
- busctl call \
- org.freedesktop.systemd1 \
- /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager \
- StopUnit 'ss' \
- 'a-b-c.slice' 'replace'
-
- rm -f "/run/systemd/system/$dropin/override.conf"
- done
-
- # Test unit with a fragment
- cat >/run/systemd/system/a-b-c.slice <<EOF
-[Slice]
-MemoryMax=1000000001
-EOF
- systemctl daemon-reload
- check_ok a-b-c.slice MemoryMax "1000000001"
-
- clear_units a-b-c.slice
-}
-
-testcase_transient_service_dropins() {
- echo "Testing dropins for a transient service..."
- echo "*** test transient service drop-ins"
-
- mkdir -p /etc/systemd/system/service.d
- mkdir -p /etc/systemd/system/a-.service.d
- mkdir -p /etc/systemd/system/a-b-.service.d
- mkdir -p /etc/systemd/system/a-b-c.service.d
-
- echo -e '[Service]\nStandardInputText=aaa' >/etc/systemd/system/service.d/drop1.conf
- echo -e '[Service]\nStandardInputText=bbb' >/etc/systemd/system/a-.service.d/drop2.conf
- echo -e '[Service]\nStandardInputText=ccc' >/etc/systemd/system/a-b-.service.d/drop3.conf
- echo -e '[Service]\nStandardInputText=ddd' >/etc/systemd/system/a-b-c.service.d/drop4.conf
-
- # There's no fragment yet, so this fails
- systemctl cat a-b-c.service && exit 1
-
- # xxx → eHh4Cg==
- systemd-run -u a-b-c.service -p StandardInputData=eHh4Cg== sleep infinity
-
- data=$(systemctl show -P StandardInputData a-b-c.service)
- # xxx\naaa\n\bbb\nccc\nddd\n → eHh4…
- test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
-
- # Do a reload and check again
- systemctl daemon-reload
- data=$(systemctl show -P StandardInputData a-b-c.service)
- test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
-
- clear_units a-b-c.service
- rm /etc/systemd/system/service.d/drop1.conf \
- /etc/systemd/system/a-.service.d/drop2.conf \
- /etc/systemd/system/a-b-.service.d/drop3.conf
-}
-
-testcase_transient_slice_dropins() {
- echo "Testing dropins for a transient slice..."
- echo "*** test transient slice drop-ins"
-
- # FIXME: implement reloading of individual units.
- #
- # The settings here are loaded twice. For most settings it doesn't matter,
- # but Documentation is not deduplicated, so we current get repeated entried
- # which is a bug.
-
- mkdir -p /etc/systemd/system/slice.d
- mkdir -p /etc/systemd/system/a-.slice.d
- mkdir -p /etc/systemd/system/a-b-.slice.d
- mkdir -p /etc/systemd/system/a-b-c.slice.d
-
- echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf
- echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf
- echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf
- echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf
-
- # Invoke daemon-reload to make sure that the call below doesn't fail
- systemctl daemon-reload
-
- # No fragment is required, so this works
- systemctl cat a-b-c.slice
-
- busctl call \
- org.freedesktop.systemd1 \
- /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager \
- StartTransientUnit 'ssa(sv)a(sa(sv))' \
- 'a-b-c.slice' 'replace' \
- 1 \
- 'Documentation' as 1 'man:drop5' \
- 0
-
- data=$(systemctl show -P Documentation a-b-c.slice)
- test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
-
- # Do a reload and check again
- systemctl daemon-reload
- data=$(systemctl show -P Documentation a-b-c.slice)
- test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
-
- clear_units a-b-c.slice
- rm /etc/systemd/system/slice.d/drop1.conf \
- /etc/systemd/system/a-.slice.d/drop2.conf \
- /etc/systemd/system/a-b-.slice.d/drop3.conf
-}
-
-testcase_template_dropins() {
- echo "Testing template dropins..."
-
- create_services foo bar@ yup@
-
- # Declare some deps to check if the body was loaded
- cat >>/etc/systemd/system/bar@.service <<EOF
-[Unit]
-After=bar-template-after.device
-EOF
-
- cat >>/etc/systemd/system/yup@.service <<EOF
-[Unit]
-After=yup-template-after.device
-EOF
-
- ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service
- check_ok foo Wants bar@1.service
-
- echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***"
- ln -s bar@.service /etc/systemd/system/bar-alias@.service
- ln -s bar@1.service /etc/systemd/system/bar-alias@1.service
- ln -s yup@.service /etc/systemd/system/bar-alias@2.service
- ln -s yup@3.service /etc/systemd/system/bar-alias@3.service
-
- # create some dropin deps
- mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/
- mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/
- mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/
-
- ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/
- ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/
- ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/
- ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/
- ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/
-
- ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/
- ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/
- ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/
- ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/
- ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/
-
- ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/
- ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/
- ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/
- ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/
- ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/
-
- systemctl daemon-reload
-
- echo '*** bar@0 is aliased by bar-alias@0 ***'
- systemctl show -p Names,Requires bar@0
- systemctl show -p Names,Requires bar-alias@0
- check_ok bar@0 Names bar@0
- check_ok bar@0 Names bar-alias@0
-
- check_ok bar@0 After bar-template-after.device
-
- check_ok bar@0 Requires bar-0-requires.device
- check_ok bar@0 Requires bar-alias-0-requires.device
- check_ok bar@0 Requires bar-template-requires.device
- check_ok bar@0 Requires bar-alias-template-requires.device
- check_ko bar@0 Requires yup-template-requires.device
-
- check_ok bar-alias@0 After bar-template-after.device
-
- check_ok bar-alias@0 Requires bar-0-requires.device
- check_ok bar-alias@0 Requires bar-alias-0-requires.device
- check_ok bar-alias@0 Requires bar-template-requires.device
- check_ok bar-alias@0 Requires bar-alias-template-requires.device
- check_ko bar-alias@0 Requires yup-template-requires.device
- check_ko bar-alias@0 Requires yup-0-requires.device
-
- echo '*** bar@1 is aliased by bar-alias@1 ***'
- systemctl show -p Names,Requires bar@1
- systemctl show -p Names,Requires bar-alias@1
- check_ok bar@1 Names bar@1
- check_ok bar@1 Names bar-alias@1
-
- check_ok bar@1 After bar-template-after.device
-
- check_ok bar@1 Requires bar-1-requires.device
- check_ok bar@1 Requires bar-alias-1-requires.device
- check_ok bar@1 Requires bar-template-requires.device
- # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418
- check_ok bar@1 Requires bar-alias-template-requires.device
- check_ko bar@1 Requires yup-template-requires.device
- check_ko bar@1 Requires yup-1-requires.device
-
- check_ok bar-alias@1 After bar-template-after.device
-
- check_ok bar-alias@1 Requires bar-1-requires.device
- check_ok bar-alias@1 Requires bar-alias-1-requires.device
- check_ok bar-alias@1 Requires bar-template-requires.device
- check_ok bar-alias@1 Requires bar-alias-template-requires.device
- check_ko bar-alias@1 Requires yup-template-requires.device
- check_ko bar-alias@1 Requires yup-1-requires.device
-
- echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***'
- systemctl show -p Names,Requires bar@2
- systemctl show -p Names,Requires bar-alias@2
- check_ok bar@2 Names bar@2
- check_ko bar@2 Names bar-alias@2
-
- check_ok bar@2 After bar-template-after.device
-
- check_ok bar@2 Requires bar-2-requires.device
- check_ko bar@2 Requires bar-alias-2-requires.device
- check_ok bar@2 Requires bar-template-requires.device
- check_ko bar@2 Requires bar-alias-template-requires.device
- check_ko bar@2 Requires yup-template-requires.device
- check_ko bar@2 Requires yup-2-requires.device
-
- check_ko bar-alias@2 After bar-template-after.device
-
- check_ko bar-alias@2 Requires bar-2-requires.device
- check_ok bar-alias@2 Requires bar-alias-2-requires.device
- check_ko bar-alias@2 Requires bar-template-requires.device
- check_ok bar-alias@2 Requires bar-alias-template-requires.device
- check_ok bar-alias@2 Requires yup-template-requires.device
- check_ok bar-alias@2 Requires yup-2-requires.device
-
- echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***'
- systemctl show -p Names,Requires bar@3
- systemctl show -p Names,Requires bar-alias@3
- check_ok bar@3 Names bar@3
- check_ko bar@3 Names bar-alias@3
-
- check_ok bar@3 After bar-template-after.device
-
- check_ok bar@3 Requires bar-3-requires.device
- check_ko bar@3 Requires bar-alias-3-requires.device
- check_ok bar@3 Requires bar-template-requires.device
- check_ko bar@3 Requires bar-alias-template-requires.device
- check_ko bar@3 Requires yup-template-requires.device
- check_ko bar@3 Requires yup-3-requires.device
-
- check_ko bar-alias@3 After bar-template-after.device
-
- check_ko bar-alias@3 Requires bar-3-requires.device
- check_ok bar-alias@3 Requires bar-alias-3-requires.device
- check_ko bar-alias@3 Requires bar-template-requires.device
- check_ok bar-alias@3 Requires bar-alias-template-requires.device
- check_ok bar-alias@3 Requires yup-template-requires.device
- check_ok bar-alias@3 Requires yup-3-requires.device
-
- clear_units foo.service {bar,yup,bar-alias}@{,1,2,3}.service
-}
-
-testcase_alias_dropins() {
- echo "Testing alias dropins..."
-
- echo "*** test a wants b1 alias of b"
- create_services test15-a test15-b
- ln -sf test15-b.service /etc/systemd/system/test15-b1.service
- ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/
- check_ok test15-a Wants test15-b.service
- systemctl start test15-a
- systemctl --quiet is-active test15-b
- systemctl stop test15-a test15-b
- rm /etc/systemd/system/test15-b1.service
- clear_units test15-{a,b}.service
-
- # Check that dependencies don't vary.
- echo "*** test 2"
- create_services test15-a test15-x test15-y
- mkdir -p /etc/systemd/system/test15-a1.service.wants/
- ln -sf test15-a.service /etc/systemd/system/test15-a1.service
- ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/
- ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/
- check_ok test15-a1 Wants test15-x.service # see [1]
- check_ok test15-a1 Wants test15-y.service
- systemctl start test15-a
- check_ok test15-a1 Wants test15-x.service # see [2]
- check_ok test15-a1 Wants test15-y.service
- systemctl stop test15-a test15-x test15-y
- rm /etc/systemd/system/test15-a1.service
-
- clear_units test15-{a,x,y}.service
-}
-
-testcase_masked_dropins() {
- echo "Testing masked dropins..."
-
- create_services test15-a test15-b
-
- # 'b' is masked for both deps
- echo "*** test a wants,requires b is masked"
- ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
- ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
- check_ko test15-a Wants test15-b.service
- check_ko test15-a Requires test15-b.service
-
- # 'a' wants 'b' and 'b' is masked at a lower level
- echo "*** test a wants b, mask override"
- ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service
- ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
- check_ok test15-a Wants test15-b.service
-
- # 'a' wants 'b' and 'b' is masked at a higher level
- echo "*** test a wants b, mask"
- ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
- ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
- check_ko test15-a Wants test15-b.service
-
- # 'a' is masked but has an override config file
- echo "*** test a is masked but has an override"
- create_services test15-a test15-b
- ln -sf /dev/null /etc/systemd/system/test15-a.service
- cat >/usr/lib/systemd/system/test15-a.service.d/override.conf <<EOF
-[Unit]
-After=test15-b.service
-EOF
- check_ok test15-a UnitFileState masked
-
- # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep
- echo "*** test a wants b, b1, and one is masked"
- create_services test15-a test15-b
- ln -sf test15-b.service /etc/systemd/system/test15-b1.service
- ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
- ln -sf ../test15-b1.service /usr/lib/systemd/system/test15-a.service.wants/test15-b1.service
- systemctl cat test15-a
- systemctl show -p Wants,Requires test15-a
- systemctl cat test15-b1
- systemctl show -p Wants,Requires test15-b1
- check_ok test15-a Wants test15-b.service
- check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
- rm /etc/systemd/system/test15-b1.service
-
- # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep
- echo "*** test a wants b, alias dep is masked"
- create_services test15-a test15-b
- ln -sf test15-b.service /etc/systemd/system/test15-b1.service
- ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b1.service
- ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
- check_ok test15-a Wants test15-b.service
- check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
- rm /etc/systemd/system/test15-b1.service
-
- # 'a' has Wants=b.service but also has a masking
- # dropin 'b': 'b' should still be pulled in.
- echo "*** test a wants b both ways"
- create_services test15-a test15-b
- ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
- cat >/usr/lib/systemd/system/test15-a.service.d/wants-b.conf <<EOF
-[Unit]
-Wants=test15-b.service
-EOF
- check_ok test15-a Wants test15-b.service
-
- # mask a dropin that points to an nonexistent unit.
- echo "*** test a wants nonexistent is masked"
- create_services test15-a
- ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/nonexistent.service
- ln -sf ../nonexistent.service /usr/lib/systemd/system/test15-a.service.requires/
- check_ko test15-a Requires nonexistent.service
-
- # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
- # masked at a higher level.
- echo "*** test a wants b is masked"
- create_services test15-a test15-b test15-c
- ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
- ln -sf ../test15-b.service /run/systemd/system/test15-c.service.requires/
- ln -sf /dev/null /etc/systemd/system/test15-c.service.requires/test15-b.service
- systemctl start test15-a
- check_ko test15-c Requires test15-b.service
- systemctl stop test15-a test15-b
-
- # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
- # masked at a lower level.
- echo "*** test a requires b is masked"
- create_services test15-a test15-b test15-c
- ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
- ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
- ln -sf /dev/null /run/systemd/system/test15-c.service.requires/test15-b.service
- systemctl start test15-a
- check_ok test15-c Requires test15-b.service
- systemctl stop test15-a test15-b
-
- # 'a' requires 2 aliases of 'b' and one of them is a mask.
- echo "*** test a requires alias of b, other alias masked"
- create_services test15-a test15-b
- ln -sf test15-b.service /etc/systemd/system/test15-b1.service
- ln -sf test15-b.service /etc/systemd/system/test15-b2.service
- ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b1.service
- ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
- ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
- check_ok test15-a Requires test15-b
-
- # Same as above but now 'b' is masked.
- echo "*** test a requires alias of b, b dep masked"
- create_services test15-a test15-b
- ln -sf test15-b.service /etc/systemd/system/test15-b1.service
- ln -sf test15-b.service /etc/systemd/system/test15-b2.service
- ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
- ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
- ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
- check_ok test15-a Requires test15-b
-
- clear_units test15-{a,b}.service
-}
-
-testcase_invalid_dropins() {
- echo "Testing invalid dropins..."
- # Assertion failed on earlier versions, command exits unsuccessfully on later versions
- systemctl cat nonexistent@.service || true
- create_services a
- systemctl daemon-reload
- # Assertion failed on earlier versions, command exits unsuccessfully on later versions
- systemctl cat a@.service || true
- systemctl stop a
- clear_units a.service
- return 0
-}
-
-testcase_symlink_dropin_directory() {
- # For issue #21920.
- echo "Testing symlink drop-in directory..."
- create_services test15-a
- rmdir /{etc,run,usr/lib}/systemd/system/test15-a.service.d
- mkdir -p /tmp/testsuite-15-test15-a-dropin-directory
- ln -s /tmp/testsuite-15-test15-a-dropin-directory /etc/systemd/system/test15-a.service.d
- cat >/tmp/testsuite-15-test15-a-dropin-directory/override.conf <<EOF
-[Unit]
-Description=hogehoge
-EOF
- ln -s /tmp/testsuite-15-test15-a-dropin-directory-nonexistent /run/systemd/system/test15-a.service.d
- touch /tmp/testsuite-15-test15-a-dropin-directory-regular
- ln -s /tmp/testsuite-15-test15-a-dropin-directory-regular /usr/lib/systemd/system/test15-a.service.d
- check_ok test15-a Description hogehoge
-
- clear_units test15-a.service
-}
-
-run_testcases
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-16-EXTEND-TIMEOUT
-# Testsuite: Assess all other testsuite-*.services worked as expected
-
-Wants=success-all.service
-Wants=success-start.service
-Wants=success-runtime.service
-Wants=success-stop.service
-Wants=fail-start.service
-Wants=fail-stop.service
-Wants=fail-runtime.service
-StopWhenUnneeded=yes
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-Type=exec
-TimeoutStartSec=infinity
-ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
-ExecStart=true
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-rm -f /test.log
-
-TESTLOG=/test.log.XXXXXXXX
-
-wait_for()
-{
- local service="${1:-wait_for: missing service argument}"
- local result="${2:-success}"
- local time="${3:-45}"
-
- while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]]; do
- sleep 1
- time=$((time - 1))
- done
-
- if [[ ! -f /${service}.${result} ]]; then
- journalctl -u "${service/_/-}.service" >>"$TESTLOG"
- fi
-}
-
-wait_for_timeout()
-{
- local unit="$1"
- local time="$2"
-
- while [[ $time -gt 0 ]]; do
- if [[ "$(systemctl show --property=Result "$unit")" == "Result=timeout" ]]; then
- return 0
- fi
-
- sleep 1
- time=$((time - 1))
- done
-
- journalctl -u "$unit" >>"$TESTLOG"
-
- return 1
-}
-
-# This checks all stages, start, runtime and stop, can be extended by
-# EXTEND_TIMEOUT_USEC
-
-wait_for success_all
-
-# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the
-# extend timeout interval but less then the stage limit (TimeoutStartSec,
-# RuntimeMaxSec, TimeoutStopSec) still succeed.
-
-wait_for success_start
-wait_for success_runtime
-wait_for success_stop
-
-# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the
-# appropriate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC
-# message isn't sent within the extend timeout interval.
-
-wait_for fail_start startfail
-wait_for fail_stop stopfail
-wait_for fail_runtime runtimefail
-
-# These ensure that RuntimeMaxSec is honored for scope and service units
-# when they are created.
-runtime_max_sec=5
-
-systemd-run \
- --property=RuntimeMaxSec=${runtime_max_sec}s \
- -u runtime-max-sec-test-1.service \
- /usr/bin/sh -c "while true; do sleep 1; done"
-wait_for_timeout runtime-max-sec-test-1.service $((runtime_max_sec + 2))
-
-systemd-run \
- --property=RuntimeMaxSec=${runtime_max_sec}s \
- --scope \
- -u runtime-max-sec-test-2.scope \
- /usr/bin/sh -c "while true; do sleep 1; done" &
-wait_for_timeout runtime-max-sec-test-2.scope $((runtime_max_sec + 2))
-
-# These ensure that RuntimeMaxSec is honored for scope and service
-# units if the value is changed and then the manager is reloaded.
-systemd-run \
- -u runtime-max-sec-test-3.service \
- /usr/bin/sh -c "while true; do sleep 1; done"
-mkdir -p /etc/systemd/system/runtime-max-sec-test-3.service.d/
-cat > /etc/systemd/system/runtime-max-sec-test-3.service.d/override.conf << EOF
-[Service]
-RuntimeMaxSec=${runtime_max_sec}s
-EOF
-systemctl daemon-reload
-wait_for_timeout runtime-max-sec-test-3.service $((runtime_max_sec + 2))
-
-systemd-run \
- --scope \
- -u runtime-max-sec-test-4.scope \
- /usr/bin/sh -c "while true; do sleep 1; done" &
-
-# Wait until the unit is running to avoid race with creating the override.
-until systemctl is-active runtime-max-sec-test-4.scope; do
- sleep 1
-done
-mkdir -p /etc/systemd/system/runtime-max-sec-test-4.scope.d/
-cat > /etc/systemd/system/runtime-max-sec-test-4.scope.d/override.conf << EOF
-[Scope]
-RuntimeMaxSec=${runtime_max_sec}s
-EOF
-systemctl daemon-reload
-wait_for_timeout runtime-max-sec-test-4.scope $((runtime_max_sec + 2))
-
-if [[ -f "$TESTLOG" ]]; then
- # no mv
- cp "$TESTLOG" /test.log
- exit 1
-fi
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Tests for issue #28588 and #28653.
-
-# On boot, services need to be started in the following order:
-# 1. systemd-tmpfiles-setup-dev-early.service
-# 2. systemd-sysusers.service
-# 3. systemd-tmpfiles-setup-dev.service
-# 4. systemd-udevd.service
-
-output="$(systemctl show --property After --value systemd-udevd.service)"
-assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
-assert_in "systemd-sysusers.service" "$output"
-assert_in "systemd-tmpfiles-setup-dev.service" "$output"
-
-output="$(systemctl show --property After --value systemd-tmpfiles-setup-dev.service)"
-assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
-assert_in "systemd-sysusers.service" "$output"
-
-output="$(systemctl show --property After --value systemd-sysusers.service)"
-assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
-
-check_owner_and_mode() {
- local dev=${1?}
- local user=${2?}
- local group=${3?}
- local mode=${4:-}
-
- if [[ -e "$dev" ]]; then
- assert_in "$user" "$(stat --format=%U "$dev")"
- assert_in "$group" "$(stat --format=%G "$dev")"
- if [[ -n "$mode" ]]; then
- assert_in "$mode" "$(stat --format=%#0a "$dev")"
- fi
- fi
-
- return 0
-}
-
-# Check owner and access mode specified in static-nodes-permissions.conf
-check_owner_and_mode /dev/snd/seq root audio 0660
-check_owner_and_mode /dev/snd/timer root audio 0660
-check_owner_and_mode /dev/loop-control root disk 0660
-check_owner_and_mode /dev/net/tun root root 0666
-check_owner_and_mode /dev/fuse root root 0666
-check_owner_and_mode /dev/vfio/vfio root root 0666
-check_owner_and_mode /dev/kvm root kvm
-check_owner_and_mode /dev/vhost-net root kvm
-check_owner_and_mode /dev/vhost-vsock root kvm
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
- (
- udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
- udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
- systemctl show -p WantedBy foobar.service | grep -q -v sda
- systemctl show -p WantedBy waldo.service | grep -q -v sda
- ) && break
-
- sleep .5
-done
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
-EOF
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
- (
- udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service
- udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
- systemctl show -p WantedBy foobar.service | grep -q sda
- systemctl show -p WantedBy waldo.service | grep -q -v sda
- ) && break
-
- sleep .5
-done
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
-EOF
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
- (
- udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
- udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service
- systemctl show -p WantedBy foobar.service | grep -q -v sda
- systemctl show -p WantedBy waldo.service | grep -q sda
- ) && break
-
- sleep .5
-done
-
-rm /run/udev/rules.d/50-testsuite.rules
-
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
- (
- udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
- udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
- systemctl show -p WantedBy foobar.service | grep -q -v sda
- systemctl show -p WantedBy waldo.service | grep -q -v sda
- ) && break
-
- sleep .5
-done
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-# disable shellcheck warning about '"aaa"' type quotation
-# shellcheck disable=SC2016
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-mkdir -p /run/udev/rules.d/
-
-# test for ID_RENAMING= udev property and device unit state
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="remove", GOTO="hoge_end"
-SUBSYSTEM!="net", GOTO="hoge_end"
-KERNEL!="hoge", GOTO="hoge_end"
-
-OPTIONS="log_level=debug"
-
-# emulate renaming
-ACTION=="online", ENV{ID_RENAMING}="1"
-
-LABEL="hoge_end"
-EOF
-
-udevadm control --log-priority=debug --reload --timeout=30
-
-ip link add hoge type dummy
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-
-udevadm trigger --action=online --settle /sys/devices/virtual/net/hoge
-assert_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
-
-udevadm trigger --action=move --settle /sys/devices/virtual/net/hoge
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-
-# test for renaming interface with NAME= (issue #25106)
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION!="add", GOTO="hoge_end"
-SUBSYSTEM!="net", GOTO="hoge_end"
-
-OPTIONS="log_level=debug"
-
-KERNEL=="hoge", NAME="foobar"
-KERNEL=="foobar", NAME="hoge"
-
-LABEL="hoge_end"
-EOF
-
-udevadm control --log-priority=debug --reload --timeout=30
-
-udevadm trigger --action=add --settle /sys/devices/virtual/net/hoge
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/foobar)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
-
-udevadm trigger --action=add --settle /sys/devices/virtual/net/foobar
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload --timeout=30
-
-# test for renaming interface with an external tool (issue #16967)
-
-ip link set hoge name foobar
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
-
-ip link set foobar name hoge
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
-
-# cleanup
-ip link del hoge
-
-# shellcheck disable=SC2317
-teardown_netif_renaming_conflict() {
- set +ex
-
- if [[ -n "$KILL_PID" ]]; then
- kill "$KILL_PID"
- fi
-
- rm -rf "$TMPDIR"
-
- rm -f /run/udev/rules.d/50-testsuite.rules
- udevadm control --reload --timeout=30
-
- ip link del hoge
- ip link del foobar
-}
-
-test_netif_renaming_conflict() {
- local since found=
-
- trap teardown_netif_renaming_conflict RETURN
-
- cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION!="add", GOTO="hoge_end"
-SUBSYSTEM!="net", GOTO="hoge_end"
-
-OPTIONS="log_level=debug"
-
-KERNEL=="foobar", NAME="hoge"
-
-LABEL="hoge_end"
-EOF
-
- udevadm control --log-priority=debug --reload --timeout=30
-
- ip link add hoge type dummy
- udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-
- TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
- udevadm monitor --udev --property --subsystem-match=net >"$TMPDIR"/monitor.txt &
- KILL_PID="$!"
-
- # make sure that 'udevadm monitor' actually monitor uevents
- sleep 1
-
- since="$(date '+%H:%M:%S')"
-
- # add another interface which will conflict with an existing interface
- ip link add foobar type dummy
-
- for _ in {1..40}; do
- if (
- grep -q 'ACTION=add' "$TMPDIR"/monitor.txt
- grep -q 'DEVPATH=/devices/virtual/net/foobar' "$TMPDIR"/monitor.txt
- grep -q 'SUBSYSTEM=net' "$TMPDIR"/monitor.txt
- grep -q 'INTERFACE=foobar' "$TMPDIR"/monitor.txt
- grep -q 'ID_NET_DRIVER=dummy' "$TMPDIR"/monitor.txt
- grep -q 'ID_NET_NAME=foobar' "$TMPDIR"/monitor.txt
- # Even when network interface renaming is failed, SYSTEMD_ALIAS with the conflicting name will be broadcast.
- grep -q 'SYSTEMD_ALIAS=/sys/subsystem/net/devices/hoge' "$TMPDIR"/monitor.txt
- grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
- grep -q 'UDEV_WORKER_ERRNO=17' "$TMPDIR"/monitor.txt
- grep -q 'UDEV_WORKER_ERRNO_NAME=EEXIST' "$TMPDIR"/monitor.txt
- ); then
- cat "$TMPDIR"/monitor.txt
- found=1
- break
- fi
- sleep .5
- done
- test -n "$found"
-
- timeout 30 bash -c "until journalctl _PID=1 _COMM=systemd --since $since | grep -q 'foobar: systemd-udevd failed to process the device, ignoring: File exists'; do sleep 1; done"
- # check if the invalid SYSTEMD_ALIAS property for the interface foobar is ignored by PID1
- assert_eq "$(systemctl show --property=SysFSPath --value /sys/subsystem/net/devices/hoge)" "/sys/devices/virtual/net/hoge"
-}
-
-test_netif_renaming_conflict
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-
-TMPDIR=
-TEST_RULE="/run/udev/rules.d/49-test.rules"
-TEST_CONF="/run/udev/udev.conf.d/test-17.conf"
-KILL_PID=
-
-setup() {
- mkdir -p "${TEST_RULE%/*}"
- mkdir -p /run/udev/udev.conf.d
-
- cat >"${TEST_RULE}" <<EOF
-ACTION!="add", GOTO="test_end"
-SUBSYSTEM!="mem", GOTO="test_end"
-KERNEL!="null", GOTO="test_end"
-
-OPTIONS="log_level=debug"
-PROGRAM=="/bin/touch /tmp/test-udev-marker"
-PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
-
-LABEL="test_end"
-EOF
- cat >"$TEST_CONF" <<EOF
-event_timeout=10
-timeout_signal=SIGABRT
-EOF
-
- systemctl restart systemd-udevd.service
-}
-
-# shellcheck disable=SC2317
-teardown() {
- set +e
-
- if [[ -n "$KILL_PID" ]]; then
- kill "$KILL_PID"
- fi
-
- rm -rf "$TMPDIR"
- rm -f "$TEST_RULE" "$TEST_CONF"
- systemctl restart systemd-udevd.service
-}
-
-run_test_timeout() {
- TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
- udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
- KILL_PID="$!"
-
- SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
-
- for _ in {1..40}; do
- if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
- sleep .5
- kill "$KILL_PID"
- KILL_PID=
-
- cat "$TMPDIR"/monitor.txt
- (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
- (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
- (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
- grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
- rm -rf "$TMPDIR"
- return 0
- fi
- sleep .5
- done
-
- return 1
-}
-
-run_test_killed() {
- local killed=
-
- TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
- udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
- KILL_PID="$!"
-
- rm -f /tmp/test-udev-marker
- SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
-
- for _ in {1..40}; do
- if [[ -z "$killed" ]]; then
- if [[ -e /tmp/test-udev-marker ]]; then
- killall --signal ABRT --regexp udev-worker
- killed=1
- fi
- elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
- sleep .5
- kill "$KILL_PID"
- KILL_PID=
-
- cat "$TMPDIR"/monitor.txt
- grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
- grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
- grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
- (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
- rm -rf "$TMPDIR"
- return 0
- fi
- sleep .5
- done
-
- return 1
-}
-
-trap teardown EXIT
-
-setup
-run_test_timeout
-run_test_killed
-
-exit 0
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-test ! -f /run/udev/tags/added/c1:3
-test ! -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added"
-ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed"
-EOF
-
-udevadm control --reload
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
-
-test -f /run/udev/tags/added/c1:3
-test ! -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && { echo 'unexpected TAGS='; exit 1; }
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action change /dev/null
-
-test -f /run/udev/tags/added/c1:3
-test -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
-
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
-
-test -f /run/udev/tags/added/c1:3
-test -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-
-rm /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-exit 0
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/echo -e HOGE=aa\\\\x20\\\\x20\\\\x20bb\nFOO=\\\\x20aaa\\\\x20\n\n\n"
-EOF
-
-udevadm control --reload
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
-
-test -f /run/udev/data/c1:3
-udevadm info /dev/null | grep -q 'E: HOGE=aa\\x20\\x20\\x20bb'
-udevadm info /dev/null | grep -q 'E: FOO=\\x20aaa\\x20'
-
-rm /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-exit 0
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# tests for udev watch
-
-function check_validity() {
- local f ID_OR_HANDLE
-
- for f in /run/udev/watch/*; do
- ID_OR_HANDLE="$(readlink "$f")"
- test -L "/run/udev/watch/${ID_OR_HANDLE}"
- test "$(readlink "/run/udev/watch/${ID_OR_HANDLE}")" = "$(basename "$f")"
- done
-}
-
-function check() {
- for _ in {1..2}; do
- systemctl restart systemd-udevd.service
- udevadm control --ping
- udevadm settle
- check_validity
-
- for _ in {1..2}; do
- udevadm trigger -w --action add --subsystem-match=block
- check_validity
- done
-
- for _ in {1..2}; do
- udevadm trigger -w --action change --subsystem-match=block
- check_validity
- done
- done
-}
-
-mkdir -p /run/udev/rules.d/
-
-cat >/run/udev/rules.d/00-debug.rules <<EOF
-SUBSYSTEM=="block", KERNEL=="sda*", OPTIONS="log_level=debug"
-EOF
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="add", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="watch"
-EOF
-
-check
-
-MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
-MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
-test -L "/run/udev/watch/b${MAJOR}:${MINOR}"
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="change", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="nowatch"
-EOF
-
-check
-
-MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
-MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
-test ! -e "/run/udev/watch/b${MAJOR}:${MINOR}"
-
-rm /run/udev/rules.d/00-debug.rules
-rm /run/udev/rules.d/50-testsuite.rules
-
-udevadm control --reload
-systemctl reset-failed systemd-udevd.service
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-wait_service_active() {(
- set +ex
- for i in {1..20}; do
- (( i > 1 )) && sleep 0.5
- if systemctl --quiet is-active "${1?}"; then
- return 0
- fi
- done
- return 1
-)}
-
-wait_service_inactive() {(
- set +ex
- for i in {1..20}; do
- (( i > 1 )) && sleep 0.5
- systemctl --quiet is-active "${1?}"
- if [[ "$?" == "3" ]]; then
- return 0
- fi
- done
- return 1
-)}
-
-mkdir -p /run/systemd/system
-cat >/run/systemd/system/both.service <<EOF
-[Service]
-ExecStart=sleep 1000
-EOF
-
-cat >/run/systemd/system/on-add.service <<EOF
-[Service]
-ExecStart=sleep 1000
-EOF
-
-cat >/run/systemd/system/on-change.service <<EOF
-[Service]
-ExecStart=sleep 1000
-EOF
-
-systemctl daemon-reload
-
-mkdir -p /run/udev/rules.d/
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="net", KERNEL=="dummy9?", OPTIONS="log_level=debug"
-SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-add.service"
-SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-change.service"
-EOF
-
-udevadm control --reload
-
-# StopWhenUnneeded=no
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-systemctl stop both.service on-add.service
-
-udevadm trigger --action=change --settle /sys/class/net/dummy99
-udevadm info /sys/class/net/dummy99
-wait_service_active both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-wait_service_active on-change.service
-systemctl stop both.service on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-assert_rc 3 systemctl --quiet is-active both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-# StopWhenUnneeded=yes
-cat >/run/systemd/system/both.service <<EOF
-[Unit]
-StopWhenUnneeded=yes
-
-[Service]
-ExecStart=sleep 1000
-Type=simple
-EOF
-
-cat >/run/systemd/system/on-add.service <<EOF
-[Unit]
-StopWhenUnneeded=yes
-
-[Service]
-ExecStart=sleep 1000
-Type=simple
-EOF
-
-cat >/run/systemd/system/on-change.service <<EOF
-[Unit]
-StopWhenUnneeded=yes
-
-[Service]
-ExecStart=echo changed
-RemainAfterExit=true
-Type=oneshot
-EOF
-
-systemctl daemon-reload
-
-# StopWhenUnneeded=yes (single device, only add event)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-wait_service_inactive both.service
-wait_service_inactive on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-# StopWhenUnneeded=yes (single device, add and change event)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-udevadm trigger --action=change --settle /sys/class/net/dummy99
-assert_rc 0 systemctl --quiet is-active both.service
-wait_service_inactive on-add.service
-wait_service_active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-wait_service_inactive both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-wait_service_inactive on-change.service
-
-# StopWhenUnneeded=yes (multiple devices, only add events)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link add dummy98 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy98
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link del dummy98
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
-wait_service_inactive both.service
-wait_service_inactive on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-# StopWhenUnneeded=yes (multiple devices, add and change events)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link add dummy98 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy98
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-udevadm trigger --action=change --settle /sys/class/net/dummy99
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-wait_service_active on-change.service
-
-ip link del dummy98
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
-assert_rc 0 systemctl --quiet is-active both.service
-wait_service_inactive on-add.service
-assert_rc 0 systemctl --quiet is-active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-wait_service_inactive both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-wait_service_inactive on-change.service
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-rm -f /run/systemd/system/on-add.service
-rm -f /run/systemd/system/on-change.service
-systemctl daemon-reload
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# This is a test for issue #24518.
-
-mkdir -p /run/udev/rules.d/
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug", TAG+="systemd"
-SUBSYSTEM=="mem", KERNEL=="null", ACTION=="add", SYMLINK+="test/symlink-to-null-on-add", ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-add"
-SUBSYSTEM=="mem", KERNEL=="null", ACTION=="change", SYMLINK+="test/symlink-to-null-on-change", ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-change"
-EOF
-
-udevadm control --reload
-
-udevadm trigger --settle --action add /dev/null
-for i in {1..20}; do
- ((i > 1)) && sleep .5
-
- (
- systemctl -q is-active /dev/test/symlink-to-null-on-add
- ! systemctl -q is-active /dev/test/symlink-to-null-on-change
- systemctl -q is-active /sys/test/alias-to-null-on-add
- ! systemctl -q is-active /sys/test/alias-to-null-on-change
- ) && break
-done
-assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
-assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
-assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
-assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
-
-udevadm trigger --settle --action change /dev/null
-for i in {1..20}; do
- ((i > 1)) && sleep .5
-
- (
- ! systemctl -q is-active /dev/test/symlink-to-null-on-add
- systemctl -q is-active /dev/test/symlink-to-null-on-change
- ! systemctl -q is-active /sys/test/alias-to-null-on-add
- systemctl -q is-active /sys/test/alias-to-null-on-change
- ) && break
-done
-assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-add
-assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-change
-assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-add
-assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-change
-
-udevadm trigger --settle --action add /dev/null
-for i in {1..20}; do
- ((i > 1)) && sleep .5
-
- (
- systemctl -q is-active /dev/test/symlink-to-null-on-add
- ! systemctl -q is-active /dev/test/symlink-to-null-on-change
- systemctl -q is-active /sys/test/alias-to-null-on-add
- ! systemctl -q is-active /sys/test/alias-to-null-on-change
- ) && break
-done
-assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
-assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
-assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
-assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# This is a test for issue #24987.
-
-mkdir -p /run/udev/rules.d/
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM!="mem", GOTO="test-end"
-KERNEL!="null", GOTO="test-end"
-ACTION=="remove", GOTO="test-end"
-
-# add 100 * 100byte of properties
-$(for i in {1..100}; do printf 'ENV{XXX%03i}="0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"\n' "$i"; done)
-
-LABEL="test-end"
-EOF
-
-udevadm control --reload
-
-TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
-SYSTEMD_LOG_LEVEL=debug udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt 2>&1 &
-KILL_PID="$!"
-
-FOUND=
-for _ in {1..40}; do
- if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then
- FOUND=1
- break
- fi
- sleep .5
-done
-[[ -n "$FOUND" ]]
-
-udevadm trigger --verbose --settle --action add /dev/null
-
-FOUND=
-for _ in {1..40}; do
- if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then
- sleep .5
- continue
- fi
-
- FOUND=1
- for i in {1..100}; do
- if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then
- FOUND=
- break
- fi
- done
- if [[ -n "$FOUND" ]]; then
- break;
- fi
-
- sleep .5
-done
-[[ -n "$FOUND" ]]
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-kill "$KILL_PID"
-rm -rf "$TMPDIR"
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Coverage test for udevadm
-
-# shellcheck disable=SC2317
-cleanup_17_10() {
- set +e
-
- losetup -d "$loopdev"
- rm -f "$blk"
-
- ip link delete "$netdev"
-}
-
-# Set up some test devices
-trap cleanup_17_10 EXIT
-
-netdev=dummy17.10
-ip link add $netdev type dummy
-
-blk="$(mktemp)"
-dd if=/dev/zero of="$blk" bs=1M count=1
-loopdev="$(losetup --show -f "$blk")"
-
-udevadm -h
-
-udevadm control -e
-udevadm control -l emerg
-udevadm control -l alert
-udevadm control -l crit
-udevadm control -l err
-udevadm control -l warning
-udevadm control -l notice
-udevadm control --log-level info
-udevadm control --log-level debug
-(! udevadm control -l hello)
-udevadm control -s
-udevadm control -S
-udevadm control -R
-udevadm control -p HELLO=world
-udevadm control -m 42
-udevadm control --ping -t 5
-udevadm control --load-credentials
-udevadm control -h
-
-udevadm info /dev/null
-udevadm info /sys/class/net/$netdev
-udevadm info "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm info --property DEVNAME /sys/class/net/$netdev
-udevadm info --property DEVNAME --value /sys/class/net/$netdev
-udevadm info --property HELLO /sys/class/net/$netdev
-udevadm info -p class/net/$netdev
-udevadm info -p /class/net/$netdev
-udevadm info --json=off -p class/net/$netdev
-udevadm info --json=pretty -p class/net/$netdev | jq .
-udevadm info --json=short -p class/net/$netdev | jq .
-udevadm info -n null
-udevadm info -q all /sys/class/net/$netdev
-udevadm info -q name /dev/null
-udevadm info -q path /sys/class/net/$netdev
-udevadm info -q property /sys/class/net/$netdev
-udevadm info -q symlink /sys/class/net/$netdev
-udevadm info -q name -r /dev/null
-udevadm info --query symlink --root /sys/class/net/$netdev
-(! udevadm info -q hello -r /sys/class/net/$netdev)
-udevadm info -a /sys/class/net/$netdev
-udevadm info -t >/dev/null
-udevadm info --tree /sys/class/net/$netdev
-udevadm info -x /sys/class/net/$netdev
-udevadm info -x -q path /sys/class/net/$netdev
-udevadm info -P TEST_ /sys/class/net/$netdev
-udevadm info -d /dev/null
-udevadm info -e >/dev/null
-udevadm info -e --json=off >/dev/null
-udevadm info -e --json=pretty | jq . >/dev/null
-udevadm info -e --json=short | jq . >/dev/null
-udevadm info -e --subsystem-match acpi >/dev/null
-udevadm info -e --subsystem-nomatch acpi >/dev/null
-udevadm info -e --attr-match ifindex=2 >/dev/null
-udevadm info -e --attr-nomatch ifindex=2 >/dev/null
-udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null
-udevadm info -e --tag-match systemd >/dev/null
-udevadm info -e --sysname-match lo >/dev/null
-udevadm info -e --name-match /sys/class/net/$netdev >/dev/null
-udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null
-udevadm info -e --initialized-match >/dev/null
-udevadm info -e --initialized-nomatch >/dev/null
-# udevadm info -c
-udevadm info -w /sys/class/net/$netdev
-udevadm info --wait-for-initialization=5 /sys/class/net/$netdev
-udevadm info -h
-
-assert_rc 124 timeout 1 udevadm monitor
-assert_rc 124 timeout 1 udevadm monitor -k
-assert_rc 124 timeout 1 udevadm monitor -u
-assert_rc 124 timeout 1 udevadm monitor -s net
-assert_rc 124 timeout 1 udevadm monitor --subsystem-match net/$netdev
-assert_rc 124 timeout 1 udevadm monitor -t systemd
-assert_rc 124 timeout 1 udevadm monitor --tag-match hello
-udevadm monitor -h
-
-udevadm settle
-udevadm settle -t 5
-udevadm settle -E /sys/class/net/$netdev
-udevadm settle -h
-
-udevadm test /dev/null
-udevadm info /sys/class/net/$netdev
-udevadm test "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm test -a add /sys/class/net/$netdev
-udevadm test -a change /sys/class/net/$netdev
-udevadm test -a move /sys/class/net/$netdev
-udevadm test -a online /sys/class/net/$netdev
-udevadm test -a offline /sys/class/net/$netdev
-udevadm test -a bind /sys/class/net/$netdev
-udevadm test -a unbind /sys/class/net/$netdev
-udevadm test -a help /sys/class/net/$netdev
-udevadm test --action help
-(! udevadm test -a hello /sys/class/net/$netdev)
-udevadm test -N early /sys/class/net/$netdev
-udevadm test -N late /sys/class/net/$netdev
-udevadm test --resolve-names never /sys/class/net/$netdev
-(! udevadm test -N hello /sys/class/net/$netdev)
-udevadm test -h
-
-# udevadm test-builtin path_id "$loopdev"
-udevadm test-builtin net_id /sys/class/net/$netdev
-udevadm test-builtin net_id "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm test-builtin -a add net_id /sys/class/net/$netdev
-udevadm test-builtin -a remove net_id /sys/class/net/$netdev
-udevadm test-builtin -a change net_id /sys/class/net/$netdev
-udevadm test-builtin -a move net_id /sys/class/net/$netdev
-udevadm test-builtin -a online net_id /sys/class/net/$netdev
-udevadm test-builtin -a offline net_id /sys/class/net/$netdev
-udevadm test-builtin -a bind net_id /sys/class/net/$netdev
-udevadm test-builtin -a unbind net_id /sys/class/net/$netdev
-udevadm test-builtin -a help net_id /sys/class/net/$netdev
-udevadm test-builtin net_setup_link /sys/class/net/$netdev
-udevadm test-builtin blkid "$loopdev"
-udevadm test-builtin input_id /sys/class/net/$netdev
-udevadm test-builtin keyboard /dev/null
-# udevadm test-builtin kmod /sys/class/net/$netdev
-udevadm test-builtin uaccess /dev/null
-# udevadm test-builtin usb_id dev/null
-(! udevadm test-builtin hello /sys/class/net/$netdev)
-# systemd-hwdb update is extremely slow when combined with sanitizers and run
-# in a VM without acceleration, so let's just skip the one particular test
-# if we detect this combination
-if ! [[ -v ASAN_OPTIONS && "$(systemd-detect-virt -v)" == "qemu" ]]; then
- modprobe scsi_debug
- scsidev=$(readlink -f /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/[0-9]*)
- mkdir -p /etc/udev/hwdb.d
- cat >/etc/udev/hwdb.d/99-test.hwdb <<EOF
-scsi:*
- ID_TEST=test
-EOF
- systemd-hwdb update
-
- udevadm test-builtin hwdb "$scsidev"
-
- rmmod scsi_debug || :
- rm -fv /etc/udev/hwdb.d/99-test.hwdb
- systemd-hwdb update
-fi
-
-
-udevadm trigger
-udevadm trigger /dev/null
-udevadm trigger /sys/class/net/$netdev
-udevadm trigger "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm trigger -v /sys/class/net/$netdev
-udevadm trigger -n /sys/class/net/$netdev
-udevadm trigger -q /sys/class/net/$netdev
-udevadm trigger -t all /sys/class/net/$netdev
-udevadm trigger -t devices /sys/class/net/$netdev
-udevadm trigger --type subsystems /sys/class/net/$netdev
-(! udevadm trigger -t hello /sys/class/net/$netdev)
-udevadm trigger -c add /sys/class/net/$netdev
-udevadm trigger -c remove /sys/class/net/$netdev
-udevadm trigger -c change /sys/class/net/$netdev
-udevadm trigger -c move /sys/class/net/$netdev
-udevadm trigger -c online /sys/class/net/$netdev
-udevadm trigger -c offline /sys/class/net/$netdev
-udevadm trigger -c bind /sys/class/net/$netdev
-udevadm trigger -c unbind /sys/class/net/$netdev
-udevadm trigger -c help /sys/class/net/$netdev
-udevadm trigger --action help /sys/class/net/$netdev
-(! udevadm trigger -c hello /sys/class/net/$netdev)
-udevadm trigger --prioritized-subsystem block
-udevadm trigger --prioritized-subsystem block,net
-udevadm trigger --prioritized-subsystem hello
-udevadm trigger -s net
-udevadm trigger -S net
-udevadm trigger -a subsystem=net
-udevadm trigger --attr-match hello=world
-udevadm trigger -p DEVNAME=null
-udevadm trigger --property-match HELLO=world
-udevadm trigger -g systemd
-udevadm trigger --tag-match hello
-udevadm trigger -y net
-udevadm trigger --sysname-match hello
-udevadm trigger --name-match /sys/class/net/$netdev
-udevadm trigger --name-match /sys/class/net/$netdev --name-match /dev/null
-udevadm trigger -b /sys/class/net/$netdev
-udevadm trigger --parent-match /sys/class/net/$netdev --name-match /dev/null
-udevadm trigger --initialized-match
-udevadm trigger --initialized-nomatch
-udevadm trigger -w
-udevadm trigger --uuid /sys/class/net/$netdev
-udevadm settle -t 300
-udevadm trigger --wait-daemon
-udevadm settle -t 300
-udevadm trigger --wait-daemon=5
-udevadm trigger -h
-
-# https://github.com/systemd/systemd/issues/29863
-if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
- udevadm control --log-level=0
- for _ in {0..9}; do
- timeout 30 udevadm trigger --settle
- done
- udevadm control --log-level=debug
-fi
-
-udevadm wait /dev/null
-udevadm wait /sys/class/net/$netdev
-udevadm wait -t 5 /sys/class/net/$netdev
-udevadm wait --initialized true /sys/class/net/$netdev
-udevadm wait --initialized false /sys/class/net/$netdev
-(! udevadm wait --initialized hello /sys/class/net/$netdev)
-assert_rc 124 timeout 5 udevadm wait --removed /sys/class/net/$netdev
-udevadm wait --settle /sys/class/net/$netdev
-udevadm wait -h
-
-udevadm lock --help
-udevadm lock --version
-for i in /dev/block/*; do
- udevadm lock --device "$i" --print
- udevadm lock --device "$i" true
- (! udevadm lock --device "$i" false)
-done
-for i in / /usr; do
- udevadm lock --backing "$i" --print
- udevadm lock --backing "$i" true
- (! udevadm lock --backing "$i" false)
-done
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# Test for udevadm verify.
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# shellcheck disable=SC2317
-cleanup() {
- cd /
- rm -rf "${workdir}"
- workdir=
-}
-
-workdir="$(mktemp -d)"
-trap cleanup EXIT
-cd "${workdir}"
-
-cat >"${workdir}/default_output_1_success" <<EOF
-
-1 udev rules files have been checked.
- Success: 1
- Fail: 0
-EOF
-cat >"${workdir}/default_output_1_fail" <<EOF
-
-1 udev rules files have been checked.
- Success: 0
- Fail: 1
-EOF
-cat >"${workdir}/output_0_files" <<EOF
-
-0 udev rules files have been checked.
- Success: 0
- Fail: 0
-EOF
-
-test_number=0
-rules=
-exp=
-err=
-out=
-next_test_number() {
- : $((++test_number))
-
- local num_str
- num_str=$(printf %05d "${test_number}")
-
- rules="sample-${num_str}.rules"
- exp="sample-${num_str}.exp"
- err="sample-${num_str}.err"
- exo="sample-${num_str}.exo"
- out="sample-${num_str}.out"
-}
-
-assert_0_impl() {
- udevadm verify "$@" >"${out}"
- if [ -f "${exo}" ]; then
- diff -u "${exo}" "${out}"
- elif [ -f "${rules}" ]; then
- diff -u "${workdir}/default_output_1_success" "${out}"
- fi
-}
-
-assert_0() {
- assert_0_impl "$@"
- next_test_number
-}
-
-assert_1_impl() {
- local rc
- set +e
- udevadm verify "$@" >"${out}" 2>"${err}"
- rc=$?
- set -e
- assert_eq "$rc" 1
-
- if [ -f "${exp}" ]; then
- diff -u "${exp}" "${err}"
- fi
-
- if [ -f "${exo}" ]; then
- diff -u "${exo}" "${out}"
- elif [ -f "${rules}" ]; then
- diff -u "${workdir}/default_output_1_fail" "${out}"
- fi
-}
-
-assert_1() {
- assert_1_impl "$@"
- next_test_number
-}
-
-# initialize variables
-next_test_number
-
-assert_0 -h
-assert_0 --help
-assert_0 -V
-assert_0 --version
-assert_0 /dev/null
-
-# unrecognized option '--unknown'
-assert_1 --unknown
-# option requires an argument -- 'N'
-assert_1 -N
-# --resolve-names= takes "early" or "never"
-assert_1 -N now
-# option '--resolve-names' requires an argument
-assert_1 --resolve-names
-# --resolve-names= takes "early" or "never"
-assert_1 --resolve-names=now
-# Failed to parse rules file ./nosuchfile: No such file or directory
-assert_1 ./nosuchfile
-# Failed to parse rules file ./nosuchfile: No such file or directory
-cat >"${exo}" <<EOF
-
-3 udev rules files have been checked.
- Success: 2
- Fail: 1
-EOF
-assert_1 /dev/null ./nosuchfile /dev/null
-
-rules_dir='etc/udev/rules.d'
-mkdir -p "${rules_dir}"
-# No rules files found in $PWD
-assert_1 --root="${workdir}"
-
-# Directory without rules.
-cp "${workdir}/output_0_files" "${exo}"
-assert_0 "${rules_dir}"
-
-# Directory with a loop.
-ln -s . "${rules_dir}/loop.rules"
-assert_1 "${rules_dir}"
-rm "${rules_dir}/loop.rules"
-
-# Empty rules.
-touch "${rules_dir}/empty.rules"
-assert_0 --root="${workdir}"
-: >"${exo}"
-assert_0 --root="${workdir}" --no-summary
-
-# Directory with a single *.rules file.
-cp "${workdir}/default_output_1_success" "${exo}"
-assert_0 "${rules_dir}"
-
-# Combination of --root= and FILEs is not supported.
-assert_1 --root="${workdir}" /dev/null
-# No rules files found in nosuchdir
-assert_1 --root=nosuchdir
-
-cd "${rules_dir}"
-
-# UDEV_LINE_SIZE 16384
-printf '%16383s\n' ' ' >"${rules}"
-assert_0 "${rules}"
-
-# Failed to parse rules file ${rules}: No buffer space available
-printf '%16384s\n' ' ' >"${rules}"
-echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}"
-assert_1 "${rules}"
-
-{
- printf 'RUN+="/bin/true",%8174s\\\n' ' '
- printf 'RUN+="/bin/false"%8174s\\\n' ' '
- echo
-} >"${rules}"
-assert_0 "${rules}"
-
-printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}"
-echo >>"${rules}"
-cat >"${exp}" <<EOF
-${rules}:5 Line is too long, ignored.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-printf '\\\n' >"${rules}"
-cat >"${exp}" <<EOF
-${rules}:1 Unexpected EOF after line continuation, line ignored.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-test_syntax_error() {
- local rule msg
-
- rule="$1"; shift
- msg="$1"; shift
-
- printf '%s\n' "${rule}" >"${rules}"
- cat >"${exp}" <<EOF
-${rules}:1 ${msg}
-${rules}: udev rules check failed.
-EOF
- assert_1 "${rules}"
-}
-
-test_style_error() {
- local rule msg
-
- rule="$1"; shift
- msg="$1"; shift
-
- printf '%s\n' "${rule}" >"${rules}"
- cat >"${exp}" <<EOF
-${rules}:1 ${msg}
-${rules}: udev rules have style issues.
-EOF
- assert_0_impl --no-style "${rules}"
- assert_1_impl "${rules}"
- next_test_number
-}
-
-test_syntax_error '=' 'Invalid key/value pair, ignoring.'
-test_syntax_error 'ACTION{a}=="b"' 'Invalid attribute for ACTION.'
-test_syntax_error 'ACTION:="b"' 'Invalid operator for ACTION.'
-test_syntax_error 'ACTION=="b"' 'The line has no effect, ignoring.'
-test_syntax_error 'DEVPATH{a}=="b"' 'Invalid attribute for DEVPATH.'
-test_syntax_error 'DEVPATH:="b"' 'Invalid operator for DEVPATH.'
-test_syntax_error 'KERNEL{a}=="b"' 'Invalid attribute for KERNEL.'
-test_syntax_error 'KERNEL:="b"' 'Invalid operator for KERNEL.'
-test_syntax_error 'KERNELS{a}=="b"' 'Invalid attribute for KERNELS.'
-test_syntax_error 'KERNELS:="b"' 'Invalid operator for KERNELS.'
-test_syntax_error 'SYMLINK{a}=="b"' 'Invalid attribute for SYMLINK.'
-test_syntax_error 'SYMLINK:="%?"' 'Invalid value "%?" for SYMLINK (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'NAME{a}=="b"' 'Invalid attribute for NAME.'
-test_syntax_error 'NAME-="b"' 'Invalid operator for NAME.'
-test_syntax_error 'NAME+="a"' "NAME key takes '==', '!=', '=', or ':=' operator, assuming '='."
-test_syntax_error 'NAME:=""' 'Ignoring NAME="", as udev will not delete any network interfaces.'
-test_syntax_error 'NAME="%k"' 'Ignoring NAME="%k", as it will take no effect.'
-test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
-test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
-test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
-test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
-test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
-test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
-test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
-test_syntax_error 'TAG{a}=="b"' 'Invalid attribute for TAG.'
-test_syntax_error 'TAG:="a"' "TAG key takes '==', '!=', '=', or '+=' operator, assuming '='."
-test_syntax_error 'TAG="%?"' 'Invalid value "%?" for TAG (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'TAGS{a}=="b"' 'Invalid attribute for TAGS.'
-test_syntax_error 'TAGS:="a"' 'Invalid operator for TAGS.'
-test_syntax_error 'SUBSYSTEM{a}=="b"' 'Invalid attribute for SUBSYSTEM.'
-test_syntax_error 'SUBSYSTEM:="b"' 'Invalid operator for SUBSYSTEM.'
-test_syntax_error 'SUBSYSTEM=="bus", NAME="b"' '"bus" must be specified as "subsystem".'
-test_syntax_error 'SUBSYSTEMS{a}=="b"' 'Invalid attribute for SUBSYSTEMS.'
-test_syntax_error 'SUBSYSTEMS:="b"' 'Invalid operator for SUBSYSTEMS.'
-test_syntax_error 'DRIVER{a}=="b"' 'Invalid attribute for DRIVER.'
-test_syntax_error 'DRIVER:="b"' 'Invalid operator for DRIVER.'
-test_syntax_error 'DRIVERS{a}=="b"' 'Invalid attribute for DRIVERS.'
-test_syntax_error 'DRIVERS:="b"' 'Invalid operator for DRIVERS.'
-test_syntax_error 'ATTR="b"' 'Invalid attribute for ATTR.'
-test_syntax_error 'ATTR{%}="b"' 'Invalid attribute "%" for ATTR (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'ATTR{a}-="b"' 'Invalid operator for ATTR.'
-test_syntax_error 'ATTR{a}+="b"' "ATTR key takes '==', '!=', or '=' operator, assuming '='."
-test_syntax_error 'ATTR{a}="%?"' 'Invalid value "%?" for ATTR (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SYSCTL=""' 'Invalid attribute for SYSCTL.'
-test_syntax_error 'SYSCTL{%}="b"' 'Invalid attribute "%" for SYSCTL (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SYSCTL{a}-="b"' 'Invalid operator for SYSCTL.'
-test_syntax_error 'SYSCTL{a}+="b"' "SYSCTL key takes '==', '!=', or '=' operator, assuming '='."
-test_syntax_error 'SYSCTL{a}="%?"' 'Invalid value "%?" for SYSCTL (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'ATTRS=""' 'Invalid attribute for ATTRS.'
-test_syntax_error 'ATTRS{%}=="b", NAME="b"' 'Invalid attribute "%" for ATTRS (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'ATTRS{a}-="b"' 'Invalid operator for ATTRS.'
-test_syntax_error 'ATTRS{device/}!="a", NAME="b"' "'device' link may not be available in future kernels."
-test_syntax_error 'ATTRS{../}!="a", NAME="b"' 'Direct reference to parent sysfs directory, may break in future kernels.'
-test_syntax_error 'TEST{a}=="b"' "Failed to parse mode 'a': Invalid argument"
-test_syntax_error 'TEST{0}=="%", NAME="b"' 'Invalid value "%" for TEST (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
-test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
-test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
-test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
-test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
-test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
-test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
-test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
-test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'
-test_syntax_error 'OPTIONS{a}="b"' 'Invalid attribute for OPTIONS.'
-test_syntax_error 'OPTIONS-="b"' 'Invalid operator for OPTIONS.'
-test_syntax_error 'OPTIONS!="b"' 'Invalid operator for OPTIONS.'
-test_syntax_error 'OPTIONS+="link_priority=a"' "Failed to parse link priority 'a': Invalid argument"
-test_syntax_error 'OPTIONS:="log_level=a"' "Failed to parse log level 'a': Invalid argument"
-test_syntax_error 'OPTIONS="a", NAME="b"' "Invalid value for OPTIONS key, ignoring: 'a'"
-test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
-test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
-test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
-test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
-test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring."
-test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
-test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
-test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
-test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
-test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring."
-test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
-test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
-test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
-test_syntax_error 'MODE+="0"' "MODE key takes '=' or ':=' operator, assuming '='."
-test_syntax_error 'MODE="%"' 'Invalid value "%" for MODE (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SECLABEL="b"' 'Invalid attribute for SECLABEL.'
-test_syntax_error 'SECLABEL{a}="%"' 'Invalid value "%" for SECLABEL (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SECLABEL{a}!="b"' 'Invalid operator for SECLABEL.'
-test_syntax_error 'SECLABEL{a}-="b"' 'Invalid operator for SECLABEL.'
-test_syntax_error 'SECLABEL{a}:="b"' "SECLABEL key takes '=' or '+=' operator, assuming '='."
-test_syntax_error 'RUN=="b"' 'Invalid operator for RUN.'
-test_syntax_error 'RUN-="b"' 'Invalid operator for RUN.'
-test_syntax_error 'RUN="%"' 'Invalid value "%" for RUN (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'RUN{builtin}+="foo"' "Unknown builtin command 'foo', ignoring."
-test_syntax_error 'GOTO{a}="b"' 'Invalid attribute for GOTO.'
-test_syntax_error 'GOTO=="b"' 'Invalid operator for GOTO.'
-test_syntax_error 'NAME="a", GOTO="b"' 'GOTO="b" has no matching label, ignoring.'
-test_syntax_error 'GOTO="a", GOTO="b"
-LABEL="a"' 'Contains multiple GOTO keys, ignoring GOTO="b".'
-test_syntax_error 'LABEL{a}="b"' 'Invalid attribute for LABEL.'
-test_syntax_error 'LABEL=="b"' 'Invalid operator for LABEL.'
-test_style_error 'LABEL="b"' 'style: LABEL="b" is unused.'
-test_syntax_error 'a="b"' "Invalid key 'a'."
-test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL=="a|b", KERNEL=="c|d|e", NAME="f"' 'conflicting match expressions, the line has no effect.'
-# shellcheck disable=SC2016
-test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}!="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
- 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'ACTION=="a*", ACTION=="bc*", NAME="d"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'ACTION=="a*|bc*", ACTION=="d*|ef*", NAME="g"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions.'
-test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions.'
-# shellcheck disable=SC2016
-test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
- 'duplicate expressions.'
-test_style_error ',ACTION=="a", NAME="b"' 'style: stray leading comma.'
-test_style_error ' ,ACTION=="a", NAME="b"' 'style: stray leading comma.'
-test_style_error ', ACTION=="a", NAME="b"' 'style: stray leading comma.'
-test_style_error 'ACTION=="a", NAME="b",' 'style: stray trailing comma.'
-test_style_error 'ACTION=="a", NAME="b", ' 'style: stray trailing comma.'
-test_style_error 'ACTION=="a" NAME="b"' 'style: a comma between tokens is expected.'
-test_style_error 'ACTION=="a",, NAME="b"' 'style: more than one comma between tokens.'
-test_style_error 'ACTION=="a" , NAME="b"' 'style: stray whitespace before comma.'
-test_style_error 'ACTION=="a",NAME="b"' 'style: whitespace after comma is expected.'
-test_syntax_error 'RESULT=="a", PROGRAM="b"' 'Reordering RESULT check after PROGRAM assignment.'
-test_syntax_error 'RESULT=="a*", PROGRAM="b", RESULT=="*c", PROGRAM="d"' \
- 'Reordering RESULT check after PROGRAM assignment.'
-
-cat >"${rules}" <<'EOF'
-KERNEL=="a|b", KERNEL=="a|c", NAME="d"
-KERNEL=="a|b", KERNEL!="a|c", NAME="d"
-KERNEL!="a", KERNEL!="b", NAME="c"
-KERNEL=="|a", KERNEL=="|b", NAME="c"
-KERNEL=="*", KERNEL=="a*", NAME="b"
-KERNEL=="a*", KERNEL=="c*|ab*", NAME="d"
-PROGRAM="a", RESULT=="b"
-EOF
-assert_0 "${rules}"
-
-echo 'GOTO="a"' >"${rules}"
-cat >"${exp}" <<EOF
-${rules}:1 GOTO="a" has no matching label, ignoring.
-${rules}:1 The line has no effect any more, dropping.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-cat >"${rules}" <<'EOF'
-GOTO="a"
-LABEL="a"
-EOF
-assert_0 "${rules}"
-
-cat >"${rules}" <<'EOF'
-GOTO="b"
-LABEL="b"
-LABEL="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:3 style: LABEL="b" is unused.
-${rules}: udev rules have style issues.
-EOF
-assert_0_impl --no-style "${rules}"
-assert_1_impl "${rules}"
-
-cat >"${rules}" <<'EOF'
-GOTO="a"
-LABEL="a", LABEL="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
-${rules}:1 GOTO="a" has no matching label, ignoring.
-${rules}:1 The line has no effect any more, dropping.
-${rules}:2 style: LABEL="b" is unused.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-cat >"${rules}" <<'EOF'
-KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
-EOF
-cat >"${exp}" <<EOF
-${rules}:1 duplicate expressions.
-${rules}:1 conflicting match expressions, the line has no effect.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-cat >"${rules}" <<'EOF'
-ACTION=="a"NAME="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:1 style: a comma between tokens is expected.
-${rules}:1 style: whitespace between tokens is expected.
-${rules}: udev rules have style issues.
-EOF
-assert_0_impl --no-style "${rules}"
-assert_1_impl "${rules}"
-next_test_number
-
-cat >"${rules}" <<'EOF'
-ACTION=="a" ,NAME="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:1 style: stray whitespace before comma.
-${rules}:1 style: whitespace after comma is expected.
-${rules}: udev rules have style issues.
-EOF
-assert_0_impl --no-style "${rules}"
-assert_1_impl "${rules}"
-next_test_number
-
-# udevadm verify --root
-sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
-cd -
-assert_1 --root="${workdir}"
-cd -
-
-# udevadm verify path/
-sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
-cd -
-assert_1 "${rules_dir}"
-cd -
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-create_link_file() {
- name=${1?}
-
- mkdir -p /run/systemd/network/
- cat >/run/systemd/network/10-test.link <<EOF
-[Match]
-Kind=dummy
-MACAddress=00:50:56:c0:00:18
-
-[Link]
-Name=$name
-AlternativeName=test1 test2 test3 test4
-EOF
- udevadm control --reload
-}
-
-udevadm control --log-level=debug
-
-create_link_file test1
-ip link add address 00:50:56:c0:00:18 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/test1
-output=$(ip link show dev test1)
-if ! [[ "$output" =~ altname ]]; then
- echo "alternative name for network interface not supported, skipping test."
- exit 0
-fi
-assert_not_in "altname test1" "$output"
-assert_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-
-# By triggering add event, Name= and AlternativeNames= are re-applied
-create_link_file test2
-udevadm trigger --action add --settle /sys/class/net/test1
-udevadm wait --settle --timeout=30 /sys/class/net/test2
-output=$(ip link show dev test2)
-assert_in "altname test1" "$output"
-assert_not_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-
-# Name= and AlternativeNames= are not applied on move event
-create_link_file test3
-udevadm trigger --action move --settle /sys/class/net/test2
-udevadm wait --settle --timeout=30 /sys/class/net/test2
-output=$(ip link show dev test2)
-assert_in "altname test1" "$output"
-assert_not_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-
-# Test move event triggered by manual renaming
-ip link set dev test2 name hoge
-udevadm wait --settle --timeout=30 /sys/class/net/hoge
-output=$(ip link show dev hoge)
-assert_in "altname test1" "$output"
-assert_not_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-assert_not_in "altname hoge" "$output"
-
-# Re-test add event
-udevadm trigger --action add --settle /sys/class/net/hoge
-udevadm wait --settle --timeout=30 /sys/class/net/test3
-output=$(ip link show dev test3)
-assert_in "altname test1" "$output"
-assert_in "altname test2" "$output"
-assert_not_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-assert_not_in "altname hoge" "$output"
-
-# cleanup
-ip link del dev test3
-
-rm -f /run/systemd/network/10-test.link
-udevadm control --reload --log-level=info
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Test for `udevadm control -p`
-
-test_not_property() {
- assert_eq "$(udevadm info --query property --property "$2" --value "$1")" ""
-}
-
-test_property() {
- assert_eq "$(udevadm info --query property --property "$2" --value "$1")" "$3"
-}
-
-# shellcheck disable=SC2317
-cleanup() {
- set +e
-
- udevadm control -p FOO= -p BAR=
-
- rm -f "$rules"
-}
-
-# Set up a test device
-trap cleanup EXIT
-
-rules="/run/udev/rules.d/99-test-17.13.rules"
-
-mkdir -p "${rules%/*}"
-cat > "$rules" <<'EOF'
-ENV{FOO}=="?*", ENV{PROP_FOO}="$env{FOO}"
-ENV{BAR}=="?*", ENV{PROP_BAR}="$env{BAR}"
-EOF
-
-udevadm control --reload
-
-test_not_property /dev/null PROP_FOO
-test_not_property /dev/null PROP_BAR
-
-: Setting of a property works
-
-udevadm control --property FOO=foo
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO foo
-test_not_property /dev/null PROP_BAR
-
-: Change of a property works
-
-udevadm control --property FOO=goo
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO goo
-
-: Removal of a property works
-
-udevadm control --property FOO=
-udevadm trigger --action change --settle /dev/null
-test_not_property /dev/null PROP_FOO
-
-: Repeated removal of a property does nothing
-
-udevadm control --property FOO=
-udevadm trigger --action change --settle /dev/null
-test_not_property /dev/null PROP_FOO
-
-: Multiple properties can be set at once
-
-udevadm control --property FOO=foo --property BAR=bar
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO foo
-test_property /dev/null PROP_BAR bar
-
-: Multiple setting of the same property is handled correctly
-
-udevadm control --property FOO=foo --property FOO=42
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO 42
-
-: Mix of settings and removals of the same property is handled correctly
-
-udevadm control -p FOO= -p FOO=foo -p BAR=car -p BAR=
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO foo
-test_not_property /dev/null PROP_BAR
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-if [[ $(systemctl is-enabled systemd-udev-load-credentials.service) == not-found ]]; then
- echo "Missing systemd-udev-load-credentials.service" >>/skipped
- exit 0
-fi
-
-at_exit() {
- rm -f /run/credstore/udev.*
- rm -f /run/udev/udev.conf.d/*
- rm -f /run/udev/rules.d/*
- rm -rf /run/systemd/system/systemd-udev-load-credentials.service.d
-}
-
-trap at_exit EXIT
-
-mkdir -p /run/credstore
-cat > /run/credstore/udev.conf.50-testme <<EOF
-udev_log=debug
-EOF
-cat > /run/credstore/udev.rules.50-testme <<EOF
-SUBSYSTEM=="net", OPTIONS="log_level=debug"
-EOF
-
-systemctl edit systemd-udev-load-credentials.service --stdin --drop-in=50-testme.conf <<EOF
-[Service]
-LoadCredential=udev.conf.50-testme
-LoadCredential=udev.rules.50-testme
-EOF
-
-systemctl restart systemd-udev-load-credentials.service
-
-diff /run/credstore/udev.conf.50-testme /run/udev/udev.conf.d/50-testme.conf
-diff /run/credstore/udev.rules.50-testme /run/udev/rules.d/50-testme.rules
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-udevadm control --log-level=debug
-
-mkdir -p /run/systemd/network/
-cat >/run/systemd/network/10-test.link <<EOF
-[Match]
-Kind=dummy
-MACAddress=00:50:56:c0:00:19
-
-[Link]
-Name=test1
-EOF
-
-mkdir /run/systemd/network/10-test.link.d
-cat >/run/systemd/network/10-test.link.d/10-override.conf <<EOF
-[Link]
-Property=HOGE=foo BAR=baz SHOULD_BE_UNSET=unset
-UnsetProperty=SHOULD_BE_UNSET
-EOF
-
-udevadm control --reload
-
-ip link add address 00:50:56:c0:00:19 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_in "HOGE=foo" "$output"
-assert_in "BAR=baz" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-cat >/run/systemd/network/10-test.link.d/11-override.conf <<EOF
-[Link]
-Property=
-Property=HOGE2=foo2 BAR2=baz2 SHOULD_BE_UNSET=unset
-ImportProperty=HOGE
-EOF
-
-udevadm control --reload
-
-udevadm trigger --settle --action add /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_in "HOGE=foo" "$output"
-assert_in "HOGE2=foo2" "$output"
-assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# On change event, .link file will not be applied.
-udevadm trigger --settle --action change /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-### testing with udevadm test-builtin
-output=$(udevadm test-builtin --action add net_setup_link /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_in "HOGE2=foo2" "$output"
-assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
-assert_in "SHOULD_BE_UNSET=" "$output" # this is expected, as an empty assignment is also logged.
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# check that test-builtin command does not update udev database.
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm test-builtin --action change net_setup_link /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-### testing with udevadm test
-output=$(udevadm test --action add /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_in "HOGE2=foo2" "$output"
-assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# check that test command does not update udev database.
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm test --action change /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# test for specifiers
-cat >/run/systemd/network/10-test.link.d/12-override.conf <<EOF
-[Link]
-Property=
-Property=LINK_VERSION=%v
-EOF
-
-udevadm control --reload
-
-output=$(udevadm test --action add /sys/class/net/test1)
-assert_in "LINK_VERSION=$(uname -r)" "$output"
-
-udevadm trigger --settle --action add /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_in "LINK_VERSION=$(uname -r)" "$output"
-
-# test for constant properties
-cat >/run/systemd/network/10-test.link.d/13-override.conf <<EOF
-[Link]
-Property=
-Property=ACTION=foo IFINDEX=bar
-UnsetProperty=DEVPATH
-EOF
-
-udevadm control --reload
-
-output=$(udevadm test --action add /sys/class/net/test1)
-assert_in "ACTION=add" "$output"
-assert_not_in "ACTION=foo" "$output"
-assert_in "IFINDEX=" "$output"
-assert_not_in "IFINDEX=bar" "$output"
-assert_in "DEVPATH=" "$output"
-
-udevadm trigger --settle --action add /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "ACTION=foo" "$output"
-assert_in "IFINDEX=" "$output"
-assert_not_in "IFINDEX=bar" "$output"
-assert_in "DEVPATH=" "$output"
-
-# cleanup
-ip link del dev test1
-
-rm -f /run/systemd/network/10-test.link
-rm -rf /run/systemd/network/10-test.link.d
-udevadm control --reload --log-level=info
-
-exit 0
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-17-UDEV
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-udevadm settle
-
-run_subtests
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-18-FAILUREACTION
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-run --wait -p FailureAction=poweroff true
-(! systemd-run --wait -p SuccessAction=poweroff false)
-
-if ! test -f /firstphase ; then
- echo OK >/firstphase
- systemd-run --wait -p SuccessAction=reboot true
-else
- echo OK >/testok
- systemd-run --wait -p FailureAction=exit -p FailureActionExitStatus=123 false
-fi
-
-sleep infinity
+++ /dev/null
-#!/usr/bin/env bash
-set -eux
-
-# Test ExitType=cgroup
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
- echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
- exit 0
-fi
-
-systemd-analyze log-level debug
-
-# Multiple level process tree, parent process stays up
-cat >/tmp/test19-exit-cgroup.sh <<EOF
-#!/usr/bin/env bash
-set -eux
-
-# process tree: systemd -> sleep
-sleep infinity &
-disown
-
-# process tree: systemd -> bash -> bash -> sleep
-((sleep infinity); true) &
-
-systemd-notify --ready
-
-# Run the stop/kill command
-\$1 &
-
-# process tree: systemd -> bash -> sleep
-sleep infinity
-EOF
-chmod +x /tmp/test19-exit-cgroup.sh
-
-# service should be stopped cleanly
-systemd-run --wait \
- --unit=one \
- --property="Type=notify" \
- --property="ExitType=cgroup" \
- /tmp/test19-exit-cgroup.sh 'systemctl stop one'
-
-# same thing with a truthy exec condition
-systemd-run --wait \
- --unit=two \
- --property="Type=notify" \
- --property="ExitType=cgroup" \
- --property="ExecCondition=true" \
- /tmp/test19-exit-cgroup.sh 'systemctl stop two'
-
-# false exec condition: systemd-run should exit immediately with status code: 1
-(! systemd-run --wait \
- --unit=three \
- --property="Type=notify" \
- --property="ExitType=cgroup" \
- --property="ExecCondition=false" \
- /tmp/test19-exit-cgroup.sh)
-
-# service should exit uncleanly (main process exits with SIGKILL)
-(! systemd-run --wait \
- --unit=four \
- --property="Type=notify" \
- --property="ExitType=cgroup" \
- /tmp/test19-exit-cgroup.sh 'systemctl kill --signal 9 four')
-
-
-# Multiple level process tree, parent process exits quickly
-cat >/tmp/test19-exit-cgroup-parentless.sh <<EOF
-#!/usr/bin/env bash
-set -eux
-
-# process tree: systemd -> sleep
-sleep infinity &
-
-# process tree: systemd -> bash -> sleep
-((sleep infinity); true) &
-
-systemd-notify --ready
-
-# Run the stop/kill command after this bash process exits
-(sleep 1; \$1) &
-EOF
-chmod +x /tmp/test19-exit-cgroup-parentless.sh
-
-# service should be stopped cleanly
-systemd-run --wait \
- --unit=five \
- --property="Type=notify" \
- --property="ExitType=cgroup" \
- /tmp/test19-exit-cgroup-parentless.sh 'systemctl stop five'
-
-# service should still exit cleanly despite SIGKILL (the main process already exited cleanly)
-systemd-run --wait \
- --unit=six \
- --property="Type=notify" \
- --property="ExitType=cgroup" \
- /tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six'
-
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-# Create service with KillMode=none inside a slice
-cat <<EOF >/run/systemd/system/test19cleanup.service
-[Unit]
-Description=Test 19 cleanup Service
-[Service]
-Slice=test19cleanup.slice
-Type=exec
-ExecStart=sleep infinity
-KillMode=none
-EOF
-cat <<EOF >/run/systemd/system/test19cleanup.slice
-[Unit]
-Description=Test 19 cleanup Slice
-EOF
-
-# Start service
-systemctl start test19cleanup.service
-assert_rc 0 systemd-cgls /test19cleanup.slice
-
-pid=$(systemctl show --property MainPID --value test19cleanup)
-ps "$pid"
-
-# Stop slice
-# The sleep process will not be killed because of KillMode=none
-# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed
-systemctl stop test19cleanup.slice
-
-ps "$pid"
-
-# Kill sleep process manually
-kill -s TERM "$pid"
-while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done
-
-timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done'
-assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service
-
-# Check that empty cgroup /test19cleanup.slice has been removed
-timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done'
-assert_rc 1 systemd-cgls /test19cleanup.slice
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test cgroup delegation in the unified hierarchy
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
- echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
- exit 0
-fi
-
-at_exit() {
- set +e
- userdel -r test
-}
-
-systemd-run --wait \
- --unit=test-0.service \
- --property="DynamicUser=1" \
- --property="Delegate=" \
- test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
- -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \
- -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control
-
-# Test if this also works for some of the more recent attrs the kernel might or might not support
-for attr in cgroup.threads memory.oom.group memory.reclaim ; do
-
- if grep -q "$attr" /sys/kernel/cgroup/delegate ; then
- systemd-run --wait \
- --unit=test-0.service \
- --property="DynamicUser=1" \
- --property="Delegate=" \
- test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
- -w /sys/fs/cgroup/system.slice/test-0.service/"$attr"
- fi
-done
-
-systemd-run --wait \
- --unit=test-1.service \
- --property="DynamicUser=1" \
- --property="Delegate=memory pids" \
- grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers
-
-systemd-run --wait \
- --unit=test-2.service \
- --property="DynamicUser=1" \
- --property="Delegate=memory pids" \
- grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers
-
-# "io" is not among the controllers enabled by default for all units, verify that
-grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-
-# Run a service with "io" enabled, and verify it works
-systemd-run --wait \
- --unit=test-3.service \
- --property="IOAccounting=yes" \
- --property="Slice=system-foo-bar-baz.slice" \
- grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers
-
-# We want to check if "io" is removed again from the controllers
-# list. However, PID 1 (rightfully) does this asynchronously. In order
-# to force synchronization on this, let's start a short-lived service
-# which requires PID 1 to refresh the cgroup tree, so that we can
-# verify that this all works.
-systemd-run --wait --unit=test-4.service true
-
-# And now check again, "io" should have vanished
-grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-
-# Check that unprivileged delegation works for scopes
-useradd test ||:
-systemd-run --uid=test \
- --property="User=test" \
- --property="Delegate=yes" \
- --slice workload.slice \
- --unit test-workload0.scope\
- --scope \
- test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \
- -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \
- -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control
-
-# Verify that DelegateSubgroup= affects ownership correctly
-unit="test-subgroup-$RANDOM.service"
-systemd-run --wait \
- --unit="$unit" \
- --property="DynamicUser=1" \
- --property="Delegate=pids" \
- --property="DelegateSubgroup=foo" \
- test -w "/sys/fs/cgroup/system.slice/$unit" -a \
- -w "/sys/fs/cgroup/system.slice/$unit/foo"
-
-# Check that for the subgroup also attributes that aren't covered by
-# regular (i.e. main cgroup) delegation ownership rules are delegated properly
-if test -f /sys/fs/cgroup/cgroup.max.depth; then
- unit="test-subgroup-$RANDOM.service"
- systemd-run --wait \
- --unit="$unit" \
- --property="DynamicUser=1" \
- --property="Delegate=pids" \
- --property="DelegateSubgroup=zzz" \
- test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth"
-fi
-
-# Check that the invoked process itself is also in the subgroup
-unit="test-subgroup-$RANDOM.service"
-systemd-run --wait \
- --unit="$unit" \
- --property="DynamicUser=1" \
- --property="Delegate=pids" \
- --property="DelegateSubgroup=bar" \
- grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-19-DELEGATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-. /etc/os-release
-# FIXME: This test fails on opensuse with the following error and others:
-# Apr 25 10:24:04 H (cat)[910]: device-mapper: create ioctl on ... failed: Device or resource busy
-if [[ "$ID" =~ "opensuse" ]]; then
- echo "Skipping due to known unexpected behaviour in OpenSUSE kernels" >>/skipped
- exit 77
-fi
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fuzz our D-Bus interfaces with dfuzzer
-After=dbus.service multi-user.target
-Wants=dbus.service multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /skipped /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# check dfuzzer is present before testing
-if ! command -v dfuzzer &>/dev/null; then
- echo "dfuzzer is not installed, skipping" | tee --append /skipped
- exit 77
-fi
-
-# Save the end.service state before we start fuzzing, as it might get changed
-# on the fly by one of the fuzzers
-systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0
-
-# shellcheck disable=SC2317
-at_exit() {
- set +e
- # We have to call the end.service/poweroff explicitly even if it's specified on
- # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls
- # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service
- # from the queue
- if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then
- # PID1 is down let's try to save the journal
- journalctl --sync # journal can be down as well so let's ignore exit codes here
- systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF)
- fi
-}
-
-add_suppression() {
- local interface="${1:?}"
- local suppression="${2:?}"
-
- sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
-}
-
-trap at_exit EXIT
-
-systemctl log-level info
-
-# FIXME: systemd-run doesn't play well with daemon-reexec
-# See: https://github.com/systemd/systemd/issues/27204
-add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"
-
-add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
-add_suppression "org.freedesktop.login1" "Sleep destructive"
-
-# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
-# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
-# degree) by the respective method counterparts on the manager object.
-for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
- add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
-done
-
-cat /etc/dfuzzer.conf
-
-# TODO
-# * check for possibly newly introduced buses?
-BUS_LIST=(
- org.freedesktop.home1
- org.freedesktop.hostname1
- org.freedesktop.import1
- org.freedesktop.locale1
- org.freedesktop.login1
- org.freedesktop.machine1
- org.freedesktop.portable1
- org.freedesktop.resolve1
- org.freedesktop.systemd1
- org.freedesktop.timedate1
-)
-
-# systemd-oomd requires PSI
-if tail -n +1 /proc/pressure/{cpu,io,memory}; then
- BUS_LIST+=(
- org.freedesktop.oom1
- )
-fi
-
-# Some services require specific conditions:
-# - systemd-timesyncd can't run in a container
-# - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability
-if ! systemd-detect-virt --container; then
- BUS_LIST+=(
- org.freedesktop.network1
- org.freedesktop.timesync1
- )
-elif busctl introspect org.freedesktop.network1 / &>/dev/null; then
- BUS_LIST+=(
- org.freedesktop.network1
- )
-fi
-
-SESSION_BUS_LIST=(
- org.freedesktop.systemd1
-)
-
-# Maximum payload size generated by dfuzzer (in bytes) - default: 50K
-PAYLOAD_MAX=50000
-# Tweak the maximum payload size if we're running under sanitizers, since
-# with larger payloads we start hitting reply timeouts
-if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
- PAYLOAD_MAX=10000 # 10K
-fi
-
-# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing
-# the org.freedesktop.machine1 stuff makes quite a mess
-mount -t tmpfs -o size=50M tmpfs /var/lib/machines
-
-# Fuzz both the system and the session buses (where applicable)
-for bus in "${BUS_LIST[@]}"; do
- echo "Bus: $bus (system)"
- systemd-run --pipe --wait \
- -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
-
- # Let's reload the systemd daemon to test (de)serialization as well
- systemctl daemon-reload
- # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
- systemctl daemon-reexec
-done
-
-umount /var/lib/machines
-
-for bus in "${SESSION_BUS_LIST[@]}"; do
- echo "Bus: $bus (session)"
- systemd-run --machine 'testuser@.host' --user --pipe --wait \
- -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
-
- # Let's reload the systemd user daemon to test (de)serialization as well
- systemctl --machine 'testuser@.host' --user daemon-reload
- # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
- systemctl --machine 'testuser@.host' --user daemon-reexec
-done
-
-touch /testok
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# With "e" don't attempt to set permissions when file doesn't exist, see
-# https://github.com/systemd/systemd/pull/6682.
-set -eux
-set -o pipefail
-
-rm -fr /tmp/test
-
-echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
-
-test ! -e /tmp/test
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Basic tests for types creating directories
-set -eux
-set -o pipefail
-
-rm -fr /tmp/{C,d,D,e}
-mkdir /tmp/{C,d,D,e}
-
-#
-# 'd'
-#
-mkdir /tmp/d/2
-chmod 777 /tmp/d/2
-
-systemd-tmpfiles --dry-run --create - <<EOF
-d /tmp/d/1 0755 daemon daemon - -
-d /tmp/d/2 0755 daemon daemon - -
-EOF
-
-test ! -d /tmp/d/1
-test -d /tmp/d/2
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/d/1 0755 daemon daemon - -
-d /tmp/d/2 0755 daemon daemon - -
-EOF
-
-test -d /tmp/d/1
-test "$(stat -c %U:%G:%a /tmp/d/1)" = "daemon:daemon:755"
-
-test -d /tmp/d/2
-test "$(stat -c %U:%G:%a /tmp/d/2)" = "daemon:daemon:755"
-
-#
-# 'D'
-#
-mkdir /tmp/D/2
-chmod 777 /tmp/D/2
-touch /tmp/D/2/foo
-
-systemd-tmpfiles --create - <<EOF
-D /tmp/D/1 0755 daemon daemon - -
-D /tmp/D/2 0755 daemon daemon - -
-EOF
-
-test -d /tmp/D/1
-test "$(stat -c %U:%G:%a /tmp/D/1)" = "daemon:daemon:755"
-
-test -d /tmp/D/2
-test "$(stat -c %U:%G:%a /tmp/D/2)" = "daemon:daemon:755"
-
-systemd-tmpfiles --remove - <<EOF
-D /tmp/D/2 0755 daemon daemon - -
-EOF
-
-# the content of '2' should be removed
-test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
-
-#
-# 'e'
-#
-mkdir -p /tmp/e/2/{d1,d2}
-chmod 777 /tmp/e/2
-chmod 777 /tmp/e/2/d*
-
-systemd-tmpfiles --create - <<EOF
-e /tmp/e/1 0755 daemon daemon - -
-e /tmp/e/2/* 0755 daemon daemon - -
-EOF
-
-test ! -d /tmp/e/1
-
-test -d /tmp/e/2
-test "$(stat -c %U:%G:%a /tmp/e/2)" = "root:root:777"
-
-test -d /tmp/e/2/d1
-test "$(stat -c %U:%G:%a /tmp/e/2/d1)" = "daemon:daemon:755"
-test -d /tmp/e/2/d2
-test "$(stat -c %U:%G:%a /tmp/e/2/d2)" = "daemon:daemon:755"
-
-# 'e' operates on directories only
-mkdir -p /tmp/e/3/{d1,d2}
-chmod 777 /tmp/e/3
-chmod 777 /tmp/e/3/d*
-touch /tmp/e/3/f1
-chmod 644 /tmp/e/3/f1
-
-systemd-tmpfiles --create - <<EOF
-e /tmp/e/3/* 0755 daemon daemon - -
-EOF
-
-# the directories should have been processed although systemd-tmpfiles failed
-# previously due to the presence of a file.
-test -d /tmp/e/3/d1
-test "$(stat -c %U:%G:%a /tmp/e/3/d1)" = "daemon:daemon:755"
-test -d /tmp/e/3/d2
-test "$(stat -c %U:%G:%a /tmp/e/3/d2)" = "daemon:daemon:755"
-
-test -f /tmp/e/3/f1
-test "$(stat -c %U:%G:%a /tmp/e/3/f1)" = "root:root:644"
-
-#
-# 'C'
-#
-
-mkdir /tmp/C/{0,1,2,3}-origin
-touch /tmp/C/{1,2,3}-origin/f1
-chmod 755 /tmp/C/{1,2,3}-origin/f1
-
-mkdir /tmp/C/{2,3}
-touch /tmp/C/3/f1
-
-systemd-tmpfiles --dry-run --create - <<EOF
-C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin
-C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin
-EOF
-
-test ! -d /tmp/C/1
-test -d /tmp/C/2
-
-systemd-tmpfiles --create - <<EOF
-C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin
-C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin
-EOF
-
-test -d /tmp/C/1
-test "$(stat -c %U:%G:%a /tmp/C/1/f1)" = "daemon:daemon:755"
-test -d /tmp/C/2
-test "$(stat -c %U:%G:%a /tmp/C/2/f1)" = "daemon:daemon:755"
-
-systemd-tmpfiles --create - <<EOF
-C /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin
-C /tmp/C/4 0755 daemon daemon - /tmp/C/definitely-missing
-EOF
-
-test "$(stat -c %U:%G:%a /tmp/C/3/f1)" = "root:root:644"
-test ! -e /tmp/C/4
-
-touch /tmp/C/3-origin/f{2,3,4}
-echo -n ABC > /tmp/C/3/f1
-
-systemd-tmpfiles --create - <<EOF
-C+ /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin
-EOF
-
-# Test that the trees got merged, even though /tmp/C/3 already exists.
-test -e /tmp/C/3/f1
-test -e /tmp/C/3/f2
-test -e /tmp/C/3/f3
-test -e /tmp/C/3/f4
-
-# Test that /tmp/C/3/f1 did not get overwritten.
-test "$(cat /tmp/C/3/f1)" = "ABC"
-
-# Check that %U expands to 0, both in the path and in the argument.
-home='/tmp/C'
-systemd-tmpfiles --create - <<EOF
-C $home/%U - - - - $home/%U-origin
-EOF
-
-test -d "$home/0"
-
-# Check that %h expands to $home, both in the path and in the argument.
-HOME="$home" \
-systemd-tmpfiles --create - <<EOF
-C %h/5 - - - - %h/3-origin
-EOF
-
-test -f "$home/5/f1"
-
-# Check that %h in the path is expanded, but
-# the result of this expansion is not expanded once again.
-root='/tmp/C/6'
-home='/%U'
-mkdir -p "$root/usr/share/factory$home"
-HOME="$home" \
-systemd-tmpfiles --create --root="$root" - <<EOF
-C %h - - - -
-EOF
-
-test -d "$root$home"
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Basic tests for types creating/writing files
-set -eux
-set -o pipefail
-
-rm -fr /tmp/{f,F,w}
-mkdir /tmp/{f,F,w}
-touch /tmp/file-owned-by-root
-
-#
-# 'f'
-#
-systemd-tmpfiles --dry-run --create - <<EOF
-f /tmp/f/1 0644 - - - -
-f /tmp/f/2 0644 - - - This string should be written
-EOF
-
-test ! -e /tmp/f/1
-test ! -e /tmp/f/2
-
-systemd-tmpfiles --create - <<EOF
-f /tmp/f/1 0644 - - - -
-f /tmp/f/2 0644 - - - This string should be written
-EOF
-
-### '1' should exist and be empty
-test -f /tmp/f/1; test ! -s /tmp/f/1
-test "$(stat -c %U:%G:%a /tmp/f/1)" = "root:root:644"
-
-test "$(stat -c %U:%G:%a /tmp/f/2)" = "root:root:644"
-test "$(< /tmp/f/2)" = "This string should be written"
-
-### The perms are supposed to be updated even if the file already exists.
-systemd-tmpfiles --create - <<EOF
-f /tmp/f/1 0666 daemon daemon - This string should not be written
-EOF
-
-# file should be empty
-test ! -s /tmp/f/1
-test "$(stat -c %U:%G:%a /tmp/f/1)" = "daemon:daemon:666"
-
-### But we shouldn't try to set perms on an existing file which is not a
-### regular one.
-mkfifo /tmp/f/fifo
-chmod 644 /tmp/f/fifo
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/f/fifo 0666 daemon daemon - This string should not be written
-EOF
-
-test -p /tmp/f/fifo
-test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
-
-### 'f' should not follow symlinks.
-ln -s missing /tmp/f/dangling
-ln -s /tmp/file-owned-by-root /tmp/f/symlink
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/f/dangling 0644 daemon daemon - -
-f /tmp/f/symlink 0644 daemon daemon - -
-EOF
-test ! -e /tmp/f/missing
-test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
-
-### Handle read-only filesystem gracefully: we shouldn't fail if the target
-### already exists and have the correct perms.
-mkdir /tmp/f/rw-fs
-mkdir /tmp/f/ro-fs
-
-touch /tmp/f/rw-fs/foo
-chmod 644 /tmp/f/rw-fs/foo
-
-mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
-
-systemd-tmpfiles --create - <<EOF
-f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written
-EOF
-test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/f/ro-fs/foo 0666 - - - -
-EOF
-test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/f/ro-fs/bar 0644 - - - -
-EOF
-test ! -e /tmp/f/ro-fs/bar
-
-### 'f' shouldn't follow unsafe paths.
-mkdir /tmp/f/daemon
-ln -s /root /tmp/f/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/f/daemon
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
-EOF
-test ! -e /tmp/f/daemon/unsafe-symlink/exploit
-
-#
-# 'F'
-#
-echo "This should be truncated" >/tmp/F/truncated
-echo "This should be truncated" >/tmp/F/truncated-with-content
-
-systemd-tmpfiles --create - <<EOF
-F /tmp/F/created 0644 - - - -
-F /tmp/F/created-with-content 0644 - - - new content
-F /tmp/F/truncated 0666 daemon daemon - -
-F /tmp/F/truncated-with-content 0666 daemon daemon - new content
-EOF
-
-test -f /tmp/F/created; test ! -s /tmp/F/created
-test -f /tmp/F/created-with-content
-test "$(< /tmp/F/created-with-content)" = "new content"
-test -f /tmp/F/truncated; test ! -s /tmp/F/truncated
-test "$(stat -c %U:%G:%a /tmp/F/truncated)" = "daemon:daemon:666"
-test -s /tmp/F/truncated-with-content
-test "$(stat -c %U:%G:%a /tmp/F/truncated-with-content)" = "daemon:daemon:666"
-
-### We shouldn't try to truncate anything but regular files since the behavior is
-### unspecified in the other cases.
-mkfifo /tmp/F/fifo
-
-(! systemd-tmpfiles --create -) <<EOF
-F /tmp/F/fifo 0644 - - - -
-EOF
-
-test -p /tmp/F/fifo
-
-### 'F' should not follow symlinks.
-ln -s missing /tmp/F/dangling
-ln -s /tmp/file-owned-by-root /tmp/F/symlink
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/F/dangling 0644 daemon daemon - -
-f /tmp/F/symlink 0644 daemon daemon - -
-EOF
-test ! -e /tmp/F/missing
-test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
-
-### Handle read-only filesystem gracefully: we shouldn't fail if the target
-### already exists and is empty.
-mkdir /tmp/F/rw-fs
-mkdir /tmp/F/ro-fs
-
-touch /tmp/F/rw-fs/foo
-chmod 644 /tmp/F/rw-fs/foo
-
-mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
-
-systemd-tmpfiles --create - <<EOF
-F /tmp/F/ro-fs/foo 0644 - - - -
-EOF
-test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo
-
-echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
-(! systemd-tmpfiles --create -) <<EOF
-F /tmp/F/ro-fs/foo 0644 - - - -
-EOF
-
-(! systemd-tmpfiles --create -) <<EOF
-F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written
-EOF
-test -f /tmp/F/ro-fs/foo
-grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo
-
-# Trying to change the perms should fail.
-: >/tmp/F/rw-fs/foo
-(! systemd-tmpfiles --create -) <<EOF
-F /tmp/F/ro-fs/foo 0666 - - - -
-EOF
-test "$(stat -c %U:%G:%a /tmp/F/ro-fs/foo)" = "root:root:644"
-
-### Try to create a new file.
-(! systemd-tmpfiles --create -) <<EOF
-F /tmp/F/ro-fs/bar 0644 - - - -
-EOF
-test ! -e /tmp/F/ro-fs/bar
-
-### 'F' shouldn't follow unsafe paths.
-mkdir /tmp/F/daemon
-ln -s /root /tmp/F/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/F/daemon
-
-(! systemd-tmpfiles --create -) <<EOF
-F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
-EOF
-test ! -e /tmp/F/daemon/unsafe-symlink/exploit
-
-#
-# 'w'
-#
-touch /tmp/w/overwritten
-touch /tmp/w/appended
-
-### nop if the target does not exist.
-systemd-tmpfiles --dry-run --create - <<EOF
-w /tmp/w/unexistent 0644 - - - new content
-EOF
-test ! -e /tmp/w/unexistent
-
-systemd-tmpfiles --create - <<EOF
-w /tmp/w/unexistent 0644 - - - new content
-EOF
-test ! -e /tmp/w/unexistent
-
-### no argument given -> fails.
-(! systemd-tmpfiles --create -) <<EOF
-w /tmp/w/unexistent 0644 - - - -
-EOF
-
-### write into an empty file.
-systemd-tmpfiles --dry-run --create - <<EOF
-w /tmp/w/overwritten 0644 - - - old content
-EOF
-test -f /tmp/w/overwritten
-test -z "$(< /tmp/w/overwritten)"
-
-systemd-tmpfiles --create - <<EOF
-w /tmp/w/overwritten 0644 - - - old content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "old content"
-
-### old content is overwritten
-systemd-tmpfiles --dry-run --create - <<EOF
-w /tmp/w/overwritten 0644 - - - new content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "old content"
-
-systemd-tmpfiles --create - <<EOF
-w /tmp/w/overwritten 0644 - - - new content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "new content"
-
-### append lines
-systemd-tmpfiles --create - <<EOF
-w+ /tmp/w/appended 0644 - - - 1
-w+ /tmp/w/appended 0644 - - - 2\n
-w+ /tmp/w/appended 0644 - - - 3
-EOF
-test -f /tmp/w/appended
-test "$(< /tmp/w/appended)" = "$(echo -ne '12\n3')"
-
-### writing into an 'exotic' file should be allowed.
-systemd-tmpfiles --dry-run --create - <<EOF
-w /dev/null - - - - new content
-EOF
-
-systemd-tmpfiles --create - <<EOF
-w /dev/null - - - - new content
-EOF
-
-### 'w' follows symlinks
-ln -s ./overwritten /tmp/w/symlink
-systemd-tmpfiles --create - <<EOF
-w /tmp/w/symlink - - - - $(readlink -e /tmp/w/symlink)
-EOF
-readlink -e /tmp/w/symlink
-test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
-
-### 'w' shouldn't follow unsafe paths.
-mkdir /tmp/w/daemon
-ln -s /root /tmp/w/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/w/daemon
-
-(! systemd-tmpfiles --create -) <<EOF
-f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
-EOF
-test ! -e /tmp/w/daemon/unsafe-symlink/exploit
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Basic tests for types creating fifos
-set -eux
-set -o pipefail
-
-rm -fr /tmp/p
-mkdir /tmp/p
-touch /tmp/p/f1
-
-systemd-tmpfiles --dry-run --create - <<EOF
-p /tmp/p/fifo1 0666 - - - -
-EOF
-
-test ! -p /tmp/p/fifo1
-
-systemd-tmpfiles --create - <<EOF
-p /tmp/p/fifo1 0666 - - - -
-EOF
-
-test -p /tmp/p/fifo1
-test "$(stat -c %U:%G:%a /tmp/p/fifo1)" = "root:root:666"
-
-# Refuse to overwrite an existing file. Error is not propagated.
-systemd-tmpfiles --create - <<EOF
-p /tmp/p/f1 0666 - - - -
-EOF
-
-test -f /tmp/p/f1
-
-# unless '+' prefix is used
-systemd-tmpfiles --create - <<EOF
-p+ /tmp/p/f1 0666 - - - -
-EOF
-
-test -p /tmp/p/f1
-test "$(stat -c %U:%G:%a /tmp/p/f1)" = "root:root:666"
-
-#
-# Must be fixed
-#
-# mkdir /tmp/p/daemon
-# #ln -s /root /tmp/F/daemon/unsafe-symlink
-# chown -R --no-dereference daemon:daemon /tmp/p/daemon
-#
-# systemd-tmpfiles --create - <<EOF
-# p /tmp/p/daemon/fifo2 0666 daemon daemon - -
-# EOF
+++ /dev/null
-#! /bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-rm -fr /tmp/{z,Z}
-mkdir /tmp/{z,Z}
-
-#
-# 'z'
-#
-mkdir /tmp/z/d{1,2}
-touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
-
-systemd-tmpfiles --dry-run --create - <<EOF
-z /tmp/z/f1 0755 daemon daemon - -
-z /tmp/z/d1 0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U /tmp/z/f1)" = "$USER"
-test "$(stat -c %U /tmp/z/d1)" = "$USER"
-test "$(stat -c %U /tmp/z/d1/f11)" = "$USER"
-
-systemd-tmpfiles --create - <<EOF
-z /tmp/z/f1 0755 daemon daemon - -
-z /tmp/z/d1 0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U:%G /tmp/z/f1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/z/d1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/z/d1/f11)" = "root:root"
-
-systemd-tmpfiles --create - <<EOF
-z /tmp/z/d2/* 0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U:%G /tmp/z/d2/f21)" = "daemon:daemon"
-
-#
-# 'Z'
-#
-mkdir /tmp/Z/d1 /tmp/Z/d1/d11
-touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
-
-systemd-tmpfiles --create - <<EOF
-Z /tmp/Z/f1 0755 daemon daemon - -
-Z /tmp/Z/d1 0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U:%G /tmp/Z/f1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1/d11)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1/f11)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1/d11/f111)" = "daemon:daemon"
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Inspired by https://github.com/systemd/systemd/issues/9508
-set -eux
-set -o pipefail
-
-test_snippet() {
- # First call with --dry-run to test the code paths
- systemd-tmpfiles --dry-run "$@" - <<EOF
-d /var/tmp/foobar-test-06
-d /var/tmp/foobar-test-06/important
-R /var/tmp/foobar-test-06
-EOF
-
- systemd-tmpfiles "$@" - <<EOF
-d /var/tmp/foobar-test-06
-d /var/tmp/foobar-test-06/important
-R /var/tmp/foobar-test-06
-EOF
-}
-
-test_snippet --create --remove
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-
-test_snippet --remove
-test ! -f /var/tmp/foobar-test-06
-test ! -f /var/tmp/foobar-test-06/important
-
-test_snippet --create
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-
-touch /var/tmp/foobar-test-06/something-else
-
-test_snippet --create
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-test -f /var/tmp/foobar-test-06/something-else
-
-test_snippet --create --remove
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-test ! -f /var/tmp/foobar-test-06/something-else
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
-set -eux
-set -o pipefail
-
-rm -rf /tmp/test-prefix
-
-mkdir /tmp/test-prefix
-touch /tmp/test-prefix/file
-
-systemd-tmpfiles --remove - <<EOF
-r /tmp/test-prefix
-r /tmp/test-prefix/file
-EOF
-
-test ! -f /tmp/test-prefix/file
-test ! -f /tmp/test-prefix
-
-mkdir /tmp/test-prefix
-touch /tmp/test-prefix/file
-
-systemd-tmpfiles --remove - <<EOF
-r /tmp/test-prefix/file
-r /tmp/test-prefix
-EOF
-
-test ! -f /tmp/test-prefix/file
-test ! -f /tmp/test-prefix
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Verify tmpfiles can run in a root directory under a path prefix that contains
-# directories owned by unprivileged users, for example when a root file system
-# is mounted in a regular user's home directory.
-#
-# https://github.com/systemd/systemd/pull/11820
-set -eux
-set -o pipefail
-
-rm -fr /tmp/root /tmp/user
-mkdir -p /tmp/root /tmp/user/root
-chown daemon:daemon /tmp/user
-
-# Verify the command works as expected with no prefix or a root-owned prefix.
-echo 'd /tmp/root/test1' | systemd-tmpfiles --create -
-test -d /tmp/root/test1
-echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create -
-test -d /tmp/root/test2
-
-# Verify the command fails to write to a root-owned subdirectory under an
-# unprivileged user's directory when it's not part of the prefix, as expected
-# by the unsafe_transition function.
-echo 'd /tmp/user/root/test' | (! systemd-tmpfiles --create -)
-test ! -e /tmp/user/root/test
-echo 'd /user/root/test' | (! systemd-tmpfiles --root=/tmp --create -)
-test ! -e /tmp/user/root/test
-
-# Verify the above works when all user-owned directories are in the prefix.
-echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
-test -d /tmp/user/root/test
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Make sure that the "stat" output is not locale dependent.
-export LANG=C LC_ALL=C
-
-# first, create file without suid/sgid
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx 0755 1 1 - -
-f /tmp/yyy 0755 1 1 - -
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
-
-# then, add suid/sgid
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx 04755
-f /tmp/yyy 02755
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
-
-# then, chown the files to somebody else
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx - 2 2
-f /tmp/yyy - 2 2
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
-
-# then, chown the files to a third user/group but also drop to a mask that has
-# both more and fewer bits set
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx 0770 3 3
-f /tmp/yyy 0770 3 3
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
-
-# return to the beginning
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx 0755 1 1 - -
-f /tmp/yyy 0755 1 1 - -
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
-
-# remove everything
-systemd-tmpfiles --remove - <<EOF
-r /tmp/xxx
-r /tmp/yyy
-EOF
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx1 0644 - - - foo
-f /tmp/xxx2 0644 - - - foo bar
-f /tmp/xxx3 0644 - - - foo\x20bar
-f /tmp/xxx4 0644 - - - \x20foobar
-f /tmp/xxx5 0644 - - - foobar\x20
-f /tmp/xxx6 0644 - - - foo bar
-f /tmp/xxx7 0644 - - - foo bar \n
-f /tmp/xxx8 0644 - - - " foo bar "
-f /tmp/xxx9 0644 - - - ' foo bar '
-EOF
-
-echo -n "foo" | cmp /tmp/xxx1 -
-echo -n "foo bar" | cmp /tmp/xxx2 -
-echo -n "foo bar" | cmp /tmp/xxx3 -
-echo -n " foobar" | cmp /tmp/xxx4 -
-echo -n "foobar " | cmp /tmp/xxx5 -
-echo -n "foo bar" | cmp /tmp/xxx6 -
-echo "foo bar " | cmp /tmp/xxx7 -
-echo -n "\" foo bar \"" | cmp /tmp/xxx8 -
-echo -n "' foo bar '" | cmp /tmp/xxx9 -
-
-rm /tmp/xxx{1,2,3,4,5,6,7,8,9}
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-set -x
-
-rm -fr /tmp/x
-mkdir /tmp/x
-
-#
-# 'x'
-#
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --clean --dry-run - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test ! -f /tmp/x/z1
-test ! -f /tmp/x/z2
-
-#
-# 'X'
-#
-
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test ! -f /tmp/x/1/x1
-test ! -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test ! -f /tmp/x/z1
-test ! -f /tmp/x/z2
-
-#
-# 'x' with glob
-#
-
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/[1345]
-x /tmp/x/z*
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/[1345]
-x /tmp/x/z*
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-#
-# 'X' with glob
-#
-
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/[1345]
-X /tmp/x/?[12]
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/[1345]
-X /tmp/x/?[12]
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test ! -f /tmp/x/1/x1
-test ! -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-#
-# 'x' with 'r'
-#
-
-mkdir -p /tmp/x/{1,2}/a
-touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-# x/X is not supposed to influence r
-x /tmp/x/1/a
-X /tmp/x/2/a
-r /tmp/x/1
-r /tmp/x/2
-EOF
-
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-systemd-tmpfiles --clean - <<EOF
-# x/X is not supposed to influence r
-x /tmp/x/1/a
-X /tmp/x/2/a
-r /tmp/x/1
-r /tmp/x/2
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -d /tmp/x/1/a
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-#
-# 'x' with 'R'
-#
-
-mkdir -p /tmp/x/{1,2}/a
-touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
-
-systemd-tmpfiles --dry-run --remove - <<EOF
-# Check that X is honoured below R
-X /tmp/x/1/a
-X /tmp/x/2/a
-R /tmp/x/1
-EOF
-
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-systemd-tmpfiles --remove - <<EOF
-# Check that X is honoured below R
-X /tmp/x/1/a
-X /tmp/x/2/a
-R /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -d /tmp/x/1/a
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-#
-# 'r/R/D' and non-directories
-#
-
-touch /tmp/x/{11,22,33}
-
-systemd-tmpfiles --dry-run --remove - <<EOF
-# Check that X is honoured below R
-r /tmp/x/11
-R /tmp/x/22
-D /tmp/x/33
-EOF
-
-test -f /tmp/x/11
-test -f /tmp/x/22
-test -f /tmp/x/33
-
-systemd-tmpfiles --remove - <<EOF
-# Check that X is honoured below R
-r /tmp/x/11
-R /tmp/x/22
-D /tmp/x/33
-EOF
-
-find /tmp/x | sort
-test ! -f /tmp/x/11
-test ! -f /tmp/x/22
-test -f /tmp/x/33
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
-set -e
-set -x
-
-# Test directory structure looks like this:
-# /tmp/ageby/
-# ├── d1
-# │ ├── f1
-# │ ├── f2
-# │ ├── f3
-# │ └── f4
-# ├── d2
-# │ ├── f1
-# │ ├── f2
-# ...
-
-export SYSTEMD_LOG_LEVEL="debug"
-
-rm -rf /tmp/ageby
-mkdir -p /tmp/ageby/d{1..4}
-
-# TODO: There is probably a better way to figure this out.
-# Test for [bB] age-by arguments only on filesystems that expose
-# the creation time. Note that this is _not_ an accurate way to
-# check if the filesystem or kernel version don't provide the
-# timestamp. But, if the timestamp is visible in "stat" it is a
-# good indicator that the test can be run.
-TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
-if stat --format "%w" /tmp/ageby 2>/dev/null | grep -qv '^[\?\-]$'; then
- TEST_TMPFILES_AGEBY_BTIME=1
-fi
-
-touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
-touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
-
-# Create a bunch of other files.
-touch /tmp/ageby/d{1,2}/f{2..4}
-
-# For "ctime".
-touch /tmp/ageby/d3/f1
-chmod +x /tmp/ageby/d3/f1
-sleep 1
-
-# For "btime".
-touch /tmp/ageby/d4/f1
-sleep 1
-
-# More files with recent "{a,b}time" values.
-touch /tmp/ageby/d{3,4}/f{2..4}
-
-# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
-systemd-tmpfiles --dry-run --clean - <<-EOF
-d /tmp/ageby/d1 - - - a:1m -
-e /tmp/ageby/d2 - - - m:3m -
-D /tmp/ageby/d3 - - - c:2s -
-EOF
-
-for d in d{1..3}; do
- test -f "/tmp/ageby/${d}/f1"
-done
-
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/d1 - - - a:1m -
-e /tmp/ageby/d2 - - - m:3m -
-D /tmp/ageby/d3 - - - c:2s -
-EOF
-
-for d in d{1..3}; do
- test ! -f "/tmp/ageby/${d}/f1"
-done
-
-if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
- systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/d4 - - - b:1s -
-EOF
-
- test ! -f "/tmp/ageby/d4/f1"
-else
- # Remove the file manually.
- rm "/tmp/ageby/d4/f1"
-fi
-
-# Check for an invalid "age" and "age-by" arguments.
-for a in ':' ':1s' '2:1h' 'nope:42h' '" :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do
- systemd-tmpfiles --clean - <<EOF 2>&1 | grep -q -F 'Invalid age'
-d /tmp/ageby - - - ${a} -
-EOF
-done
-
-for d in d{1..4}; do
- for f in f{2..4}; do
- test -f "/tmp/ageby/${d}/${f}"
- done
-done
-
-# Check for parsing with whitespace, repeated values
-# for "age-by" (valid arguments).
-for a in '" a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
- systemd-tmpfiles --clean - <<EOF
-d /tmp/ageby - - - ${a} -
-EOF
-done
-
-for d in d{1..4}; do
- for f in f{2..4}; do
- test -f "/tmp/ageby/${d}/${f}"
- done
-done
-
-# Check that all files are removed if the "Age" is
-# set to "0" (regardless of "age-by" argument).
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/d1 - - - abc:0 -
-e /tmp/ageby/d2 - - - cmb:0 -
-EOF
-
-for d in d{1,2}; do
- for f in f{2..4}; do
- test ! -f "/tmp/ageby/${d}/${f}"
- done
-done
-
-# Check for combinations:
-# - "/tmp/ageby/d3/f2" has file timestamps that
-# are older than the specified age, it will be
-# removed
-# - "/tmp/ageby/d4/f2", has not aged for the given
-# timestamp combination, it will not be removed
-touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
-touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
-systemd-tmpfiles --clean - <<-EOF
-e /tmp/ageby/d3 - - - am:3m -
-D /tmp/ageby/d4 - - - mc:7m -
-EOF
-
-test ! -f "/tmp/ageby/d3/f2"
-test -f "/tmp/ageby/d4/f2"
-
-# Check that all files are removed if only "Age" is set to 0.
-systemd-tmpfiles --clean - <<-EOF
-e /tmp/ageby/d3 - - - 0s
-d /tmp/ageby/d4 - - - 0s
-EOF
-
-for d in d{3,4}; do
- for f in f{2..4}; do
- test ! -f "/tmp/ageby/$d/${f}"
- done
-done
-
-# Check "age-by" argument for sub-directories in "/tmp/ageby".
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/ - - - A:1m -
-EOF
-
-for d in d{1..4}; do
- test -d "/tmp/ageby/${d}"
-done
-
-# Check for combinations.
-touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
-systemd-tmpfiles --clean - <<-EOF
-e /tmp/ageby/ - - - AM:4s -
-EOF
-
-for d in d{1,2}; do
- test ! -d "/tmp/ageby/${d}"
-done
-
-for d in d{3,4}; do
- test -d "/tmp/ageby/${d}"
-done
-
-# Check "btime" for directories.
-if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
- systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/ - - - B:8s -
-EOF
-
- for d in d{3,4}; do
- test -d "/tmp/ageby/${d}"
- done
-fi
-
-# To bump "atime".
-touch -a --date "1 second ago" /tmp/ageby/d3
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/ - - - A:2s -
-EOF
-
-test -d /tmp/ageby/d3
-test ! -d /tmp/ageby/d4
-
-# Check if sub-directories are removed regardless
-# of "age-by", when "Age" is set to "0".
-systemd-tmpfiles --clean - <<-EOF
-D /tmp/ageby/ - - - AM:0 -
-EOF
-
-test ! -d /tmp/ageby/d3
-
-# Cleanup the test directory (fail if not empty).
-rmdir /tmp/ageby
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for configuration directory and file precedences
-#
-set -eux
-
-rm -f /{usr/lib,etc}/tmpfiles.d/{L,w}-*.conf
-rm -fr /tmp/precedence/{L,w}
-
-mkdir -p /{usr/lib,etc}/tmpfiles.d
-mkdir -p /tmp/precedence/{L,w}
-
-#
-# 'L'
-#
-ln -s /dev/null /tmp/precedence/L
-
-# Overwrite the existing symlink
-cat >/usr/lib/tmpfiles.d/L-z.conf<<EOF
-L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-z.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-z.conf"
-
-# Files in /etc should override those in /usr
-cat >/etc/tmpfiles.d/L-z.conf<<EOF
-L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-z.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-z.conf"
-
-# /usr/…/L-a.conf has higher prio than /etc/…/L-z.conf
-cat >/usr/lib/tmpfiles.d/L-a.conf<<EOF
-L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-a.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-a.conf"
-
-# Files in /etc should override those in /usr
-cat >/etc/tmpfiles.d/L-a.conf<<EOF
-L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-a.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-a.conf"
-
-#
-# 'w'
-#
-touch /tmp/precedence/w/f
-
-# Multiple configuration files specifying 'w+' for the same path is allowed.
-for i in a c; do
- cat >/usr/lib/tmpfiles.d/w-$i.conf<<EOF
-w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-$i.conf\n
-EOF
- cat >/etc/tmpfiles.d/w-$i.conf<<EOF
-w+ /tmp/precedence/w/f - - - - /etc/tmpfiles.d/w-$i.conf\n
-EOF
-done
-
-cat >/usr/lib/tmpfiles.d/w-b.conf<<EOF
-w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-b.conf\n
-EOF
-
-systemd-tmpfiles --create
-cmp /tmp/precedence/w/f <<EOF
-/etc/tmpfiles.d/w-a.conf
-/usr/lib/tmpfiles.d/w-b.conf
-/etc/tmpfiles.d/w-c.conf
-EOF
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for the ":" uid/gid/mode modifier
-#
-set -eux
-
-rm -rf /tmp/someinode
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0123 :1 :1
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0321 :2 :2
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode 0321 2 2
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0123 :1 :1
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
-
-rm -rf /tmp/someinode
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0123 :1 :1
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
-
-rm -rf /tmp/someinode
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Check specifier expansion in L lines.
-#
-set -eux
-
-rm -fr /tmp/L
-mkdir /tmp/L
-
-# Check that %h expands to $home.
-home='/somewhere'
-dst='/tmp/L/1'
-src="$home"
-HOME="$home" \
-systemd-tmpfiles --dry-run --create - <<EOF
-L $dst - - - - %h
-EOF
-test ! -h "$dst"
-
-HOME="$home" \
-systemd-tmpfiles --create - <<EOF
-L $dst - - - - %h
-EOF
-test "$(readlink "$dst")" = "$src"
-
-# Check that %h in the path is expanded, but
-# the result of this expansion is not expanded once again.
-root='/tmp/L/2'
-home='/%U'
-src="/usr/share/factory$home"
-mkdir -p "$root$src"
-dst="$root$home"
-HOME="$home" \
-systemd-tmpfiles --create --dry-run --root="$root" - <<EOF
-L %h - - - -
-EOF
-test ! -h "$dst"
-
-HOME="$home" \
-systemd-tmpfiles --create --root="$root" - <<EOF
-L %h - - - -
-EOF
-test "$(readlink "$dst")" = "$src"
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Test for conditionalized execute bit ('X' bit)
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-rm -f /tmp/acl_exec
-touch /tmp/acl_exec
-
-# No ACL set yet
-systemd-tmpfiles --dry-run --create - <<EOF
-a /tmp/acl_exec - - - - u:root:rwX
-EOF
-assert_not_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
-
-systemd-tmpfiles --create - <<EOF
-a /tmp/acl_exec - - - - u:root:rwX
-EOF
-assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
-
-# Set another ACL and append
-setfacl -m g:root:x /tmp/acl_exec
-
-systemd-tmpfiles --create - <<EOF
-a+ /tmp/acl_exec - - - - u:root:rwX
-EOF
-acl="$(getfacl -Ec /tmp/acl_exec)"
-assert_in 'user:root:rwx' "$acl"
-assert_in 'group:root:--x' "$acl"
-
-# Reset ACL (no append)
-systemd-tmpfiles --create - <<EOF
-a /tmp/acl_exec - - - - u:root:rwX
-EOF
-assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
-
-rm -f /tmp/acl_exec
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Test for C-style escapes in file names and contents
-set -eux
-set -o pipefail
-
-data="\x20foo\nbar"
-dst="/tmp/x/\x20a\nb"
-
-systemd-tmpfiles --create - <<EOF
-f "$dst" 0644 0 0 - $data
-EOF
-
-diff "$(printf "/tmp/x/\x20a\nb")" <(printf "\x20foo\nbar")
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for the --purge switch
-#
-set -eux
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-
-c='
-d /tmp/somedir
-f /tmp/somedir/somefile - - - - baz
-'
-
-systemd-tmpfiles --create - <<<"$c"
-test -f /tmp/somedir/somefile
-grep -q baz /tmp/somedir/somefile
-
-systemd-tmpfiles --purge --dry-run - <<<"$c"
-test -f /tmp/somedir/somefile
-grep -q baz /tmp/somedir/somefile
-
-systemd-tmpfiles --purge - <<<"$c"
-test ! -f /tmp/somedir/somefile
-test ! -d /tmp/somedir/
-
-systemd-tmpfiles --create --purge --dry-run - <<<"$c"
-test ! -f /tmp/somedir/somefile
-test ! -d /tmp/somedir/
-
-systemd-tmpfiles --create --purge - <<<"$c"
-test -f /tmp/somedir/somefile
-grep -q baz /tmp/somedir/somefile
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for character and block device creation
-#
-set -eux
-set -o pipefail
-
-rm -rf /tmp/dev
-mkdir /tmp/dev
-
-# We are running tests in /tmp, which would normally be mounted nodev,
-# so we only try with --dry-run.
-
-systemd-tmpfiles --dry-run --create - <<EOF
-c /tmp/dev/char - - - - 11:12
-b /tmp/dev/block - - - - 11:14
-EOF
-
-test ! -e /tmp/dev/char
-test ! -e /tmp/dev/block
-
-systemd-tmpfiles --dry-run --create - <<EOF
-c+ /tmp/dev/char - - - - 11:12
-b+ /tmp/dev/block - - - - 11:14
-EOF
-
-test ! -e /tmp/dev/char
-test ! -e /tmp/dev/block
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-22-TMPFILES
-After=systemd-tmpfiles-setup.service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-assert_eq "$LISTEN_FDS" "$1"
-assert_eq "$LISTEN_FDNAMES" "$2"
-
-for ((i = 3; i < 3 + LISTEN_FDS; i++)); do
- read -r -u "$i" text
- assert_eq "$text" "${!i}" # Dereference $i to get i'th arg
-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-
-if [ -f /tmp/testsuite-23.counter ] ; then
- read -r counter < /tmp/testsuite-23.counter
- counter=$((counter + 1))
-else
- counter=0
-fi
-
-echo "$counter" >/tmp/testsuite-23.counter
-
-if [ "$counter" -eq 5 ] ; then
- systemctl kill --kill-whom=main -sUSR1 testsuite-23.service
-fi
-
-exec sleep 1.5
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test ExecReload= (PR #13098)
-
-systemd-analyze log-level debug
-
-export SYSTEMD_PAGER=
-SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
-SERVICE_NAME="${SERVICE_PATH##*/}"
-
-echo "[#1] Failing ExecReload= should not kill the service"
-cat >"$SERVICE_PATH" <<EOF
-[Service]
-ExecStart=sleep infinity
-ExecReload=/bin/false
-EOF
-
-systemctl daemon-reload
-systemctl start "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-# The reload SHOULD fail but SHOULD NOT affect the service state
-(! systemctl reload "$SERVICE_NAME")
-systemctl status "$SERVICE_NAME"
-systemctl stop "$SERVICE_NAME"
-
-
-echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
-cat >"$SERVICE_PATH" <<EOF
-[Service]
-ExecStart=sleep infinity
-ExecReload=/bin/true
-ExecReload=/bin/false
-ExecReload=/bin/true
-EOF
-
-systemctl daemon-reload
-systemctl start "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-# The reload SHOULD fail but SHOULD NOT affect the service state
-(! systemctl reload "$SERVICE_NAME")
-systemctl status "$SERVICE_NAME"
-systemctl stop "$SERVICE_NAME"
-
-echo "[#3] Failing ExecReload=- should not affect reload's exit code"
-cat >"$SERVICE_PATH" <<EOF
-[Service]
-ExecStart=sleep infinity
-ExecReload=-/bin/false
-EOF
-
-systemctl daemon-reload
-systemctl start "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-systemctl reload "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-systemctl stop "$SERVICE_NAME"
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-
-# Test that ExecStopPost= is always run
-
-systemd-analyze log-level debug
-
-systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
- -p ExecStopPost='/bin/touch /run/simple1' true
-test -f /run/simple1
-
-(! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
- -p ExecStopPost='/bin/touch /run/simple2' false)
-test -f /run/simple2
-
-systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
- -p ExecStopPost='/bin/touch /run/exec1' sleep 1
-test -f /run/exec1
-
-(! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
- -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false')
-test -f /run/exec2
-
-cat >/tmp/forking1.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-sleep 4 &
-MAINPID=\$!
-disown
-
-systemd-notify MAINPID=\$MAINPID
-EOF
-chmod +x /tmp/forking1.sh
-
-systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
- -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
-test -f /run/forking1
-
-cat >/tmp/forking2.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-(sleep 4; exit 1) &
-MAINPID=\$!
-disown
-
-systemd-notify MAINPID=\$MAINPID
-EOF
-chmod +x /tmp/forking2.sh
-
-(! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
- -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh)
-test -f /run/forking2
-
-systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
- -p ExecStopPost='/bin/touch /run/oneshot1' true
-test -f /run/oneshot1
-
-(! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
- -p ExecStopPost='/bin/touch /run/oneshot2' false)
-test -f /run/oneshot2
-
-systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
- -p ExecStopPost='/bin/touch /run/dbus1' \
- busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 || :
-test -f /run/dbus1
-
-systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
- -p ExecStopPost='/bin/touch /run/dbus2' true
-test -f /run/dbus2
-
-# https://github.com/systemd/systemd/issues/19920
-(! systemd-run --unit=dbus3.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus \
- -p ExecStopPost='/bin/touch /run/dbus3' true)
-
-cat >/tmp/notify1.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-systemd-notify --ready
-EOF
-chmod +x /tmp/notify1.sh
-
-systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
- -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
-test -f /run/notify1
-
-(! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
- -p ExecStopPost='/bin/touch /run/notify2' true)
-test -f /run/notify2
-
-systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
-test -f /run/idle1
-
-(! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle \
- -p ExecStopPost='/bin/touch /run/idle2' false)
-test -f /run/idle2
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# Test JoinsNamespaceOf= with PrivateTmp=yes
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# simple case
-systemctl start testsuite-23-joins-namespace-of-1.service
-systemctl start testsuite-23-joins-namespace-of-2.service
-systemctl start testsuite-23-joins-namespace-of-3.service
-systemctl stop testsuite-23-joins-namespace-of-1.service
-
-# inverse dependency
-systemctl start testsuite-23-joins-namespace-of-4.service
-systemctl start testsuite-23-joins-namespace-of-5.service
-systemctl stop testsuite-23-joins-namespace-of-4.service
-
-# transitive dependency
-systemctl start testsuite-23-joins-namespace-of-6.service
-systemctl start testsuite-23-joins-namespace-of-7.service
-systemctl start testsuite-23-joins-namespace-of-8.service
-systemctl start testsuite-23-joins-namespace-of-9.service
-systemctl stop testsuite-23-joins-namespace-of-6.service
-systemctl stop testsuite-23-joins-namespace-of-8.service
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test RuntimeDirectoryPreserve=yes
-
-at_exit() {
- set +e
-
- rm -fr /run/hoge /tmp/aaa
-}
-
-trap at_exit EXIT
-
-systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
-
-touch /run/hoge/foo
-touch /tmp/aaa/bbb
-
-systemctl restart tmp-aaa.mount
-
-test -e /run/hoge/foo
-test ! -e /tmp/aaa/bbb
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test StandardOutput=file:
-
-systemd-analyze log-level debug
-
-systemd-run --wait --unit=testsuite-23-standard-output-one \
- -p StandardOutput=file:/tmp/stdout \
- -p StandardError=file:/tmp/stderr \
- -p Type=exec \
- sh -c 'echo x ; echo y >&2'
-cmp /tmp/stdout <<EOF
-x
-EOF
-cmp /tmp/stderr <<EOF
-y
-EOF
-
-systemd-run --wait --unit=testsuite-23-standard-output-two \
- -p StandardOutput=file:/tmp/stdout \
- -p StandardError=file:/tmp/stderr \
- -p Type=exec \
- sh -c 'echo z ; echo a >&2'
-cmp /tmp/stdout <<EOF
-z
-EOF
-cmp /tmp/stderr <<EOF
-a
-EOF
-
-systemd-run --wait --unit=testsuite-23-standard-output-three \
- -p StandardOutput=append:/tmp/stdout \
- -p StandardError=append:/tmp/stderr \
- -p Type=exec \
- sh -c 'echo b ; echo c >&2'
-cmp /tmp/stdout <<EOF
-z
-b
-EOF
-cmp /tmp/stderr <<EOF
-a
-c
-EOF
-
-systemd-run --wait --unit=testsuite-23-standard-output-four \
- -p StandardOutput=truncate:/tmp/stdout \
- -p StandardError=truncate:/tmp/stderr \
- -p Type=exec \
- sh -c 'echo a ; echo b >&2'
-cmp /tmp/stdout <<EOF
-a
-EOF
-cmp /tmp/stderr <<EOF
-b
-EOF
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# Test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo=
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# Idea is this:
-# 1. we start testsuite-23-success.service
-# 2. which through OnSuccess= starts testsuite-23-fail.service,
-# 3. which through OnFailure= starts testsuite-23-uphold.service,
-# 4. which through Uphold= starts/keeps testsuite-23-short-lived.service running,
-# 5. which will sleep 1s when invoked, and on the 5th invocation send us a SIGUSR1
-# 6. once we got that we finish cleanly
-
-sigusr1=0
-trap sigusr1=1 SIGUSR1
-
-trap -p SIGUSR1
-
-systemctl start testsuite-23-success.service
-
-while [ "$sigusr1" -eq 0 ] ; do
- sleep .5
-done
-
-systemctl stop testsuite-23-uphold.service
-
-systemctl enable testsuite-23-upheldby-install.service
-
-# Idea is this:
-# 1. we start testsuite-23-retry-uphold.service
-# 2. which through Uphold= starts testsuite-23-retry-upheld.service
-# 3. which through Requires= starts testsuite-23-retry-fail.service
-# 4. which fails as /tmp/testsuite-23-retry-fail does not exist, so testsuite-23-retry-upheld.service
-# is no longer restarted
-# 5. we create /tmp/testsuite-23-retry-fail
-# 6. now testsuite-23-retry-upheld.service will be restarted since upheld, and its dependency will
-# be satisfied
-
-rm -f /tmp/testsuite-23-retry-fail
-systemctl start testsuite-23-retry-uphold.service
-systemctl is-active testsuite-23-upheldby-install.service
-
-until systemctl is-failed testsuite-23-retry-fail.service ; do
- sleep .5
-done
-
-(! systemctl is-active testsuite-23-retry-upheld.service)
-
-touch /tmp/testsuite-23-retry-fail
-
-until systemctl is-active testsuite-23-retry-upheld.service ; do
- sleep .5
-done
-
-systemctl stop testsuite-23-retry-uphold.service testsuite-23-retry-fail.service testsuite-23-retry-upheld.service
-
-# Idea is this:
-# 1. we start testsuite-23-prop-stop-one.service
-# 2. which through Wants=/After= pulls in testsuite-23-prop-stop-two.service as well
-# 3. testsuite-23-prop-stop-one.service then sleeps indefinitely
-# 4. testsuite-23-prop-stop-two.service sleeps a short time and exits
-# 5. the StopPropagatedFrom= dependency between the two should ensure *both* will exit as result
-# 6. an ExecStopPost= line on testsuite-23-prop-stop-one.service will send us a SIGUSR2
-# 7. once we got that we finish cleanly
-
-sigusr2=0
-trap sigusr2=1 SIGUSR2
-
-systemctl start testsuite-23-prop-stop-one.service
-
-while [ "$sigusr2" -eq 0 ] ; do
- sleep .5
-done
-
-
-# Idea is this:
-# 1. we start testsuite-23-binds-to.service
-# 2. which through BindsTo=/After= pulls in testsuite-23-bound-by.service as well
-# 3. testsuite-23-bound-by.service suddenly dies
-# 4. testsuite-23-binds-to.service should then also be pulled down (it otherwise just hangs)
-# 6. an ExecStopPost= line on testsuite-23-binds-to.service will send us a SIGRTMIN1+1
-# 7. once we got that we finish cleanly
-
-sigrtmin1=0
-trap sigrtmin1=1 SIGRTMIN+1
-
-systemctl start testsuite-23-binds-to.service
-
-while [ "$sigrtmin1" -eq 0 ] ; do
- sleep .5
-done
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test unit configuration/state/cache/log/runtime data cleanup
-
-at_exit() {
- set +e
-
- rm -fr /{etc,run,var/lib,var/cache,var/log}/test-service
- rm -fr /{etc,run,var/lib,var/cache,var/log}/private/test-service
- rm -fr /{etc,run,var/lib,var/cache,var/log}/hoge
- rm -fr /{etc,run,var/lib,var/cache,var/log}/test-socket
-}
-
-trap at_exit EXIT
-
-cat >/run/systemd/system/test-service.service <<EOF
-[Service]
-ConfigurationDirectory=test-service
-RuntimeDirectory=test-service
-StateDirectory=test-service
-CacheDirectory=test-service
-LogsDirectory=test-service
-RuntimeDirectoryPreserve=yes
-ExecStart=sleep infinity
-Type=exec
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test ! -e /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-systemctl start test-service
-
-test -d /etc/test-service
-test -d /run/test-service
-test -d /var/lib/test-service
-test -d /var/cache/test-service
-test -d /var/log/test-service
-
-(! systemctl clean test-service)
-
-systemctl stop test-service
-
-test -d /etc/test-service
-test -d /run/test-service
-test -d /var/lib/test-service
-test -d /var/cache/test-service
-test -d /var/log/test-service
-
-systemctl clean test-service --what=configuration
-
-test ! -e /etc/test-service
-test -d /run/test-service
-test -d /var/lib/test-service
-test -d /var/cache/test-service
-test -d /var/log/test-service
-
-systemctl clean test-service
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test -d /var/lib/test-service
-test ! -e /var/cache/test-service
-test -d /var/log/test-service
-
-systemctl clean test-service --what=logs
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test -d /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-systemctl clean test-service --what=all
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test ! -e /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-cat >/run/systemd/system/test-service.service <<EOF
-[Service]
-DynamicUser=yes
-ConfigurationDirectory=test-service
-RuntimeDirectory=test-service
-StateDirectory=test-service
-CacheDirectory=test-service
-LogsDirectory=test-service
-RuntimeDirectoryPreserve=yes
-ExecStart=sleep infinity
-Type=exec
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test ! -e /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-systemctl restart test-service
-
-test -d /etc/test-service
-test -d /run/private/test-service
-test -d /var/lib/private/test-service
-test -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test -L /run/test-service
-test -L /var/lib/test-service
-test -L /var/cache/test-service
-test -L /var/log/test-service
-
-(! systemctl clean test-service)
-
-systemctl stop test-service
-
-test -d /etc/test-service
-test -d /run/private/test-service
-test -d /var/lib/private/test-service
-test -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test -L /run/test-service
-test -L /var/lib/test-service
-test -L /var/cache/test-service
-test -L /var/log/test-service
-
-systemctl clean test-service --what=configuration
-
-test ! -d /etc/test-service
-test -d /run/private/test-service
-test -d /var/lib/private/test-service
-test -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test -L /run/test-service
-test -L /var/lib/test-service
-test -L /var/cache/test-service
-test -L /var/log/test-service
-
-systemctl clean test-service
-
-test ! -d /etc/test-service
-test ! -d /run/private/test-service
-test -d /var/lib/private/test-service
-test ! -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test ! -L /run/test-service
-test -L /var/lib/test-service
-test ! -L /var/cache/test-service
-test -L /var/log/test-service
-
-systemctl clean test-service --what=logs
-
-test ! -d /etc/test-service
-test ! -d /run/private/test-service
-test -d /var/lib/private/test-service
-test ! -d /var/cache/private/test-service
-test ! -d /var/log/private/test-service
-test ! -L /run/test-service
-test -L /var/lib/test-service
-test ! -L /var/cache/test-service
-test ! -L /var/log/test-service
-
-systemctl clean test-service --what=all
-
-test ! -d /etc/test-service
-test ! -d /run/private/test-service
-test ! -d /var/lib/private/test-service
-test ! -d /var/cache/private/test-service
-test ! -d /var/log/private/test-service
-test ! -L /run/test-service
-test ! -L /var/lib/test-service
-test ! -L /var/cache/test-service
-test ! -L /var/log/test-service
-
-cat >/run/systemd/system/tmp-hoge.mount <<EOF
-[Mount]
-What=tmpfs
-Type=tmpfs
-ConfigurationDirectory=hoge
-RuntimeDirectory=hoge
-StateDirectory=hoge
-CacheDirectory=hoge
-LogsDirectory=hoge
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/hoge
-test ! -e /run/hoge
-test ! -e /var/lib/hoge
-test ! -e /var/cache/hoge
-test ! -e /var/log/hoge
-
-systemctl start tmp-hoge.mount
-
-test -d /etc/hoge
-test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-(! systemctl clean tmp-hoge.mount)
-
-test -d /etc/hoge
-test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl stop tmp-hoge.mount
-
-test -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=configuration
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test ! -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=logs
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test ! -d /var/cache/hoge
-test ! -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=all
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test ! -d /var/lib/hoge
-test ! -d /var/cache/hoge
-test ! -d /var/log/hoge
-
-cat >/run/systemd/system/test-service.socket <<EOF
-[Socket]
-ListenSequentialPacket=/run/test-service.socket
-RemoveOnStop=yes
-ExecStartPre=true
-ConfigurationDirectory=test-socket
-RuntimeDirectory=test-socket
-StateDirectory=test-socket
-CacheDirectory=test-socket
-LogsDirectory=test-socket
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test ! -e /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test ! -e /var/log/test-socket
-
-systemctl start test-service.socket
-
-test -d /etc/test-socket
-test -d /run/test-socket
-test -d /var/lib/test-socket
-test -d /var/cache/test-socket
-test -d /var/log/test-socket
-
-(! systemctl clean test-service.socket)
-
-systemctl stop test-service.socket
-
-test -d /etc/test-socket
-test ! -d /run/test-socket
-test -d /var/lib/test-socket
-test -d /var/cache/test-socket
-test -d /var/log/test-socket
-
-systemctl clean test-service.socket --what=configuration
-
-test ! -e /etc/test-socket
-test ! -d /run/test-socket
-test -d /var/lib/test-socket
-test -d /var/cache/test-socket
-test -d /var/log/test-socket
-
-systemctl clean test-service.socket
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test -d /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test -d /var/log/test-socket
-
-systemctl clean test-service.socket --what=logs
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test -d /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test ! -e /var/log/test-socket
-
-systemctl clean test-service.socket --what=all
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test ! -e /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test ! -e /var/log/test-socket
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test ExecXYZEx= service unit dbus hookups
-
-systemd-analyze log-level debug
-
-declare -A property
-
-property[1_one]=ExecCondition
-property[2_two]=ExecStartPre
-property[3_three]=ExecStart
-property[4_four]=ExecStartPost
-property[5_five]=ExecReload
-property[6_six]=ExecStop
-property[7_seven]=ExecStopPost
-
-# These should all get upgraded to the corresponding Ex property as the non-Ex variant
-# does not support the ":" prefix (no-env-expand).
-for c in "${!property[@]}"; do
- systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
- systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
- systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
-done
-
-declare -A property_ex
-
-property_ex[1_one_ex]=ExecConditionEx
-property_ex[2_two_ex]=ExecStartPreEx
-property_ex[3_three_ex]=ExecStartEx
-property_ex[4_four_ex]=ExecStartPostEx
-property_ex[5_five_ex]=ExecReloadEx
-property_ex[6_six_ex]=ExecStopEx
-property_ex[7_seven_ex]=ExecStopPostEx
-
-for c in "${!property_ex[@]}"; do
- systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
- systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
- systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
-done
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Test oneshot unit restart on failure
-
-# wait this many secs for each test service to succeed in what is being tested
-MAX_SECS=60
-
-systemctl log-level debug
-
-# test one: Restart=on-failure should restart the service
-(! systemd-run --unit=oneshot-restart-one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1")
-
-for ((secs = 0; secs < MAX_SECS; secs++)); do
- [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]] || break
- sleep 1
-done
-if [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]]; then
- exit 1
-fi
-
-TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
-
-: >$TMP_FILE
-
-# test two: make sure StartLimitBurst correctly limits the number of restarts
-# and restarts execution of the unit from the first ExecStart=
-(! systemd-run --unit=oneshot-restart-two \
- -p StartLimitIntervalSec=120 \
- -p StartLimitBurst=3 \
- -p Type=oneshot \
- -p Restart=on-failure \
- -p ExecStart="/bin/bash -c 'printf a >>$TMP_FILE'" /bin/bash -c "exit 1")
-
-# wait for at least 3 restarts
-for ((secs = 0; secs < MAX_SECS; secs++)); do
- [[ $(cat $TMP_FILE) != "aaa" ]] || break
- sleep 1
-done
-if [[ $(cat $TMP_FILE) != "aaa" ]]; then
- exit 1
-fi
-
-# wait for 5 more seconds to make sure there aren't excess restarts
-sleep 5
-if [[ $(cat $TMP_FILE) != "aaa" ]]; then
- exit 1
-fi
-rm "$TMP_FILE"
-
-# Test RestartForceExitStatus=. Note that success exit statuses are meant to be skipped
-
-TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
-UNIT_NAME="testsuite-23-oneshot-restartforce.service"
-ONSUCCESS_UNIT_NAME="testsuite-23-oneshot-restartforce-onsuccess.service"
-FIFO_FILE="/tmp/test-23-oneshot-restart-test-fifo"
-
-cat >"/run/systemd/system/$UNIT_NAME" <<EOF
-[Unit]
-OnSuccess=$ONSUCCESS_UNIT_NAME
-
-[Service]
-Type=oneshot
-RestartForceExitStatus=0 2
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-23.units/testsuite-23-oneshot-restartforce.sh "$TMP_FILE"
-
-[Install]
-WantedBy=multi-user.target
-EOF
-
-cat >"/run/systemd/system/$ONSUCCESS_UNIT_NAME" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c 'echo finished >$FIFO_FILE'
-EOF
-
-mkfifo "$FIFO_FILE"
-
-# Pin the unit in memory
-systemctl enable "$UNIT_NAME"
-# Initial run should fail
-(! systemctl start "$UNIT_NAME")
-# Wait for OnSuccess=
-read -r x <"$FIFO_FILE"
-assert_eq "$x" "finished"
-
-cmp -b <(systemctl show "$UNIT_NAME" -p Result -p NRestarts -p SubState) <<EOF
-Result=success
-NRestarts=1
-SubState=dead
-EOF
-
-systemctl disable "$UNIT_NAME"
-rm "$TMP_FILE" /run/systemd/system/{"$UNIT_NAME","$ONSUCCESS_UNIT_NAME"} "$FIFO_FILE"
-
-systemctl log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
- set +e
-
- rm -rf /tmp/test-open-file/
-}
-
-trap at_exit EXIT
-
-systemctl log-level debug
-
-# Existing files
-
-mkdir /tmp/test-open-file
-echo "Open" >'/tmp/test-open-file/open.txt'
-echo "File" >'/tmp/test-open-file/file:colon.txt'
-
-systemd-run -p DynamicUser=yes -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
- -p OpenFile='/tmp/test-open-file/open.txt::read-only' \
- -p OpenFile='/tmp/test-open-file/file\x3Acolon.txt:colon' \
- -p RemainAfterExit=yes \
- --unit=test-23-openfile-existing.service \
- --service-type=oneshot \
- /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 2 "open.txt:colon" "Open" "File"
-
-cmp <(systemctl show -p OpenFile test-23-openfile-existing.service) <<EOF
-OpenFile=/tmp/test-open-file/open.txt::read-only
-OpenFile=/tmp/test-open-file/file\\x3acolon.txt:colon
-EOF
-
-systemctl stop test-23-openfile-existing.service
-
-# Sockets
-
-systemctl start testsuite-23-openfile-server.socket
-
-systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
- --wait \
- /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 1 "socket" "Socket"
-
-systemctl stop testsuite-23-openfile-server.socket
-
-# Ignore when missing
-
-assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
-assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
-
-systemctl log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Ensure %j Wants directives work
-systemd-run --wait \
- --property="Type=oneshot" \
- --property="Wants=testsuite-23-specifier-j-wants.service" \
- --property="After=testsuite-23-specifier-j-wants.service" \
- true
-
-test -f /tmp/tetsuite-23-specifier-j-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# Test adding new BindPaths while unit is already running
-
-at_exit() {
- set +e
-
- rm -f /run/testsuite-23-marker-{fixed,runtime}
- rm -fr /run/inaccessible
-}
-
-trap at_exit EXIT
-
-echo "MARKER_FIXED" >/run/testsuite-23-marker-fixed
-mkdir /run/inaccessible
-
-systemctl start testsuite-23-namespaced.service
-
-# Ensure that inaccessible paths aren't bypassed by the runtime setup,
-(! systemctl bind --mkdir testsuite-23-namespaced.service /run/testsuite-23-marker-fixed /run/inaccessible/testfile-marker-fixed)
-
-echo "MARKER_WRONG" >/run/testsuite-23-marker-wrong
-echo "MARKER_RUNTIME" >/run/testsuite-23-marker-runtime
-
-# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
-systemctl bind --mkdir testsuite-23-namespaced.service /run/testsuite-23-marker-wrong /tmp/testfile-marker-runtime
-test "$(systemctl show -P SubState testsuite-23-namespaced.service)" = "running"
-systemctl bind --mkdir testsuite-23-namespaced.service /run/testsuite-23-marker-runtime /tmp/testfile-marker-runtime
-
-timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState testsuite-23-namespaced.service)" == running ]]; do sleep .5; done'
-systemctl is-active testsuite-23-namespaced.service
-
-# Now test that systemctl bind fails when attempted on a non-namespaced unit
-systemctl start testsuite-23-non-namespaced.service
-
-(! systemctl bind --mkdir testsuite-49-non-namespaced.service /run/testsuite-23-marker-runtime /tmp/testfile-marker-runtime)
-
-timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState testsuite-23-non-namespaced.service)" == running ]]; do sleep .5; done'
-(! systemctl is-active testsuite-23-non-namespaced.service)
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-23-TYPE-EXEC
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# Note: the signal shenanigans are necessary for the Upholds= tests
-run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test start & stop operations without daemon-reload
-
-at_exit() {
- set +e
-
- rm -f /run/systemd/system/testsuite-23-no-reload.{service,target}
-}
-
-trap at_exit EXIT
-
-cat >/run/systemd/system/testsuite-23-no-reload.target <<EOF
-[Unit]
-Wants=testsuite-23-no-reload.service
-EOF
-
-systemctl daemon-reload
-
-systemctl start testsuite-23-no-reload.target
-
-# The filesystem on the test image, despite being ext4, seems to have a mtime
-# granularity of one second, which means the manager's unit cache won't be
-# marked as dirty when writing the unit file, unless we wait at least a full
-# second after the previous daemon-reload.
-# May 07 23:12:20 H testsuite-48.sh[30]: + cat
-# May 07 23:12:20 H testsuite-48.sh[30]: + ls -l --full-time /etc/systemd/system/testsuite-23-no-reload.service
-# May 07 23:12:20 H testsuite-48.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 /
-# May 07 23:12:20 H testsuite-48.sh[30]: + stat -f --format=%t /etc/systemd/system/testsuite-23-no-reload.servic
-# May 07 23:12:20 H testsuite-48.sh[53]: ef53
-sleep 3.1
-
-cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
-[Service]
-ExecStart=sleep infinity
-EOF
-
-systemctl start testsuite-23-no-reload.service
-
-systemctl is-active testsuite-23-no-reload.service
-
-# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992
-systemctl stop testsuite-23-no-reload.service
-rm -f /run/systemd/system/testsuite-23-no-reload.service
-systemctl daemon-reload
-
-sleep 3.1
-
-cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
-[Service]
-ExecStart=sleep infinity
-EOF
-
-# Start a non-existing unit first, so that the cache is reloaded for an unrelated
-# reason. Starting the existing unit later should still work thanks to the check
-# for the last load attempt vs cache timestamp.
-systemctl start testsuite-23-no-reload-nonexistent.service || true
-
-systemctl start testsuite-23-no-reload.service
-
-systemctl is-active testsuite-23-no-reload.service
-
-# Stop and remove, and try again to exercise the transaction setup code path by
-# having the target pull in the unloaded but available unit
-systemctl stop testsuite-23-no-reload.service testsuite-23-no-reload.target
-rm -f /run/systemd/system/testsuite-23-no-reload.service /run/systemd/system/testsuite-23-no-reload.target
-systemctl daemon-reload
-
-sleep 3.1
-
-cat >/run/systemd/system/testsuite-23-no-reload.target <<EOF
-[Unit]
-Conflicts=shutdown.target
-Wants=testsuite-23-no-reload.service
-EOF
-
-systemctl daemon-reload
-
-systemctl start testsuite-23-no-reload.target
-
-cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
-[Service]
-ExecStart=sleep infinity
-EOF
-
-systemctl restart testsuite-23-no-reload.target
-
-systemctl is-active testsuite-23-no-reload.service
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test unit configuration/state/cache/log/runtime data cleanup
-
-export HOME=/root
-export XDG_RUNTIME_DIR=/run/user/0
-
-systemctl start user@0.service
-
-( ! test -d "$HOME"/.local/state/foo)
-( ! test -d "$HOME"/.config/foo)
-
-systemd-run --user -p StateDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-( ! test -L "$HOME"/.local/state/foo)
-( ! test -d "$HOME"/.config/foo)
-
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-( ! test -L "$HOME"/.local/state/foo)
-test -d "$HOME"/.config/foo
-
-rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
-
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-( ! test -L "$HOME"/.local/state/foo)
-test -d "$HOME"/.config/foo
-
-rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
-
-# Now trigger an update scenario by creating a config dir first
-systemd-run --user -p ConfigurationDirectory=foo --wait /bin/true
-
-( ! test -d "$HOME"/.local/state/foo)
-test -d "$HOME"/.config/foo
-
-# This will look like an update and result in a symlink
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-test -L "$HOME"/.local/state/foo
-test -d "$HOME"/.config/foo
-
-test "$(readlink "$HOME"/.local/state/foo)" = ../../.config/foo
-
-# Check that this will work safely a second time
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-rm "$HOME"/.local/state/foo
-rmdir "$HOME"/.config/foo
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test OnSuccess=/OnFailure= in combination
-
-systemd-analyze log-level debug
-
-# Start-up should fail, but the automatic restart should fix it
-(! systemctl start success-failure-test )
-
-# Wait until the first invocation finished & failed
-while test ! -f /tmp/success-failure-test-ran ; do
- sleep .5
-done
-
-# Wait until the second invocation finished & succeeded
-while test ! -f /tmp/success-failure-test-ran2 ; do
- sleep .5
-done
-
-# Verify it is indeed running
-systemctl is-active -q success-failure-test
-
-# The above should have caused the failure service to start (asynchronously)
-while test "$(systemctl is-active success-failure-test-failure)" != "active" ; do
- sleep .5
-done
-
-# But the success service should not have started
-test "$(systemctl is-active success-failure-test-success)" = "inactive"
-
-systemctl stop success-failure-test-failure
-
-# Do a clean kill of the service now
-systemctl kill success-failure-test
-
-# This should result in the success service to start
-while test "$(systemctl is-active success-failure-test-success)" != "active" ; do
- sleep .5
-done
-
-# But the failure service should not have started again
-test "$(systemctl is-active success-failure-test-failure)" = "inactive"
-
-systemctl stop success-failure-test success-failure-test-success
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test Type=exec
-
-systemd-analyze log-level debug
-
-# Create a binary for which execve() will fail
-touch /tmp/brokenbinary
-chmod +x /tmp/brokenbinary
-
-# These three commands should succeed.
-systemd-run --unit=exec-one -p Type=simple /bin/sleep infinity
-systemd-run --unit=exec-two -p Type=simple -p User=idontexist /bin/sleep infinity
-systemd-run --unit=exec-three -p Type=simple /tmp/brokenbinary
-
-# And now, do the same with Type=exec, where the latter two should fail
-systemd-run --unit=exec-four -p Type=exec /bin/sleep infinity
-(! systemd-run --unit=exec-five -p Type=exec -p User=idontexist /bin/sleep infinity)
-(! systemd-run --unit=exec-six -p Type=exec /tmp/brokenbinary)
-
-systemd-run --unit=exec-seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
-# Both TERM and SIGINT happen to have the same number on all architectures
-test "$(systemctl show --value -p KillSignal exec-seven.service)" -eq 15
-test "$(systemctl show --value -p RestartKillSignal exec-seven.service)" -eq 2
-
-systemctl restart exec-seven.service
-systemctl stop exec-seven.service
-
-# For issue #20933
-
-# Should work normally
-busctl call \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager StartTransientUnit \
- "ssa(sv)a(sa(sv))" test-20933-ok.service replace 1 \
- ExecStart "a(sasb)" 1 \
- /usr/bin/sleep 2 /usr/bin/sleep 1 true \
- 0
-
-# DBus call should fail but not crash systemd
-(! busctl call \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager StartTransientUnit \
- "ssa(sv)a(sa(sv))" test-20933-bad.service replace 1 \
- ExecStart "a(sasb)" 1 \
- /usr/bin/sleep 0 true \
- 0)
-
-# Same but with the empty argv in the middle
-(! busctl call \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager StartTransientUnit \
- "ssa(sv)a(sa(sv))" test-20933-bad-middle.service replace 1 \
- ExecStart "a(sasb)" 3 \
- /usr/bin/sleep 2 /usr/bin/sleep 1 true \
- /usr/bin/sleep 0 true \
- /usr/bin/sleep 2 /usr/bin/sleep 1 true \
- 0)
-
-systemd-analyze log-level info
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-USER="test-23-utmp"
-
-cleanup() {
- userdel "$USER"
-}
-
-trap cleanup EXIT
-useradd "$USER"
-
-assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user -p User=$USER whoami)" "$USER"
-assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user whoami)" "$(whoami)"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# Verify our own unit files (where applicable)
-
-# This is generated by meson during build
-UNIT_FILE_LIST="/usr/lib/systemd/tests/testdata/installed-unit-files.txt"
-
-if [[ ! -f "$UNIT_FILE_LIST" ]]; then
- echo "Couldn't find the list of installed units, skipping the test"
- exit 0
-fi
-
-if ! command -v systemd-analyze >/dev/null; then
- echo "Built without systemd-analyze, skipping the test"
- exit 0
-fi
-
-mapfile -t UNIT_FILES <"$UNIT_FILE_LIST"
-
-if [[ "${#UNIT_FILES[@]}" -le 0 ]]; then
- echo >&2 "The unit file list is empty, this is most likely a bug"
- exit 1
-fi
-
-for unit_file in "${UNIT_FILES[@]}"; do
- # Skip the check for a couple of units, namely:
- # - syslog.socket: the corresponding syslog.service might not be installed
- # - rc-local.service: compat API, /etc/rc.d/rc.local most likely won't be present
- if [[ "$unit_file" =~ /(syslog.socket|rc-local.service)$ ]]; then
- continue
- fi
-
- # Skip missing unit files - this is useful for $NO_BUILD runs, where certain unit files might be dropped
- # in distro packaging
- if [[ ! -e "$unit_file" ]]; then
- echo "$unit_file not found, skipping"
- continue
- fi
-
- systemd-analyze --recursive-errors=no --man=no verify "$unit_file"
-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-test "$(systemctl whoami)" = testsuite-23.service
-test "$(systemctl whoami $$)" = testsuite-23.service
-
-systemctl whoami 1 $$ 1 | cmp - /dev/fd/3 3<<'EOF'
-init.scope
-testsuite-23.service
-init.scope
-EOF
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-24-CRYPTSETUP
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# TODO:
-# - /proc/cmdline parsing
-# - expect + interactive auth?
-
-# We set up an encrypted /var partition which should get mounted automatically
-# on boot
-mountpoint /var
-
-systemctl --state=failed --no-legend --no-pager | tee /failed
-if [[ -s /failed ]]; then
- echo >&2 "Found units in failed state"
- exit 1
-fi
-
-at_exit() {
- set +e
-
- mountpoint -q /proc/cmdline && umount /proc/cmdline
- rm -f /etc/crypttab
- [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab
- [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP"
- [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"
-
- systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-cryptsetup_start_and_check() {
- local expect_fail=0
- local umount_header_and_key=0
- local ec volume unit
-
- if [[ "${1:?}" == "-f" ]]; then
- expect_fail=1
- shift
- fi
-
- if [[ "${1:?}" == "-u" ]]; then
- umount_header_and_key=1
- shift
- fi
-
- for volume in "$@"; do
- unit="systemd-cryptsetup@$volume.service"
-
- # The unit existence check should always pass
- [[ "$(systemctl show -P LoadState "$unit")" == loaded ]]
- systemctl list-unit-files "$unit"
-
- systemctl start "$unit" && ec=0 || ec=$?
- if [[ "$expect_fail" -ne 0 ]]; then
- if [[ "$ec" -eq 0 ]]; then
- echo >&2 "Unexpected pass when starting $unit"
- return 1
- fi
-
- return 0
- fi
-
- if [[ "$ec" -ne 0 ]]; then
- echo >&2 "Unexpected fail when starting $unit"
- return 1
- fi
-
- if [[ "$umount_header_and_key" -ne 0 ]]; then
- umount "$TMPFS_DETACHED_KEYFILE"
- umount "$TMPFS_DETACHED_HEADER"
- udevadm settle --timeout=30
- fi
-
- systemctl status "$unit"
- test -e "/dev/mapper/$volume"
- systemctl stop "$unit"
- test ! -e "/dev/mapper/$volume"
- done
-
- return 0
-}
-
-# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2,
-# so focus more on other areas instead
-
-# Use a common workdir to make the cleanup easier
-WORKDIR="$(mktemp -d)"
-
-# Prepare a couple of LUKS2-encrypted disk images
-#
-# 1) Image with an empty password
-IMAGE_EMPTY="$WORKDIR/empty.img)"
-IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile"
-IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile"
-IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)"
-truncate -s 32M "$IMAGE_EMPTY"
-echo -n passphrase >"$IMAGE_EMPTY_KEYFILE"
-chmod 0600 "$IMAGE_EMPTY_KEYFILE"
-cryptsetup luksFormat --batch-mode \
- --pbkdf pbkdf2 \
- --pbkdf-force-iterations 1000 \
- --use-urandom \
- "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE"
-PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY"
-# Duplicate the key file to test keyfile-erase as well
-cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE"
-# The key should get erased even on a failed attempt, so test that too
-cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
-
-# 2) Image with a detached header and a key file offset + size
-IMAGE_DETACHED="$WORKDIR/detached.img"
-IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile"
-IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2"
-IMAGE_DETACHED_HEADER="$WORKDIR/detached.header"
-truncate -s 32M "$IMAGE_DETACHED"
-dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1
-dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1
-chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2"
-cryptsetup luksFormat --batch-mode \
- --pbkdf pbkdf2 \
- --pbkdf-force-iterations 1000 \
- --use-urandom \
- --header "$IMAGE_DETACHED_HEADER" \
- --keyfile-offset 32 \
- --keyfile-size 16 \
- "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE"
-# Also, add a second key file to key slot 8
-# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility
-cryptsetup luksAddKey --batch-mode \
- --header "$IMAGE_DETACHED_HEADER" \
- --key-file "$IMAGE_DETACHED_KEYFILE" \
- --keyfile-offset 32 \
- --keyfile-size 16 \
- --key-slot 8 \
- "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2"
-
-# Prepare a couple of dummy devices we'll store a copy of the detached header
-# and one of the keys on to test if systemd-cryptsetup correctly mounts them
-# when necessary
-STORE_IMAGE="$WORKDIR/store.img"
-truncate -s 64M "$STORE_IMAGE"
-STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")"
-sfdisk "$STORE_LOOP" <<EOF
-label: gpt
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=header_store size=32M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=keyfile_store
-EOF
-udevadm settle --timeout=30
-mkdir -p /mnt
-mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store"
-mount "/dev/disk/by-partlabel/header_store" /mnt
-cp "$IMAGE_DETACHED_HEADER" /mnt/header
-umount /mnt
-mkfs.ext4 -L keyfile_store "/dev/disk/by-partlabel/keyfile_store"
-mount "/dev/disk/by-partlabel/keyfile_store" /mnt
-cp "$IMAGE_DETACHED_KEYFILE2" /mnt/keyfile
-umount /mnt
-
-# Also copy the key and header on a tmpfs that we will umount after unlocking
-TMPFS_DETACHED_KEYFILE="$(mktemp -d)"
-TMPFS_DETACHED_HEADER="$(mktemp -d)"
-mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_KEYFILE"
-mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_HEADER"
-cp "$IMAGE_DETACHED_KEYFILE" "$TMPFS_DETACHED_KEYFILE/keyfile"
-cp "$IMAGE_DETACHED_HEADER" "$TMPFS_DETACHED_HEADER/header"
-
-udevadm settle --timeout=30
-
-# Prepare our test crypttab
-[[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak
-cat >/etc/crypttab <<EOF
-# headless should translate to headless=1
-empty_key $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE headless,x-systemd.device-timeout=1m
-empty_key_erase $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE_ERASE headless=1,keyfile-erase=1
-empty_key_erase_fail $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE_ERASE_FAIL headless=1,keyfile-erase=1,keyfile-offset=4
-# Empty passphrase without try-empty-password(=yes) shouldn't work
-empty_fail0 $IMAGE_EMPTY - headless=1
-empty_fail1 $IMAGE_EMPTY - headless=1,try-empty-password=0
-empty0 $IMAGE_EMPTY - headless=1,try-empty-password
-empty1 $IMAGE_EMPTY - headless=1,try-empty-password=1
-# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
-empty_nokey $IMAGE_EMPTY - headless=1
-empty_pkcs11_auto $IMAGE_EMPTY - headless=1,pkcs11-uri=auto
-
-detached $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
-detached_store0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
-detached_store1 $IMAGE_DETACHED /keyfile:LABEL=keyfile_store headless=1,header=$IMAGE_DETACHED_HEADER
-detached_store2 $IMAGE_DETACHED /keyfile:LABEL=keyfile_store headless=1,header=/header:LABEL=header_store
-detached_fail0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32
-detached_fail1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER
-detached_fail2 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1
-detached_fail3 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=16,keyfile-size=16
-detached_fail4 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=8
-detached_slot0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER
-detached_slot1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=8
-detached_slot_fail $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=0
-detached_nofail $IMAGE_DETACHED $TMPFS_DETACHED_KEYFILE/keyfile headless=1,header=$TMPFS_DETACHED_HEADER/header,keyfile-offset=32,keyfile-size=16,nofail
-EOF
-
-# Temporarily drop luks.name=/luks.uuid= from the kernel command line, as it makes
-# systemd-cryptsetup-generator ignore mounts from /etc/crypttab that are not also
-# specified on the kernel command line
-sed -r 's/luks.(name|uuid)=[^[:space:]+]//' /proc/cmdline >/tmp/cmdline.tmp
-mount --bind /tmp/cmdline.tmp /proc/cmdline
-# Run the systemd-cryptsetup-generator once explicitly, to collect coverage,
-# as during daemon-reload we run generators in a sandbox
-mkdir -p /tmp/systemd-cryptsetup-generator.out
-/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/
-systemctl daemon-reload
-systemctl list-unit-files "systemd-cryptsetup@*"
-
-cryptsetup_start_and_check empty_key
-test -e "$IMAGE_EMPTY_KEYFILE_ERASE"
-cryptsetup_start_and_check empty_key_erase
-test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE"
-test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
-cryptsetup_start_and_check -f empty_key_erase_fail
-test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
-cryptsetup_start_and_check -f empty_fail{0..1}
-cryptsetup_start_and_check empty{0..1}
-# First, check if we correctly fail without any key
-cryptsetup_start_and_check -f empty_nokey
-# And now provide the key via /{etc,run}/cryptsetup-keys.d/
-mkdir -p /run/cryptsetup-keys.d
-cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
-cryptsetup_start_and_check empty_nokey
-
-if [[ -r /etc/softhsm2.conf ]]; then
- # Test unlocking with a PKCS#11 token
- export SOFTHSM2_CONF="/etc/softhsm2.conf"
-
- PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
- cryptsetup_start_and_check empty_pkcs11_auto
- cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
- cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
- PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
- cryptsetup_start_and_check empty_pkcs11_auto
- cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
- cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
- PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
- cryptsetup_start_and_check empty_pkcs11_auto
- cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
- cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
- PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
- cryptsetup_start_and_check empty_pkcs11_auto
- cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
- cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
- PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
- cryptsetup_start_and_check empty_pkcs11_auto
- cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
- cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
- PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
- cryptsetup_start_and_check empty_pkcs11_auto
- cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
- cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-fi
-
-cryptsetup_start_and_check detached
-cryptsetup_start_and_check detached_store{0..2}
-cryptsetup_start_and_check -f detached_fail{0..4}
-cryptsetup_start_and_check detached_slot{0..1}
-cryptsetup_start_and_check -f detached_slot_fail
-cryptsetup_start_and_check -u detached_nofail
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-25-IMPORT
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export SYSTEMD_PAGER=cat
-
-dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
-
-# Test import
-machinectl import-raw /var/tmp/testimage.raw
-machinectl image-status testimage
-test -f /var/lib/machines/testimage.raw
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
-
-# Test export
-machinectl export-raw testimage /var/tmp/testimage2.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
-rm /var/tmp/testimage2.raw
-
-# Test compressed export (gzip)
-machinectl export-raw testimage /var/tmp/testimage2.raw.gz
-gunzip /var/tmp/testimage2.raw.gz
-cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
-rm /var/tmp/testimage2.raw
-
-# Test clone
-machinectl clone testimage testimage3
-test -f /var/lib/machines/testimage3.raw
-machinectl image-status testimage3
-test -f /var/lib/machines/testimage.raw
-machinectl image-status testimage
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
-
-# Test removal
-machinectl remove testimage
-test ! -f /var/lib/machines/testimage.raw
-(! machinectl image-status testimage)
-
-# Test export of clone
-machinectl export-raw testimage3 /var/tmp/testimage3.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
-rm /var/tmp/testimage3.raw
-
-# Test rename
-machinectl rename testimage3 testimage4
-test -f /var/lib/machines/testimage4.raw
-machinectl image-status testimage4
-test ! -f /var/lib/machines/testimage3.raw
-(! machinectl image-status testimage3)
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
-
-# Test export of rename
-machinectl export-raw testimage4 /var/tmp/testimage4.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
-rm /var/tmp/testimage4.raw
-
-# Test removal
-machinectl remove testimage4
-test ! -f /var/lib/machines/testimage4.raw
-(! machinectl image-status testimage4)
-
-# → And now, let's test directory trees ← #
-
-# Set up a directory we can import
-mkdir /var/tmp/scratch
-mv /var/tmp/testimage.raw /var/tmp/scratch/
-touch /var/tmp/scratch/anotherfile
-mkdir /var/tmp/scratch/adirectory
-echo "piep" >/var/tmp/scratch/adirectory/athirdfile
-
-# Test import-fs
-machinectl import-fs /var/tmp/scratch/
-test -d /var/lib/machines/scratch
-machinectl image-status scratch
-
-# Test export-tar
-machinectl export-tar scratch /var/tmp/scratch.tar.gz
-test -f /var/tmp/scratch.tar.gz
-mkdir /var/tmp/extract
-(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
-diff -r /var/tmp/scratch/ /var/tmp/extract/
-rm -rf /var/tmp/extract
-
-# Test import-tar
-machinectl import-tar /var/tmp/scratch.tar.gz scratch2
-test -d /var/lib/machines/scratch2
-machinectl image-status scratch2
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
-
-# Test removal
-machinectl remove scratch
-test ! -f /var/lib/machines/scratch
-(! machinectl image-status scratch)
-
-# Test clone
-machinectl clone scratch2 scratch3
-test -d /var/lib/machines/scratch2
-machinectl image-status scratch2
-test -d /var/lib/machines/scratch3
-machinectl image-status scratch3
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
-
-# Test removal
-machinectl remove scratch2
-test ! -f /var/lib/machines/scratch2
-(! machinectl image-status scratch2)
-
-# Test rename
-machinectl rename scratch3 scratch4
-test -d /var/lib/machines/scratch4
-machinectl image-status scratch4
-test ! -f /var/lib/machines/scratch3
-(! machinectl image-status scratch3)
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
-
-# Test removal
-machinectl remove scratch4
-test ! -f /var/lib/machines/scratch4
-(! machinectl image-status scratch4)
-
-# Test import-tar hyphen/stdin pipe behavior
-# shellcheck disable=SC2002
-cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
-test -d /var/lib/machines/scratch5
-machinectl image-status scratch5
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
-
-# Test export-tar hyphen/stdout pipe behavior
-mkdir -p /var/tmp/extract
-machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
-diff -r /var/tmp/scratch/ /var/tmp/extract/
-rm -rf /var/tmp/extract
-
-rm -rf /var/tmp/scratch
-
-# Test removal
-machinectl remove scratch5
-test ! -f /var/lib/machines/scratch5
-(! machinectl image-status scratch5)
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-26-SYSTEMCTL
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
- if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then
- rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
- fi
-
- rm -f /etc/init.d/issue-24990
- return 0
-}
-
-# Create a simple unit file for testing
-# Note: the service file is created under /usr on purpose to test
-# the 'revert' verb as well
-export UNIT_NAME="systemctl-test-$RANDOM.service"
-export UNIT_NAME2="systemctl-test-$RANDOM.service"
-
-cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
-[Unit]
-Description=systemctl test
-
-[Service]
-ExecStart=sleep infinity
-ExecReload=true
-
-# For systemctl clean
-CacheDirectory=%n
-ConfigurationDirectory=%n
-LogsDirectory=%n
-RuntimeDirectory=%n
-StateDirectory=%n
-
-[Install]
-WantedBy=multi-user.target
-EOF
-
-# Configure the preset setting for the unit file
-mkdir /run/systemd/system-preset/
-echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset
-
-EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
-[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ]
-
-printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4"
-EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
-printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
-
-printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
-EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
-printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
-
-systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf <<EOF
-[Unit]
-Description=spectacular
-# this comment should remain
-
-EOF
-printf '%s\n' '[Unit]' 'Description=spectacular' '# this comment should remain' | \
- cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
-
-# Test simultaneous editing of two units and creation of drop-in for a nonexistent unit
-systemctl edit "$UNIT_NAME" "$UNIT_NAME2" --stdin --force --drop-in=override2.conf <<<'[X-Section]'
-printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
-printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME2.d/override2.conf"
-
-# Double free when editing a template unit (#26483)
-EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
-
-# Argument help
-systemctl --state help
-systemctl --signal help
-systemctl --type help
-
-# list-dependencies
-systemctl list-dependencies systemd-journald
-systemctl list-dependencies --after systemd-journald
-systemctl list-dependencies --before systemd-journald
-systemctl list-dependencies --after --reverse systemd-journald
-systemctl list-dependencies --before --reverse systemd-journald
-systemctl list-dependencies --plain systemd-journald
-
-# list-* verbs
-systemctl list-units
-systemctl list-units --recursive
-systemctl list-units --type=socket
-systemctl list-units --type=service,timer
-# Compat: --type= allows load states for compatibility reasons
-systemctl list-units --type=loaded
-systemctl list-units --type=loaded,socket
-systemctl list-units --legend=yes -a "systemd-*"
-systemctl list-units --state=active
-systemctl list-units --with-dependencies systemd-journald.service
-systemctl list-units --with-dependencies --after systemd-journald.service
-systemctl list-units --with-dependencies --before --reverse systemd-journald.service
-systemctl list-sockets
-systemctl list-sockets --legend=no -a "*journal*"
-systemctl list-sockets --show-types
-systemctl list-sockets --state=listening
-systemctl list-timers -a -l
-systemctl list-jobs
-systemctl list-jobs --after
-systemctl list-jobs --before
-systemctl list-jobs --after --before
-systemctl list-jobs "*"
-systemctl list-dependencies sysinit.target --type=socket,mount
-systemctl list-dependencies multi-user.target --state=active
-systemctl list-dependencies sysinit.target --state=mounted --all
-systemctl list-paths
-systemctl list-paths --legend=no -a "systemd*"
-
-test_list_unit_files() {
- systemctl list-unit-files "$@"
- systemctl list-unit-files "$@" "*journal*"
-}
-
-test_list_unit_files
-test_list_unit_files --root=/
-
-# is-* verbs
-# Should return 4 for a missing unit file
-assert_rc 4 systemctl --quiet is-active not-found.service
-assert_rc 4 systemctl --quiet is-failed not-found.service
-assert_rc 4 systemctl --quiet is-enabled not-found.service
-# is-active: return 3 when the unit exists but inactive
-assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
-# is-enabled: return 1 when the unit exists but disabled
-assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
-
-# Basic service management
-systemctl start --show-transaction "$UNIT_NAME"
-systemctl status -n 5 "$UNIT_NAME"
-systemctl is-active "$UNIT_NAME"
-systemctl reload -T "$UNIT_NAME"
-systemctl restart -T "$UNIT_NAME"
-systemctl try-restart --show-transaction "$UNIT_NAME"
-systemctl try-reload-or-restart --show-transaction "$UNIT_NAME"
-timeout 10 systemctl kill --wait "$UNIT_NAME"
-(! systemctl is-active "$UNIT_NAME")
-systemctl restart "$UNIT_NAME"
-systemctl is-active "$UNIT_NAME"
-systemctl restart "$UNIT_NAME"
-systemctl stop "$UNIT_NAME"
-(! systemctl is-active "$UNIT_NAME")
-
-assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)"
-
-# enable/disable/preset
-test_enable_disable_preset() {
- (! systemctl is-enabled "$@" "$UNIT_NAME")
- systemctl enable "$@" "$UNIT_NAME"
- systemctl is-enabled "$@" -l "$UNIT_NAME"
- # We created a preset file for this unit above with a "disable" policy
- systemctl preset "$@" "$UNIT_NAME"
- (! systemctl is-enabled "$@" "$UNIT_NAME")
- systemctl reenable "$@" "$UNIT_NAME"
- systemctl is-enabled "$@" "$UNIT_NAME"
- systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME"
- systemctl is-enabled "$@" "$UNIT_NAME"
- systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME"
- (! systemctl is-enabled "$@" "$UNIT_NAME")
- systemctl enable "$@" --runtime "$UNIT_NAME"
- [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]]
- systemctl is-enabled "$@" "$UNIT_NAME"
- systemctl disable "$@" "$UNIT_NAME"
- # The unit should be still enabled, as we didn't use the --runtime switch
- systemctl is-enabled "$@" "$UNIT_NAME"
- systemctl disable "$@" --runtime "$UNIT_NAME"
- (! systemctl is-enabled "$@" "$UNIT_NAME")
-}
-
-test_enable_disable_preset
-test_enable_disable_preset --root=/
-
-# mask/unmask/revert
-test_mask_unmask_revert() {
- systemctl disable "$@" "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
- systemctl mask "$@" "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
- systemctl unmask "$@" "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
- systemctl mask "$@" "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
- systemctl revert "$@" "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
- systemctl mask "$@" --runtime "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
- # This should be a no-op without the --runtime switch
- systemctl unmask "$@" "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
- systemctl unmask "$@" --runtime "$UNIT_NAME"
- [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
-}
-
-test_mask_unmask_revert
-test_mask_unmask_revert --root=/
-
-# disable --now with template unit
-cat >/run/systemd/system/test-disable@.service <<EOF
-[Service]
-ExecStart=sleep infinity
-
-[Install]
-WantedBy=multi-user.target
-EOF
-systemctl enable --now test-disable@1.service test-disable@2.service
-systemctl is-active test-disable@1.service
-systemctl is-active test-disable@2.service
-systemctl disable --now test-disable@.service
-for u in test-disable@{1,2}.service; do
- (! systemctl is-active "$u")
- (! systemctl is-enabled "$u")
-done
-rm /run/systemd/system/test-disable@.service
-
-# add-wants/add-requires
-(! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service")
-systemctl add-wants "$UNIT_NAME" "systemd-journald.service"
-systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service"
-(! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service")
-systemctl add-requires "$UNIT_NAME" "systemd-journald.service"
-systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service"
-
-# set-property
-systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567
-systemctl cat "$UNIT_NAME"
-# These properties should be saved to a persistent storage
-grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/"
-grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d"
-systemctl revert "$UNIT_NAME"
-(! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
-(! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
-# Same stuff, but with --runtime, which should use /run
-systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10%
-systemctl cat "$UNIT_NAME"
-grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/"
-grep -r "CPUQuota=10%" "/run/systemd/system.control/${UNIT_NAME}.d/"
-systemctl revert "$UNIT_NAME"
-(! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/")
-(! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/")
-
-# Failed-unit related tests
-(! systemd-run --wait --unit "failed.service" /bin/false)
-systemctl is-failed failed.service
-systemctl --state=failed | grep failed.service
-systemctl --failed | grep failed.service
-systemctl reset-failed "fail*.service"
-(! systemctl is-failed failed.service)
-
-# clean
-systemctl restart "$UNIT_NAME"
-systemctl stop "$UNIT_NAME"
-# Check if the directories from *Directory= directives exist
-# (except RuntimeDirectory= in /run, which is removed when the unit is stopped)
-for path in /var/lib /var/cache /var/log /etc; do
- [[ -e "$path/$UNIT_NAME" ]]
-done
-# Run the cleanup
-for what in "" configuration state cache logs runtime all; do
- systemctl clean ${what:+--what="$what"} "$UNIT_NAME"
-done
-# All respective directories should be removed
-for path in /run /var/lib /var/cache /var/log /etc; do
- [[ ! -e "$path/$UNIT_NAME" ]]
-done
-
-# --timestamp
-for value in pretty us µs utc us+utc µs+utc; do
- systemctl show -P KernelTimestamp --timestamp="$value"
-done
-
-# set-default/get-default
-test_get_set_default() {
- target="$(systemctl get-default "$@")"
- systemctl set-default "$@" emergency.target
- [[ "$(systemctl get-default "$@")" == emergency.target ]]
- systemctl set-default "$@" "$target"
- [[ "$(systemctl get-default "$@")" == "$target" ]]
-}
-
-test_get_set_default
-test_get_set_default --root=/
-
-# show/status
-systemctl show --property ""
-# Pick a heavily sandboxed unit for the best effect on coverage
-systemctl show systemd-logind.service
-systemctl status
-# Ignore the exit code in this case, as it might try to load non-existing units
-systemctl status -a >/dev/null || :
-systemctl status -a --state active,running,plugged >/dev/null
-systemctl status "systemd-*.timer"
-systemctl status "systemd-journald*.socket"
-systemctl status "sys-devices-*-ttyS0.device"
-systemctl status -- -.mount
-systemctl status 1
-
-# --marked
-systemctl restart "$UNIT_NAME"
-systemctl set-property "$UNIT_NAME" Markers=needs-restart
-systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
-systemctl reload-or-restart --marked
-(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart)
-
-# --dry-run with destructive verbs
-# kexec is skipped intentionally, as it requires a bit more involved setup
-VERBS=(
- default
- emergency
- exit
- halt
- hibernate
- hybrid-sleep
- poweroff
- reboot
- rescue
- suspend
- suspend-then-hibernate
-)
-
-for verb in "${VERBS[@]}"; do
- systemctl --dry-run "$verb"
-
- if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then
- systemctl --dry-run --message "Hello world" "$verb"
- systemctl --dry-run --no-wall "$verb"
- systemctl --dry-run -f "$verb"
- systemctl --dry-run -ff "$verb"
- fi
-done
-
-# Aux verbs & assorted checks
-systemctl is-active "*-journald.service"
-systemctl cat "*udevd*"
-systemctl cat "$UNIT_NAME"
-systemctl help "$UNIT_NAME"
-systemctl service-watchdogs
-systemctl service-watchdogs "$(systemctl service-watchdogs)"
-
-# show/set-environment
-# Make sure PATH is set
-systemctl show-environment | grep -q '^PATH='
-# Let's add an entry and override a built-in one
-systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
-# Check that both are set
-systemctl show-environment | grep -q '^PATH=.*testaddition$'
-systemctl show-environment | grep -q '^FOO=BAR$'
-systemctl daemon-reload
-# Check again after the reload
-systemctl show-environment | grep -q '^PATH=.*testaddition$'
-systemctl show-environment | grep -q '^FOO=BAR$'
-# Check that JSON output is supported
-systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
-# Drop both
-systemctl unset-environment FOO PATH
-# Check that one is gone and the other reverted to the built-in
-systemctl show-environment | grep '^FOO=$' && exit 1
-systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
-systemctl show-environment | grep -q '^PATH='
-# Check import-environment
-export IMPORT_THIS=hello
-export IMPORT_THIS_TOO=world
-systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO
-systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS"
-systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO"
-systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
-(! systemctl show-environment | grep "^IMPORT_THIS=")
-(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
-
-# test for sysv-generator (issue #24990)
-if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
- # This is configurable via -Dsysvinit-path=, but we can't get the value
- # at runtime, so let's just support the two most common paths for now.
- [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
-
- # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
- # but may not create the directory if there's no services that use it.
- mkdir -p "$SYSVINIT_PATH"
-
- # invalid dependency
- cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
-#!/bin/bash
-
-### BEGIN INIT INFO
-# Provides:test1 test2
-# Required-Start:test1 $remote_fs $network
-# Required-Stop:test1 $remote_fs $network
-# Description:Test
-# Short-Description: Test
-### END INIT INFO
-
-case "$1" in
- start)
- echo "Starting issue-24990.service"
- sleep 1000 &
- ;;
- stop)
- echo "Stopping issue-24990.service"
- sleep 10 &
- ;;
- *)
- echo "Usage: service test {start|stop|restart|status}"
- ;;
-esac
-EOF
-
- chmod +x "$SYSVINIT_PATH/issue-24990"
- systemctl daemon-reload
- [[ -L /run/systemd/generator.late/test1.service ]]
- [[ -L /run/systemd/generator.late/test2.service ]]
- assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
- assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
- output=$(systemctl cat issue-24990)
- assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
- assert_in "Description=LSB: Test" "$output"
- assert_in "After=test1.service" "$output"
- assert_in "After=remote-fs.target" "$output"
- assert_in "After=network-online.target" "$output"
- assert_in "Wants=network-online.target" "$output"
- assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
- assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
- systemctl status issue-24990 || :
- systemctl show issue-24990
- assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
- assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
-
- if ! systemctl is-active network-online.target; then
- systemctl start network-online.target
- fi
-
- systemctl restart issue-24990
- systemctl stop issue-24990
-
- # valid dependency
- cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
-#!/bin/bash
-
-### BEGIN INIT INFO
-# Provides:test1 test2
-# Required-Start:$remote_fs
-# Required-Stop:$remote_fs
-# Description:Test
-# Short-Description: Test
-### END INIT INFO
-
-case "$1" in
- start)
- echo "Starting issue-24990.service"
- sleep 1000 &
- ;;
- stop)
- echo "Stopping issue-24990.service"
- sleep 10 &
- ;;
- *)
- echo "Usage: service test {start|stop|restart|status}"
- ;;
-esac
-EOF
-
- chmod +x "$SYSVINIT_PATH/issue-24990"
- systemctl daemon-reload
- [[ -L /run/systemd/generator.late/test1.service ]]
- [[ -L /run/systemd/generator.late/test2.service ]]
- assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
- assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
- output=$(systemctl cat issue-24990)
- assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
- assert_in "Description=LSB: Test" "$output"
- assert_in "After=remote-fs.target" "$output"
- assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
- assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
- systemctl status issue-24990 || :
- systemctl show issue-24990
- assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
- assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
-
- systemctl restart issue-24990
- systemctl stop issue-24990
-fi
-
-# %J in WantedBy= causes ABRT (#26467)
-cat >/run/systemd/system/test-WantedBy.service <<EOF
-[Service]
-ExecStart=true
-
-[Install]
-WantedBy=user-%i@%J.service
-EOF
-systemctl daemon-reload
-systemctl enable --now test-WantedBy.service || :
-systemctl daemon-reload
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-29-PORTABLE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-install_extension_images
-
-# Set longer timeout for slower machines, e.g. non-KVM vm.
-mkdir -p /run/systemd/system.conf.d
-cat >/run/systemd/system.conf.d/10-timeout.conf <<EOF
-[Manager]
-DefaultEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
-ManagerEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
-EOF
-
-systemctl daemon-reexec
-
-export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
-
-udevadm control --log-level debug
-
-ARGS=()
-STATE_DIRECTORY=/var/lib/private/
-if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
- # If we're running under sanitizers, we need to use a less restrictive
- # profile, otherwise LSan syscall would get blocked by seccomp
- ARGS+=(--profile=trusted)
- # With the trusted profile DynamicUser is disabled, so the storage is not in private/
- STATE_DIRECTORY=/var/lib/
-fi
-
-systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
-systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service'
-systemd-dissect --no-pager /tmp/app0.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /tmp/app1.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /tmp/conf0.raw | grep -q '✓ confext for portable service'
-
-export SYSTEMD_LOG_LEVEL=debug
-mkdir -p /run/systemd/system/systemd-portabled.service.d/
-cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
-
-portablectl is-attached minimal-app0
-portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-foo.service
-systemctl is-active minimal-app0-bar.service && exit 1
-
-portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl is-attached minimal-app0
-portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-bar.service
-systemctl is-active minimal-app0-foo.service && exit 1
-
-portablectl list | grep -q -F "minimal_1"
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
-
-portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl list | grep -q -F "No images."
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
-
-# Ensure we don't regress (again) when using --force
-
-portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
-
-portablectl is-attached --force minimal-app0
-portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-foo.service
-systemctl is-active minimal-app0-bar.service && exit 1
-
-portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl is-attached --force minimal-app0
-portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-bar.service
-systemctl is-active minimal-app0-foo.service && exit 1
-
-portablectl list | grep -q -F "minimal_1"
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
-
-portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl list | grep -q -F "No images."
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
-
-# portablectl also works with directory paths rather than images
-
-unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
-unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
-
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
-
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-foo.service
-systemctl is-active minimal-app0-bar.service && exit 1
-
-portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
-
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-bar.service
-systemctl is-active minimal-app0-foo.service && exit 1
-
-portablectl list | grep -q -F "minimal_1"
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
-
-portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
-
-portablectl list | grep -q -F "No images."
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
-
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension app0 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
-
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension app0 minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
-
-portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
-
-# Ensure versioned images are accepted without needing to use --force to override the extension-release
-# matching
-
-cp /tmp/app0.raw /tmp/app0_1.0.raw
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension app0_1 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
-rm -f /tmp/app0_1.0.raw
-
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-# Ensure that adding or removing a version to the image doesn't break reattaching
-cp /tmp/app1.raw /tmp/app1_2.raw
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1_2 minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1 minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
-portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-systemctl daemon-reload
-systemctl restart app1.service
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-
-# Ensure vpick works, including reattaching to a new image
-mkdir -p /tmp/app1.v/
-cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw
-cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-rm -f /tmp/app1.v/app1_2.0.raw
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
-rm -f /tmp/app1.v/app1_1.0.raw
-
-# Ensure that the combination of read-only images, state directory and dynamic user works, and that
-# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
-# after the service is attached before the file appears.
-grep -q -F bar "${STATE_DIRECTORY}/app0/foo"
-grep -q -F baz "${STATE_DIRECTORY}/app1/foo"
-
-# Ensure that we can override the check on extension-release.NAME
-cp /tmp/app0.raw /tmp/app10.raw
-portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw"
-
-# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
-# portablectl won't find it)
-rm -f /tmp/app10.raw
-systemctl stop app0.service
-portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
-
-# portablectl also accepts confexts
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw"
-
-portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
-
-# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
-portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-test -f /run/portables/app0.raw
-test -f /run/portables/minimal_0.raw
-test -f /run/systemd/system.attached/app0.service
-test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
-portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-
-# Ensure that when two portables share the same base image, removing one doesn't remove the other too
-
-portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-
-status="$(portablectl is-attached --extension app0 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-
-(! portablectl detach --runtime /usr/share/minimal_0.raw app)
-
-status="$(portablectl is-attached --extension app0 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-
-# Ensure 'portablectl list' shows the correct status for both images
-portablectl list
-portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
-portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
-portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
-
-portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app
-
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-
-portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app
-
-# portablectl also works with directory paths rather than images
-
-mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
-mount /tmp/app0.raw /tmp/app0
-mount /tmp/app1.raw /tmp/app1
-mount /usr/share/minimal_0.raw /tmp/rootdir
-
-# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
-# bypassing the sysext logic in portabled here it will otherwise not see the
-# extensions additional valid prefix)
-grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
-
-mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
-
-grep . /tmp/overlay/usr/lib/extension-release.d/*
-grep . /tmp/overlay/etc/os-release
-
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
-
-systemctl is-active app1.service
-
-portablectl detach --now --runtime overlay app1
-
-umount /tmp/overlay
-
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-
-systemctl is-active app0.service
-systemctl is-active app1.service
-
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
-
-grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
-
-grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
-
-portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-
-# Ensure --clean remove state and other directories belonging to the portable image being detached
-test ! -d /var/lib/app0
-test ! -d /run/app0
-
-# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
-portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-test -d /run/portables/app0
-test -d /run/portables/app1
-test -d /run/portables/rootdir
-test -f /run/systemd/system.attached/app0.service
-test -f /run/systemd/system.attached/app1.service
-test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
-test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
-portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-
-# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
-# Provides coverage for https://github.com/systemd/systemd/issues/23481
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
-portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
-# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
-portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
-
-umount /tmp/rootdir
-umount /tmp/app0
-umount /tmp/app1
-
-# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead
-mkdir -p /tmp/emptyroot/usr/lib
-mkdir -p /tmp/emptyext/usr/lib/extension-release.d
-touch /tmp/emptyroot/usr/lib/os-release
-touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext
-
-# Remote peer disconnected -> portabled crashed
-res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))"
-test -z "${res}"
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-30-ONCLOCKCHANGE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-analyze log-level debug
-
-systemctl disable --now systemd-timesyncd.service
-
-timedatectl set-timezone Europe/Berlin
-timedatectl set-time 1980-10-15
-
-systemd-run --on-timezone-change touch /tmp/timezone-changed
-systemd-run --on-clock-change touch /tmp/clock-changed
-
-test ! -f /tmp/timezone-changed
-test ! -f /tmp/clock-changed
-
-timedatectl set-timezone Europe/Kyiv
-
-while test ! -f /tmp/timezone-changed ; do sleep .5 ; done
-
-timedatectl set-time 2018-1-1
-
-while test ! -f /tmp/clock-changed ; do sleep .5 ; done
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-31-DEVICE-ENUMERATION
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
- exit 1
-fi
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-32-OOMPOLICY
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
-MemoryAccounting=yes
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Let's run this test only if the "memory.oom.group" cgroupfs attribute
-# exists. This test is a bit too strict, since the "memory.events"/"oom_kill"
-# logic has been around since a longer time than "memory.oom.group", but it's
-# an easier thing to test for, and also: let's not get confused by older
-# kernels where the concept was still new.
-
-if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; then
- systemd-analyze log-level debug
-
- # Run a service that is guaranteed to be the first candidate for OOM killing
- systemd-run --unit=oomtest.service \
- -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \
- sleep infinity
-
- # Trigger an OOM killer run
- echo 1 >/proc/sys/kernel/sysrq
- echo f >/proc/sysrq-trigger
-
- while : ; do
- STATE="$(systemctl show -P ActiveState oomtest.service)"
- [ "$STATE" = "failed" ] && break
- sleep .5
- done
-
- RESULT="$(systemctl show -P Result oomtest.service)"
- test "$RESULT" = "oom-kill"
-
- systemd-analyze log-level info
-fi
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-34-DYNAMICUSERMIGRATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-analyze log-level debug
-
-test_directory() {
- local directory="$1"
- local path="$2"
-
- # cleanup for previous invocation
- for i in xxx xxx2 yyy zzz x:yz x:yz2; do
- rm -rf "${path:?}/${i}" "${path:?}/private/${i}"
- done
-
- # Set everything up without DynamicUser=1
-
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
- (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
-
- test -d "${path}"/zzz
- test ! -L "${path}"/zzz
- test ! -e "${path}"/private/zzz
-
- test ! -e "${path}"/xxx
- test ! -e "${path}"/private/xxx
- test ! -e "${path}"/xxx2
- test ! -e "${path}"/private/xxx2
- test -L "${path}"/yyy
- test ! -e "${path}"/private/yyy
-
- test -f "${path}"/zzz/test
- test ! -e "${path}"/zzz/test-missing
-
- # Convert to DynamicUser=1
-
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}=zzz:xxx zzz:xxx2" \
- -p TemporaryFileSystem="${path}" -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
- (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
-
- test -L "${path}"/zzz
- test -d "${path}"/private/zzz
-
- test ! -e "${path}"/xxx
- test ! -e "${path}"/private/xxx
- test ! -e "${path}"/xxx2
- test ! -e "${path}"/private/xxx2
- test -L "${path}"/yyy # previous symlink is not removed
- test ! -e "${path}"/private/yyy
-
- test -f "${path}"/zzz/test
- test ! -e "${path}"/zzz/test-missing
-
- # Convert back
-
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
- systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
- (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
-
- test -d "${path}"/zzz
- test ! -L "${path}"/zzz
- test ! -e "${path}"/private/zzz
-
- test ! -e "${path}"/xxx
- test ! -e "${path}"/private/xxx
- test ! -e "${path}"/xxx2
- test ! -e "${path}"/private/xxx2
- test -L "${path}"/yyy
- test ! -e "${path}"/private/yyy
-
- test -f "${path}"/zzz/test
- test ! -e "${path}"/zzz/test-missing
-
- # Exercise the unit parsing paths too
- cat >/run/systemd/system/testservice-34.service <<EOF
-[Service]
-Type=oneshot
-TemporaryFileSystem=${path}
-RuntimeDirectoryPreserve=yes
-${directory}=zzz:x\:yz zzz:x\:yz2
-ExecStart=test -f ${path}/x:yz2/test
-ExecStart=test -f ${path}/x:yz/test
-ExecStart=test -f ${path}/zzz/test
-EOF
- systemctl daemon-reload
- systemctl start --wait testservice-34.service
-
- test -d "${path}"/zzz
- test ! -L "${path}"/zzz
- test ! -e "${path}"/private/zzz
-
- test ! -L "${path}"/x:yz
- test ! -L "${path}"/x:yz2
-}
-
-test_check_writable() {
- # cleanup for previous invocation
- for i in aaa quux waldo xxx; do
- rm -rf "/var/lib/$i" "/var/lib/private/$i"
- done
-
- cat >/run/systemd/system/testservice-34-check-writable.service <<\EOF
-[Unit]
-Description=Check writable directories when DynamicUser= with StateDirectory=
-
-[Service]
-# Relevant only for sanitizer runs
-EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
-
-Type=oneshot
-DynamicUser=yes
-StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333
-
-# Make sure that the state directories are really the only writable directory besides the obvious candidates
-ExecStart=bash -c ' \
- set -eux; \
- set -o pipefail; \
- declare -a writable_dirs; \
- readarray -t writable_dirs < <(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o \
- -path /sys/fs/bpf -o -path /dev/.lxc -o -path /sys/devices/system/cpu \) \
- -prune -o -type d -writable -print 2>/dev/null | sort -u); \
- [[ "$${#writable_dirs[@]}" == "8" ]]; \
- [[ "$${writable_dirs[0]}" == "/var/lib/private/aaa" ]]; \
- [[ "$${writable_dirs[1]}" == "/var/lib/private/aaa/bbb" ]]; \
- [[ "$${writable_dirs[2]}" == "/var/lib/private/aaa/ccc" ]]; \
- [[ "$${writable_dirs[3]}" == "/var/lib/private/quux/pief" ]]; \
- [[ "$${writable_dirs[4]}" == "/var/lib/private/waldo" ]]; \
- [[ "$${writable_dirs[5]}" == "/var/lib/private/xxx" ]]; \
- [[ "$${writable_dirs[6]}" == "/var/lib/private/xxx/yyy" ]]; \
- [[ "$${writable_dirs[7]}" == "/var/lib/private/xxx/zzz" ]]; \
-'
-EOF
- systemctl daemon-reload
- systemctl start testservice-34-check-writable.service
-}
-
-test_directory "StateDirectory" "/var/lib"
-test_directory "RuntimeDirectory" "/run"
-test_directory "CacheDirectory" "/var/cache"
-test_directory "LogsDirectory" "/var/log"
-
-test_check_writable
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-35-LOGIN
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-cleanup_test_user() (
- set +ex
-
- pkill -u "$(id -u logind-test-user)"
- sleep 1
- pkill -KILL -u "$(id -u logind-test-user)"
- userdel -r logind-test-user
-
- return 0
-)
-
-setup_test_user() {
- mkdir -p /var/spool/cron /var/spool/mail
- useradd -m -s /bin/bash logind-test-user
- trap cleanup_test_user EXIT
-}
-
-test_write_dropin() {
- systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
- # We test "coldplug" (completely stop and start logind) here. So we need to preserve
- # the fdstore, which might contain session leader pidfds. This is extremely rare use case
- # and shall not be considered fully supported.
- # See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
- systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
-[Service]
-FileDescriptorStorePreserve=yes
-EOF
-
- systemctl restart systemd-logind.service
-}
-
-testcase_properties() {
- mkdir -p /run/systemd/logind.conf.d
-
- cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
-[Login]
-KillUserProcesses=no
-EOF
-
- systemctl restart systemd-logind.service
- assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b false"
-
- cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
-[Login]
-KillUserProcesses=yes
-EOF
-
- systemctl restart systemd-logind.service
- assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b true"
-
- rm -rf /run/systemd/logind.conf.d
-}
-
-testcase_sleep_automated() {
- assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 3 "suspend-then-hibernate" "suspend" "hibernate"'
-
- mkdir -p /run/systemd/logind.conf.d
-
- cat >/run/systemd/logind.conf.d/sleep-operations.conf <<EOF
-[Login]
-SleepOperation=suspend hybrid-sleep
-EOF
-
- systemctl restart systemd-logind.service
-
- assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 2 "hybrid-sleep" "suspend"'
-
- busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSleep
-
- rm -rf /run/systemd/logind.conf.d
-}
-
-testcase_started() {
- local pid
-
- systemctl restart systemd-logind.service
-
- # should start at boot, not with D-BUS activation
- pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
-
- # loginctl should succeed
- loginctl
-
- # logind should still be running
- assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-}
-
-wait_suspend() {
- timeout "${1?}" bash -c "while [[ ! -e /run/suspend.flag ]]; do sleep 1; done"
- rm /run/suspend.flag
-}
-
-teardown_suspend() (
- set +eux
-
- pkill evemu-device
-
- rm -rf /run/systemd/system/systemd-suspend.service.d
- systemctl daemon-reload
-
- rm -f /run/udev/rules.d/70-logindtest-lid.rules
- udevadm control --reload
-
- return 0
-)
-
-testcase_suspend_on_lid() {
- local pid input_name lid_dev
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping suspend test in container"
- return
- fi
- if ! grep -s -q mem /sys/power/state; then
- echo "suspend not supported on this testbed, skipping"
- return
- fi
- if ! command -v evemu-device >/dev/null; then
- echo "command evemu-device not found, skipping"
- return
- fi
- if ! command -v evemu-event >/dev/null; then
- echo "command evemu-event not found, skipping"
- return
- fi
-
- trap teardown_suspend RETURN
-
- # save pid
- pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
-
- # create fake suspend
- mkdir -p /run/systemd/system/systemd-suspend.service.d
- cat >/run/systemd/system/systemd-suspend.service.d/override.conf <<EOF
-[Service]
-ExecStart=
-ExecStart=touch /run/suspend.flag
-EOF
- systemctl daemon-reload
-
- # create fake lid switch
- mkdir -p /run/udev/rules.d
- cat >/run/udev/rules.d/70-logindtest-lid.rules <<EOF
-SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="Fake Lid Switch", TAG+="power-switch"
-EOF
- udevadm control --reload
-
- cat >/run/lidswitch.evemu <<EOF
-# EVEMU 1.2
-# Input device name: "Lid Switch"
-# Input device ID: bus 0x19 vendor 0000 product 0x05 version 0000
-# Supported events:
-# Event type 0 (EV_SYN)
-# Event code 0 (SYN_REPORT)
-# Event code 5 (FF_STATUS_MAX)
-# Event type 5 (EV_SW)
-# Event code 0 (SW_LID)
-# Properties:
-N: Fake Lid Switch
-I: 0019 0000 0005 0000
-P: 00 00 00 00 00 00 00 00
-B: 00 21 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 02 00 00 00 00 00 00 00 00
-B: 03 00 00 00 00 00 00 00 00
-B: 04 00 00 00 00 00 00 00 00
-B: 05 01 00 00 00 00 00 00 00
-B: 11 00 00 00 00 00 00 00 00
-B: 12 00 00 00 00 00 00 00 00
-B: 15 00 00 00 00 00 00 00 00
-B: 15 00 00 00 00 00 00 00 00
-EOF
-
- evemu-device /run/lidswitch.evemu &
-
- timeout 20 bash -c 'until grep "^Fake Lid Switch" /sys/class/input/*/device/name; do sleep .5; done'
- input_name=$(grep -l '^Fake Lid Switch' /sys/class/input/*/device/name || :)
- if [[ -z "$input_name" ]]; then
- echo "cannot find fake lid switch." >&2
- exit 1
- fi
- input_name=${input_name%/device/name}
- lid_dev=/dev/${input_name#/sys/class/}
- udevadm info --wait-for-initialization=10s "$lid_dev"
- udevadm settle
-
- # close lid
- evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
- # need to wait for 30s suspend inhibition after boot
- wait_suspend 31
- # open lid again
- evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
-
- # waiting for 30s inhibition time between suspends
- sleep 30
-
- # now closing lid should cause instant suspend
- evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
- wait_suspend 2
- evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
-
- assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-}
-
-testcase_shutdown() {
- local pid
-
- # save pid
- pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
-
- # scheduled shutdown with wall message
- shutdown 2>&1
- sleep 5
- shutdown -c || :
- # logind should still be running
- assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-
- # scheduled shutdown without wall message
- shutdown --no-wall 2>&1
- sleep 5
- shutdown -c --no-wall || true
- assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-}
-
-cleanup_session() (
- set +ex
-
- local uid s
-
- uid=$(id -u logind-test-user)
-
- loginctl disable-linger logind-test-user
-
- systemctl stop getty@tty2.service
-
- for s in $(loginctl --no-legend list-sessions | grep -v manager | awk '$3 == "logind-test-user" { print $1 }'); do
- echo "INFO: stopping session $s"
- loginctl terminate-session "$s"
- done
-
- loginctl terminate-user logind-test-user
-
- if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
- echo "WARNING: session for logind-test-user still active, ignoring."
- fi
-
- pkill -u "$uid"
- sleep 1
- pkill -KILL -u "$uid"
-
- if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then
- echo "WARNING: user@${uid}.service is still active, ignoring."
- fi
-
- if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then
- echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring."
- fi
-
- if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then
- echo "WARNING: user-${uid}.slice is still active, ignoring."
- fi
-
- rm -rf /run/systemd/system/getty@tty2.service.d
- systemctl daemon-reload
-
- return 0
-)
-
-teardown_session() (
- set +ex
-
- cleanup_session
-
- rm -f /run/udev/rules.d/70-logindtest-scsi_debug-user.rules
- udevadm control --reload
- rmmod scsi_debug
-
- return 0
-)
-
-check_session() (
- set +ex
-
- local seat session leader_pid
-
- if [[ $(loginctl --no-legend | grep -v manager | grep -c "logind-test-user") != 1 ]]; then
- echo "no session or multiple sessions for logind-test-user." >&2
- return 1
- fi
-
- seat=$(loginctl --no-legend | grep -v manager | grep 'logind-test-user *seat' | awk '{ print $4 }')
- if [[ -z "$seat" ]]; then
- echo "no seat found for user logind-test-user" >&2
- return 1
- fi
-
- session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
- if [[ -z "$session" ]]; then
- echo "no session found for user logind-test-user" >&2
- return 1
- fi
-
- if ! loginctl session-status "$session" | grep -q "Unit: session-${session}\.scope"; then
- echo "cannot find scope unit for session $session" >&2
- return 1
- fi
-
- leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }')
- if [[ -z "$leader_pid" ]]; then
- echo "cannot found leader process for session $session" >&2
- return 1
- fi
-
- # cgroup v1: "1:name=systemd:/user.slice/..."; unified hierarchy: "0::/user.slice"
- if ! grep -q -E '(name=systemd|^0:):.*session.*scope' /proc/"$leader_pid"/cgroup; then
- echo "FAIL: process $leader_pid is not in the session cgroup" >&2
- cat /proc/self/cgroup
- return 1
- fi
-)
-
-create_session() {
- # login with the test user to start a session
- mkdir -p /run/systemd/system/getty@tty2.service.d
- cat >/run/systemd/system/getty@tty2.service.d/override.conf <<EOF
-[Service]
-Type=simple
-ExecStart=
-ExecStart=-/sbin/agetty --autologin logind-test-user --noclear %I $TERM
-Restart=no
-EOF
- systemctl daemon-reload
-
- systemctl restart getty@tty2.service
-
- # check session
- for i in {1..30}; do
- (( i > 1 )) && sleep 1
- check_session && break
- done
- check_session
- assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2"
-}
-
-testcase_sanity_check() {
- # Exercise basic loginctl options
-
- if [[ ! -c /dev/tty2 ]]; then
- echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
- return
- fi
-
- trap cleanup_session RETURN
- create_session
-
- # Run most of the loginctl commands from a user session to make
- # the seat/session autodetection work-ish
- systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF
- loginctl list-sessions
- loginctl list-sessions -j
- loginctl list-sessions --json=short
- loginctl session-status
- loginctl show-session
- loginctl show-session -P DelayInhibited
-
- # We're not in the same session scope, so in this case we need to specify
- # the session ID explicitly
- session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1; exit; }')
- loginctl kill-session --signal=SIGCONT "$session"
- # FIXME(?)
- #loginctl kill-session --signal=SIGCONT --kill-whom=leader "$session"
-
- loginctl list-users
- loginctl user-status
- loginctl show-user -a
- loginctl show-user -P IdleAction
- loginctl kill-user --signal=SIGCONT ""
-
- loginctl list-seats
- loginctl seat-status
- loginctl show-seat
- loginctl show-seat -P IdleActionUSec
-EOF
-
- # Requires root privileges
- loginctl lock-sessions
- loginctl unlock-sessions
- loginctl flush-devices
-}
-
-testcase_session() {
- local dev
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping ACL tests in container"
- return
- fi
-
- if [[ ! -c /dev/tty2 ]]; then
- echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
- return
- fi
-
- trap teardown_session RETURN
-
- create_session
-
- # scsi_debug should not be loaded yet
- if [[ -d /sys/bus/pseudo/drivers/scsi_debug ]]; then
- echo "scsi_debug module is already loaded." >&2
- exit 1
- fi
-
- # we use scsi_debug to create new devices which we can put ACLs on
- # tell udev about the tagging, so that logind can pick it up
- mkdir -p /run/udev/rules.d
- cat >/run/udev/rules.d/70-logindtest-scsi_debug-user.rules <<EOF
-SUBSYSTEM=="block", ATTRS{model}=="scsi_debug*", TAG+="uaccess"
-EOF
- udevadm control --reload
-
- # coldplug: logind started with existing device
- systemctl stop systemd-logind.service
- modprobe scsi_debug
- timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
- dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
- if [[ ! -b "$dev" ]]; then
- echo "cannot find suitable scsi block device" >&2
- exit 1
- fi
- udevadm settle
- udevadm info "$dev"
-
- # trigger logind and activate session
- loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
-
- # check ACL
- sleep 1
- assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
-
- # hotplug: new device appears while logind is running
- rmmod scsi_debug
- modprobe scsi_debug
- timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
- dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
- if [[ ! -b "$dev" ]]; then
- echo "cannot find suitable scsi block device" >&2
- exit 1
- fi
- udevadm settle
-
- # check ACL
- sleep 1
- assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
-}
-
-teardown_lock_idle_action() (
- set +eux
-
- rm -f /run/systemd/logind.conf.d/idle-action-lock.conf
- systemctl restart systemd-logind.service
-
- cleanup_session
-
- return 0
-)
-
-testcase_lock_idle_action() {
- local ts
-
- if [[ ! -c /dev/tty2 ]]; then
- echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
- return
- fi
-
- if loginctl --no-legend | grep -v manager | grep -q logind-test-user; then
- echo >&2 "Session of the 'logind-test-user' is already present."
- exit 1
- fi
-
- trap teardown_lock_idle_action RETURN
-
- create_session
-
- ts="$(date '+%H:%M:%S')"
-
- mkdir -p /run/systemd/logind.conf.d
- cat >/run/systemd/logind.conf.d/idle-action-lock.conf <<EOF
-[Login]
-IdleAction=lock
-IdleActionSec=1s
-EOF
- systemctl restart systemd-logind.service
-
- # Wait for 35s, in that interval all sessions should have become idle
- # and "Lock" signal should have been sent out. Then we wrote to tty to make
- # session active again and next we slept for another 35s so sessions have
- # become idle again. 'Lock' signal is sent out for each session, we have at
- # least one session, so minimum of 2 "Lock" signals must have been sent.
- timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt 1 ]]; do sleep 1; done"
-
- # We need to know that a new message was sent after waking up,
- # so we must track how many happened before sleeping to check we have extra.
- locks="$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'Sent message type=signal .* member=Lock')"
-
- # Wakeup
- touch /dev/tty2
-
- # Wait again
- timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt $((locks + 1)) ]]; do sleep 1; done"
-
- if [[ "$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'System idle. Will be locked now.')" -lt 2 ]]; then
- echo >&2 "System haven't entered idle state at least 2 times."
- exit 1
- fi
-}
-
-testcase_session_properties() {
- local s
-
- if [[ ! -c /dev/tty2 ]]; then
- echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
- return
- fi
-
- trap cleanup_session RETURN
- create_session
-
- s=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
- /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2
-}
-
-testcase_list_users_sessions_seats() {
- local session seat
-
- if [[ ! -c /dev/tty2 ]]; then
- echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
- return
- fi
-
- trap cleanup_session RETURN
- create_session
-
- # Activate the session
- loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
-
- session=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
- : check that we got a valid session id
- busctl get-property org.freedesktop.login1 "/org/freedesktop/login1/session/_3${session?}" org.freedesktop.login1.Session Id
- assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
- seat=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $4 }')
- assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $6 }')" user
- assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $7 }')" tty2
- assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $8 }')" no
- assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $9 }')" '-'
-
- loginctl list-seats --no-legend | grep -Fwq "${seat?}"
-
- assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)"
- assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no
- assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" active
-
- loginctl enable-linger logind-test-user
- assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
-
- for s in $(loginctl list-sessions --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1 }'); do
- loginctl terminate-session "$s"
- done
- if ! timeout 30 bash -c "while loginctl --no-legend | grep tty | grep -q logind-test-user; do sleep 1; done"; then
- echo "WARNING: session for logind-test-user still active, ignoring."
- return
- fi
-
- assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" lingering
-}
-
-teardown_stop_idle_session() (
- set +eux
-
- rm -f /run/systemd/logind.conf.d/stop-idle-session.conf
- systemctl restart systemd-logind.service
-
- cleanup_session
-)
-
-testcase_stop_idle_session() {
- local id ts
-
- if [[ ! -c /dev/tty2 ]]; then
- echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
- return
- fi
-
- create_session
- trap teardown_stop_idle_session RETURN
-
- id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')"
- ts="$(date '+%H:%M:%S')"
-
- mkdir -p /run/systemd/logind.conf.d
- cat >/run/systemd/logind.conf.d/stop-idle-session.conf <<EOF
-[Login]
-StopIdleSessionSec=2s
-EOF
- systemctl restart systemd-logind.service
- sleep 5
-
- assert_eq "$(journalctl -b -u systemd-logind.service --since="$ts" --grep "Session \"$id\" of user \"logind-test-user\" is idle, stopping." | wc -l)" 1
- assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0
-}
-
-testcase_ambient_caps() {
- local PAMSERVICE TRANSIENTUNIT SCRIPT
-
- # Verify that pam_systemd works and assigns ambient caps as it should
-
- if ! grep -q 'CapAmb:' /proc/self/status ; then
- echo "ambient caps not available, skipping test." >&2
- return
- fi
-
- typeset -i BND MASK
-
- # Get PID 1's bounding set
- BND="0x$(grep 'CapBnd:' /proc/1/status | cut -d: -f2 | tr -d '[:space:]')"
-
- # CAP_CHOWN | CAP_KILL
- MASK=$(((1 << 0) | (1 << 5)))
-
- if [ $((BND & MASK)) -ne "$MASK" ] ; then
- echo "CAP_CHOWN or CAP_KILL not available in bounding set, skipping test." >&2
- return
- fi
-
- PAMSERVICE="pamserv$RANDOM"
- TRANSIENTUNIT="capwakealarm$RANDOM.service"
- SCRIPT="/tmp/capwakealarm$RANDOM.sh"
-
- cat > /etc/pam.d/"$PAMSERVICE" <<EOF
-auth sufficient pam_unix.so
-auth required pam_deny.so
-account sufficient pam_unix.so
-account required pam_permit.so
-session optional pam_systemd.so default-capability-ambient-set=CAP_CHOWN,CAP_KILL debug
-session required pam_unix.so
-EOF
-
- cat > "$SCRIPT" <<'EOF'
-#!/bin/bash
-set -ex
-typeset -i AMB MASK
-AMB="0x$(grep 'CapAmb:' /proc/self/status | cut -d: -f2 | tr -d '[:space:]')"
-MASK=$(((1 << 0) | (1 << 5)))
-test "$AMB" -eq "$MASK"
-EOF
-
- chmod +x "$SCRIPT"
-
- systemd-run -u "$TRANSIENTUNIT" -p PAMName="$PAMSERVICE" -p Type=oneshot -p User=logind-test-user -p StandardError=tty "$SCRIPT"
-
- rm -f "$SCRIPT" "$PAMSERVICE"
-}
-
-background_at_return() {
- rm -f /etc/pam.d/"$PAMSERVICE"
- unset PAMSERVICE
-}
-
-testcase_background() {
-
- local uid TRANSIENTUNIT1 TRANSIENTUNIT2
-
- uid=$(id -u logind-test-user)
-
- systemctl stop user@"$uid".service
-
- PAMSERVICE="pamserv$RANDOM"
- TRANSIENTUNIT1="bg$RANDOM.service"
- TRANSIENTUNIT2="bgg$RANDOM.service"
-
- trap background_at_return RETURN
-
- cat > /etc/pam.d/"$PAMSERVICE" <<EOF
-auth sufficient pam_unix.so
-auth required pam_deny.so
-account sufficient pam_unix.so
-account required pam_permit.so
-session optional pam_systemd.so debug
-session required pam_unix.so
-EOF
-
- systemd-run -u "$TRANSIENTUNIT1" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background-light" -p Type=exec -p User=logind-test-user sleep infinity
-
- # This was a 'light' background service, hence the service manager should not be running
- (! systemctl is-active user@"$uid".service )
-
- systemctl stop "$TRANSIENTUNIT1"
-
- systemd-run -u "$TRANSIENTUNIT2" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background" -p Type=exec -p User=logind-test-user sleep infinity
-
- # This was a regular background service, hence the service manager should be running
- systemctl is-active user@"$uid".service
-
- systemctl stop "$TRANSIENTUNIT2"
-
- systemctl stop user@"$uid".service
-}
-
-setup_test_user
-test_write_dropin
-run_testcases
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-36-NUMAPOLICY
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck disable=SC2317
-at_exit() {
- # shellcheck disable=SC2181
- if [[ $? -ne 0 ]]; then
- # We're exiting with a non-zero EC, let's dump test artifacts
- # for easier debugging
- [[ -v straceLog && -f "$straceLog" ]] && cat "$straceLog"
- [[ -v journalLog && -f "$journalLog" ]] && cat "$journalLog"
- fi
-}
-
-trap at_exit EXIT
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# Log files
-straceLog='strace.log'
-journalLog='journal.log'
-
-# Systemd config files
-testUnit='numa-test.service'
-testUnitFile="/run/systemd/system/$testUnit"
-testUnitNUMAConf="$testUnitFile.d/numa.conf"
-
-# Sleep constants (we should probably figure out something better but nothing comes to mind)
-sleepAfterStart=3
-
-# Journal cursor for easier navigation
-journalCursorFile="jounalCursorFile"
-
-startStrace() {
- coproc strace -qq -p 1 -o "$straceLog" -e set_mempolicy -s 1024 ${1:+"$1"}
- # Wait for strace to properly "initialize", i.e. until PID 1 has the TracerPid
- # field set to the current strace's PID
- until awk -v spid="$COPROC_PID" '/^TracerPid:/ {exit !($2 == spid);}' /proc/1/status; do sleep 0.1; done
-}
-
-stopStrace() {
- [[ -v COPROC_PID ]] || return
-
- local PID=$COPROC_PID
- kill -s TERM "$PID"
- # Make sure the strace process is indeed dead
- while kill -0 "$PID" 2>/dev/null; do sleep 0.1; done
-}
-
-startJournalctl() {
- : >"$journalCursorFile"
- # Save journal's cursor for later navigation
- journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat
-}
-
-stopJournalctl() {
- local unit="${1:-init.scope}"
- # Using journalctl --sync should be better than using SIGRTMIN+1, as
- # the --sync wait until the synchronization is complete
- echo "Force journald to write all queued messages"
- journalctl --sync
- journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog"
-}
-
-checkNUMA() {
- # NUMA enabled system should have at least NUMA node0
- test -e /sys/devices/system/node/node0
-}
-
-writePID1NUMAPolicy() {
- cat >"$confDir/numa.conf" <<EOF
-[Manager]
-NUMAPolicy=${1:?}
-NUMAMask=${2:-""}
-EOF
-}
-
-writeTestUnit() {
- mkdir -p "$testUnitFile.d/"
- printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile"
-}
-
-writeTestUnitNUMAPolicy() {
- cat >"$testUnitNUMAConf" <<EOF
-[Service]
-NUMAPolicy=${1:?}
-NUMAMask=${2:-""}
-EOF
- systemctl daemon-reload
-}
-
-pid1ReloadWithStrace() {
- startStrace
- systemctl daemon-reload
- sleep $sleepAfterStart
- stopStrace
-}
-
-pid1ReloadWithJournal() {
- startJournalctl
- systemctl daemon-reload
- stopJournalctl
-}
-
-pid1StartUnitWithStrace() {
- startStrace '-f'
- systemctl start "${1:?}"
- sleep $sleepAfterStart
- stopStrace
-}
-
-pid1StartUnitWithJournal() {
- startJournalctl
- systemctl start "${1:?}"
- sleep $sleepAfterStart
- stopJournalctl
-}
-
-pid1StopUnit() {
- systemctl stop "${1:?}"
-}
-
-systemctlCheckNUMAProperties() {
- local UNIT_NAME="${1:?}"
- local NUMA_POLICY="${2:?}"
- local NUMA_MASK="${3:-""}"
- local LOGFILE
-
- LOGFILE="$(mktemp)"
-
- systemctl show -p NUMAPolicy "$UNIT_NAME" >"$LOGFILE"
- grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE"
-
- : >"$LOGFILE"
-
- if [ -n "$NUMA_MASK" ]; then
- systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE"
- grep "NUMAMask=$NUMA_MASK" "$LOGFILE"
- fi
-}
-
-writeTestUnit
-
-# Create systemd config drop-in directory
-confDir="/run/systemd/system.conf.d/"
-mkdir -p "$confDir"
-
-if ! checkNUMA; then
- echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check"
-
- echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support"
- writePID1NUMAPolicy "default" "0"
- startJournalctl
- systemctl daemon-reload
- stopJournalctl
- grep "NUMA support not available, ignoring" "$journalLog"
-
- echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support"
- runUnit='numa-systemd-run-test.service'
- startJournalctl
- systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
- sleep $sleepAfterStart
- pid1StopUnit "$runUnit"
- stopJournalctl "$runUnit"
- grep "NUMA support not available, ignoring" "$journalLog"
-
-else
- echo "PID1 NUMAPolicy support - Default policy w/o mask"
- writePID1NUMAPolicy "default"
- pid1ReloadWithStrace
- # Kernel requires that nodemask argument is set to NULL when setting default policy
- grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
- echo "PID1 NUMAPolicy support - Default policy w/ mask"
- writePID1NUMAPolicy "default" "0"
- pid1ReloadWithStrace
- grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
- echo "PID1 NUMAPolicy support - Bind policy w/o mask"
- writePID1NUMAPolicy "bind"
- pid1ReloadWithJournal
- grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
-
- echo "PID1 NUMAPolicy support - Bind policy w/ mask"
- writePID1NUMAPolicy "bind" "0"
- pid1ReloadWithStrace
- grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
-
- echo "PID1 NUMAPolicy support - Interleave policy w/o mask"
- writePID1NUMAPolicy "interleave"
- pid1ReloadWithJournal
- grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
-
- echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
- writePID1NUMAPolicy "interleave" "0"
- pid1ReloadWithStrace
- grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
-
- echo "PID1 NUMAPolicy support - Preferred policy w/o mask"
- writePID1NUMAPolicy "preferred"
- pid1ReloadWithJournal
- # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default
- grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; }
-
- echo "PID1 NUMAPolicy support - Preferred policy w/ mask"
- writePID1NUMAPolicy "preferred" "0"
- pid1ReloadWithStrace
- grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
-
- echo "PID1 NUMAPolicy support - Local policy w/o mask"
- writePID1NUMAPolicy "local"
- pid1ReloadWithStrace
- # Kernel requires that nodemask argument is set to NULL when setting default policy
- # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and
- # return a numerical constant instead (with a comment):
- # set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0
- # Let's cover this scenario as well
- grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
- echo "PID1 NUMAPolicy support - Local policy w/ mask"
- writePID1NUMAPolicy "local" "0"
- pid1ReloadWithStrace
- grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Default policy w/o mask"
- writeTestUnitNUMAPolicy "default"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "default"
- pid1StopUnit "$testUnit"
- grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Default policy w/ mask"
- writeTestUnitNUMAPolicy "default" "0"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "default" "0"
- pid1StopUnit $testUnit
- # Mask must be ignored
- grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Bind policy w/o mask"
- writeTestUnitNUMAPolicy "bind"
- pid1StartUnitWithJournal "$testUnit"
- pid1StopUnit "$testUnit"
- [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
-
- echo "Unit file NUMAPolicy support - Bind policy w/ mask"
- writeTestUnitNUMAPolicy "bind" "0"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "bind" "0"
- pid1StopUnit "$testUnit"
- grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Interleave policy w/o mask"
- writeTestUnitNUMAPolicy "interleave"
- pid1StartUnitWithStrace "$testUnit"
- pid1StopUnit "$testUnit"
- [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
-
- echo "Unit file NUMAPolicy support - Interleave policy w/ mask"
- writeTestUnitNUMAPolicy "interleave" "0"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "interleave" "0"
- pid1StopUnit "$testUnit"
- grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Preferred policy w/o mask"
- writeTestUnitNUMAPolicy "preferred"
- pid1StartUnitWithJournal "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "preferred"
- pid1StopUnit "$testUnit"
- [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] && { echo >&2 "unexpected pass"; exit 1; }
-
- echo "Unit file NUMAPolicy support - Preferred policy w/ mask"
- writeTestUnitNUMAPolicy "preferred" "0"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "preferred" "0"
- pid1StopUnit "$testUnit"
- grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Local policy w/o mask"
- writeTestUnitNUMAPolicy "local"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "local"
- pid1StopUnit "$testUnit"
- grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
- echo "Unit file NUMAPolicy support - Local policy w/ mask"
- writeTestUnitNUMAPolicy "local" "0"
- pid1StartUnitWithStrace "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "local" "0"
- pid1StopUnit "$testUnit"
- # Mask must be ignored
- grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
- echo "Unit file CPUAffinity=NUMA support"
- writeTestUnitNUMAPolicy "bind" "0"
- echo "CPUAffinity=numa" >>"$testUnitNUMAConf"
- systemctl daemon-reload
- systemctl start "$testUnit"
- systemctlCheckNUMAProperties "$testUnit" "bind" "0"
- cpulist="$(cat /sys/devices/system/node/node0/cpulist)"
- affinity_systemd="$(systemctl show --value -p CPUAffinity "$testUnit")"
- [ "$cpulist" = "$affinity_systemd" ]
- pid1StopUnit "$testUnit"
-
- echo "systemd-run NUMAPolicy support"
- runUnit='numa-systemd-run-test.service'
-
- systemd-run -p NUMAPolicy=default --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "default"
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "default" ""
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "bind" "0"
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "interleave" "0"
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "preferred" "0"
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=local --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "local"
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "local" ""
- pid1StopUnit "$runUnit"
-
- systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit "$runUnit" sleep 1000
- systemctlCheckNUMAProperties "$runUnit" "local" ""
- systemctl cat "$runUnit" | grep -q 'CPUAffinity=numa'
- pid1StopUnit "$runUnit"
-fi
-
-# Cleanup
-rm -rf "$confDir"
-systemctl daemon-reload
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-ExecStart=sleep 3600
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-38-FREEZER
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2317
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-systemd-analyze log-level debug
-
-unit=testsuite-38-sleep.service
-
-start_test_service() {
- systemctl daemon-reload
- systemctl start "${unit}"
-}
-
-dbus_freeze() {
- local name object_path suffix
-
- suffix="${1##*.}"
- name="${1%".$suffix"}"
- object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
- busctl call \
- org.freedesktop.systemd1 \
- "${object_path}" \
- org.freedesktop.systemd1.Unit \
- Freeze
-}
-
-dbus_thaw() {
- local name object_path suffix
-
- suffix="${1##*.}"
- name="${1%".$suffix"}"
- object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
- busctl call \
- org.freedesktop.systemd1 \
- "${object_path}" \
- org.freedesktop.systemd1.Unit \
- Thaw
-}
-
-dbus_freeze_unit() {
- busctl call \
- org.freedesktop.systemd1 \
- /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager \
- FreezeUnit \
- s \
- "$1"
-}
-
-dbus_thaw_unit() {
- busctl call \
- org.freedesktop.systemd1 \
- /org/freedesktop/systemd1 \
- org.freedesktop.systemd1.Manager \
- ThawUnit \
- s \
- "$1"
-}
-
-dbus_can_freeze() {
- local name object_path suffix
-
- suffix="${1##*.}"
- name="${1%".$suffix"}"
- object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
- busctl get-property \
- org.freedesktop.systemd1 \
- "${object_path}" \
- org.freedesktop.systemd1.Unit \
- CanFreeze
-}
-
-check_freezer_state() {
- local name object_path suffix
-
- suffix="${1##*.}"
- name="${1%".$suffix"}"
- object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
- for _ in {0..10}; do
- state=$(busctl get-property \
- org.freedesktop.systemd1 \
- "${object_path}" \
- org.freedesktop.systemd1.Unit \
- FreezerState | cut -d " " -f2 | tr -d '"')
-
- # Ignore the intermediate freezing & thawing states in case we check
- # the unit state too quickly
- [[ "$state" =~ ^(freezing|thawing) ]] || break
- sleep .5
- done
-
- [ "$state" = "$2" ] || {
- echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
- exit 1
- }
-}
-
-check_cgroup_state() {
- # foo.unit -> /system.slice/foo.unit/
- # foo.slice/ -> /foo.slice/./
- # foo.slice/foo.unit -> /foo.slice/foo.unit/
- local slice unit
- unit="${1##*/}"
- slice="${1%"$unit"}"
- slice="${slice%/}"
- grep -q "frozen $2" /sys/fs/cgroup/"${slice:-system.slice}"/"${unit:-.}"/cgroup.events
-}
-
-testcase_dbus_api() {
- echo "Test that DBus API works:"
- echo -n " - Freeze(): "
- dbus_freeze "${unit}"
- check_freezer_state "${unit}" "frozen"
- check_cgroup_state "$unit" 1
- echo "[ OK ]"
-
- echo -n " - Thaw(): "
- dbus_thaw "${unit}"
- check_freezer_state "${unit}" "running"
- check_cgroup_state "$unit" 0
- echo "[ OK ]"
-
- echo -n " - FreezeUnit(): "
- dbus_freeze_unit "${unit}"
- check_freezer_state "${unit}" "frozen"
- check_cgroup_state "$unit" 1
- echo "[ OK ]"
-
- echo -n " - ThawUnit(): "
- dbus_thaw_unit "${unit}"
- check_freezer_state "${unit}" "running"
- check_cgroup_state "$unit" 0
- echo "[ OK ]"
-
- echo -n " - CanFreeze(): "
- output=$(dbus_can_freeze "${unit}")
- [ "$output" = "b true" ]
- echo "[ OK ]"
-
- echo
-}
-
-testcase_systemctl() {
- echo "Test that systemctl freeze/thaw verbs:"
-
- systemctl start "$unit"
-
- echo -n " - freeze: "
- systemctl freeze "$unit"
- check_freezer_state "${unit}" "frozen"
- check_cgroup_state "$unit" 1
- # Freezing already frozen unit should be NOP and return quickly
- timeout 3s systemctl freeze "$unit"
- echo "[ OK ]"
-
- echo -n " - thaw: "
- systemctl thaw "$unit"
- check_freezer_state "${unit}" "running"
- check_cgroup_state "$unit" 0
- # Likewise thawing already running unit shouldn't block
- timeout 3s systemctl thaw "$unit"
- echo "[ OK ]"
-
- systemctl stop "$unit"
-
- echo
-}
-
-testcase_systemctl_show() {
- echo "Test systemctl show integration:"
-
- systemctl start "$unit"
-
- echo -n " - FreezerState property: "
- state=$(systemctl show -p FreezerState --value "$unit")
- [ "$state" = "running" ]
- systemctl freeze "$unit"
- state=$(systemctl show -p FreezerState --value "$unit")
- [ "$state" = "frozen" ]
- systemctl thaw "$unit"
- echo "[ OK ]"
-
- echo -n " - CanFreeze property: "
- state=$(systemctl show -p CanFreeze --value "$unit")
- [ "$state" = "yes" ]
- echo "[ OK ]"
-
- systemctl stop "$unit"
- echo
-}
-
-testcase_recursive() {
- local slice="bar.slice"
- local unit="baz.service"
-
- systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
-
- echo "Test recursive freezing:"
-
- echo -n " - freeze/thaw parent: "
- systemctl freeze "$slice"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen-by-parent"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$slice"
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "running"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 0
- echo "[ OK ]"
-
- echo -n " - child freeze/thaw during frozen parent: "
- systemctl freeze "$slice"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen-by-parent"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl freeze "$unit"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$unit"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen-by-parent"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$slice"
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "running"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 0
- echo "[ OK ]"
-
- echo -n " - pre-frozen child not thawed by parent: "
- systemctl freeze "$unit"
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 1
- systemctl freeze "$slice"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$slice"
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 1
- echo "[ OK ]"
-
- echo -n " - pre-frozen child demoted and thawed by parent: "
- systemctl freeze "$slice"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$unit"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen-by-parent"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$slice"
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "running"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 0
- echo "[ OK ]"
-
- echo -n " - child promoted and not thawed by parent: "
- systemctl freeze "$slice"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen-by-parent"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl freeze "$unit"
- check_freezer_state "$slice" "frozen"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- systemctl thaw "$slice"
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "frozen"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 1
- echo "[ OK ]"
-
- echo -n " - can't stop a frozen unit: "
- (! systemctl -q stop "$unit" )
- echo "[ OK ]"
- systemctl thaw "$unit"
-
- systemctl stop "$unit"
- systemctl stop "$slice"
-
- echo
-}
-
-testcase_preserve_state() {
- local slice="bar.slice"
- local unit="baz.service"
-
- systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
-
- echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
-
- echo -n " - freeze from outside: "
- echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
- # Give kernel some time to freeze the slice
- sleep 1
-
- # Our state should not be affected
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "running"
-
- # However actual kernel state should be frozen
- check_cgroup_state "$slice/" 1
- check_cgroup_state "$slice/$unit" 1
- echo "[ OK ]"
-
- echo -n " - thaw from outside: "
- echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
- sleep 1
-
- check_freezer_state "$unit" "running"
- check_freezer_state "$slice" "running"
- check_cgroup_state "$slice/" 0
- check_cgroup_state "$slice/$unit" 0
- echo "[ OK ]"
-
- echo -n " - thaw from outside while inner service is frozen: "
- systemctl freeze "$unit"
- check_freezer_state "$unit" "frozen"
- echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
- echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
- check_freezer_state "$slice" "running"
- check_freezer_state "$unit" "frozen"
- echo "[ OK ]"
-
- systemctl thaw "$unit"
- systemctl stop "$unit"
- systemctl stop "$slice"
-
- echo
-}
-
-if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then
- start_test_service
- run_testcases
-fi
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-43-PRIVATEUSER-UNPRIV
-After=systemd-logind.service user@4711.service
-Wants=user@4711.service
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-install_extension_images
-
-if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
- echo "Cannot create unprivileged user namespaces" >/skipped
- exit 77
-fi
-
-systemd-analyze log-level debug
-
-runas testuser systemd-run --wait --user --unit=test-private-users \
- -p PrivateUsers=yes -P echo hello
-
-runas testuser systemctl --user log-level debug
-
-runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
- -p PrivateTmp=yes \
- -P touch /tmp/innerfile.txt
-# File should not exist outside the job's tmp directory.
-test ! -e /tmp/innerfile.txt
-
-touch /tmp/outerfile.txt
-# File should not appear in unit's private tmp.
-runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \
- -p PrivateTmp=yes \
- -P test ! -e /tmp/outerfile.txt
-
-# Confirm that creating a file in home works
-runas testuser systemd-run --wait --user --unit=test-unprotected-home \
- -P touch /home/testuser/works.txt
-test -e /home/testuser/works.txt
-
-# Confirm that creating a file in home is blocked under read-only
-(! runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
- -p ProtectHome=read-only \
- -P bash -c '
- test -e /home/testuser/works.txt || exit 10
- touch /home/testuser/blocked.txt && exit 11
- ')
-test ! -e /home/testuser/blocked.txt
-
-# Check that tmpfs hides the whole directory
-runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \
- -p ProtectHome=tmpfs \
- -P test ! -e /home/testuser
-
-# Confirm that home, /root, and /run/user are inaccessible under "yes"
-# shellcheck disable=SC2016
-runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
- -p ProtectHome=yes \
- -P bash -c '
- test "$(stat -c %a /home)" = "0"
- test "$(stat -c %a /root)" = "0"
- test "$(stat -c %a /run/user)" = "0"
- '
-
-# Confirm we cannot change groups because we only have one mapping in the user
-# namespace (no CAP_SETGID in the parent namespace to write the additional
-# mapping of the user supplied group and thus cannot change groups to an
-# unmapped group ID)
-(! runas testuser systemd-run --wait --user --unit=test-group-fail \
- -p PrivateUsers=yes -p Group=daemon \
- -P true)
-
-# Check that with a new user namespace we can bind mount
-# files and use a different root directory
-runas testuser systemd-run --wait --user --unit=test-bind-mount \
- -p BindPaths=/dev/null:/etc/os-release \
- test ! -s /etc/os-release
-
-runas testuser systemd-run --wait --user --unit=test-read-write \
- -p ReadOnlyPaths=/ \
- -p ReadWritePaths="/var /run /tmp" \
- -p NoExecPaths=/ -p ExecPaths=/usr \
- test ! -w /etc/os-release
-
-runas testuser systemd-run --wait --user --unit=test-caps \
- -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \
- -p CapabilityBoundingSet=CAP_SYS_ADMIN \
- test -s /etc/os-release
-
-runas testuser systemd-run --wait --user --unit=test-devices \
- -p PrivateDevices=yes -p PrivateIPC=yes \
- sh -c "ls -1 /dev/ | wc -l | grep -q -F 18"
-
-# Same check as test/test-execute/exec-privatenetwork-yes.service
-runas testuser systemd-run --wait --user --unit=test-network \
- -p PrivateNetwork=yes \
- /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"'
-
-(! runas testuser systemd-run --wait --user --unit=test-hostname \
- -p ProtectHostname=yes \
- hostnamectl hostname foo)
-
-(! runas testuser systemd-run --wait --user --unit=test-clock \
- -p ProtectClock=yes \
- timedatectl set-time "2012-10-30 18:17:16")
-
-(! runas testuser systemd-run --wait --user --unit=test-kernel-tunable \
- -p ProtectKernelTunables=yes \
- sh -c "echo 0 >/proc/sys/user/max_user_namespaces")
-
-(! runas testuser systemd-run --wait --user --unit=test-kernel-mod \
- -p ProtectKernelModules=yes \
- sh -c "modprobe -r overlay && modprobe overlay")
-
-if sysctl kernel.dmesg_restrict=0; then
- (! runas testuser systemd-run --wait --user --unit=test-kernel-log \
- -p ProtectKernelLogs=yes -p LogNamespace=yes \
- dmesg)
-fi
-
-unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw
-runas testuser systemd-run --wait --user --unit=test-root-dir \
- -p RootDirectory=/tmp/img \
- grep MARKER=1 /etc/os-release
-
-mkdir /tmp/img_bind
-mount --bind /tmp/img /tmp/img_bind
-runas testuser systemd-run --wait --user --unit=test-root-dir-bind \
- -p RootDirectory=/tmp/img_bind -p MountFlags=private \
- grep MARKER=1 /etc/os-release
-umount /tmp/img_bind
-
-# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first
-mkdir -p /tmp/a /tmp/b /tmp/c
-if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then
- unsquashfs -no-xattrs -d /tmp/app2 /tmp/app1.raw
- runas testuser systemd-run --wait --user --unit=test-extension-dir \
- -p ExtensionDirectories=/tmp/app2 \
- -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \
- -p MountAPIVFS=yes \
- grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2
-fi
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-44-LOG-NAMESPACE
-Before=getty-pre.target
-Wants=getty-pre.target
-Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
-After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-
-systemd-analyze log-level debug
-
-journalctl --list-namespaces -o json | jq .
-
-systemd-run --wait -p LogNamespace=foobar echo "hello world"
-systemd-run --wait -p LogNamespace=foobaz echo "hello world"
-
-journalctl --namespace=foobar --sync
-journalctl --namespace=foobaz --sync
-ls -l /var/log/journal/
-journalctl --list-namespaces
-
-journalctl -o cat --namespace=foobar >/tmp/hello-world
-journalctl -o cat >/tmp/no-hello-world
-
-journalctl --list-namespaces | grep foobar
-journalctl --list-namespaces | grep foobaz
-journalctl --list-namespaces -o json | jq .
-[[ "$(journalctl --root=/tmp --list-namespaces --quiet)" == "" ]]
-
-grep "^hello world$" /tmp/hello-world
-(! grep "^hello world$" /tmp/no-hello-world)
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-45-TIMEDATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-testcase_timedatectl() {
- timedatectl --no-pager --help
- timedatectl --version
-
- timedatectl
- timedatectl --no-ask-password
- timedatectl status --machine=testuser@.host
- timedatectl status
- timedatectl show
- timedatectl show --all
- timedatectl show -p NTP
- timedatectl show -p NTP --value
- timedatectl list-timezones
-
- if ! systemd-detect-virt -qc; then
- systemctl enable --runtime --now systemd-timesyncd
- timedatectl timesync-status
- timedatectl show-timesync
- fi
-}
-
-restore_timezone() {
- if [[ -f /tmp/timezone.bak ]]; then
- mv /tmp/timezone.bak /etc/timezone
- else
- rm -f /etc/timezone
- fi
-}
-
-testcase_timezone() {
- local ORIG_TZ=
-
- # Debian/Ubuntu specific file
- if [[ -f /etc/timezone ]]; then
- mv /etc/timezone /tmp/timezone.bak
- fi
-
- trap restore_timezone RETURN
-
- if [[ -L /etc/localtime ]]; then
- ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')
- echo "original tz: $ORIG_TZ"
- fi
-
- echo 'timedatectl works'
- assert_in "Local time:" "$(timedatectl --no-pager)"
-
- echo 'change timezone'
- assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" ""
- assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv"
- if [[ -f /etc/timezone ]]; then
- assert_eq "$(cat /etc/timezone)" "Europe/Kyiv"
- fi
- assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
-
- if [[ -n "$ORIG_TZ" ]]; then
- echo 'reset timezone to original'
- assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" ""
- assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ"
- if [[ -f /etc/timezone ]]; then
- assert_eq "$(cat /etc/timezone)" "$ORIG_TZ"
- fi
- fi
-}
-
-restore_adjtime() {
- if [[ -e /etc/adjtime.bak ]]; then
- mv /etc/adjtime.bak /etc/adjtime
- else
- rm /etc/adjtime
- fi
-}
-
-check_adjtime_not_exist() {
- if [[ -e /etc/adjtime ]]; then
- echo "/etc/adjtime unexpectedly exists." >&2
- exit 1
- fi
-}
-
-testcase_adjtime() {
- # test setting UTC vs. LOCAL in /etc/adjtime
- if [[ -e /etc/adjtime ]]; then
- mv /etc/adjtime /etc/adjtime.bak
- fi
-
- trap restore_adjtime RETURN
-
- echo 'no adjtime file'
- rm -f /etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
-
- echo 'UTC set in adjtime file'
- printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime
- timedatectl set-local-rtc 0
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-UTC"
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
- echo 'non-zero values in adjtime file'
- printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime
- timedatectl set-local-rtc 0
- assert_eq "$(cat /etc/adjtime)" "0.1 123 0
-0
-UTC"
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.1 123 0
-0
-LOCAL"
-
- echo 'fourth line adjtime file'
- printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime
- timedatectl set-local-rtc 0
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-UTC
-somethingelse"
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL
-somethingelse"
-
- echo 'no final newline in adjtime file'
- printf '0.0 0 0\n0\nUTC' >/etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- printf '0.0 0 0\n0\nUTC' >/etc/adjtime
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
- echo 'only one line in adjtime file'
- printf '0.0 0 0\n' >/etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- printf '0.0 0 0\n' >/etc/adjtime
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
- echo 'only one line in adjtime file, no final newline'
- printf '0.0 0 0' >/etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- printf '0.0 0 0' >/etc/adjtime
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
- echo 'only two lines in adjtime file'
- printf '0.0 0 0\n0\n' >/etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- printf '0.0 0 0\n0\n' >/etc/adjtime
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
- echo 'only two lines in adjtime file, no final newline'
- printf '0.0 0 0\n0' >/etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- printf '0.0 0 0\n0' >/etc/adjtime
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
- echo 'unknown value in 3rd line of adjtime file'
- printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
- timedatectl set-local-rtc 0
- check_adjtime_not_exist
- printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
- timedatectl set-local-rtc 1
- assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-}
-
-assert_ntp() {
- local value="${1:?}"
-
- for _ in {0..9}; do
- [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0
- sleep .5
- done
-
- return 1
-}
-
-assert_timedated_signal() {
- local timestamp="${1:?}"
- local value="${2:?}"
- local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
-
- journalctl --sync
-
- for _ in {0..9}; do
- if journalctl "${args[@]}" --grep .; then
- [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]];
- return 0
- fi
-
- sleep .5
- done
-
- return 1
-}
-
-assert_timesyncd_state() {
- local state="${1:?}"
-
- for _ in {0..9}; do
- [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0
- sleep .5
- done
-
- return 1
-}
-
-testcase_ntp() {
- # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing
- if systemd-detect-virt --container --quiet; then
- systemctl disable --quiet --now systemd-timesyncd
- mkdir -p /run/systemd/system/systemd-timesyncd.service.d
- cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf <<EOF
-[Unit]
-ConditionVirtualization=
-
-[Service]
-Type=simple
-AmbientCapabilities=
-ExecStart=
-ExecStart=sleep infinity
-EOF
- systemctl daemon-reload
- fi
-
- systemd-run --unit busctl-monitor.service --service-type=notify \
- busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timedate1,member=PropertiesChanged,path=/org/freedesktop/timedate1"
-
- : 'Disable NTP'
- ts="$(date +"%F %T.%6N")"
- timedatectl set-ntp false
- assert_timedated_signal "$ts" "false"
- assert_timesyncd_state "inactive"
- assert_ntp "false"
- assert_rc 3 systemctl is-active --quiet systemd-timesyncd
-
- : 'Enable NTP'
- ts="$(date +"%F %T.%6N")"
- timedatectl set-ntp true
- assert_timedated_signal "$ts" "true"
- assert_ntp "true"
- assert_timesyncd_state "active"
- assert_rc 0 systemctl is-active --quiet systemd-timesyncd
-
- : 'Re-disable NTP'
- ts="$(date +"%F %T.%6N")"
- timedatectl set-ntp false
- assert_timedated_signal "$ts" "false"
- assert_ntp "false"
- assert_rc 3 systemctl is-active --quiet systemd-timesyncd
-
- systemctl stop busctl-monitor.service
- rm -rf /run/systemd/system/systemd-timesyncd.service.d/
- systemctl daemon-reload
-}
-
-assert_timesyncd_signal() {
- local timestamp="${1:?}"
- local property="${2:?}"
- local value="${3:?}"
- local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
-
- journalctl --sync
-
- for _ in {0..9}; do
- if journalctl "${args[@]}" --grep .; then
- [[ "$(journalctl "${args[@]}" -o cat | jq -r ".payload.data[1].$property.data | join(\" \")")" == "$value" ]];
- return 0
- fi
-
- sleep .5
- done
-
- return 1
-}
-
-assert_networkd_ntp() {
- local interface="${1:?}"
- local value="${2:?}"
- # Go through the array of NTP servers and for each entry do:
- # - if the entry is an IPv4 address, join the Address array into a dot separated string
- # - if the entry is a server address, select it unchanged
- # These steps produce an array of strings, that is then joined into a space-separated string
- # Note: this doesn't support IPv6 addresses, since converting them to a string is a bit more
- # involved than a simple join(), but let's leave that to another time
- local expr='[.NTP[] | (select(.Family == 2).Address | join(".")), select(has("Server")).Server] | join(" ")'
-
- [[ "$(networkctl status "$interface" --json=short | jq -r "$expr")" == "$value" ]]
-}
-
-testcase_timesyncd() {
- if systemd-detect-virt -cq; then
- echo "This test case requires a VM, skipping..."
- return 0
- fi
-
- if ! command -v networkctl >/dev/null; then
- echo "This test requires systemd-networkd, skipping..."
- return 0
- fi
-
- # Create a dummy interface managed by networkd, so we can configure link NTP servers
- mkdir -p /run/systemd/network/
- cat >/etc/systemd/network/10-ntp99.netdev <<EOF
-[NetDev]
-Name=ntp99
-Kind=dummy
-EOF
- cat >/etc/systemd/network/10-ntp99.network <<EOF
-[Match]
-Name=ntp99
-
-[Network]
-Address=10.0.0.1/24
-EOF
-
- systemctl unmask systemd-timesyncd systemd-networkd
- systemctl restart systemd-timesyncd
- systemctl restart systemd-networkd
- networkctl status ntp99
-
- systemd-run --unit busctl-monitor.service --service-type=notify \
- busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timesync1,member=PropertiesChanged,path=/org/freedesktop/timesync1"
-
- # LinkNTPServers
- #
- # Single IP
- ts="$(date +"%F %T.%6N")"
- timedatectl ntp-servers ntp99 10.0.0.1
- assert_networkd_ntp ntp99 10.0.0.1
- assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1
- # Setting NTP servers to the same value shouldn't emit a PropertiesChanged signal
- ts="$(date +"%F %T.%6N")"
- timedatectl ntp-servers ntp99 10.0.0.1
- assert_networkd_ntp ntp99 10.0.0.1
- (! assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1)
- # Multiple IPs
- ts="$(date +"%F %T.%6N")"
- timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99
- assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99"
- assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99"
- # Multiple IPs + servers
- ts="$(date +"%F %T.%6N")"
- timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13
- assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
- assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
-
- # RuntimeNTPServers
- #
- # There's no user-facing API that allows changing this property (afaik), so let's
- # call SetRuntimeNTPServers() directly to test things out. The inner workings should
- # be exactly the same as in the previous case, so do just one test to make sure
- # things work
- ts="$(date +"%F %T.%6N")"
- busctl call org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager \
- SetRuntimeNTPServers as 4 "10.0.0.1" foo "192.168.99.1" bar
- servers="$(busctl get-property org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager RuntimeNTPServers)"
- [[ "$servers" == 'as 4 "10.0.0.1" "foo" "192.168.99.1" "bar"' ]]
- assert_timesyncd_signal "$ts" RuntimeNTPServers "10.0.0.1 foo 192.168.99.1 bar"
-
- # Cleanup
- systemctl stop systemd-networkd systemd-timesyncd
- rm -f /run/systemd/network/ntp99.*
-}
-
-run_testcases
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-46-HOMED
-Wants=getty-pre.target
-Before=getty-pre.target
-Requires=systemd-homed.service systemd-userdbd.socket
-After=systemd-homed.service systemd-userdbd.socket
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
-NotifyAccess=all
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check if homectl is installed, and if it isn't bail out early instead of failing
-if ! test -x /usr/bin/homectl ; then
- echo "no homed" >/skipped
- exit 77
-fi
-
-inspect() {
- # As updating disk-size-related attributes can take some time on some
- # filesystems, let's drop these fields before comparing the outputs to
- # avoid unexpected fails. To see the full outputs of both homectl &
- # userdbctl (for debugging purposes) drop the fields just before the
- # comparison.
- local USERNAME="${1:?}"
- homectl inspect "$USERNAME" | tee /tmp/a
- userdbctl user "$USERNAME" | tee /tmp/b
-
- # diff uses the grep BREs for pattern matching
- diff -I '^\s*Disk \(Size\|Free\|Floor\|Ceiling\|Usage\):' /tmp/{a,b}
- rm /tmp/{a,b}
-
- homectl inspect --json=pretty "$USERNAME"
-}
-
-wait_for_state() {
- for i in {1..10}; do
- (( i > 1 )) && sleep 0.5
- homectl inspect "$1" | grep -qF "State: $2" && break
- done
-}
-
-FSTYPE="$(stat --file-system --format "%T" /)"
-
-systemd-analyze log-level debug
-systemctl service-log-level systemd-homed debug
-
-# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
-mkdir -p /home
-mount -t tmpfs tmpfs /home -o size=290M
-
-# we enable --luks-discard= since we run our tests in a tight VM, hence don't
-# needlessly pressure for storage. We also set the cheapest KDF, since we don't
-# want to waste CI CPU cycles on it. We also effectively disable rate-limiting on
-# the user by allowing 1000 logins per second
-NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
- --disk-size=min \
- --luks-discard=yes \
- --image-path=/home/test-user.home \
- --luks-pbkdf-type=pbkdf2 \
- --luks-pbkdf-time-cost=1ms \
- --rate-limit-interval=1s \
- --rate-limit-burst=1000
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
-inspect test-user
-
-PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
-inspect test-user
-
-SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-homectl update test-user --real-name "Offline test" --offline
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-# Ensure that the offline changes were propagated in
-grep "Offline test" /home/test-user/.identity
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inactive test"
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
-# (See: https://github.com/systemd/systemd/issues/17606)
-if ! systemd-detect-virt -cq ; then
- PASSWORD=xEhErW0ndafV4s homectl activate test-user
- inspect test-user
-
- # Key should now be in the keyring
- homectl update test-user --real-name "Keyring Test"
- inspect test-user
-
- # These commands shouldn't use the keyring
- (! timeout 5s homectl authenticate test-user )
- (! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
-
- homectl lock test-user
- inspect test-user
-
- # Key should be gone from keyring
- (! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
-
- PASSWORD=xEhErW0ndafV4s homectl unlock test-user
- inspect test-user
-
- # Key should have been re-instantiated into the keyring
- homectl update test-user --real-name "Keyring Test 3"
- inspect test-user
-
- homectl deactivate test-user
- inspect test-user
-fi
-
-# Do some resize tests, but only if we run on real kernels and are on btrfs, as quota inside of containers
-# will fail and minimizing while active only works on btrfs.
-if ! systemd-detect-virt -cq && [[ "$FSTYPE" == "btrfs" ]]; then
- # grow while inactive
- PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
- inspect test-user
-
- # minimize while inactive
- PASSWORD=xEhErW0ndafV4s homectl resize test-user min
- inspect test-user
-
- PASSWORD=xEhErW0ndafV4s homectl activate test-user
- inspect test-user
-
- # grow while active
- PASSWORD=xEhErW0ndafV4s homectl resize test-user max
- inspect test-user
-
- # minimize while active
- PASSWORD=xEhErW0ndafV4s homectl resize test-user 0
- inspect test-user
-
- # grow while active
- PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
- inspect test-user
-
- # shrink to original size while active
- PASSWORD=xEhErW0ndafV4s homectl resize test-user 256M
- inspect test-user
-
- # minimize again
- PASSWORD=xEhErW0ndafV4s homectl resize test-user min
- inspect test-user
-
- # Increase space, so that we can reasonably rebalance free space between to home dirs
- mount /home -o remount,size=800M
-
- # create second user
- NEWPASSWORD=uuXoo8ei homectl create test-user2 \
- --disk-size=min \
- --luks-discard=yes \
- --image-path=/home/test-user2.home \
- --luks-pbkdf-type=pbkdf2 \
- --luks-pbkdf-time-cost=1ms \
- --rate-limit-interval=1s \
- --rate-limit-burst=1000
- inspect test-user2
-
- # activate second user
- PASSWORD=uuXoo8ei homectl activate test-user2
- inspect test-user2
-
- # set second user's rebalance weight to 100
- PASSWORD=uuXoo8ei homectl update test-user2 --rebalance-weight=100
- inspect test-user2
-
- # set first user's rebalance weight to quarter of that of the second
- PASSWORD=xEhErW0ndafV4s homectl update test-user --rebalance-weight=25
- inspect test-user
-
- # synchronously rebalance
- homectl rebalance
- inspect test-user
- inspect test-user2
-
- wait_for_state test-user2 active
- homectl deactivate test-user2
- wait_for_state test-user2 inactive
- homectl remove test-user2
-fi
-
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
-
-# Regression tests
-wait_for_state test-user inactive
-/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
-
-wait_for_state test-user inactive
-homectl remove test-user
-
-# blob directory tests
-# See docs/USER_RECORD_BLOB_DIRS.md
-checkblob() {
- test -f "/var/cache/systemd/home/blob-user/$1"
- stat -c "%u %#a" "/var/cache/systemd/home/blob-user/$1" | grep "^0 0644"
- test -f "/home/blob-user/.identity-blob/$1"
- stat -c "%u %#a" "/home/blob-user/.identity-blob/$1" | grep "^12345 0644"
-
- diff "/var/cache/systemd/home/blob-user/$1" "$2"
- diff "/var/cache/systemd/home/blob-user/$1" "/home/blob-user/.identity-blob/$1"
-}
-
-mkdir /tmp/blob1 /tmp/blob2
-echo data1 blob1 >/tmp/blob1/test1
-echo data1 blob2 >/tmp/blob2/test1
-echo data2 blob1 >/tmp/blob1/test2
-echo data2 blob2 >/tmp/blob2/test2
-echo invalid filename >/tmp/blob1/файл
-echo data3 >/tmp/external-test3
-echo avatardata >/tmp/external-avatar
-ln -s /tmp/external-avatar /tmp/external-avatar-lnk
-dd if=/dev/urandom of=/tmp/external-barely-fits bs=1M count=64
-dd if=/dev/urandom of=/tmp/external-toobig bs=1M count=65
-
-# create w/ prepopulated blob dir
-NEWPASSWORD=EMJuc3zQaMibJo homectl create blob-user \
- --disk-size=min --luks-discard=yes \
- --luks-pbkdf-type=pbkdf2 --luks-pbkdf-time-cost=1ms \
- --rate-limit-interval=1s --rate-limit-burst=1000 \
- --uid=12345 \
- --blob=/tmp/blob1
-inspect blob-user
-PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
-inspect blob-user
-
-test -d /var/cache/systemd/home/blob-user
-stat -c "%u %#a" /var/cache/systemd/home/blob-user | grep "^0 0755"
-test -d /home/blob-user/.identity-blob
-stat -c "%u %#a" /home/blob-user/.identity-blob | grep "^12345 0700"
-
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-checkblob test2 /tmp/blob1/test2
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# append files to existing blob, both well-known and other
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
- -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar
-inspect blob-user
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-checkblob test2 /tmp/blob1/test2
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-checkblob test3 /tmp/external-test3
-checkblob avatar /tmp/external-avatar
-
-# delete files from existing blob, both well-known and other
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
- -b test3= --avatar=
-inspect blob-user
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-checkblob test2 /tmp/blob1/test2
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# swap entire blob directory
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
- -b /tmp/blob2
-inspect blob-user
-(! checkblob test1 /tmp/blob1/test1 )
-checkblob test1 /tmp/blob2/test1
-(! checkblob test2 /tmp/blob1/test2 )
-checkblob test2 /tmp/blob2/test2
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# create and delete files while swapping blob directory. Also symlinks.
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
- -b /tmp/blob1 -b test2= -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar-lnk
-inspect blob-user
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-(! checkblob test2 /tmp/blob1/test2 )
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-checkblob test3 /tmp/external-test3
-checkblob avatar /tmp/external-avatar # target of the link
-
-# clear the blob directory
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
- -b /tmp/blob2 -b test3=/tmp/external-test3 --blob=
-inspect blob-user
-(! checkblob test1 /tmp/blob1/test1 )
-(! checkblob test1 /tmp/blob2/test1 )
-(! checkblob test2 /tmp/blob1/test2 )
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# file that's exactly 64M still fits
-# FIXME: Figure out why this fails on ext4.
-if [[ "$FSTYPE" != "ext2/ext3" ]]; then
- PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
- -b barely-fits=/tmp/external-barely-fits
- (! checkblob test1 /tmp/blob1/test1 )
- (! checkblob test1 /tmp/blob2/test1 )
- (! checkblob test2 /tmp/blob1/test2 )
- (! checkblob test2 /tmp/blob2/test2 )
- (! checkblob фаил /tmp/blob1/фаил )
- (! checkblob test3 /tmp/external-test3 )
- (! checkblob avatar /tmp/external-avatar )
- checkblob barely-fits /tmp/external-barely-fits
-fi
-
-# error out if the file is too big
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b huge=/tmp/external-toobig )
-
-# error out if filenames are invalid
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b .hidden=/tmp/external-test3 )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b "with spaces=/tmp/external-test3" )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b with=equals=/tmp/external-test3 )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b файл=/tmp/external-test3 )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b special@chars=/tmp/external-test3 )
-
-# Make sure offline updates to blobs get propagated in
-homectl deactivate blob-user
-inspect blob-user
-homectl update blob-user --offline -b barely-fits= -b propagated=/tmp/external-test3
-inspect blob-user
-PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
-inspect blob-user
-(! checkblob barely-fits /tmp/external-barely-fits )
-checkblob propagated /tmp/external-test3
-
-homectl deactivate blob-user
-wait_for_state blob-user inactive
-homectl remove blob-user
-
-# userdbctl tests
-export PAGER=
-
-# Create a couple of user/group records to test io.systemd.DropIn
-# See docs/USER_RECORD.md and docs/GROUP_RECORD.md
-mkdir -p /run/userdb/
-cat >"/run/userdb/dropingroup.group" <<\EOF
-{
- "groupName" : "dropingroup",
- "gid" : 1000000
-}
-EOF
-cat >"/run/userdb/dropinuser.user" <<\EOF
-{
- "userName" : "dropinuser",
- "uid" : 2000000,
- "realName" : "🐱",
- "memberOf" : [
- "dropingroup"
- ]
-}
-EOF
-cat >"/run/userdb/dropinuser.user-privileged" <<\EOF
-{
- "privileged" : {
- "hashedPassword" : [
- "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
- ],
- "sshAuthorizedKeys" : [
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA//dxI2xLg4MgxIKKZv1nqwTEIlE/fdakii2Fb75pG+ foo@bar.tld",
- "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMlaqG2rTMje5CQnfjXJKmoSpEVJ2gWtx4jBvsQbmee2XbU/Qdq5+SRisssR9zVuxgg5NA5fv08MgjwJQMm+csc= hello@world.tld"
- ]
- }
-}
-EOF
-# Set permissions and create necessary symlinks as described in nss-systemd(8)
-chmod 0600 "/run/userdb/dropinuser.user-privileged"
-ln -svrf "/run/userdb/dropingroup.group" "/run/userdb/1000000.group"
-ln -svrf "/run/userdb/dropinuser.user" "/run/userdb/2000000.user"
-ln -svrf "/run/userdb/dropinuser.user-privileged" "/run/userdb/2000000.user-privileged"
-
-userdbctl
-userdbctl --version
-userdbctl --help --no-pager
-userdbctl --no-legend
-userdbctl --output=classic
-userdbctl --output=friendly
-userdbctl --output=table
-userdbctl --output=json | jq
-userdbctl -j --json=pretty | jq
-userdbctl -j --json=short | jq
-userdbctl --with-varlink=no
-
-userdbctl user
-userdbctl user testuser
-userdbctl user root
-userdbctl user testuser root
-userdbctl user -j testuser root | jq
-# Check only UID for the nobody user, since the name is build-configurable
-userdbctl user --with-nss=no --synthesize=yes
-userdbctl user --with-nss=no --synthesize=yes 0 root 65534
-userdbctl user dropinuser
-userdbctl user 2000000
-userdbctl user --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropinuser
-userdbctl user --with-nss=no 2000000
-(! userdbctl user '')
-(! userdbctl user 🐱)
-(! userdbctl user 🐱 '' bar)
-(! userdbctl user i-do-not-exist)
-(! userdbctl user root i-do-not-exist testuser)
-(! userdbctl user --with-nss=no --synthesize=no 0 root 65534)
-(! userdbctl user -N root nobody)
-(! userdbctl user --with-dropin=no dropinuser)
-(! userdbctl user --with-dropin=no 2000000)
-
-userdbctl group
-userdbctl group testuser
-userdbctl group root
-userdbctl group testuser root
-userdbctl group -j testuser root | jq
-# Check only GID for the nobody group, since the name is build-configurable
-userdbctl group --with-nss=no --synthesize=yes
-userdbctl group --with-nss=no --synthesize=yes 0 root 65534
-userdbctl group dropingroup
-userdbctl group 1000000
-userdbctl group --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropingroup
-userdbctl group --with-nss=no 1000000
-(! userdbctl group '')
-(! userdbctl group 🐱)
-(! userdbctl group 🐱 '' bar)
-(! userdbctl group i-do-not-exist)
-(! userdbctl group root i-do-not-exist testuser)
-(! userdbctl group --with-nss=no --synthesize=no 0 root 65534)
-(! userdbctl group --with-dropin=no dropingroup)
-(! userdbctl group --with-dropin=no 1000000)
-
-userdbctl users-in-group
-userdbctl users-in-group testuser
-userdbctl users-in-group testuser root
-userdbctl users-in-group -j testuser root | jq
-userdbctl users-in-group 🐱
-(! userdbctl users-in-group '')
-(! userdbctl users-in-group foo '' bar)
-
-userdbctl groups-of-user
-userdbctl groups-of-user testuser
-userdbctl groups-of-user testuser root
-userdbctl groups-of-user -j testuser root | jq
-userdbctl groups-of-user 🐱
-(! userdbctl groups-of-user '')
-(! userdbctl groups-of-user foo '' bar)
-
-userdbctl services
-userdbctl services -j | jq
-
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"testuser","service":"io.systemd.Multiplexer"}'
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"root","service":"io.systemd.Multiplexer"}'
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"uid":2000000,"service":"io.systemd.Multiplexer"}'
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"","service":"io.systemd.Multiplexer"}')
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"🐱","service":"io.systemd.Multiplexer"}')
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
-
-userdbctl ssh-authorized-keys dropinuser | tee /tmp/authorized-keys
-grep "ssh-ed25519" /tmp/authorized-keys
-grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
-echo "my-top-secret-key 🐱" >/tmp/my-top-secret-key
-userdbctl ssh-authorized-keys dropinuser --chain /bin/cat /tmp/my-top-secret-key | tee /tmp/authorized-keys
-grep "ssh-ed25519" /tmp/authorized-keys
-grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
-grep "my-top-secret-key 🐱" /tmp/authorized-keys
-(! userdbctl ssh-authorized-keys 🐱)
-(! userdbctl ssh-authorized-keys dropin-user --chain)
-(! userdbctl ssh-authorized-keys dropin-user --chain '')
-(! SYSTEMD_LOG_LEVEL=debug userdbctl ssh-authorized-keys dropin-user --chain /bin/false)
-
-(! userdbctl '')
-for opt in json multiplexer output synthesize with-dropin with-nss with-varlink; do
- (! userdbctl "--$opt=''")
- (! userdbctl "--$opt='🐱'")
- (! userdbctl "--$opt=foo")
- (! userdbctl "--$opt=foo" "--$opt=''" "--$opt=🐱")
-done
-
-# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
-if command -v ssh &>/dev/null && command -v sshd &>/dev/null && ! [[ -v ASAN_OPTIONS ]]; then
- at_exit() {
- set +e
-
- systemctl is-active -q mysshserver.socket && systemctl stop mysshserver.socket
- rm -f /tmp/homed.id_ecdsa /run/systemd/system/mysshserver{@.service,.socket}
- systemctl daemon-reload
- homectl remove homedsshtest
- for dir in /etc /usr/lib; do
- if [[ -f "$dir/pam.d/sshd.bak" ]]; then
- mv "$dir/pam.d/sshd.bak" "$dir/pam.d/sshd"
- fi
- done
- }
-
- trap at_exit EXIT
-
- # Test that SSH logins work with delayed unlocking
- ssh-keygen -N '' -C '' -t ecdsa -f /tmp/homed.id_ecdsa
- NEWPASSWORD=hunter4711 homectl create \
- --disk-size=min \
- --luks-discard=yes \
- --luks-pbkdf-type=pbkdf2 \
- --luks-pbkdf-time-cost=1ms \
- --rate-limit-interval=1s \
- --rate-limit-burst=1000 \
- --enforce-password-policy=no \
- --ssh-authorized-keys=@/tmp/homed.id_ecdsa.pub \
- --stop-delay=0 \
- homedsshtest
- homectl inspect homedsshtest
-
- mkdir -p /etc/ssh
- test -f /etc/ssh/ssh_host_ecdsa_key || ssh-keygen -t ecdsa -C '' -N '' -f /etc/ssh/ssh_host_ecdsa_key
-
- # ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that
- # are aware of distros use
- mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
-
- for dir in /etc /usr/lib; do
- if [[ -f "$dir/pam.d/sshd" ]]; then
- mv "$dir/pam.d/sshd" "$dir/pam.d/sshd.bak"
- cat >"$dir/pam.d/sshd" <<EOF
-auth sufficient pam_unix.so nullok
-auth sufficient pam_systemd_home.so debug
-auth required pam_deny.so
-account sufficient pam_systemd_home.so debug
-account sufficient pam_unix.so
-account required pam_permit.so
-session optional pam_systemd_home.so debug
-session optional pam_systemd.so
-session required pam_unix.so
-EOF
- break
- fi
- done
-
- mkdir -p /etc/sshd/
- cat >/etc/ssh/sshd_config <<EOF
-AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
-AuthorizedKeysCommandUser root
-UsePAM yes
-AcceptEnv PASSWORD
-LogLevel DEBUG3
-EOF
-
- cat >/run/systemd/system/mysshserver.socket <<EOF
-[Socket]
-ListenStream=4711
-Accept=yes
-EOF
-
- cat >/run/systemd/system/mysshserver@.service <<EOF
-[Service]
-ExecStart=-/usr/sbin/sshd -i -d -e
-StandardInput=socket
-StandardOutput=socket
-StandardError=journal
-EOF
-
- systemctl daemon-reload
- systemctl start mysshserver.socket
-
- userdbctl user -j homedsshtest
-
- ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
- -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
- homedsshtest@localhost echo zzz | tr -d '\r' | tee /tmp/homedsshtest.out
- grep -E "^zzz$" /tmp/homedsshtest.out
- rm /tmp/homedsshtest.out
-
- ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
- -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
- homedsshtest@localhost env
-
- wait_for_state homedsshtest inactive
-fi
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check that the /sbin/mount.ddi helper works
-dir="/tmp/mounthelper.$RANDOM"
-mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard
-umount -R "$dir"
-
-# Test systemd-repart --make-ddi=:
-if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then
- echo "Skipping --make-ddi= tests"
- exit 0
-fi
-
-openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
- -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
- -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
-mkdir -p /tmp/test-50-confext/etc/extension-release.d/
-echo "foobar50" >/tmp/test-50-confext/etc/waldo
-{
- grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
- echo IMAGE_ID=waldo
- echo IMAGE_VERSION=7
-} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
-mkdir -p /run/confexts
-
-SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
- systemd-repart -C \
- -s /tmp/test-50-confext \
- --certificate=/tmp/test-50-cert.crt \
- --private-key=/tmp/test-50-privkey.key \
- /run/confexts/waldo.confext.raw
-rm -rf /tmp/test-50-confext
-
-mkdir -p /run/verity.d
-cp /tmp/test-50-cert.crt /run/verity.d/
-systemd-dissect --mtree /run/confexts/waldo.confext.raw
-
-systemd-confext refresh
-test "$(</etc/waldo)" = foobar50
-rm /run/confexts/waldo.confext.raw
-systemd-confext refresh
-test ! -f /etc/waldo
-
-mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
-# Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
-# Minimize= logic is working.
-truncate --size=50M /tmp/test-50-sysext/usr/waldo
-{
- grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
- echo IMAGE_ID=waldo
- echo IMAGE_VERSION=7
-} >/tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
-mkdir -p /run/extensions
-
-SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
- systemd-repart -S \
- -s /tmp/test-50-sysext \
- --certificate=/tmp/test-50-cert.crt \
- --private-key=/tmp/test-50-privkey.key \
- /run/extensions/waldo.sysext.raw
-
-systemd-dissect --mtree /run/extensions/waldo.sysext.raw
-systemd-sysext refresh
-test -f /usr/waldo
-rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
-systemd-sysext refresh
-test ! -f /usr/waldo
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-# shellcheck disable=SC2233,SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-BIND_LOG_SOCKETS=(
- --property BindReadOnlyPaths=/dev/log
- --property BindReadOnlyPaths=/run/systemd/journal/socket
- --property BindReadOnlyPaths=/run/systemd/journal/stdout
-)
-
-systemd-dissect --json=short "$MINIMAL_IMAGE.raw" | \
- grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F "MARKER=1"
-# shellcheck disable=SC2153
-systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
-
-systemd-dissect --list "$MINIMAL_IMAGE.raw" | grep -q '^etc/os-release$'
-systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash yes | \
- grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
-systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash no | \
- grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "$MINIMAL_IMAGE.raw" etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "$MINIMAL_IMAGE.raw" sha256sum etc/os-release)
-test "$SHA256SUM2" != ""
-test "$SHA256SUM1" = "$SHA256SUM2"
-
-if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
- # Make sure tarballs are reproducible
- read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
- test "$SHA256SUM1" != ""
- read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
- test "$SHA256SUM2" != ""
- test "$SHA256SUM1" = "$SHA256SUM2"
- # Also check that a file we expect to be there is there
- systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | tar t | grep etc/os-release
-fi
-
-mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
-mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
-systemd-dissect "$MINIMAL_IMAGE.raw" \
- --json=short \
- --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
- --verity-data="$MINIMAL_IMAGE.fooverity" | \
- grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "$MINIMAL_IMAGE.raw" \
- --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
- --verity-data="$MINIMAL_IMAGE.fooverity" | \
- grep -q -F "MARKER=1"
-systemd-dissect "$MINIMAL_IMAGE.raw" \
- --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
- --verity-data="$MINIMAL_IMAGE.fooverity" | \
- grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
-mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
-mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
-
-mkdir -p "$IMAGE_DIR/mount" "$IMAGE_DIR/mount2"
-systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
-grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
-# Verity volume should be shared (opened only once)
-systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount2"
-verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
-# In theory we should check that count is exactly one. In practice, libdevmapper
-# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
-# (and even mounted and in use), so best-effort is the most we can do for now
-if [[ "$verity_count" -lt 1 ]]; then
- echo "Verity device $MINIMAL_IMAGE.raw not found in /dev/mapper/"
- exit 1
-fi
-systemd-dissect --umount "$IMAGE_DIR/mount"
-systemd-dissect --umount "$IMAGE_DIR/mount2"
-
-systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
-mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
-systemd-run -P \
- -p RootImage="$MINIMAL_IMAGE.raw" \
- -p RootHash="$MINIMAL_IMAGE.foohash" \
- -p RootVerity="$MINIMAL_IMAGE.fooverity" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-# Let's use the long option name just here as a test
-systemd-run -P \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- --property RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- --property RootVerity="$MINIMAL_IMAGE.fooverity" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
-mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
-
-# Derive partition UUIDs from root hash, in UUID syntax
-ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
-VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
-
-systemd-dissect --json=short \
- --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
- "$MINIMAL_IMAGE.gpt" | \
- grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$ARCHITECTURE"'","verity":"signed",'
-systemd-dissect --json=short \
- --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
- "$MINIMAL_IMAGE.gpt" | \
- grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$ARCHITECTURE"'","verity":null,'
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
- systemd-dissect --json=short \
- --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
- "$MINIMAL_IMAGE.gpt" | \
- grep -qE '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$ARCHITECTURE"'","verity":null,'
-fi
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F "MARKER=1"
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
-
-# Test image policies
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt"
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='*'
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='~')
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='-')
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=absent)
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=swap=unprotected+encrypted+verity)
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=unprotected
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent+unprotected
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity=unused+absent)
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity=unused+absent)
-
-# Test RootImagePolicy= unit file setting
-systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='*' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='~' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='-' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='root=absent' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1")
-systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='root=verity' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='root=signed' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p RootImagePolicy='root=encrypted' \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1")
-
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" "$IMAGE_DIR/mount"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
-grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
-systemd-dissect --umount "$IMAGE_DIR/mount"
-
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" --in-memory "$IMAGE_DIR/mount"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
-grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
-systemd-dissect --umount "$IMAGE_DIR/mount"
-
-# add explicit -p MountAPIVFS=yes once to test the parser
-systemd-run -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p MountAPIVFS=yes \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
- -p RootImage="$MINIMAL_IMAGE.raw" \
- -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
- "${BIND_LOG_SOCKETS[@]}" \
- mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootImageOptions="root:ro,noatime root:ro,dev" \
- "${BIND_LOG_SOCKETS[@]}" \
- mount | grep -F "squashfs" | grep -q -F "noatime"
-
-mkdir -p "$IMAGE_DIR/result"
-cat >/run/systemd/system/testservice-50a.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/a"
-BindPaths=$IMAGE_DIR/result:/run/result
-TemporaryFileSystem=/run
-RootImage=$MINIMAL_IMAGE.raw
-RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
-RootImageOptions=nosuid,dev
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-EOF
-systemctl start testservice-50a.service
-grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime"
-grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid"
-
-cat >/run/systemd/system/testservice-50b.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/b"
-BindPaths=$IMAGE_DIR/result:/run/result
-TemporaryFileSystem=/run
-RootImage=$MINIMAL_IMAGE.gpt
-RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
-RootImageOptions=home:ro,dev nosuid,dev,%%foo
-# this is the default, but let's specify once to test the parser
-MountAPIVFS=yes
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-EOF
-systemctl start testservice-50b.service
-grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime"
-
-# Check that specifier escape is applied %%foo → %foo
-busctl get-property org.freedesktop.systemd1 \
- /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
- org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
-
-# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
-systemd-run -P \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
- cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
- cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \
- mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \
- mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
- -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \
- cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
- -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \
- mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
- -p TemporaryFileSystem=/run \
- -p RootImage="$MINIMAL_IMAGE.raw" \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
- -p TemporaryFileSystem=/run \
- -p RootImage="$MINIMAL_IMAGE.raw" \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
- -p TemporaryFileSystem=/run \
- -p RootImage="$MINIMAL_IMAGE.gpt" \
- -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
- -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-cat >/run/systemd/system/testservice-50c.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run
-RootImage=$MINIMAL_IMAGE.raw
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-MountImages=$MINIMAL_IMAGE.gpt:/run/img1:root:noatime:home:relatime
-MountImages=$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid
-ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
-ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
-ExecStart=bash -c "mount >>/run/result/c"
-BindPaths=$IMAGE_DIR/result:/run/result
-Type=oneshot
-EOF
-systemctl start testservice-50c.service
-grep -q -F "MARKER=1" "$IMAGE_DIR/result/c"
-grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime"
-grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid"
-
-# Adding a new mounts at runtime works if the unit is in the active state,
-# so use Type=notify to make sure there's no race condition in the test
-cat >/run/systemd/system/testservice-50d.service <<EOF
-[Service]
-RuntimeMaxSec=300
-Type=notify
-RemainAfterExit=yes
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=sh -c ' \\
- systemd-notify --ready; \\
- while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
- sleep 0.1; \\
- done; \\
- mount; \\
- mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
-'
-EOF
-systemctl start testservice-50d.service
-
-# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
-mkdir -p /tmp/wrong/foo
-mksquashfs /tmp/wrong/foo /tmp/wrong.raw
-systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
-test "$(systemctl show -P SubState testservice-50d.service)" = "running"
-systemctl mount-image --mkdir testservice-50d.service "$MINIMAL_IMAGE.raw" /tmp/img root:nosuid
-# shellcheck disable=SC2016
-timeout 30s bash -xec 'while [[ $(systemctl show -P SubState testservice-50d.service) == running ]]; do sleep .2; done'
-systemctl is-active testservice-50d.service
-
-# ExtensionImages will set up an overlay
-systemd-run -P \
- --property ExtensionImages=/tmp/app0.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
- --property ExtensionImages=/tmp/app0.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
- --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P \
- --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionImages=/tmp/app-nodistro.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionImages=/etc/service-scoped-test.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
-mkdir -p /tmp/symlink-test/
-cp /tmp/app-nodistro.raw /tmp/symlink-test/app-nodistro-v1.raw
-ln -fs /tmp/symlink-test/app-nodistro-v1.raw /tmp/symlink-test/app-nodistro.raw
-systemd-run -P \
- --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-# Symlink check again but for confext
-mkdir -p /etc/symlink-test/
-cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
-ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
-systemd-run -P \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# And again mixing sysext and confext
-systemd-run -P \
- --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-systemd-run -P \
- --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-cat >/run/systemd/system/testservice-50e.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=$MINIMAL_IMAGE.raw
-ExtensionImages=/tmp/app0.raw /tmp/app1.raw:nosuid
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50e.service
-systemctl is-active testservice-50e.service
-
-# Check vpick support in ExtensionImages=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/$VBASE.v"
-mkdir "$VDIR"
-
-ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
-ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
-
-systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
-
-rm -rf "$VDIR"
-
-# ExtensionDirectories will set up an overlay
-mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
-(! systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/nonexistent" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script0.sh)
-(! systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script0.sh)
-systemd-dissect --mount /tmp/app0.raw "$IMAGE_DIR/app0"
-systemd-dissect --mount /tmp/app1.raw "$IMAGE_DIR/app1"
-systemd-dissect --mount /tmp/app-nodistro.raw "$IMAGE_DIR/app-nodistro"
-systemd-dissect --mount /etc/service-scoped-test.raw "$IMAGE_DIR/service-scoped-test"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/app-nodistro" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
- --property ExtensionDirectories="$IMAGE_DIR/service-scoped-test" \
- --property RootImage="$MINIMAL_IMAGE.raw" \
- "${BIND_LOG_SOCKETS[@]}" \
- cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-cat >/run/systemd/system/testservice-50f.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=$MINIMAL_IMAGE.raw
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-ExtensionDirectories=$IMAGE_DIR/app0 $IMAGE_DIR/app1
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50f.service
-systemctl is-active testservice-50f.service
-
-# Check vpick support in ExtensionDirectories=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/$VBASE.v"
-mkdir "$VDIR"
-
-ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
-ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
-
-systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-
-rm -rf "$VDIR"
-
-systemd-dissect --umount "$IMAGE_DIR/app0"
-systemd-dissect --umount "$IMAGE_DIR/app1"
-
-# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
-mkdir -p /var/lib/extensions/
-ln -s /tmp/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
-systemd-sysext merge
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-mkdir -p /etc/extensions/app-nodistro
-systemd-sysext merge
-test ! -e /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-rmdir /etc/extensions/app-nodistro
-
-# Similar, but go via varlink
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-
-# Check that extensions cannot contain os-release
-mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
-touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
-(! systemd-sysext merge)
-test ! -e /usr/lib/systemd/system/some_file
-test ! -e /usr/lib/systemd/system/other_file
-systemd-sysext unmerge
-rm -rf /run/extensions/app-reject
-rm /var/lib/extensions/app-nodistro.raw
-
-# Some super basic test that RootImage= works with .v/ dirs
-VBASE="vtest$RANDOM"
-VDIR="/tmp/$VBASE.v"
-mkdir "$VDIR"
-
-ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw"
-ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw"
-ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw"
-
-systemd-run -P -p RootImage="$VDIR" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
-rmdir "$VDIR"
-
-mkdir -p /run/machines /run/portables /run/extensions
-touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-systemd-dissect --discover --json=short >/tmp/discover.json
-grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
-grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
-grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
-rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
-
-# Wait until the symlinks we want to test are established
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/loop/* symlinks really reference the right device
-test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
-
-if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then
- # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
- # instead. Let's simply skip the test on such old systems.
- test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP"
-fi
-
-# Detach by loopback device
-systemd-dissect --detach "$LOOP"
-
-# Test long reference name.
-# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
-# and --loop-ref accepts upto 63 characters, and udev creates symlink
-# based on the name when it has upto _62_ characters.
-name="$(for _ in {1..62}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
-test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
-
-# Detach by the /dev/disk/by-loop-ref symlink
-systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
-
-name="$(for _ in {1..63}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
-test ! -e "/dev/disk/by-loop-ref/$name"
-
-# Detach by backing inode
-systemd-dissect --detach "$MINIMAL_IMAGE.raw"
-(! systemd-dissect --detach "$MINIMAL_IMAGE.raw")
-
-# check for confext functionality
-mkdir -p /run/confexts/test/etc/extension-release.d
-echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
-cat <<EOF >/run/confexts/test/etc/testscript
-#!/bin/bash
-echo "This should not happen"
-EOF
-chmod +x /run/confexts/test/etc/testscript
-systemd-confext merge
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-(! /etc/testscript)
-systemd-confext status
-systemd-confext unmerge
-rm -rf /run/confexts/
-
-unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw"
-systemd-run --unit=test-root-ephemeral \
- -p RootDirectory=/tmp/img \
- -p RootEphemeral=yes \
- -p Type=exec \
- "${BIND_LOG_SOCKETS[@]}" \
- bash -c "touch /abc && sleep infinity"
-test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
-systemctl stop test-root-ephemeral
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
-test ! -f /tmp/img/abc
-
-systemd-dissect --mtree /tmp/img >/dev/null
-systemd-dissect --list /tmp/img >/dev/null
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-
-echo abc > abc
-systemd-dissect --copy-to /tmp/img abc /abc
-test -f /tmp/img/abc
-
-# Test for dissect tool support with systemd-sysext
-mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
-echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
-mksquashfs testkit/ testkit.raw
-cp testkit.raw /run/extensions/
-unsquashfs -l /run/extensions/testkit.raw
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
-systemd-sysext merge
-systemd-sysext status
-grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
-systemd-sysext unmerge
-rm -rf /run/extensions/ testkit/
-
-# Test for dissect tool support with systemd-confext
-mkdir -p /run/confexts/ testjob/etc/extension-release.d/
-echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
-echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
-echo "MARKER_CONFEXT_123" >testjob/etc/testfile
-mksquashfs testjob/ testjob.raw
-cp testjob.raw /run/confexts/
-unsquashfs -l /run/confexts/testjob.raw
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
-systemd-confext merge
-systemd-confext status
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-systemd-confext unmerge
-rm -rf /run/confexts/ testjob/
-
-systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /run/host/os-release | cmp "$OS_RELEASE"
-
-# Test that systemd-sysext reloads the daemon.
-mkdir -p /var/lib/extensions/
-ln -s /tmp/app-reload.raw /var/lib/extensions/app-reload.raw
-systemd-sysext merge --no-reload
-# the service should not be running
-(! systemctl --quiet is-active foo.service)
-systemd-sysext unmerge --no-reload
-systemd-sysext merge
-# shellcheck disable=SC2016
-timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done'
-systemd-sysext unmerge --no-reload
-# Grep on the Warning to find the warning helper mentioning the daemon reload.
-systemctl status foo.service 2>&1 | grep -q -F "Warning"
-systemd-sysext merge
-systemd-sysext unmerge
-systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
-rm /var/lib/extensions/app-reload.raw
-
-# Sneak in a couple of expected-to-fail invocations to cover
-# https://github.com/systemd/systemd/issues/29610
-(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
-(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \
- [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \
- ! command -v mksquashfs || \
- ! grep -q bpf /sys/kernel/security/lsm ||
- ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep . || \
- systemd-analyze compare-versions "$(uname -r)" lt 6.5 || \
- systemd-analyze compare-versions "$(pkcheck --version | awk '{print $3}')" lt 124; then
- echo "Skipping mountfsd/nsresourced tests"
- exit 0
-fi
-
-at_exit() {
- set +e
-
- umount -R /tmp/unpriv/mount
- rmdir /tmp/unpriv
- rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt
- rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
- rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3
-}
-
-trap at_exit EXIT
-
-systemctl start systemd-mountfsd.socket systemd-nsresourced.socket
-
-openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
- -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
- -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt
-
-systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount
-SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
- systemd-repart -P \
- -s /tmp/unpriv/mount \
- --certificate=/tmp/test-50-unpriv-cert.crt \
- --private-key=/tmp/test-50-unpriv-privkey.key \
- /var/tmp/unpriv.raw
-systemd-dissect --rmdir --umount /tmp/unpriv/mount
-
-systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw
-systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw >/tmp/unpriv.raw.mtree
-
-# Run unpriv, should fail due to lack of privs
-(! runas testuser systemd-dissect /var/tmp/unpriv.raw)
-(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw)
-
-if (SYSTEMD_LOG_TARGET=console varlinkctl call \
- /run/systemd/userdb/io.systemd.NamespaceResource \
- io.systemd.NamespaceResource.AllocateUserRange \
- '{"name":"test-supported","size":65536,"userNamespaceFileDescriptor":0}' 2>&1 || true) |
- grep -q "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported"; then
- echo "User namespace interface not supported, skipping mountfsd/nsresourced tests"
- exit 0
-fi
-
-# Install key in keychain
-cp /tmp/test-50-unpriv-cert.crt /run/verity.d
-
-# Now run unpriv again, should be OK now.
-runas testuser systemd-dissect /var/tmp/unpriv.raw
-runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw >/tmp/unpriv2.raw.mtree
-
-# Check that unpriv and priv run yielded same results
-cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
-
-# Make sure nspawn works unpriv, too (for now do not nest)
-if ! systemd-detect-virt -c; then
- systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out
- echo thisisatest | cmp /tmp/unpriv.out -
-
- # The unpriv user has no rights to lock the image or write to it. Let's
- # turn off both for this test, so that we don't have to copy the image
- # around.
- systemd-run -M testuser@ --user --pipe \
- -p Environment=SYSTEMD_NSPAWN_LOCK=0 \
- -p Delegate=1 \
- -p DelegateSubgroup=supervisor \
- -p Environment=SYSTEMD_LOG_LEVEL=debug \
- --wait -- \
- systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2
- echo thisisatest | cmp /tmp/unpriv.out2 -
-fi
-
-systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3
-echo thisisatest | cmp /tmp/unpriv.out3 -
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-50-DISSECT
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Setup shared stuff & run all subtests
-
-at_exit() {
- set +e
-
- if [[ -z "${IMAGE_DIR:-}" ]]; then
- return
- fi
-
- while read -r dir; do
- if mountpoint -q "$dir"; then
- umount -Rv "$dir"
- fi
- done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
-
- rm -rf "$IMAGE_DIR"
-}
-
-trap at_exit EXIT
-
-: "Setup base images"
-
-export SYSTEMD_LOG_LEVEL=debug
-export ARCHITECTURE
-export IMAGE_DIR
-export MACHINE
-export MINIMAL_IMAGE
-export MINIMAL_IMAGE_ROOTHASH
-export OPENSSL_CONFIG
-export OS_RELEASE
-export ROOT_GUID
-export SIGNATURE_GUID
-export VERITY_GUID
-
-machine="$(uname -m)"
-if [[ "$machine" == "x86_64" ]]; then
- ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
- VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
- SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176
- ARCHITECTURE="x86-64"
-elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then
- ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a
- VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
- SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676
- ARCHITECTURE="x86"
-elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then
- ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae
- VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820
- SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3
- ARCHITECTURE="arm64"
-elif [[ "$machine" == "arm" ]]; then
- ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3
- VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6
- SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037
- ARCHITECTURE="arm"
-elif [[ "$machine" == "ia64" ]]; then
- ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97
- VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571
- SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a
- ARCHITECTURE="ia64"
-elif [[ "$machine" == "loongarch64" ]]; then
- ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6
- VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535
- SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
- ARCHITECTURE="loongarch64"
-elif [[ "$machine" == "s390x" ]]; then
- ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306
- VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b
- SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9
- ARCHITECTURE="s390x"
-elif [[ "$machine" == "ppc64le" ]]; then
- ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599
- VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a
- SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
- ARCHITECTURE="ppc64-le"
-elif [[ "$machine" == "riscv64" ]]; then
- ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224
- VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d
- SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a
- ARCHITECTURE="riscv64"
-elif [[ "$machine" == "riscv32" ]]; then
- ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1
- VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de
- SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448
- ARCHITECTURE="riscv32"
-else
- echo "Unexpected uname -m: $machine in testsuite-50.sh, please fix me"
- exit 1
-fi
-
-udevadm control --log-level=debug
-
-IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)"
-cp -v /usr/share/minimal* "$IMAGE_DIR/"
-MINIMAL_IMAGE="$IMAGE_DIR/minimal_0"
-MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")"
-
-install_extension_images
-
-OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
-
-if systemctl --version | grep -q -- +OPENSSL ; then
- # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is
- # missing, fail early with a proper error message.
- if ! command -v openssl &>/dev/null; then
- echo "openssl binary is missing" >/failed
- exit 1
- fi
-
- OPENSSL_CONFIG="$(mktemp)"
- # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
- cat >"${OPENSSL_CONFIG:?}" <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = DE
-ST = Test State
-L = Test Locality
-O = Org Name
-OU = Org Unit Name
-CN = Common Name
-emailAddress = test@email.com
-EOF
-fi
-
-# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
-#
-# du rounds up to block size, which is more helpful for partitioning
-root_size="$(du --apparent-size -k "$MINIMAL_IMAGE.raw" | cut -f1)"
-verity_size="$(du --apparent-size -k "$MINIMAL_IMAGE.verity" | cut -f1)"
-signature_size=4
-# 4MB seems to be the minimum size blkid will accept, below that probing fails
-dd if=/dev/zero of="$MINIMAL_IMAGE.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
-# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
-# so do some basic rounding up if the minimal image is more than 1 MB
-if [[ "$root_size" -ge 1024 ]]; then
- root_size="$((root_size/1024 + 1))MiB"
-else
- root_size="${root_size}KiB"
-fi
-verity_size="$((verity_size * 2))KiB"
-signature_size="$((signature_size * 2))KiB"
-
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
- # Create key pair
- openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 \
- -keyout "$MINIMAL_IMAGE.key" -out "$MINIMAL_IMAGE.crt" -days 365 -nodes
- # Sign Verity root hash with it
- openssl smime -sign -nocerts -noattr -binary \
- -in "$MINIMAL_IMAGE.roothash" \
- -inkey "$MINIMAL_IMAGE.key" \
- -signer "$MINIMAL_IMAGE.crt" \
- -outform der \
- -out "$MINIMAL_IMAGE.roothash.p7s"
- # Generate signature partition JSON data
- echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig"
- # Pad it
- truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig"
- # Register certificate in the (userspace) verity key ring
- mkdir -p /run/verity.d
- ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt
-fi
-
-# Construct a UUID from hash
-# input: 11111111222233334444555566667777
-# output: 11111111-2222-3333-4444-555566667777
-uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt"
-uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
- echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append
-fi
-sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition"
-sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition"
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
- sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition"
-fi
-loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")"
-partitions=(
- "${loop:?}p1"
- "${loop:?}p2"
-)
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
- partitions+=("${loop:?}p3")
-fi
-# The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
-# Let's not expect the devices to be initialized.
-udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
-udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1"
-udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2"
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
- udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3"
-fi
-losetup -d "$loop"
-
-: "Run subtests"
-
-run_subtests
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
-FSTYPE=$(stat --file-system --format "%T" /usr)
-
-shopt -s nullglob
-
-# shellcheck disable=SC2317
-at_exit() {
- set +ex
-
- local target
-
- # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise
- # things get very confusing once we start unmounting things, due to changing file offsets.
- # shellcheck disable=SC2002
- cat /proc/mounts | while read -r _ target _ _ _ _; do
- if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then
- umount -Rv "$target"
- fi
- done
-
- rm -rf "${FAKE_ROOTS_DIR}"
-}
-
-trap at_exit EXIT
-
-# Clears the trap command - it needs to be invoked for every test-case subshell
-# so prepending commands with prepend_trap inside the subshell won't preserve
-# the trap commands from outer shell.
-init_trap() {
- trap - EXIT
-}
-
-prepend_trap() {
- set +x
-
- local command=${1}; shift
- local previous_commands
-
- previous_commands=$(trap -p EXIT)
- if [[ -z $previous_commands ]]; then
- previous_commands=':'
- else
- previous_commands=${previous_commands#'trap -- '}
- previous_commands=${previous_commands%' EXIT'}
- previous_commands=$(xargs <<<"$previous_commands")
- fi
-
- # shellcheck disable=SC2064 # We use double quotes on purpose here.
- trap "${command}; ${previous_commands}" EXIT
-
- set -x
-}
-
-prepare_root() {
- local root=${1:-}
- local hierarchy=${2:?}
- local dir
-
- if [[ -n $root ]] && [[ -d $root ]]; then
- echo >&2 "Directory $root already exists, possible copy-paste error?"
- exit 1
- fi
-
- local -a leftovers=( "$root/var/lib/extensions/"* "$root/var/lib/extensions.mutable/"* )
- if [[ ${#leftovers[@]} -gt 0 ]]; then
- echo >&2 "Leftovers remained, make sure to clean them up in the test case: ${leftovers[*]}"
- exit 1
- fi
-
- for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
- mkdir -p "$root$dir"
- done
-
- if [[ -e $root/usr/lib/os-release ]]; then
- mv "$root/usr/lib/os-release" "$root/usr/lib/os-release.orig"
- fi
-
- {
- echo "ID=testtest"
- echo "VERSION=1.2.3"
- } >"$root/usr/lib/os-release"
-
- prepend_trap "cleanup_os_release ${root@Q}"
-}
-
-cleanup_os_release() {
- # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
- local root=${1:-}
-
- # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
- rm -f "$root/usr/lib/os-release"
- # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
- if [[ -e $root/usr/lib/os-release.orig ]]; then
- # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
- mv "$root/usr/lib/os-release.orig" "$root/usr/lib/os-release"
- fi
-}
-
-prepare_extension_image() {
- local root=${1:-}
- local hierarchy=${2:?}
- local ext_dir ext_release name
-
- name="test-extension"
- ext_dir="$root/var/lib/extensions/$name"
- ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
- mkdir -p "${ext_release%/*}"
- echo "ID=_any" >"$ext_release"
- mkdir -p "$ext_dir/$hierarchy"
- touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
-
- prepend_trap "rm -rf ${ext_dir@Q}"
-}
-
-prepare_extension_mutable_dir() {
- local dir=${1:?}
-
- mkdir -p "$dir"
- touch "$dir/preexisting-file-in-extensions-mutable"
- prepend_trap "rm -rf ${dir@Q}"
-}
-
-make_read_only() {
- local root=${1:-}
- local hierarchy=${2:?}
-
- mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
- prepend_trap "umount ${root@Q}${hierarchy@Q}"
-}
-
-prepare_hierarchy() {
- local root=${1:-}
- local hierarchy=${2:?}
- local file
-
- file="$root$hierarchy/preexisting-file-in-hierarchy"
- touch "$file"
- prepend_trap "rm -f ${file@Q}"
-}
-
-prepare_read_only_hierarchy() {
- local root=${1:-}
- local hierarchy=${2:?}
-
- prepare_hierarchy "$root" "$hierarchy"
- make_read_only "$root" "$hierarchy"
-}
-
-move_existing_hierarchy_aside() {
- local root=${1:-}
- local hierarchy=${2:?}
-
- if [[ -z $root ]] && [[ $hierarchy = /usr ]]; then
- echo >&2 "Hell no, not moving /usr aside"
- exit 1
- fi
-
- local path=$root$hierarchy
-
- if [[ -e $path ]]; then
- mv "$path" "$path.orig"
- prepend_trap "mv ${path@Q}.orig ${path@Q}"
- fi
-}
-
-# Extra arguments:
-# -e: check for a preexisting file in extension
-# -h: check for a preexisting file in hierarchy
-# -u: check for a preexisting file in upperdir
-extension_verify() {
- local root=${1:-}
- local hierarchy=${2:?}
- local message=${3:?}
- shift 3
- # Map each option to a pre-defined file name
- local -A option_files_map=(
- [e]="preexisting-file-in-extension-image"
- [h]="preexisting-file-in-hierarchy"
- [u]="preexisting-file-in-extensions-mutable"
- )
- local -A args=(
- [e]=0
- [h]=0
- [u]=0
- )
- local file full_path opt option
-
- while getopts "ehu" opt; do
- case "$opt" in
- e|h|u)
- args["$opt"]=1
- ;;
- *)
- echo >&2 "Unxexpected option: $opt"
- exit 1
- esac
- done
-
- for option in "${!option_files_map[@]}"; do
- file=${option_files_map["$option"]}
- full_path="$root$hierarchy/$file"
-
- if [[ ${args["$option"]} -ne 0 ]]; then
- if [[ ! -f $full_path ]]; then
- ls -la "$root$hierarchy"
- echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
- exit 1
- fi
- else
- if [[ -f $full_path ]]; then
- ls -la "$root$hierarchy"
- echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
- exit 1
- fi
- fi
- done
-}
-
-extension_verify_after_merge() (
- set +x
-
- local root=${1:-}
- local hierarchy=${2:?}
- shift 2
-
- extension_verify "$root" "$hierarchy" "after merge" "$@"
-)
-
-extension_verify_after_unmerge() (
- set +x
-
- local root=${1:-}
- local hierarchy=${2:?}
- shift 2
-
- extension_verify "$root" "$hierarchy" "after unmerge" "$@"
-)
-
-run_systemd_sysext() {
- local root=${1:-}
- shift
-
- local -a sysext_args
- sysext_args=()
-
- if [[ -n $root ]]; then
- sysext_args+=( "--root=$root" )
- fi
- sysext_args+=( "$@" )
- systemd-sysext "${sysext_args[@]}"
-}
-
-# General systemd-sysext tests
-
-run_sysext_tests() {
- # The roots_dir variable may be empty - in such case all the tests will run
- # on /, otherwise they will run on $roots_dir/<SEPARATE_DIR_FOR_TEST>.
- local roots_dir=${1}; shift
-
- # Each test runs in a subshell, so we can use traps for cleanups without
- # clobbering toplevel traps, and we can do skips by invoking "exit 0".
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-read-only-hierarchy"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-)
-
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-mutable-hierarchy"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_hierarchy "$fake_root" "$hierarchy"
-touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
-)
-
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-missing-hierarchy"}
-hierarchy=/opt
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-rmdir "$fake_root/$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-)
-
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-empty-hierarchy"}
-hierarchy=/opt
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-make_read_only "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy-disabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-be-read-only")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-missing-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-rmdir "$fake_root/$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-empty-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-make_read_only "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/mutable-symlink-with-read-only-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root/upperdir"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/mutable-self-upper"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
-fake_root=${roots_dir:+"$roots_dir/failure-self-upper-ro"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-
-(! run_systemd_sysext "$fake_root" --mutable=auto merge)
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/read-only-mutable-dangling-symlink"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-ln -sfTr "/should/not/exist/" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/disabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=no merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/imported"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=import merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, mutability enabled, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/enabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-test ! -d "$extension_data_dir"
-
-run_systemd_sysext "$fake_root" --mutable=yes merge
-# systemd-sysext with --mutable=yes creates extensions.mutable directory for
-# the hierarchy, so delete it after the test
-prepend_trap "rm -rf ${extension_data_dir@Q}"
-# systemd-sysext with --mutable=yes creates extensions.mutable directory also
-# for the /usr hierarchy, because the image needs to have
-# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
-# /usr hierarchy to also become mutable
-prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
-test -d "$extension_data_dir"
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, auto-mutability, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-explicit"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, mutability enabled through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/enabled-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-test ! -d "$extension_data_dir"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" merge
-# systemd-sysext with --mutable=yes creates extensions.mutable directory for
-# the hierarchy, so delete it after the test
-prepend_trap "rm -rf ${extension_data_dir@Q}"
-# systemd-sysext with --mutable=yes creates extensions.mutable directory also
-# for the /usr hierarchy, because the image needs to have
-# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
-# /usr hierarchy to also become mutable
-prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
-test -d "$extension_data_dir"
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, auto-mutability enabled through env var, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/read-only-auto-env-var"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" --mutable=auto merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/auto-mutable-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/env-var-disabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-be-read-only")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/imported-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/env-var-overridden"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" --mutable=no merge
-(! touch "$fake_root$hierarchy/should-be-read-only")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=ephemeral merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test ! -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test ! -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-import"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test ! -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-import-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test ! -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-import-self"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_hierarchy "$fake_root" "$hierarchy"
-touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
-
-(! run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge)
-)
-
-
-( init_trap
-: "Extension data pointing to mutable hierarchy, import mutability, expected fail"
-fake_root=${roots_dir:+"$roots_dir/import-self"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_hierarchy "$fake_root" "$hierarchy"
-touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
-
-(! run_systemd_sysext "$fake_root" --mutable=import merge)
-)
-
-
-for mutable_mode in no yes ephemeral; do
- ( init_trap
- : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}"
-
- fake_root=${roots_dir:+"$roots_dir/perm-checks-mutable-$mutable_mode"}
- hierarchy=/opt
- extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
- [[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
- prepare_root "$fake_root" "$hierarchy"
- prepare_extension_image "$fake_root" "$hierarchy"
- prepare_extension_mutable_dir "$extension_data_dir"
- prepare_read_only_hierarchy "${fake_root}" "${hierarchy}"
-
- full_path="$fake_root$hierarchy"
- permissions_before_merge=$(stat --format=%A "$full_path")
-
- run_systemd_sysext "$fake_root" "--mutable=$mutable_mode" merge
- if [[ $mutable_mode = yes ]]; then
- # systemd-sysext with --mutable=yes creates extensions.mutable
- # directory also for the /usr hierarchy, because the image needs to
- # have /usr/lib/extension-release.d/extension-release.<NAME> file -
- # this causes the /usr hierarchy to also become mutable
- extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
- prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
- fi
-
- permissions_after_merge=$(stat --format=%A "$full_path")
-
- run_systemd_sysext "$fake_root" unmerge
-
- permissions_after_unmerge=$(stat --format=%A "$full_path")
-
- if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then
- echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}"
- exit 1
- fi
-
- if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then
- echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}"
- exit 1
- fi
- )
-done
-
-
-( init_trap
-: "Check if merging fails in case of invalid mutable directory permissions"
-
-fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_hierarchy "$fake_root" "$hierarchy"
-
-old_mode=$(stat --format '%#a' "$fake_root$hierarchy")
-chmod 0755 "$fake_root$hierarchy"
-prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}"
-chmod 0700 "$extension_data_dir"
-
-(! run_systemd_sysext "$fake_root" --mutable=yes merge)
-)
-
-} # End of run_sysext_tests
-
-
-# For preparing /, we need mutable /usr/. If it is read only, skip running the
-# sysext tests on /.
-if [[ -w /usr ]]; then
- run_sysext_tests ''
-fi
-run_sysext_tests "$FAKE_ROOTS_DIR"
-
-
-exit 0
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-
-systemctl enable test-honor-first-shutdown.service
-systemctl start test-honor-first-shutdown.service
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-53-ISSUE-16347
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-: >/failed
-
-# Reset host date to current time, 3 days in the past.
-date -s "-3 days"
-
-# Run a timer for every 15 minutes.
-systemd-run --unit test-timer --on-calendar "*:0/15:0" true
-
-next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
-next_elapsed=$(date -d "${next_elapsed}" +%s)
-now=$(date +%s)
-time_delta=$((next_elapsed - now))
-
-# Check that the timer will elapse in less than 20 minutes.
-((0 < time_delta && time_delta < 1200)) || {
- echo 'Timer elapse outside of the expected 20 minute window.'
- echo " next_elapsed=${next_elapsed}"
- echo " now=${now}"
- echo " time_delta=${time_delta}"
- echo ''
-} >>/failed
-
-if test ! -s /failed ; then
- rm -f /failed
- touch /testok
-fi
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-54-CREDS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-analyze log-level debug
-
-run_with_cred_compare() (
- local cred="${1:?}"
- local exp="${2?}"
- local log_file
- shift 2
-
- log_file="$(mktemp)"
- # shellcheck disable=SC2064
- trap "rm -f '$log_file'" RETURN
-
- set -o pipefail
- systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
- diff "$log_file" <(echo -ne "$exp")
-)
-
-# Sanity checks
-#
-# Create a dummy "full" disk (similar to /dev/full) to check out-of-space
-# scenarios
-mkdir /tmp/full
-mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full
-
-# verb: setup
-# Run this first, otherwise any encrypted credentials wouldn't be decryptable
-# as we regenerate the host key
-rm -fv /var/lib/systemd/credential.secret
-systemd-creds setup
-test -e /var/lib/systemd/credential.secret
-rm -fv /var/lib/systemd/credential.secret
-
-# Prepare a couple of dummy credentials for the cat/list verbs
-CRED_DIR="$(mktemp -d)"
-ENC_CRED_DIR="$(mktemp -d)"
-echo foo >"$CRED_DIR/secure-or-weak"
-echo foo >"$CRED_DIR/insecure"
-echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted"
-echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed"
-chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR"
-chmod -R 0444 "$CRED_DIR/insecure"
-mkdir /tmp/empty/
-
-systemd-creds --system
-systemd-creds --no-pager --help
-systemd-creds --version
-systemd-creds has-tpm2 || :
-systemd-creds has-tpm2 -q || :
-
-# verb: list
-systemd-creds list --system
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off
-ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list
-
-# verb: cat
-for cred in secure-or-weak insecure encrypted encrypted-unnamed; do
- ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred"
-done
-run_with_cred_compare "mycred:" "" cat mycred
-run_with_cred_compare "mycred:\n" "\n" cat mycred
-run_with_cred_compare "mycred:foo" "foo" cat mycred
-run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred
-# Note: --newline= does nothing when stdout is not a tty, which is the case here
-run_with_cred_compare "mycred:foo" "foo" --newline=yes cat mycred
-run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred
-run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred
-run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred
-run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred
-run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred
-run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred
-run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred
-run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred
-run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred
-run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred
-run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred
-run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred
-run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred
-run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred
-systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq
-
-# verb: encrypt/decrypt
-echo "According to all known laws of aviation..." >/tmp/cred.orig
-systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc
-systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.{enc,dec}
-# --pretty
-cred_name="fo'''o''bar"
-cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)"
-mkdir -p /run/systemd/system
-cat >/run/systemd/system/test-54-pretty-cred.service <<EOF
-[Service]
-Type=oneshot
-${cred_option:?}
-ExecStart=bash -c "diff <(systemd-creds cat \"$cred_name\") /tmp/cred.orig"
-EOF
-systemctl daemon-reload
-systemctl start test-54-pretty-cred
-rm /run/systemd/system/test-54-pretty-cred.service
-# Credential validation: name
-systemd-creds --name="foo" -H encrypt /tmp/cred.orig /tmp/cred.enc
-(! systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec)
-(! systemd-creds --name="bar" decrypt /tmp/cred.enc /tmp/cred.dec)
-systemd-creds --name="" decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.dec
-systemd-creds --name="foo" decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.{enc,dec}
-# Credential validation: time
-systemd-creds --not-after="+1d" encrypt /tmp/cred.orig /tmp/cred.enc
-(! systemd-creds --timestamp="+2d" decrypt /tmp/cred.enc /tmp/cred.dec)
-systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.{enc,dec}
-
-(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds list")
-(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds --system list")
-(! CREDENTIALS_DIRECTORY="" systemd-creds list)
-(! systemd-creds --system --foo)
-(! systemd-creds --system -@)
-(! systemd-creds --system --json=)
-(! systemd-creds --system --json="")
-(! systemd-creds --system --json=foo)
-(! systemd-creds --system cat)
-(! systemd-creds --system cat "")
-(! systemd-creds --system cat this-should-not-exist)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode= cat mycred)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode="" cat mycred)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode=foo cat mycred)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --newline=foo cat mycred)
-(! systemd-run -p SetCredential=mycred:notbase64 --wait --pipe -- systemd-creds --transcode=unbase64 cat mycred)
-(! systemd-run -p SetCredential=mycred:nothex --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
-(! systemd-run -p SetCredential=mycred:a --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
-(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=short cat mycred)
-(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=pretty cat mycred)
-(! systemd-creds encrypt /foo/bar/baz -)
-(! systemd-creds decrypt /foo/bar/baz -)
-(! systemd-creds decrypt / -)
-(! systemd-creds encrypt / -)
-(! echo foo | systemd-creds --with-key=foo encrypt - -)
-(! echo {0..20} | systemd-creds decrypt - -)
-(! systemd-creds --not-after= encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --not-after="" encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --not-after="-1d" encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --timestamp= encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --timestamp="" encrypt /tmp/cred.orig /tmp/cred.enc)
-(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent encrypt - /dev/null)
-(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent decrypt - /dev/null)
-(! echo foo | systemd-creds encrypt - /tmp/full/foo)
-(! echo foo | systemd-creds encrypt - - | systemd-creds decrypt - /tmp/full/foo)
-
-# Verify that the creds are properly loaded and we can read them from the service's unpriv user
-systemd-run -p LoadCredential=passwd:/etc/passwd \
- -p LoadCredential=shadow:/etc/shadow \
- -p SetCredential=dog:wuff \
- -p DynamicUser=1 \
- --unit=test-54-unpriv.service \
- --wait \
- --pipe \
- cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' \
- >/tmp/ts54-concat
-(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat
-rm /tmp/ts54-concat
-
-# Test that SetCredential= acts as fallback for LoadCredential=
-echo piff >/tmp/ts54-fallback
-[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ]
-rm /tmp/ts54-fallback
-[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ]
-
-if systemd-detect-virt -q -c ; then
- expected_credential=mynspawncredential
- expected_value=strangevalue
-elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
- # Verify that passing creds through kernel cmdline works
- [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
- [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
-
- # And that it also works via SMBIOS
- [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
- [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
-
- # If we aren't run in nspawn, we are run in qemu
- systemd-detect-virt -q -v
- expected_credential=myqemucredential
- expected_value=othervalue
-
- # Verify that writing a sysctl via the kernel cmdline worked
- [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
-
- # Verify that creating a user via sysusers via the kernel cmdline worked
- grep -q ^credtestuser: /etc/passwd
-
- # Verify that writing a file via tmpfiles worked
- [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
- [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
- [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
-
- # Verify that adding a unit and drop-in via credentials worked
- systemctl start my-service
- test -f /tmp/unit-cred
- test -f /tmp/unit-dropin
- test -f /tmp/unit-named-dropin
-else
- echo "qemu_fw_cfg support missing in kernel. Sniff!"
- expected_credential=""
- expected_value=""
-fi
-
-if [ "$expected_credential" != "" ] ; then
- # If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh
- [ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ]
-
- # Test that propagation from system credential to service credential works
- [ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
-
- # Check it also works, if we rename it while propagating it
- [ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ]
-
- # Combine it with a fallback (which should have no effect, given the cred should be passed down)
- [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
-
- # This should succeed
- systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
-
- # And this should fail
- (! systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true)
-fi
-
-# Verify that the creds are immutable
-(! systemd-run -p LoadCredential=passwd:/etc/passwd \
- -p DynamicUser=1 \
- --unit=test-54-immutable-touch.service \
- --wait \
- touch '${CREDENTIALS_DIRECTORY}/passwd')
-(! systemd-run -p LoadCredential=passwd:/etc/passwd \
- -p DynamicUser=1 \
- --unit=test-54-immutable-rm.service \
- --wait \
- rm '${CREDENTIALS_DIRECTORY}/passwd')
-
-# Check directory-based loading
-mkdir -p /tmp/ts54-creds/sub
-echo -n a >/tmp/ts54-creds/foo
-echo -n b >/tmp/ts54-creds/bar
-echo -n c >/tmp/ts54-creds/baz
-echo -n d >/tmp/ts54-creds/sub/qux
-systemd-run -p LoadCredential=cred:/tmp/ts54-creds \
- -p DynamicUser=1 \
- --unit=test-54-dir.service \
- --wait \
- --pipe \
- cat '${CREDENTIALS_DIRECTORY}/cred_foo' \
- '${CREDENTIALS_DIRECTORY}/cred_bar' \
- '${CREDENTIALS_DIRECTORY}/cred_baz' \
- '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat
-cmp /tmp/ts54-concat <(echo -n abcd)
-rm /tmp/ts54-concat
-rm -rf /tmp/ts54-creds
-
-# Check that globs work as expected
-mkdir -p /run/credstore
-echo -n a >/run/credstore/test.creds.first
-echo -n b >/run/credstore/test.creds.second
-mkdir -p /etc/credstore
-echo -n c >/etc/credstore/test.creds.third
-systemd-run -p "ImportCredential=test.creds.*" \
- --unit=test-54-ImportCredential.service \
- -p DynamicUser=1 \
- --wait \
- --pipe \
- cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
- '${CREDENTIALS_DIRECTORY}/test.creds.second' \
- '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
-cmp /tmp/ts54-concat <(echo -n abc)
-
-# Now test encrypted credentials (only supported when built with OpenSSL though)
-if systemctl --version | grep -q -- +OPENSSL ; then
- echo -n $RANDOM >/tmp/test-54-plaintext
- systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
- systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
-
- systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \
- --wait \
- --pipe \
- cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
-
- echo -n $RANDOM >/tmp/test-54-plaintext
- systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
- systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
-
- systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \
- --wait \
- --pipe \
- cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
-
- rm /tmp/test-54-plaintext /tmp/test-54-ciphertext
-fi
-
-# https://github.com/systemd/systemd/issues/27275
-systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
- -p 'ExecStartPre=true' \
- -p 'ExecStartPre=systemd-creds cat os' \
- --unit=test-54-exec-start-pre.service \
- --wait \
- --pipe \
- true | cmp /etc/os-release
-
-# https://github.com/systemd/systemd/issues/31194
-systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
- -p 'ExecStartPost=systemd-creds cat os' \
- --unit=test-54-exec-start-post.service \
- --service-type=oneshot --wait --pipe \
- true | cmp /etc/os-release
-
-# https://github.com/systemd/systemd/pull/24734#issuecomment-1925440546
-# Also ExecStartPre= should be able to update creds
-dd if=/dev/urandom of=/tmp/cred-huge bs=600K count=1
-chmod 777 /tmp/cred-huge
-systemd-run -p ProtectSystem=full \
- -p 'LoadCredential=huge:/tmp/cred-huge' \
- -p 'ExecStartPre=true' \
- -p 'ExecStartPre=bash -c "echo fresh >/tmp/cred-huge"' \
- --unit=test-54-huge-cred.service \
- --wait --pipe \
- systemd-creds cat huge | cmp - <(echo "fresh")
-rm /tmp/cred-huge
-
-echo stable >/tmp/cred-stable
-systemd-run -p 'LoadCredential=stable:/tmp/cred-stable' \
- -p 'ExecStartPost=systemd-creds cat stable' \
- --unit=test-54-stable.service \
- --service-type=oneshot --wait --pipe \
- bash -c "echo bogus >/tmp/cred-stable" | cmp - <(echo "stable")
-assert_eq "$(cat /tmp/cred-stable)" "bogus"
-rm /tmp/cred-stable
-
-if ! systemd-detect-virt -q -c ; then
- # Validate that the credential we inserted via the initrd logic arrived
- test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
-
- # Check that the fstab credential logic worked
- test -d /injected
- grep -q /injected /proc/self/mountinfo
-
- # Make sure the getty generator processed the credentials properly
- systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
-fi
-
-# Decrypt/encrypt via varlink
-
-echo '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
-
-varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
- varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
-
-cmp /tmp/vlcredsdata /tmp/vlcredsdata2
-rm /tmp/vlcredsdata /tmp/vlcredsdata2
-
-clean_usertest() {
- rm -f /tmp/usertest.data /tmp/usertest.data
-}
-
-trap clean_usertest EXIT
-dd if=/dev/urandom of=/tmp/usertest.data bs=4096 count=1
-
-systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.cred
-
-systemd-creds decrypt --user /tmp/usertest.cred - | cmp /tmp/usertest.data
-
-# Decryption must fail if it's not done in user context
-(! systemd-creds decrypt /tmp/usertest.cred - )
-
-# Decryption must also fail if a different user is used
-(! systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - )
-
-# Try the reverse
-systemd-creds encrypt --user --uid=65534 /tmp/usertest.data /tmp/usertest.cred
-(! systemd-creds decrypt --user /tmp/usertest.cred - )
-systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - | cmp /tmp/usertest.data
-
-systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytest
-
-# Make sure we actually can decode this in user context
-systemctl start user@0.service
-XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Create a lot of memory pressure
-
-[Service]
-# A VERY small memory.high will cause the 'stress' (trying to use a lot of memory)
-# to throttle and be put under heavy pressure.
-MemoryHigh=3M
-Slice=testsuite-55-workload.slice
-ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=No memory pressure
-
-[Service]
-MemoryHigh=3M
-Slice=testsuite-55-workload.slice
-ExecStart=sleep infinity
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Create some memory pressure
-
-[Service]
-MemoryHigh=12M
-Slice=testsuite-55-workload.slice
-ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Test slice for memory pressure kills
-
-[Slice]
-CPUAccounting=true
-MemoryAccounting=true
-IOAccounting=true
-TasksAccounting=true
-ManagedOOMMemoryPressure=kill
-ManagedOOMMemoryPressureLimit=20%
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-55-OOMD
-After=user@4711.service
-Wants=user@4711.service
-
-[Service]
-ExecStartPre=rm -f /failed /skipped /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
- . "$(dirname "$0")"/util.sh
-
-. /etc/os-release
-# OpenSUSE does not have the stress tool packaged. It does have stress-ng but the stress-ng does not support
-# --vm-stride which this test uses.
-if [[ "$ID" =~ "opensuse" ]]; then
- echo "Skipping due to missing stress package in OpenSUSE" >>/skipped
- exit 77
-fi
-
-systemd-analyze log-level debug
-
-# Ensure that the init.scope.d drop-in is applied on boot
-test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max"
-
-# Loose checks to ensure the environment has the necessary features for systemd-oomd
-[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped
-[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
-[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
-if [[ -s /skipped ]]; then
- exit 77
-fi
-
-rm -rf /run/systemd/system/testsuite-55-testbloat.service.d
-
-# Activate swap file if we are in a VM
-if systemd-detect-virt --vm --quiet; then
- swapoff --all
- if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then
- btrfs filesystem mkswapfile -s 64M /swapfile
- else
- dd if=/dev/zero of=/swapfile bs=1M count=64
- chmod 0600 /swapfile
- mkswap /swapfile
- fi
-
- swapon /swapfile
- swapon --show
-fi
-
-# Configure oomd explicitly to avoid conflicts with distro dropins
-mkdir -p /run/systemd/oomd.conf.d/
-cat >/run/systemd/oomd.conf.d/99-oomd-test.conf <<EOF
-[OOM]
-DefaultMemoryPressureDurationSec=2s
-EOF
-
-mkdir -p /run/systemd/system/-.slice.d/
-cat >/run/systemd/system/-.slice.d/99-oomd-test.conf <<EOF
-[Slice]
-ManagedOOMSwap=auto
-EOF
-
-mkdir -p /run/systemd/system/user@.service.d/
-cat >/run/systemd/system/user@.service.d/99-oomd-test.conf <<EOF
-[Service]
-ManagedOOMMemoryPressure=auto
-ManagedOOMMemoryPressureLimit=0%
-EOF
-
-mkdir -p /run/systemd/system/systemd-oomd.service.d/
-cat >/run/systemd/system/systemd-oomd.service.d/debug.conf <<EOF
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-systemctl daemon-reload
-
-# enable the service to ensure dbus-org.freedesktop.oom1.service exists
-# and D-Bus activation works
-systemctl enable systemd-oomd.service
-
-# if oomd is already running for some reasons, then restart it to make sure the above settings to be applied
-if systemctl is-active systemd-oomd.service; then
- systemctl restart systemd-oomd.service
-fi
-
-if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
- # If we're running with sanitizers, sd-executor might pull in quite a significant chunk of shared
- # libraries, which in turn causes a lot of pressure that can put us in the front when sd-oomd decides to
- # go on a killing spree. This fact is exacerbated further on Arch Linux which ships unstripped gcc-libs,
- # so sd-executor pulls in over 30M of libs on startup. Let's make the MemoryHigh= limit a bit more
- # generous when running with sanitizers to make the test happy.
- systemctl edit --runtime --stdin --drop-in=99-MemoryHigh.conf testsuite-55-testchill.service <<EOF
-[Service]
-MemoryHigh=60M
-EOF
- # Do the same for the user instance as well
- mkdir -p /run/systemd/user/
- cp -rfv /run/systemd/system/testsuite-55-testchill.service.d/ /run/systemd/user/
-else
- # Ensure that we can start services even with a very low hard memory cap without oom-kills, but skip
- # under sanitizers as they balloon memory usage.
- systemd-run -t -p MemoryMax=10M -p MemorySwapMax=0 -p MemoryZSwapMax=0 /bin/true
-fi
-
-systemctl start testsuite-55-testchill.service
-systemctl start testsuite-55-testbloat.service
-
-# Verify systemd-oomd is monitoring the expected units
-timeout 1m bash -xec 'until oomctl | grep "/testsuite-55-workload.slice"; do sleep 1; done'
-oomctl | grep "/testsuite-55-workload.slice"
-oomctl | grep "20.00%"
-oomctl | grep "Default Memory Pressure Duration: 2s"
-
-systemctl status testsuite-55-testchill.service
-
-# systemd-oomd watches for elevated pressure for 2 seconds before acting.
-# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-for _ in {0..59}; do
- if ! systemctl status testsuite-55-testbloat.service; then
- break
- fi
- oomctl
- sleep 2
-done
-
-# testbloat should be killed and testchill should be fine
-if systemctl status testsuite-55-testbloat.service; then exit 42; fi
-if ! systemctl status testsuite-55-testchill.service; then exit 24; fi
-
-# Make sure we also work correctly on user units.
-loginctl enable-linger testuser
-
-systemctl start --machine "testuser@.host" --user testsuite-55-testchill.service
-systemctl start --machine "testuser@.host" --user testsuite-55-testbloat.service
-
-# Verify systemd-oomd is monitoring the expected units
-# Try to avoid racing the oomctl output check by checking in a loop with a timeout
-timeout 1m bash -xec 'until oomctl | grep "/testsuite-55-workload.slice"; do sleep 1; done'
-oomctl | grep -E "/user.slice.*/testsuite-55-workload.slice"
-oomctl | grep "20.00%"
-oomctl | grep "Default Memory Pressure Duration: 2s"
-
-systemctl --machine "testuser@.host" --user status testsuite-55-testchill.service
-
-# systemd-oomd watches for elevated pressure for 2 seconds before acting.
-# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-for _ in {0..59}; do
- if ! systemctl --machine "testuser@.host" --user status testsuite-55-testbloat.service; then
- break
- fi
- oomctl
- sleep 2
-done
-
-# testbloat should be killed and testchill should be fine
-if systemctl --machine "testuser@.host" --user status testsuite-55-testbloat.service; then exit 42; fi
-if ! systemctl --machine "testuser@.host" --user status testsuite-55-testchill.service; then exit 24; fi
-
-loginctl disable-linger testuser
-
-# only run this portion of the test if we can set xattrs
-if cgroupfs_supports_user_xattrs; then
- sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
-
- mkdir -p /run/systemd/system/testsuite-55-testbloat.service.d/
- cat >/run/systemd/system/testsuite-55-testbloat.service.d/override.conf <<EOF
-[Service]
-ManagedOOMPreference=avoid
-EOF
-
- systemctl daemon-reload
- systemctl start testsuite-55-testchill.service
- systemctl start testsuite-55-testmunch.service
- systemctl start testsuite-55-testbloat.service
-
- for _ in {0..59}; do
- if ! systemctl status testsuite-55-testmunch.service; then
- break
- fi
- oomctl
- sleep 2
- done
-
- # testmunch should be killed since testbloat had the avoid xattr on it
- if ! systemctl status testsuite-55-testbloat.service; then exit 25; fi
- if systemctl status testsuite-55-testmunch.service; then exit 43; fi
- if ! systemctl status testsuite-55-testchill.service; then exit 24; fi
-fi
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-58-REPART
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2317
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if ! command -v systemd-repart >/dev/null; then
- echo "no systemd-repart" >/skipped
- exit 77
-fi
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-export PAGER=cat
-
-# Disable use of special glyphs such as →
-export SYSTEMD_UTF8=0
-
-seed=750b6cd5c4ae4012a15e7be3c29e6a47
-
-if ! systemd-detect-virt --quiet --container; then
- udevadm control --log-level debug
-fi
-
-machine="$(uname -m)"
-if [ "${machine}" = "x86_64" ]; then
- root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
- root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0
- root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC
- usr_guid=8484680C-9521-48C6-9C11-B0720656F69E
- usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16
- architecture="x86-64"
-elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
- root_guid=44479540-F297-41B2-9AF7-D131D5F0458A
- root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87
- root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC
- usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812
- usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648
- architecture="x86"
-elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
- root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE
- root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483
- root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A
- usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9
- usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F
- architecture="arm64"
-elif [ "${machine}" = "arm" ]; then
- root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3
- root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034
- root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E
- usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625
- usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966
- architecture="arm"
-elif [ "${machine}" = "loongarch64" ]; then
- root_guid=77055800-792C-4F94-B39A-98C91B762BB6
- root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8
- root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56
- usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F
- usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7
- architecture="loongarch64"
-elif [ "${machine}" = "ia64" ]; then
- root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97
- root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D
- root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38
- usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA
- usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241
- architecture="ia64"
-elif [ "${machine}" = "s390x" ]; then
- root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306
- root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E
- root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB
- usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA
- usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5
- architecture="s390x"
-elif [ "${machine}" = "ppc64le" ]; then
- root_guid=C31C45E6-3F39-412E-80FB-4809C4980599
- root_uuid=061E67A1-092F-482F-8150-B525D50D6654
- root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739
- usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C
- usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB
- architecture="ppc64-le"
-else
- echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me"
- exit 1
-fi
-
-testcase_basic() {
- local defs imgs output
- local loop volume
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** 1. create an empty image ***"
-
- systemd-repart --offline="$OFFLINE" \
- --empty=create \
- --size=1G \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118"
-
- echo "*** 2. Testing with root, root2, home, and swap ***"
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root
-EOF
-
- ln -s root.conf "$defs/root2.conf"
-
- tee "$defs/home.conf" <<EOF
-[Partition]
-Type=home
-Label=home-first
-Label=home-always-too-long-xxxxxxxxxxxxxx-%v
-EOF
-
- tee "$defs/swap.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-PaddingMinBytes=92M
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- --include-partitions=home,swap \
- --offline="$OFFLINE" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --empty=create \
- --size=50M \
- --seed="$seed" \
- --include-partitions=root,home \
- "$imgs/qqq"
-
- sfdisk -d "$imgs/qqq" | grep -v -e 'sector-size' -e '^$'
-
- systemd-repart --offline="$OFFLINE" \
- --empty=create \
- --size=1G \
- --dry-run=no \
- --seed="$seed" \
- --definitions "" \
- --copy-from="$imgs/qqq" \
- --copy-from="$imgs/qqq" \
- "$imgs/copy"
-
- output=$(sfdisk -d "$imgs/copy" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/copy
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/copy1 : start= 2048, size= 33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/copy2 : start= 35480, size= 33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/copy3 : start= 68920, size= 33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/copy4 : start= 102360, size= 33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/copy5 : start= 135792, size= 33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/copy6 : start= 169232, size= 33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\""
-
- rm "$imgs/qqq" "$imgs/copy" # Save disk space
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- --empty=force \
- --defer-partitions=home,root \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
-
- echo "*** 3. Testing with root, root2, home, swap, and another partition ***"
-
- tee "$defs/swap.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-EOF
-
- tee "$defs/extra.conf" <<EOF
-[Partition]
-Type=linux-generic
-Label=custom_label
-UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
-EOF
-
- echo "Label=ignored_label" >>"$defs/home.conf"
- echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf"
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
-
- echo "*** 4. Resizing to 2G ***"
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --size=2G \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 4194270
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
-
- echo "*** 5. Testing with root, root2, home, swap, another partition, and partition copy ***"
-
- dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240
-
- tee "$defs/extra2.conf" <<EOF
-[Partition]
-Type=linux-generic
-Label=block-copy
-UUID=2a1d97e1d0a346cca26eadc643926617
-CopyBlocks=$imgs/block-copy
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --size=3G \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 6291422
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
-$imgs/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\""
-
- cmp --bytes=$((4096*10240)) --ignore-initial=0:$((512*4194264)) "$imgs/block-copy" "$imgs/zzz"
-
- echo "*** 6. Testing Format=/Encrypt=/CopyFiles= ***"
-
- tee "$defs/extra3.conf" <<EOF
-[Partition]
-Type=linux-generic
-Label=luks-format-copy
-UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
-Format=ext4
-Encrypt=yes
-CopyFiles=$defs:/def
-SizeMinBytes=48M
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --size=auto \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
- assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 6389726
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
-$imgs/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\"
-$imgs/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\""
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping encrypt mount tests in container."
- return
- fi
-
- loop="$(losetup -P --show --find "$imgs/zzz")"
- udevadm wait --timeout 60 --settle "${loop:?}"
-
- volume="test-repart-$RANDOM"
-
- touch "$imgs/empty-password"
- cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p7" "$volume"
- mkdir -p "$imgs/mount"
- mount -t ext4 "/dev/mapper/$volume" "$imgs/mount"
- # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount
- cryptsetup close --deferred "$volume"
- losetup -d "$loop"
- diff -r "$imgs/mount/def" "$defs" >/dev/null
- umount "$imgs/mount"
-}
-
-testcase_dropin() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
-EOF
-
- mkdir -p "$defs/root.conf.d"
- tee "$defs/root.conf.d/override1.conf" <<EOF
-[Partition]
-Label=label1
-SizeMaxBytes=32M
-EOF
-
- tee "$defs/root.conf.d/override2.conf" <<EOF
-[Partition]
-Label=label2
-EOF
-
- output=$(systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --empty=create \
- --size=100M \
- --json=pretty \
- "$imgs/zzz")
-
- diff -u <(echo "$output") - <<EOF
-[
- {
- "type" : "swap",
- "label" : "label2",
- "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
- "partno" : 0,
- "file" : "$defs/root.conf",
- "node" : "$imgs/zzz1",
- "offset" : 1048576,
- "old_size" : 0,
- "raw_size" : 33554432,
- "size" : "-> 32.0M",
- "old_padding" : 0,
- "raw_padding" : 0,
- "padding" : "-> 0B",
- "activity" : "create",
- "drop-in_files" : [
- "$defs/root.conf.d/override1.conf",
- "$defs/root.conf.d/override2.conf"
- ]
- }
-]
-EOF
-}
-
-testcase_multiple_definitions() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- mkdir -p "$defs/1"
- tee "$defs/1/root1.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=32M
-UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
-Label=label1
-EOF
-
- mkdir -p "$defs/2"
- tee "$defs/2/root2.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=32M
-UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
-Label=label2
-EOF
-
- output=$(systemd-repart --offline="$OFFLINE" \
- --definitions="$defs/1" \
- --definitions="$defs/2" \
- --empty=create \
- --size=100M \
- --json=pretty \
- "$imgs/zzz")
-
- diff -u <(echo "$output") - <<EOF
-[
- {
- "type" : "swap",
- "label" : "label1",
- "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
- "partno" : 0,
- "file" : "$defs/1/root1.conf",
- "node" : "$imgs/zzz1",
- "offset" : 1048576,
- "old_size" : 0,
- "raw_size" : 33554432,
- "size" : "-> 32.0M",
- "old_padding" : 0,
- "raw_padding" : 0,
- "padding" : "-> 0B",
- "activity" : "create"
- },
- {
- "type" : "swap",
- "label" : "label2",
- "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
- "partno" : 1,
- "file" : "$defs/2/root2.conf",
- "node" : "$imgs/zzz2",
- "offset" : 34603008,
- "old_size" : 0,
- "raw_size" : 33554432,
- "size" : "-> 32.0M",
- "old_padding" : 0,
- "raw_padding" : 0,
- "padding" : "-> 0B",
- "activity" : "create"
- }
-]
-EOF
-}
-
-testcase_copy_blocks() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** First, create a disk image and verify its in order ***"
-
- tee "$defs/esp.conf" <<EOF
-[Partition]
-Type=esp
-SizeMinBytes=10M
-Format=vfat
-EOF
-
- tee "$defs/usr.conf" <<EOF
-[Partition]
-Type=usr-${architecture}
-SizeMinBytes=10M
-Format=ext4
-ReadOnly=yes
-EOF
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-SizeMinBytes=10M
-Format=ext4
-MakeDirectories=/usr /efi
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --empty=create \
- --size=auto \
- --seed="$seed" \
- "$imgs/zzz"
-
- output=$(sfdisk --dump "$imgs/zzz")
-
- assert_in "$imgs/zzz1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name=\"esp\"" "$output"
- assert_in "$imgs/zzz2 : start= 22528, size= 20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
- assert_in "$imgs/zzz3 : start= 43008, size= 20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output"
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping second part of copy blocks tests in container."
- return
- fi
-
- echo "*** Second, create another image with CopyBlocks=auto ***"
-
- tee "$defs/esp.conf" <<EOF
-[Partition]
-Type=esp
-CopyBlocks=auto
-EOF
-
- tee "$defs/usr.conf" <<EOF
-[Partition]
-Type=usr-${architecture}
-ReadOnly=yes
-CopyBlocks=auto
-EOF
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyBlocks=auto
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --empty=create \
- --size=auto \
- --seed="$seed" \
- --image="$imgs/zzz" \
- "$imgs/yyy"
-
- cmp "$imgs/zzz" "$imgs/yyy"
-}
-
-testcase_unaligned_partition() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** Operate on an image with unaligned partition ***"
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-EOF
-
- truncate -s 10g "$imgs/unaligned"
- sfdisk "$imgs/unaligned" <<EOF
-label: gpt
-
-start=2048, size=69044
-start=71092, size=3591848
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/unaligned"
-
- output=$(sfdisk --dump "$imgs/unaligned")
-
- assert_in "$imgs/unaligned1 : start= 2048, size= 69044," "$output"
- assert_in "$imgs/unaligned2 : start= 71092, size= 3591848," "$output"
- assert_in "$imgs/unaligned3 : start= 3662944, size= 17308536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
-}
-
-testcase_issue_21817() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** testcase for #21817 ***"
-
- tee "$defs/test.conf" <<EOF
-[Partition]
-Type=root
-EOF
-
- truncate -s 100m "$imgs/21817.img"
- sfdisk "$imgs/21817.img" <<EOF
-label: gpt
-
-size=50M, type=${root_guid}
-,
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --pretty=yes \
- --definitions "$imgs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/21817.img"
-
- output=$(sfdisk --dump "$imgs/21817.img")
-
- assert_in "$imgs/21817.img1 : start= 2048, size= 102400, type=${root_guid}," "$output"
- # Accept both unpadded (pre-v2.38 util-linux) and padded (v2.38+ util-linux) sizes
- assert_in "$imgs/21817.img2 : start= 104448, size= (100319| 98304)," "$output"
-}
-
-testcase_issue_24553() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** testcase for #24553 ***"
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root
-SizeMinBytes=10G
-SizeMaxBytes=120G
-EOF
-
- tee "$imgs/partscript" <<EOF
-label: gpt
-label-id: C9FFE979-A415-C449-B729-78C7AA664B10
-unit: sectors
-first-lba: 40
-
-start=40, size=524288, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=F2E89C8A-DC5D-4C4C-A29C-6CFB643B74FD, name="ESP System Partition"
-start=524328, size=14848000, type=${root_guid}, uuid=${root_uuid}, name="root-${architecture}"
-EOF
-
- echo "*** 1. Operate on a small image compared with SizeMinBytes= ***"
- truncate -s 8g "$imgs/zzz"
- sfdisk "$imgs/zzz" <"$imgs/partscript"
-
- # This should fail, but not trigger assertions.
- assert_rc 1 systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
-
- output=$(sfdisk --dump "$imgs/zzz")
- assert_in "$imgs/zzz2 : start= 524328, size= 14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
-
- echo "*** 2. Operate on an larger image compared with SizeMinBytes= ***"
- rm -f "$imgs/zzz"
- truncate -s 12g "$imgs/zzz"
- sfdisk "$imgs/zzz" <"$imgs/partscript"
-
- # This should succeed.
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
-
- output=$(sfdisk --dump "$imgs/zzz")
- assert_in "$imgs/zzz2 : start= 524328, size= 24641456, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
-
- echo "*** 3. Multiple partitions with Priority= (small disk) ***"
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root
-SizeMinBytes=10G
-SizeMaxBytes=120G
-Priority=100
-EOF
-
- tee "$defs/usr.conf" <<EOF
-[Partition]
-Type=usr
-SizeMinBytes=10M
-Priority=10
-EOF
-
- rm -f "$imgs/zzz"
- truncate -s 8g "$imgs/zzz"
- sfdisk "$imgs/zzz" <"$imgs/partscript"
-
- # This should also succeed, but root is not extended.
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
-
- output=$(sfdisk --dump "$imgs/zzz")
- assert_in "$imgs/zzz2 : start= 524328, size= 14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
- assert_in "$imgs/zzz3 : start= 15372328, size= 1404848, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
-
- echo "*** 4. Multiple partitions with Priority= (large disk) ***"
- rm -f "$imgs/zzz"
- truncate -s 12g "$imgs/zzz"
- sfdisk "$imgs/zzz" <"$imgs/partscript"
-
- # This should also succeed, and root is extended.
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
-
- output=$(sfdisk --dump "$imgs/zzz")
- assert_in "$imgs/zzz2 : start= 524328, size= 20971520, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
- assert_in "$imgs/zzz3 : start= 21495848, size= 3669936, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
-}
-
-testcase_zero_uuid() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** Test image with zero UUID ***"
-
- tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-UUID=null
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- "$imgs/zero"
-
- output=$(sfdisk --dump "$imgs/zero")
-
- assert_in "$imgs/zero1 : start= 2048, size= 20480, type=${root_guid}, uuid=00000000-0000-0000-0000-000000000000" "$output"
-}
-
-testcase_verity() {
- local defs imgs output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** dm-verity ***"
-
- tee "$defs/verity-data.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyFiles=${defs}
-Verity=data
-VerityMatchKey=root
-Minimize=guess
-EOF
-
- tee "$defs/verity-hash.conf" <<EOF
-[Partition]
-Type=root-${architecture}-verity
-Verity=hash
-VerityMatchKey=root
-Minimize=yes
-EOF
-
- tee "$defs/verity-sig.conf" <<EOF
-[Partition]
-Type=root-${architecture}-verity-sig
-Verity=signature
-VerityMatchKey=root
-EOF
-
- # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
- tee >"$defs/verity.openssl.cnf" <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = DE
-ST = Test State
-L = Test Locality
-O = Org Name
-OU = Org Unit Name
-CN = Common Name
-emailAddress = test@email.com
-EOF
-
- openssl req \
- -config "$defs/verity.openssl.cnf" \
- -new -x509 \
- -newkey rsa:1024 \
- -keyout "$defs/verity.key" \
- -out "$defs/verity.crt" \
- -days 365 \
- -nodes
-
- mkdir -p /run/verity.d
- ln -sf "$defs/verity.crt" /run/verity.d/ok.crt
-
- output=$(systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --json=pretty \
- --private-key="$defs/verity.key" \
- --certificate="$defs/verity.crt" \
- "$imgs/verity")
-
- drh=$(jq -r ".[] | select(.type == \"root-${architecture}\") | .roothash" <<<"$output")
- hrh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<<"$output")
- srh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity-sig\") | .roothash" <<<"$output")
-
- assert_eq "$drh" "$hrh"
- assert_eq "$hrh" "$srh"
-
- # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic)
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping verity test dissect part in container."
- return
- fi
-
- systemd-dissect "$imgs/verity" --root-hash "$drh"
- systemd-dissect "$imgs/verity" --root-hash "$drh" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"'
- systemd-dissect "$imgs/verity" --root-hash "$drh" -M "$imgs/mnt"
- systemd-dissect -U "$imgs/mnt"
-}
-
-testcase_verity_explicit_block_size() {
- local defs imgs loop
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping verity block size tests in container."
- return
- fi
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- echo "*** varying-dm-verity-block-sizes ***"
-
- tee "$defs/verity-data.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyFiles=${defs}
-Verity=data
-VerityMatchKey=root
-Minimize=guess
-EOF
-
- tee "$defs/verity-hash.conf" <<EOF
-[Partition]
-Type=root-${architecture}-verity
-Verity=hash
-VerityMatchKey=root
-VerityHashBlockSizeBytes=1024
-VerityDataBlockSizeBytes=4096
-Minimize=yes
-EOF
-
- systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --json=pretty \
- "$imgs/verity"
-
- loop="$(losetup --partscan --show --find "$imgs/verity")"
-
- # Make sure the loopback device gets cleaned up
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR
-
- udevadm wait --timeout 60 --settle "${loop:?}"
-
- # Check that the verity block sizes are as expected
- veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096'
- veritysetup dump "${loop}p2" | grep 'Hash block size:' | grep -q '1024'
-}
-
-testcase_exclude_files() {
- local defs imgs root output
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- root="$(mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs' '$root'" RETURN
- chmod 0755 "$defs"
-
- echo "*** file exclusion ***"
-
- touch "$root/abc"
- mkdir "$root/usr"
- touch "$root/usr/def"
- touch "$root/usr/qed"
- mkdir "$root/tmp"
- touch "$root/tmp/prs"
- mkdir "$root/proc"
- touch "$root/proc/prs"
- mkdir "$root/zzz"
- mkdir "$root/zzz/usr"
- touch "$root/zzz/usr/prs"
- mkdir "$root/zzz/proc"
- touch "$root/zzz/proc/prs"
-
- tee "$defs/00-root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyFiles=/
-CopyFiles=/zzz:/
-CopyFiles=/:/oiu
-ExcludeFilesTarget=/oiu/usr
-EOF
-
- tee "$defs/10-usr.conf" <<EOF
-[Partition]
-Type=usr-${architecture}
-CopyFiles=/usr:/
-ExcludeFiles=/usr/qed
-EOF
-
- output=$(systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --json=pretty \
- --root="$root" \
- "$imgs/zzz")
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping issue 24786 test loop/mount parts in container."
- return
- fi
-
- loop=$(losetup -P --show -f "$imgs/zzz")
- udevadm wait --timeout 60 --settle "${loop:?}"
-
- # Test that /usr/def did not end up in the root partition but other files did.
- mkdir "$imgs/mnt"
- mount -t ext4 "${loop}p1" "$imgs/mnt"
- assert_rc 0 ls "$imgs/mnt/abc"
- assert_rc 0 ls "$imgs/mnt/usr"
- assert_rc 2 ls "$imgs/mnt/usr/def"
-
- # Test that /zzz/usr/prs did not end up in the root partition under /usr but did end up in /zzz/usr/prs
- assert_rc 2 ls "$imgs/mnt/usr/prs"
- assert_rc 0 ls "$imgs/mnt/zzz/usr/prs"
-
- # Test that /tmp/prs did not end up in the root partition but /tmp did.
- assert_rc 0 ls "$imgs/mnt/tmp"
- assert_rc 2 ls "$imgs/mnt/tmp/prs"
-
- # Test that /usr/qed did not end up in the usr partition but /usr/def did.
- mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
- assert_rc 0 ls "$imgs/mnt/usr/def"
- assert_rc 2 ls "$imgs/mnt/usr/qed"
-
- # Test that /zzz/proc/prs did not end up in the root partition but /proc did.
- assert_rc 0 ls "$imgs/mnt/proc"
- assert_rc 2 ls "$imgs/mnt/proc/prs"
-
- # Test that /zzz/usr/prs did not end up in the usr partition.
- assert_rc 2 ls "$imgs/mnt/usr/prs"
-
- # Test that /oiu/ and /oiu/zzz ended up in the root partition but /oiu/usr did not.
- assert_rc 0 ls "$imgs/mnt/oiu"
- assert_rc 0 ls "$imgs/mnt/oiu/zzz"
- assert_rc 2 ls "$imgs/mnt/oiu/usr"
-
- umount -R "$imgs/mnt"
- losetup -d "$loop"
-}
-
-testcase_minimize() {
- local defs imgs output
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping minimize test in container."
- return
- fi
-
- echo "*** minimization ***"
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
-
- for format in ext4 vfat erofs; do
- if ! command -v "mkfs.$format" >/dev/null; then
- continue
- fi
-
- tee "$defs/root-$format.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-Format=${format}
-CopyFiles=${defs}
-Minimize=guess
-EOF
- done
-
- if command -v mksquashfs >/dev/null; then
- tee "$defs/root-squashfs.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-Format=squashfs
-CopyFiles=${defs}
-Minimize=best
-EOF
- fi
-
- output=$(systemd-repart --offline="$OFFLINE" \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --json=pretty \
- "$imgs/zzz")
-
- # Check that we can dissect, mount and unmount a minimized image.
-
- systemd-dissect "$imgs/zzz"
- systemd-dissect "$imgs/zzz" -M "$imgs/mnt"
- systemd-dissect -U "$imgs/mnt"
-}
-
-testcase_free_area_calculation() {
- local defs imgs output
-
- if ! command -v mksquashfs >/dev/null; then
- echo "Skipping free area calculation test without squashfs."
- return
- fi
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
- chmod 0755 "$defs"
-
- # https://github.com/systemd/systemd/issues/28225
- echo "*** free area calculation ***"
-
- tee "$defs/00-ESP.conf" <<EOF
-[Partition]
-Type = esp
-Label = ESP
-Format = vfat
-
-SizeMinBytes = 128M
-SizeMaxBytes = 128M
-
-# Sufficient for testing
-CopyFiles = /etc:/
-EOF
-
- tee "$defs/10-os.conf" <<EOF
-[Partition]
-Type = root-${architecture}
-Label = test
-Format = squashfs
-
-Minimize = best
-# Sufficient for testing
-CopyFiles = /etc/:/
-
-VerityMatchKey = os
-Verity = data
-EOF
-
- tee "$defs/11-os-verity.conf" <<EOF
-[Partition]
-Type = root-${architecture}-verity
-Label = test
-
-Minimize = best
-
-VerityMatchKey = os
-Verity = hash
-EOF
-
- # Set sector size for VFAT to 512 bytes because there will not be enough FAT clusters otherwise
- output1=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --sector-size=4096 \
- --defer-partitions=esp \
- --json=pretty \
- "$imgs/zzz")
-
- # The second invocation
- output2=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
- --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=allow \
- --size=auto \
- --sector-size=4096 \
- --defer-partitions=esp \
- --json=pretty \
- "$imgs/zzz")
-
- diff -u <(echo "$output1" | grep -E "(offset|raw_size|raw_padding)") <(echo "$output2" | grep -E "(offset|raw_size|raw_padding)")
-}
-
-test_sector() {
- local defs imgs output loop
- local start size ratio
- local sector="${1?}"
-
- if systemd-detect-virt --quiet --container; then
- echo "Skipping sector size tests in container."
- return
- fi
-
- echo "*** sector sizes ***"
-
- defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$defs' '$imgs'" RETURN
-
- tee "$defs/a.conf" <<EOF
-[Partition]
-Type=root
-SizeMaxBytes=15M
-SizeMinBytes=15M
-EOF
- tee "$defs/b.conf" <<EOF
-[Partition]
-Type=linux-generic
-Weight=250
-EOF
-
- tee "$defs/c.conf" <<EOF
-[Partition]
-Type=linux-generic
-Weight=750
-EOF
-
- truncate -s 100m "$imgs/$sector.img"
- loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" )
- udevadm wait --timeout 60 --settle "${loop:?}"
-
- systemd-repart --offline="$OFFLINE" \
- --pretty=yes \
- --definitions="$defs" \
- --seed="$seed" \
- --empty=require \
- --dry-run=no \
- "$loop"
-
- sfdisk --verify "$loop"
- output=$(sfdisk --dump "$loop")
- losetup -d "$loop"
-
- ratio=$(( sector / 512 ))
- start=$(( 2048 / ratio ))
- size=$(( 30720 / ratio ))
- assert_in "${loop}p1 : start= *${start}, size= *${size}, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
- start=$(( start + size ))
- size=$(( 42992 / ratio ))
- assert_in "${loop}p2 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DF71F5E3-080A-4D16-824B-18591B881380, name=\"linux-generic\"" "$output"
- start=$(( start + size ))
- size=$(( 129000 / ratio ))
- assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output"
-}
-
-testcase_dropped_partitions() {
- local workdir image defs
-
- workdir="$(mktemp --directory "/tmp/test-repart.dropped-partitions.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '${workdir:?}'" RETURN
-
- image="$workdir/image.img"
- truncate -s 32M "$image"
-
- defs="$workdir/defs"
- mkdir "$defs"
- echo -ne "[Partition]\nType=root\n" >"$defs/10-part1.conf"
- echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf"
- echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf"
- echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf"
-
- systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image"
-
- sfdisk -q -l "$image"
- [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]]
-}
-
-OFFLINE="yes"
-run_testcases
-
-# Online image builds need loop devices so we can't run them in nspawn.
-if ! systemd-detect-virt --container; then
- OFFLINE="no"
- run_testcases
-fi
-
-# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
-# must be powers of 2. Which leaves exactly four different ones to test on
-# typical hardware
-test_sector 512
-test_sector 1024
-test_sector 2048
-test_sector 4096
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-59-RELOADING-RESTART
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-fail() {
- systemd-analyze log-level info
- exit 1
-}
-
-# Wait for a service to enter a state within a timeout period, if it doesn't
-# enter the desired state within the timeout period then this function will
-# exit the test case with a non zero exit code.
-wait_on_state_or_fail() {
- service=$1
- expected_state=$2
- timeout=$3
-
- state=$(systemctl show "$service" --property=ActiveState --value)
- while [ "$state" != "$expected_state" ]; do
- if [ "$timeout" = "0" ]; then
- fail
- fi
- timeout=$((timeout - 1))
- sleep 1
- state=$(systemctl show "$service" --property=ActiveState --value)
- done
-}
-
-systemd-analyze log-level debug
-
-
-cat >/run/systemd/system/testservice-fail-59.service <<EOF
-[Unit]
-Description=TEST-59-RELOADING-RESTART Normal exit
-
-[Service]
-Type=notify
-ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
-EOF
-
-cat >/run/systemd/system/testservice-fail-restart-59.service <<EOF
-[Unit]
-Description=TEST-59-RELOADING-RESTART Restart=on-failure
-
-[Service]
-Type=notify
-ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
-Restart=on-failure
-StartLimitBurst=1
-EOF
-
-
-cat >/run/systemd/system/testservice-abort-restart-59.service <<EOF
-[Unit]
-Description=TEST-59-RELOADING-RESTART Restart=on-abort
-
-[Service]
-Type=notify
-ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
-Restart=on-abort
-EOF
-
-systemctl daemon-reload
-
-# This service sends a RELOADING=1 message then exits before it sends a
-# READY=1. Ensure it enters failed state and does not linger in reloading
-# state.
-systemctl start testservice-fail-59.service
-wait_on_state_or_fail "testservice-fail-59.service" "failed" "30"
-
-# This service sends a RELOADING=1 message then exits before it sends a
-# READY=1. It should automatically restart on failure. Ensure it enters failed
-# state and does not linger in reloading state.
-systemctl start testservice-fail-restart-59.service
-wait_on_state_or_fail "testservice-fail-restart-59.service" "failed" "30"
-
-# This service sends a RELOADING=1 message then exits before it sends a
-# READY=1. It should automatically restart on abort. It will sleep for 5s
-# to allow us to send it a SIGABRT. Ensure the service enters the failed state
-# and does not linger in reloading state.
-systemctl start testservice-abort-restart-59.service
-systemctl --signal=SIGABRT kill testservice-abort-restart-59.service
-wait_on_state_or_fail "testservice-abort-restart-59.service" "failed" "30"
-
-systemd-analyze log-level info
-
-# Test that rate-limiting daemon-reload works
-mkdir -p /run/systemd/system.conf.d/
-cat >/run/systemd/system.conf.d/50-test-59-reload.conf <<EOF
-[Manager]
-ReloadLimitIntervalSec=9
-ReloadLimitBurst=3
-EOF
-
-# Pick up the new config
-systemctl daemon-reload
-
-# The timeout will hit (and the test will fail) if the reloads are not rate-limited
-timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
-
-# Rate limit should reset after 9s
-sleep 10
-
-systemctl daemon-reload
-
-# Same test for reexec, but we wait here
-timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
-
-# Rate limit should reset after 9s
-sleep 10
-
-systemctl daemon-reexec
-
-# Let's now test the notify-reload logic
-
-cat >/run/notify-reload-test.sh <<EOF
-#!/usr/bin/env bash
-set -eux
-set -o pipefail
-
-EXIT_STATUS=88
-LEAVE=0
-
-function reload() {
- systemd-notify --reloading --status="Adding 11 to exit status"
- EXIT_STATUS=\$((EXIT_STATUS + 11))
- systemd-notify --ready --status="Back running"
-}
-
-function leave() {
- systemd-notify --stopping --status="Adding 7 to exit status"
- EXIT_STATUS=\$((EXIT_STATUS + 7))
- LEAVE=1
- return 0
-}
-
-trap reload SIGHUP
-trap leave SIGTERM
-
-systemd-notify --ready
-systemd-notify --status="Running now"
-
-while [ \$LEAVE = 0 ] ; do
- sleep 1
-done
-
-systemd-notify --status="Adding 3 to exit status"
-EXIT_STATUS=\$((EXIT_STATUS + 3))
-exit \$EXIT_STATUS
-EOF
-
-chmod +x /run/notify-reload-test.sh
-
-systemd-analyze log-level debug
-
-systemd-run --unit notify-reload-test -p Type=notify-reload -p KillMode=process /run/notify-reload-test.sh
-systemctl reload notify-reload-test
-systemctl stop notify-reload-test
-
-test "$(systemctl show -p ExecMainStatus --value notify-reload-test)" = 109
-
-systemctl reset-failed notify-reload-test
-rm /run/notify-reload-test.sh
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-60-MOUNT-RATELIMIT
-
-[Service]
-Type=oneshot
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-teardown_test_dependencies() (
- set +eux
-
- if mountpoint /tmp/deptest; then
- umount /tmp/deptest
- fi
-
- if [[ -n "${LOOP}" ]]; then
- losetup -d "${LOOP}" || :
- fi
- if [[ -n "${LOOP_0}" ]]; then
- losetup -d "${LOOP_0}" || :
- fi
- if [[ -n "${LOOP_1}" ]]; then
- losetup -d "${LOOP_1}" || :
- fi
-
- rm -f /tmp/testsuite-60-dependencies-0.img
- rm -f /tmp/testsuite-60-dependencies-1.img
-
- rm -f /run/systemd/system/tmp-deptest.mount
- systemctl daemon-reload
-
- return 0
-)
-
-setup_loop() {
- truncate -s 30m "/tmp/testsuite-60-dependencies-${1?}.img"
- sfdisk --wipe=always "/tmp/testsuite-60-dependencies-${1?}.img" <<EOF
-label:gpt
-
-name="loop${1?}-part1"
-EOF
- LOOP=$(losetup -P --show -f "/tmp/testsuite-60-dependencies-${1?}.img")
- udevadm wait --settle --timeout=10 "${LOOP}"
- udevadm lock --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
-}
-
-check_dependencies() {
- local escaped_0 escaped_1 after
-
- escaped_0=$(systemd-escape -p "${LOOP_0}p1")
- escaped_1=$(systemd-escape -p "${LOOP_1}p1")
-
- if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_not_in "local-fs-pre.target" "$after"
- assert_in "remote-fs-pre.target" "$after"
- assert_in "network.target" "$after"
- fi
-
- # mount LOOP_0
- mount -t ext4 "${LOOP_0}p1" /tmp/deptest
- sleep 1
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_in "local-fs-pre.target" "$after"
- assert_not_in "remote-fs-pre.target" "$after"
- assert_not_in "network.target" "$after"
- assert_in "${escaped_0}.device" "$after"
- assert_in "blockdev@${escaped_0}.target" "$after"
- assert_not_in "${escaped_1}.device" "$after"
- assert_not_in "blockdev@${escaped_1}.target" "$after"
- umount /tmp/deptest
-
- if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_not_in "local-fs-pre.target" "$after"
- assert_in "remote-fs-pre.target" "$after"
- assert_in "network.target" "$after"
- fi
-
- # mount LOOP_1 (using fake _netdev option)
- mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
- sleep 1
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_not_in "local-fs-pre.target" "$after"
- assert_in "remote-fs-pre.target" "$after"
- assert_in "network.target" "$after"
- assert_not_in "${escaped_0}.device" "$after"
- assert_not_in "blockdev@${escaped_0}.target" "$after"
- assert_in "${escaped_1}.device" "$after"
- assert_in "blockdev@${escaped_1}.target" "$after"
- umount /tmp/deptest
-
- if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_not_in "local-fs-pre.target" "$after"
- assert_in "remote-fs-pre.target" "$after"
- assert_in "network.target" "$after"
- fi
-
- # mount tmpfs
- mount -t tmpfs tmpfs /tmp/deptest
- sleep 1
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_in "local-fs-pre.target" "$after"
- assert_not_in "remote-fs-pre.target" "$after"
- assert_not_in "network.target" "$after"
- assert_not_in "${escaped_0}.device" "$after"
- assert_not_in "blockdev@${escaped_0}.target" "$after"
- assert_not_in "${escaped_1}.device" "$after"
- assert_not_in "blockdev@${escaped_1}.target" "$after"
- umount /tmp/deptest
-
- if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
- after=$(systemctl show --property=After --value tmp-deptest.mount)
- assert_not_in "local-fs-pre.target" "$after"
- assert_in "remote-fs-pre.target" "$after"
- assert_in "network.target" "$after"
- fi
-}
-
-test_dependencies() {
- if systemd-detect-virt --quiet --container; then
- echo "Skipping test_dependencies in container"
- return
- fi
-
- trap teardown_test_dependencies RETURN
-
- setup_loop 0
- LOOP_0="${LOOP}"
- LOOP=
- setup_loop 1
- LOOP_1="${LOOP}"
- LOOP=
-
- mkdir -p /tmp/deptest
-
- # without .mount file
- check_dependencies
-
- # create .mount file
- mkdir -p /run/systemd/system
- cat >/run/systemd/system/tmp-deptest.mount <<EOF
-[Mount]
-Where=/tmp/deptest
-What=192.168.0.1:/tmp/mnt
-Type=nfs
-EOF
- systemctl daemon-reload
-
- # with .mount file
- check_dependencies
-}
-
-test_issue_20329() {
- local tmpdir unit
- tmpdir="$(mktemp -d)"
- unit=$(systemd-escape --suffix mount --path "$tmpdir")
-
- # Set up test mount unit
- cat >/run/systemd/system/"$unit" <<EOF
-[Mount]
-What=tmpfs
-Where=$tmpdir
-Type=tmpfs
-Options=defaults,nofail
-EOF
-
- # Start the unit
- systemctl daemon-reload
- systemctl start "$unit"
-
- [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
- echo >&2 "Test mount \"$unit\" unit isn't mounted"
- return 1
- }
- mountpoint -q "$tmpdir"
-
- trap 'systemctl stop $unit' RETURN
-
- # Trigger the mount ratelimiting
- cd "$(mktemp -d)"
- mkdir foo
- for _ in {1..50}; do
- mount --bind foo foo
- umount foo
- done
-
- # Unmount the test mount and start it immediately again via systemd
- umount "$tmpdir"
- systemctl start "$unit"
-
- # Make sure it is seen as mounted by systemd and it actually is mounted
- [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
- echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state"
- return 1
- }
-
- mountpoint -q "$tmpdir" || {
- echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted"
- return 1
- }
-}
-
-test_issue_23796() {
- local mount_path mount_mytmpfs
-
- mount_path="$(command -v mount 2>/dev/null)"
- mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
- cat >"$mount_mytmpfs" <<EOF
-#!/bin/bash
-sleep ".\$RANDOM"
-exec -- $mount_path -t tmpfs tmpfs "\$2"
-EOF
- chmod +x "$mount_mytmpfs"
-
- mkdir -p /run/systemd/system
- cat >/run/systemd/system/tmp-hoge.mount <<EOF
-[Mount]
-What=mytmpfs
-Where=/tmp/hoge
-Type=mytmpfs
-EOF
-
- # shellcheck disable=SC2064
- trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN
-
- for _ in {1..10}; do
- systemctl --no-block start tmp-hoge.mount
- sleep ".$RANDOM"
- systemctl daemon-reexec
-
- sleep 1
-
- if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
- journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then
- exit 1
- fi
-
- systemctl stop tmp-hoge.mount
- done
-}
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-NUM_DIRS=20
-
-# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
-LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
-LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"
-TS="$(date '+%H:%M:%S')"
-
-mkdir -p "$LONGPATH"
-mount -t tmpfs tmpfs "$LONGPATH"
-systemctl daemon-reload
-
-# check that unit is active(mounted)
-systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted
-
-# check that relevant part of journal doesn't contain any errors related to unit
-[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]
-
-# check that we can successfully stop the mount unit
-systemctl stop "$LONGPATH"
-rm -rf "$LONGPATH"
-
-# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
-
-for ((i = 0; i < NUM_DIRS; i++)); do
- mkdir "/tmp/meow${i}"
-done
-
-TS="$(date '+%H:%M:%S')"
-
-for ((i = 0; i < NUM_DIRS; i++)); do
- mount -t tmpfs tmpfs "/tmp/meow${i}"
-done
-
-systemctl daemon-reload
-systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
-
-for ((i = 0; i < NUM_DIRS; i++)); do
- umount "/tmp/meow${i}"
-done
-
-# Figure out if we have entered the rate limit state.
-# If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
-if timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) entered rate limit'; do sleep 1; done"; then
- timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) left rate limit'; do sleep 1; done"
-fi
-
-# Verify that the mount units are always cleaned up at the end.
-# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
-timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
-
-# test for issue #19983 and #23552.
-test_dependencies
-
-# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
-test_issue_20329
-
-# test for reexecuting with background mount job
-test_issue_23796
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-all-pings-work
-[Service]
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=
-Type=oneshot
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-allow-list
-[Service]
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0
-RestrictNetworkInterfaces=veth1
-Type=oneshot
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-deny-list
-[Service]
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=~veth0
-RestrictNetworkInterfaces=~veth1
-Type=oneshot
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-empty-assignment
-[Service]
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0
-RestrictNetworkInterfaces=
-Type=oneshot
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-invert-assignment
-[Service]
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0
-RestrictNetworkInterfaces=veth0 veth1
-RestrictNetworkInterfaces=~veth0
-Type=oneshot
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-altname
-[Service]
-ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars
-RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars
-Type=oneshot
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-setup() {
- systemd-analyze log-level debug
-
- for i in {0..3};
- do
- ip netns del "ns${i}" || true
- ip link del "veth${i}" || true
- ip netns add "ns${i}"
- ip link add "veth${i}" type veth peer name "veth${i}_"
- ip link set "veth${i}_" netns "ns${i}"
- ip -n "ns${i}" link set dev "veth${i}_" up
- ip -n "ns${i}" link set dev lo up
- ip -n "ns${i}" addr add "192.168.113."$((4*i+1))/30 dev "veth${i}_"
- ip link set dev "veth${i}" up
- ip link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars"
- ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}"
- done
-}
-
-# shellcheck disable=SC2317
-teardown() {
- set +e
-
- for i in {0..3}; do
- ip netns del "ns${i}"
- ip link del "veth${i}"
- done
-
- systemd-analyze log-level info
-}
-
-if systemd-analyze compare-versions "$(uname -r)" lt 5.7; then
- echo "kernel is not 5.7+" >>/skipped
- exit 77
-fi
-
-if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
- echo "bpf-framework is disabled" >>/skipped
- exit 77
-fi
-
-trap teardown EXIT
-setup
-
-systemctl start --wait testsuite-62-1.service
-systemctl start --wait testsuite-62-2.service
-systemctl start --wait testsuite-62-3.service
-systemctl start --wait testsuite-62-4.service
-systemctl start --wait testsuite-62-5.service
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-63-PATH
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemctl log-level debug
-
-# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
-# the trigger-limit-hit error.
-rm -f /tmp/nonexistent
-systemctl start test63.path
-touch /tmp/test63
-
-# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
-# shellcheck disable=SC2016
-timeout 30 bash -c 'until test "$(systemctl show test63.path -P ActiveState)" = failed; do sleep .2; done'
-test "$(systemctl show test63.service -P ActiveState)" = inactive
-test "$(systemctl show test63.service -P Result)" = success
-test "$(systemctl show test63.path -P Result)" = trigger-limit-hit
-
-# Test that starting the service manually doesn't affect the path unit.
-rm -f /tmp/test63
-systemctl reset-failed
-systemctl start test63.path
-systemctl start test63.service
-test "$(systemctl show test63.service -P ActiveState)" = inactive
-test "$(systemctl show test63.service -P Result)" = success
-test "$(systemctl show test63.path -P ActiveState)" = active
-test "$(systemctl show test63.path -P Result)" = success
-
-# Test that glob matching works too, with $TRIGGER_PATH
-systemctl start test63-glob.path
-touch /tmp/test63-glob-foo
-timeout 60 bash -c 'until systemctl -q is-active test63-glob.service; do sleep .2; done'
-test "$(systemctl show test63-glob.service -P ActiveState)" = active
-test "$(systemctl show test63-glob.service -P Result)" = success
-
-test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}'
-
-systemctl stop test63-glob.path test63-glob.service
-
-test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}'
-
-# tests for issue https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
-rm -f /tmp/hoge
-systemctl start test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_not_in "test63-issue-24577.service" "$output"
-assert_not_in "test63-issue-24577-dep.service" "$output"
-
-touch /tmp/hoge
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_in "test63-issue-24577.service" "$output"
-assert_in "test63-issue-24577-dep.service" "$output"
-
-# even if the service is stopped, it will be soon retriggered.
-systemctl stop test63-issue-24577.service
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_in "test63-issue-24577.service" "$output"
-assert_in "test63-issue-24577-dep.service" "$output"
-
-rm -f /tmp/hoge
-systemctl stop test63-issue-24577.service
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_not_in "test63-issue-24577.service" "$output"
-assert_in "test63-issue-24577-dep.service" "$output"
-
-# Test for race condition fixed by https://github.com/systemd/systemd/pull/30768
-# Here's the schedule of events that we to happen during this test:
-# (This test) (The service)
-# .path unit monitors /tmp/copyme for changes
-# Take lock on /tmp/noexeit ↓
-# Write to /tmp/copyme ↓
-# Wait for deactivating Started
-# ↓ Copies /tmp/copyme to /tmp/copied
-# ↓ Tells manager it's shutting down
-# Ensure service did the copy Tries to lock /tmp/noexit and blocks
-# Write to /tmp/copyme ↓
-#
-# Now at this point the test can diverge. If we regress, this second write is
-# missed and we'll see:
-# ... (second write) ... (blocked)
-# Drop lock on /tmp/noexit ↓
-# Wait for service to do copy Unblocks and exits
-# ↓ (dead)
-# ↓
-# (timeout)
-# Test fails
-#
-# Otherwise, we'll see:
-# ... (second write) ... (blocked)
-# Drop lock on /tmp/noexit ↓ and .path unit queues a new start job
-# Wait for service to do copy Unblocks and exits
-# ↓ Starts again b/c of queued job
-# ↓ Copies again
-# Test Passes
-systemctl start test63-pr-30768.path
-exec {lock}<>/tmp/noexit
-flock -e $lock
-echo test1 > /tmp/copyme
-# shellcheck disable=SC2016
-timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P ActiveState)" = deactivating; do sleep .2; done'
-diff /tmp/copyme /tmp/copied
-echo test2 > /tmp/copyme
-exec {lock}<&-
-timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied; do sleep .2; done'
-
-systemctl log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-64-UDEV
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# vi: ts=4 sw=4 tw=0 et:
-
-set -eux
-set -o pipefail
-
-# Check if all symlinks under /dev/disk/ are valid
-# shellcheck disable=SC2120
-helper_check_device_symlinks() {(
- set +x
-
- local dev link path paths target
-
- [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper")
-
- # Check if all given paths are valid
- for path in "${paths[@]}"; do
- if ! test -e "$path"; then
- echo >&2 "Path '$path' doesn't exist"
- return 1
- fi
- done
-
- while read -r link; do
- target="$(readlink -f "$link")"
- # Both checks should do virtually the same thing, but check both to be
- # on the safe side
- if [[ ! -e "$link" || ! -e "$target" ]]; then
- echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist"
- return 1
- fi
-
- # Check if the symlink points to the correct device in /dev
- dev="/dev/$(udevadm info -q name "$link")"
- if [[ "$target" != "$dev" ]]; then
- echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected"
- return 1
- fi
- done < <(find "${paths[@]}" -type l)
-)}
-
-helper_check_udev_watch() {(
- set +x
-
- local link target id dev
-
- while read -r link; do
- target="$(readlink "$link")"
- if [[ ! -L "/run/udev/watch/$target" ]]; then
- echo >&2 "ERROR: symlink /run/udev/watch/$target does not exist"
- return 1
- fi
- if [[ "$(readlink "/run/udev/watch/$target")" != "$(basename "$link")" ]]; then
- echo >&2 "ERROR: symlink target of /run/udev/watch/$target is inconsistent with $link"
- return 1
- fi
-
- if [[ "$target" =~ ^[0-9]+$ ]]; then
- # $link is ID -> wd
- id="$(basename "$link")"
- else
- # $link is wd -> ID
- id="$target"
- fi
-
- if [[ "${id:0:1}" == "b" ]]; then
- dev="/dev/block/${id:1}"
- elif [[ "${id:0:1}" == "c" ]]; then
- dev="/dev/char/${id:1}"
- else
- echo >&2 "ERROR: unexpected device ID '$id'"
- return 1
- fi
-
- if [[ ! -e "$dev" ]]; then
- echo >&2 "ERROR: device '$dev' corresponding to symlink '$link' does not exist"
- return 1
- fi
- done < <(find /run/udev/watch -type l)
-)}
-
-check_device_unit() {(
- set +x
-
- local log_level link links path syspath unit
-
- log_level="${1?}"
- path="${2?}"
- unit=$(systemd-escape --path --suffix=device "$path")
-
- [[ "$log_level" == 1 ]] && echo "INFO: check_device_unit($unit)"
-
- syspath=$(systemctl show --value --property SysFSPath "$unit" 2>/dev/null)
- if [[ -z "$syspath" ]]; then
- [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit not found."
- return 1
- fi
-
- if [[ ! -L "$path" ]]; then
- if [[ ! -d "$syspath" ]]; then
- [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not exist."
- return 1
- fi
- return 0
- fi
-
- if [[ ! -b "$path" && ! -c "$path" ]]; then
- [[ "$log_level" == 1 ]] && echo >&2 "ERROR: invalid file type $path"
- return 1
- fi
-
- read -r -a links < <(udevadm info -q symlink "$syspath" 2>/dev/null)
- for link in "${links[@]}"; do
- if [[ "/dev/$link" == "$path" ]]; then # DEVLINKS= given by -q symlink are relative to /dev
- return 0
- fi
- done
-
- read -r -a links < <(udevadm info "$syspath" | sed -ne '/SYSTEMD_ALIAS=/ { s/^E: SYSTEMD_ALIAS=//; p }' 2>/dev/null)
- for link in "${links[@]}"; do
- if [[ "$link" == "$path" ]]; then # SYSTEMD_ALIAS= are absolute
- return 0
- fi
- done
-
- [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not have the corresponding DEVLINKS or SYSTEMD_ALIAS."
- return 1
-)}
-
-check_device_units() {(
- set +x
-
- local log_level path paths
-
- log_level="${1?}"
- shift
- paths=("$@")
-
- for path in "${paths[@]}"; do
- if ! check_device_unit "$log_level" "$path"; then
- return 1
- fi
- done
-
- while read -r unit _; do
- path=$(systemd-escape --path --unescape "$unit")
- if ! check_device_unit "$log_level" "$path"; then
- return 1
- fi
- done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
-
- return 0
-)}
-
-helper_check_device_units() {(
- set +x
-
- local i
-
- for i in {1..20}; do
- (( i > 1 )) && sleep 0.5
- if check_device_units 0 "$@"; then
- return 0
- fi
- done
-
- check_device_units 1 "$@"
-)}
-
-testcase_megasas2_basic() {
- lsblk -S
- [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
-}
-
-testcase_nvme_basic() {
- local expected_symlinks=()
- local i
-
- for (( i = 0; i < 5; i++ )); do
- expected_symlinks+=(
- # both replace mode provides the same devlink
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"
- # with nsid
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"_1
- )
- done
- for (( i = 5; i < 10; i++ )); do
- expected_symlinks+=(
- # old replace mode
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl__deadbeef_"$i"
- # newer replace mode
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"
- # with nsid
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"_1
- )
- done
- for (( i = 10; i < 15; i++ )); do
- expected_symlinks+=(
- # old replace mode does not provide devlink, as serial contains "/"
- # newer replace mode
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"
- # with nsid
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"_1
- )
- done
- for (( i = 15; i < 20; i++ )); do
- expected_symlinks+=(
- # old replace mode does not provide devlink, as serial contains "/"
- # newer replace mode
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"
- # with nsid
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"_1
- )
- done
-
- udevadm settle
- ls /dev/disk/by-id
- for i in "${expected_symlinks[@]}"; do
- udevadm wait --settle --timeout=30 "$i"
- done
-
- lsblk --noheadings | grep "^nvme"
- [[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]]
-}
-
-testcase_nvme_subsystem() {
- local expected_symlinks=(
- # Controller(s)
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17
- # Shared namespaces
- /dev/disk/by-path/pci-*-nvme-16
- /dev/disk/by-path/pci-*-nvme-17
- )
-
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-}
-
-testcase_virtio_scsi_identically_named_partitions() {
- local num_part num_disk i j
- local alphabet='abcdefghijklmnopqrstuvwxyz'
-
- if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- num_part=4
- num_disk=4
- else
- num_part=8
- num_disk=16
- fi
-
- for ((i = 0; i < num_disk; i++)); do
- sfdisk "/dev/sd${alphabet:$i:1}" <<EOF
-label: gpt
-
-$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
-EOF
- done
-
- lsblk --noheadings -a -o NAME,PARTLABEL
- [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
-}
-
-testcase_multipath_basic_failover() {
- local dmpath i path wwid
-
- # Configure multipath
- cat >/etc/multipath.conf <<\EOF
-defaults {
- # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX
- user_friendly_names no
- find_multipaths yes
- enable_foreign "^$"
-}
-
-blacklist_exceptions {
- property "(SCSI_IDENT_|ID_WWN)"
-}
-
-blacklist {
-}
-EOF
-
- sfdisk /dev/sda <<EOF
-label: gpt
-
-name="first_partition", size=5M
-uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
-EOF
- udevadm settle
- mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "/dev/sda2"
-
- modprobe -v dm_multipath
- systemctl start multipathd.service
- systemctl status multipathd.service
- multipath -ll
- udevadm settle
- ls -l /dev/disk/by-id/
-
- for i in {0..15}; do
- wwid="deaddeadbeef$(printf "%.4d" "$i")"
- path="/dev/disk/by-id/wwn-0x$wwid"
- dmpath="$(readlink -f "$path")"
-
- lsblk "$path"
- multipath -C "$dmpath"
- # We should have 4 active paths for each multipath device
- [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]]
- done
-
- # Test failover (with the first multipath device that has a partitioned disk)
- echo "${FUNCNAME[0]}: test failover"
- local device expected link mpoint part
- local -a devices
- mkdir -p /mnt
- mpoint="$(mktemp -d /mnt/mpathXXX)"
- wwid="deaddeadbeef0000"
- path="/dev/disk/by-id/wwn-0x$wwid"
-
- # All following symlinks should exists and should be valid
- local -a part_links=(
- "/dev/disk/by-id/wwn-0x$wwid-part2"
- "/dev/disk/by-partlabel/failover_part"
- "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
- "/dev/disk/by-label/failover_vol"
- "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
- )
- udevadm wait --settle --timeout=30 "${part_links[@]}"
- helper_check_device_units "${part_links[@]}"
-
- # Choose a random symlink to the failover data partition each time, for
- # a better coverage
- part="${part_links[$RANDOM % ${#part_links[@]}]}"
-
- # Get all devices attached to a specific multipath device (in H:C:T:L format)
- # and sort them in a random order, so we cut off different paths each time
- mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R)
- if [[ "${#devices[@]}" -ne 4 ]]; then
- echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead"
- return 1
- fi
- # Drop the last path from the array, since we want to leave at least one path active
- unset "devices[3]"
- # Mount the first multipath partition, write some data we can check later,
- # and then disconnect the remaining paths one by one while checking if we
- # can still read/write from the mount
- mount -t ext4 "$part" "$mpoint"
- expected=0
- echo -n "$expected" >"$mpoint/test"
- # Sanity check we actually wrote what we wanted
- [[ "$(<"$mpoint/test")" == "$expected" ]]
-
- for device in "${devices[@]}"; do
- echo offline >"/sys/class/scsi_device/$device/device/state"
- [[ "$(<"$mpoint/test")" == "$expected" ]]
- expected="$((expected + 1))"
- echo -n "$expected" >"$mpoint/test"
-
- # Make sure all symlinks are still valid
- udevadm wait --settle --timeout=30 "${part_links[@]}"
- helper_check_device_units "${part_links[@]}"
- done
-
- multipath -l "$path"
- # Three paths should be now marked as 'offline' and one as 'running'
- [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]]
- [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]]
-
- umount "$mpoint"
- rm -fr "$mpoint"
-}
-
-testcase_simultaneous_events_1() {
- local disk expected i iterations key link num_part part partscript rule target timeout
- local -a devices symlinks
- local -A running
-
- if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- num_part=2
- iterations=10
- timeout=240
- else
- num_part=10
- iterations=100
- timeout=30
- fi
-
- for disk in {0..9}; do
- link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
- target="$(readlink -f "$link")"
- if [[ ! -b "$target" ]]; then
- echo "ERROR: failed to find the test SCSI block device $link"
- return 1
- fi
-
- devices+=("$target")
- done
-
- for ((part = 1; part <= num_part; part++)); do
- symlinks+=(
- "/dev/disk/by-partlabel/test${part}"
- )
- done
-
- partscript="$(mktemp)"
-
- cat >"$partscript" <<EOF
-$(for ((part = 1; part <= num_part; part++)); do printf 'name="test%d", size=2M\n' "$part"; done)
-EOF
-
- rule=/run/udev/rules.d/50-test.rules
- mkdir -p "${rule%/*}"
- cat >"$rule" <<EOF
-SUBSYSTEM=="block", KERNEL=="${devices[4]##*/}*|${devices[5]##*/}*", OPTIONS="link_priority=10"
-EOF
-
- udevadm control --reload
-
- # initialize partition table
- for disk in {0..9}; do
- echo 'label: gpt' | udevadm lock --device="${devices[$disk]}" sfdisk -q "${devices[$disk]}"
- done
-
- # Delete the partitions, immediately recreate them, wait for udev to settle
- # down, and then check if we have any dangling symlinks in /dev/disk/. Rinse
- # and repeat.
- #
- # On unpatched udev versions the delete-recreate cycle may trigger a race
- # leading to dead symlinks in /dev/disk/
- for ((i = 1; i <= iterations; i++)); do
- for disk in {0..9}; do
- if ((disk % 2 == i % 2)); then
- udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
- else
- udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$partscript" &
- fi
- running[$disk]=$!
- done
-
- for key in "${!running[@]}"; do
- wait "${running[$key]}"
- unset "running[$key]"
- done
-
- if ((i % 10 <= 1)); then
- udevadm wait --settle --timeout="$timeout" "${devices[@]}" "${symlinks[@]}"
- helper_check_device_symlinks
- helper_check_udev_watch
- for ((part = 1; part <= num_part; part++)); do
- link="/dev/disk/by-partlabel/test${part}"
- target="$(readlink -f "$link")"
- if ((i % 2 == 0)); then
- expected="${devices[5]}$part"
- else
- expected="${devices[4]}$part"
- fi
- if [[ "$target" != "$expected" ]]; then
- echo >&2 "ERROR: symlink '/dev/disk/by-partlabel/test${part}' points to '$target' but '$expected' was expected"
- return 1
- fi
- done
- fi
- done
-
- helper_check_device_units
- rm -f "$rule" "$partscript"
-
- udevadm control --reload
-}
-
-testcase_simultaneous_events_2() {
- local disk expected i iterations key link num_part part script_dir target timeout
- local -a devices symlinks
- local -A running
-
- script_dir="$(mktemp --directory "/tmp/test-udev-storage.script.XXXXXXXXXX")"
- # shellcheck disable=SC2064
- trap "rm -rf '$script_dir'" RETURN
-
- if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- num_part=20
- iterations=1
- timeout=2400
- else
- num_part=100
- iterations=3
- timeout=300
- fi
-
- for disk in {0..9}; do
- link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
- target="$(readlink -f "$link")"
- if [[ ! -b "$target" ]]; then
- echo "ERROR: failed to find the test SCSI block device $link"
- return 1
- fi
-
- devices+=("$target")
- done
-
- for ((i = 1; i <= iterations; i++)); do
- cat >"$script_dir/partscript-$i" <<EOF
-$(for ((part = 1; part <= num_part; part++)); do printf 'name="testlabel-%d", size=1M\n' "$i"; done)
-EOF
- done
-
- echo "## $iterations iterations start: $(date '+%H:%M:%S.%N')"
- for ((i = 1; i <= iterations; i++)); do
-
- for disk in {0..9}; do
- udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
- running[$disk]=$!
- done
-
- for key in "${!running[@]}"; do
- wait "${running[$key]}"
- unset "running[$key]"
- done
-
- for disk in {0..9}; do
- udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$script_dir/partscript-$i" &
- running[$disk]=$!
- done
-
- for key in "${!running[@]}"; do
- wait "${running[$key]}"
- unset "running[$key]"
- done
-
- udevadm wait --settle --timeout="$timeout" "${devices[@]}" "/dev/disk/by-partlabel/testlabel-$i"
- done
- echo "## $iterations iterations end: $(date '+%H:%M:%S.%N')"
-}
-
-testcase_simultaneous_events() {
- testcase_simultaneous_events_1
- testcase_simultaneous_events_2
-}
-
-testcase_lvm_basic() {
- local i iterations partitions part timeout
- local vgroup="MyTestGroup$RANDOM"
- local devices=(
- /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
- )
-
- if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- timeout=180
- else
- timeout=30
- fi
- # Make sure all the necessary soon-to-be-LVM devices exist
- ls -l "${devices[@]}"
-
- # Add all test devices into a volume group, create two logical volumes,
- # and check if necessary symlinks exist (and are valid)
- lvm pvcreate -y "${devices[@]}"
- lvm pvs
- lvm vgcreate "$vgroup" -y "${devices[@]}"
- lvm vgs
- lvm vgchange -ay "$vgroup"
- lvm lvcreate -y -L 4M "$vgroup" -n mypart1
- lvm lvcreate -y -L 32M "$vgroup" -n mypart2
- lvm lvs
- udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
- mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
- udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
-
- # Mount mypart1 through by-label devlink
- mkdir -p /tmp/mypart1-mount-point
- mount /dev/disk/by-label/mylvpart1 /tmp/mypart1-mount-point
- timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
- # Extend the partition and check if the device and mount units are still active.
- # See https://bugzilla.redhat.com/show_bug.cgi?id=2158628
- # Note, the test below may be unstable with LVM2 without the following patch:
- # https://github.com/lvmteam/lvm2/pull/105
- # But, to reproduce the issue, udevd must start to process the first 'change' uevent
- # earlier than extending the volume has been finished, and in most case, the extension
- # is hopefully fast.
- lvm lvextend -y --size 8M "/dev/$vgroup/mypart1"
- udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
- timeout 30 bash -c "until systemctl -q is-active '/dev/$vgroup/mypart1'; do sleep .2; done"
- timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
- # Umount the partition, otherwise the underlying device unit will stay in
- # the inactive state and not be collected, and helper_check_device_units() will fail.
- systemctl show /tmp/mypart1-mount-point
- umount /tmp/mypart1-mount-point
-
- # Rename partitions (see issue #24518)
- lvm lvrename "/dev/$vgroup/mypart1" renamed1
- lvm lvrename "/dev/$vgroup/mypart2" renamed2
- udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
- udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
-
- # Rename them back
- lvm lvrename "/dev/$vgroup/renamed1" mypart1
- lvm lvrename "/dev/$vgroup/renamed2" mypart2
- udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
- udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
-
- # Do not "unready" suspended encrypted devices w/o superblock info
- # See:
- # - https://github.com/systemd/systemd/pull/24177
- # - https://bugzilla.redhat.com/show_bug.cgi?id=1985288
- dd if=/dev/urandom of=/etc/lvm_keyfile bs=64 count=1 iflag=fullblock
- chmod 0600 /etc/lvm_keyfile
- # Intentionally use weaker cipher-related settings, since we don't care
- # about security here as it's a throwaway LUKS partition
- cryptsetup luksFormat -q --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
- "/dev/$vgroup/mypart2" /etc/lvm_keyfile
- # Mount the LUKS partition & create a filesystem on it
- mkdir -p /tmp/lvmluksmnt
- cryptsetup open --key-file=/etc/lvm_keyfile "/dev/$vgroup/mypart2" "lvmluksmap"
- udevadm wait --settle --timeout="$timeout" "/dev/mapper/lvmluksmap"
- mkfs.ext4 -L lvmluksfs "/dev/mapper/lvmluksmap"
- udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/lvmluksfs"
- # Make systemd "interested" in the mount by adding it to /etc/fstab
- echo "/dev/disk/by-label/lvmluksfs /tmp/lvmluksmnt ext4 defaults 0 2" >>/etc/fstab
- systemctl daemon-reload
- mount "/tmp/lvmluksmnt"
- mountpoint "/tmp/lvmluksmnt"
- # Temporarily suspend the LUKS device and trigger udev - basically what `cryptsetup resize`
- # does but in a more deterministic way suitable for a test/reproducer
- for _ in {0..5}; do
- dmsetup suspend "/dev/mapper/lvmluksmap"
- udevadm trigger -v --settle "/dev/mapper/lvmluksmap"
- dmsetup resume "/dev/mapper/lvmluksmap"
- # The mount should survive this sequence of events
- mountpoint "/tmp/lvmluksmnt"
- done
- # Cleanup
- umount "/tmp/lvmluksmnt"
- cryptsetup close "/dev/mapper/lvmluksmap"
- sed -i "/lvmluksfs/d" "/etc/fstab"
- systemctl daemon-reload
-
- # Disable the VG and check symlinks...
- lvm vgchange -an "$vgroup"
- udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1"
- helper_check_device_symlinks "/dev/disk"
- helper_check_device_units
-
- # reenable the VG and check the symlinks again if all LVs are properly activated
- lvm vgchange -ay "$vgroup"
- udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
-
- # Same as above, but now with more "stress"
- if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- iterations=10
- else
- iterations=50
- fi
-
- for ((i = 1; i <= iterations; i++)); do
- lvm vgchange -an "$vgroup"
- lvm vgchange -ay "$vgroup"
-
- if ((i % 5 == 0)); then
- udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
- fi
- done
-
- # Remove the first LV
- lvm lvremove -y "$vgroup/mypart1"
- udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1"
- udevadm wait --timeout=0 "/dev/$vgroup/mypart2"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
-
- # Create & remove LVs in a loop, i.e. with more "stress"
- if [[ -v ASAN_OPTIONS ]]; then
- iterations=8
- partitions=16
- elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
- iterations=8
- partitions=8
- else
- iterations=16
- partitions=16
- fi
-
- for ((i = 1; i <= iterations; i++)); do
- # 1) Create some logical volumes
- for ((part = 0; part < partitions; part++)); do
- lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part"
- done
-
- # 2) Immediately remove them
- lvm lvremove -y $(seq -f "$vgroup/looppart%g" 0 "$((partitions - 1))")
-
- # 3) On every 4th iteration settle udev and check if all partitions are
- # indeed gone, and if all symlinks are still valid
- if ((i % 4 == 0)); then
- for ((part = 0; part < partitions; part++)); do
- udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/looppart$part"
- done
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
- fi
- done
-}
-
-testcase_btrfs_basic() {
- local dev_stub i label mpoint uuid
- local devices=(
- /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3}
- )
-
- ls -l "${devices[@]}"
-
- echo "Single device: default settings"
- uuid="deadbeef-dead-dead-beef-000000000000"
- label="btrfs_root"
- udevadm lock --device="${devices[0]}" mkfs.btrfs -f -L "$label" -U "$uuid" "${devices[0]}"
- udevadm wait --settle --timeout=30 "${devices[0]}" "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
- btrfs filesystem show
- helper_check_device_symlinks
- helper_check_device_units
-
- echo "Multiple devices: using partitions, data: single, metadata: raid1"
- uuid="deadbeef-dead-dead-beef-000000000001"
- label="btrfs_mpart"
- udevadm lock --device="${devices[0]}" sfdisk --wipe=always "${devices[0]}" <<EOF
-label: gpt
-
-name="diskpart1", size=85M
-name="diskpart2", size=85M
-name="diskpart3", size=85M
-name="diskpart4", size=85M
-EOF
- udevadm wait --settle --timeout=30 /dev/disk/by-partlabel/diskpart{1..4}
- udevadm lock --device="${devices[0]}" mkfs.btrfs -f -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4}
- udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
- btrfs filesystem show
- helper_check_device_symlinks
- helper_check_device_units
- wipefs -a -f "${devices[0]}"
- udevadm wait --settle --timeout=30 --removed /dev/disk/by-partlabel/diskpart{1..4}
-
- echo "Multiple devices: using disks, data: raid10, metadata: raid10, mixed mode"
- uuid="deadbeef-dead-dead-beef-000000000002"
- label="btrfs_mdisk"
- udevadm lock \
- --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs0 \
- --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs1 \
- --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs2 \
- --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs3 \
- mkfs.btrfs -f -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}"
- udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
- btrfs filesystem show
- helper_check_device_symlinks
- helper_check_device_units
-
- echo "Multiple devices: using LUKS encrypted disks, data: raid1, metadata: raid1, mixed mode"
- uuid="deadbeef-dead-dead-beef-000000000003"
- label="btrfs_mencdisk"
- mpoint="/btrfs_enc$RANDOM"
- mkdir "$mpoint"
- # Create a key-file
- dd if=/dev/urandom of=/etc/btrfs_keyfile bs=64 count=1 iflag=fullblock
- chmod 0600 /etc/btrfs_keyfile
- # Encrypt each device and add it to /etc/crypttab, so it can be mounted
- # automagically later
- : >/etc/crypttab
- for ((i = 0; i < ${#devices[@]}; i++)); do
- # Intentionally use weaker cipher-related settings, since we don't care
- # about security here as it's a throwaway LUKS partition
- cryptsetup luksFormat -q \
- --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
- --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile
- udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i" "/dev/disk/by-label/encdisk$i"
- # Add the device into /etc/crypttab, reload systemd, and then activate
- # the device so we can create a filesystem on it later
- echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks" >>/etc/crypttab
- systemctl daemon-reload
- systemctl start "systemd-cryptsetup@encbtrfs$i"
- done
- helper_check_device_symlinks
- helper_check_device_units
- # Check if we have all necessary DM devices
- ls -l /dev/mapper/encbtrfs{0..3}
- # Create a multi-device btrfs filesystem on the LUKS devices
- udevadm lock \
- --device=/dev/mapper/encbtrfs0 \
- --device=/dev/mapper/encbtrfs1 \
- --device=/dev/mapper/encbtrfs2 \
- --device=/dev/mapper/encbtrfs3 \
- mkfs.btrfs -f -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3}
- udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
- btrfs filesystem show
- helper_check_device_symlinks
- helper_check_device_units
- # Mount it and write some data to it we can compare later
- mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint"
- echo "hello there" >"$mpoint/test"
- # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise)
- umount "$mpoint"
- systemctl stop systemd-cryptsetup@encbtrfs{0..3}
- udevadm wait --settle --timeout=30 --removed "/dev/disk/by-uuid/$uuid"
- helper_check_device_symlinks
- helper_check_device_units
- # Add the mount point to /etc/fstab and check if the device can be put together
- # automagically. The source device is the DM name of the first LUKS device
- # (from /etc/crypttab). We have to specify all LUKS devices manually, as
- # registering the necessary devices is usually initrd's job (via btrfs device scan)
- dev_stub="/dev/mapper/encbtrfs"
- echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab
- # Tell systemd about the new mount
- systemctl daemon-reload
- # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab
- systemctl restart cryptsetup.target
- # Start the corresponding mount unit and check if the btrfs device was reconstructed
- # correctly
- systemctl start "${mpoint##*/}.mount"
- udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
- btrfs filesystem show
- helper_check_device_symlinks
- helper_check_device_units
- grep "hello there" "$mpoint/test"
- # Cleanup
- systemctl stop "${mpoint##*/}.mount"
- systemctl stop systemd-cryptsetup@encbtrfs{0..3}
- sed -i "/${mpoint##*/}/d" /etc/fstab
- : >/etc/crypttab
- rm -fr "$mpoint"
- systemctl daemon-reload
- udevadm settle
-}
-
-testcase_iscsi_lvm() {
- local dev i label link lun_id mpoint target_name uuid
- local target_ip="127.0.0.1"
- local target_port="3260"
- local vgroup="iscsi_lvm$RANDOM"
- local expected_symlinks=()
- local devices=(
- /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3}
- )
-
- ls -l "${devices[@]}"
-
- # Start the target daemon
- systemctl start tgtd
- systemctl status tgtd
-
- echo "iSCSI LUNs backed by devices"
- # See RFC3721 and RFC7143
- target_name="iqn.2021-09.com.example:iscsi.test"
- # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
- # backed by a device
- tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name"
- for ((i = 0; i < ${#devices[@]}; i++)); do
- # lun-0 is reserved by iSCSI
- lun_id="$((i + 1))"
- tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}"
- tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id"
- expected_symlinks+=(
- "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id"
- )
- done
- tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
- # Configure the iSCSI initiator
- iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
- iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- helper_check_device_symlinks
- helper_check_device_units
- # Cleanup
- iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
- tgtadm --lld iscsi --op delete --mode target --tid=1
-
- echo "iSCSI LUNs backed by files + LVM"
- # Note: we use files here to "trick" LVM the disks are indeed on a different
- # host, so it doesn't automagically detect another path to the backing
- # device once we disconnect the iSCSI devices
- target_name="iqn.2021-09.com.example:iscsi.lvm.test"
- mpoint="$(mktemp -d /iscsi_storeXXX)"
- expected_symlinks=()
- # Use the first device as it's configured with larger capacity
- mkfs.ext4 -L iscsi_store "${devices[0]}"
- udevadm wait --settle --timeout=30 "${devices[0]}"
- mount "${devices[0]}" "$mpoint"
- for i in {1..4}; do
- dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32
- done
- # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
- # backed by a file
- tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name"
- # lun-0 is reserved by iSCSI
- for i in {1..4}; do
- tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img"
- tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i"
- expected_symlinks+=(
- "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i"
- )
- done
- tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL
- # Configure the iSCSI initiator
- iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
- iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- helper_check_device_symlinks
- helper_check_device_units
- # Add all iSCSI devices into a LVM volume group, create two logical volumes,
- # and check if necessary symlinks exist (and are valid)
- lvm pvcreate -y "${expected_symlinks[@]}"
- lvm pvs
- lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}"
- lvm vgs
- lvm vgchange -ay "$vgroup"
- lvm lvcreate -y -L 4M "$vgroup" -n mypart1
- lvm lvcreate -y -L 8M "$vgroup" -n mypart2
- lvm lvs
- udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
- mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
- udevadm wait --settle --timeout=30 "/dev/disk/by-label/mylvpart1"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
- # Disconnect the iSCSI devices and check all the symlinks
- iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
- # "Reset" the DM state, since we yanked the backing storage from under the LVM,
- # so the currently active VGs/LVs are invalid
- dmsetup remove_all --deferred
- # The LVM and iSCSI related symlinks should be gone
- udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}"
- helper_check_device_symlinks "/dev/disk"
- helper_check_device_units
- # Reconnect the iSCSI devices and check if everything get detected correctly
- iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
- iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
- helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
- helper_check_device_units
- # Cleanup
- iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
- tgtadm --lld iscsi --op delete --mode target --tid=2
- umount "$mpoint"
- rm -rf "$mpoint"
-}
-
-testcase_long_sysfs_path() {
- local cursor link logfile mpoint
- local expected_symlinks=(
- "/dev/disk/by-label/data_vol"
- "/dev/disk/by-label/swap_vol"
- "/dev/disk/by-partlabel/test_swap"
- "/dev/disk/by-partlabel/test_part"
- "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
- "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
- "/dev/disk/by-uuid/deadbeef-dead-dead-beef-222222222222"
- )
-
- # Create a cursor file to skip messages generated by udevd in initrd, as it
- # might not be the same up-to-date version as we currently run (hence generating
- # messages we check for later and making the test fail)
- cursor="$(mktemp)"
- journalctl --cursor-file="${cursor:?}" -n0 -q
-
- # Make sure the test device is connected and show its "wonderful" path
- stat /sys/block/vda
- readlink -f /sys/block/vda/dev
-
- dev="/dev/vda"
- sfdisk "${dev:?}" <<EOF
-label: gpt
-
-name="test_swap", size=32M
-uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
-EOF
- udevadm settle
- mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
- mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${dev}2"
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-
- # Try to mount the data partition manually (using its label)
- mpoint="$(mktemp -d /logsysfsXXX)"
- mount LABEL=data_vol "$mpoint"
- touch "$mpoint/test"
- umount "$mpoint"
- # Do the same, but with UUID and using fstab
- echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
- systemctl daemon-reload
- mount "$mpoint"
- timeout 30 bash -c "until systemctl -q is-active '$mpoint'; do sleep .2; done"
- test -e "$mpoint/test"
- umount "$mpoint"
-
- # Test out the swap partition
- swapon -v -L swap_vol
- swapoff -v -L swap_vol
-
- udevadm settle
-
- logfile="$(mktemp)"
- # Check state of affairs after https://github.com/systemd/systemd/pull/22759
- # Note: can't use `--cursor-file` here, since we don't want to update the cursor
- # after using it
- [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]]
- [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic --grep "Unit name .*vda.?\.device\" too long, falling back to hashed unit name" | wc -l)" -gt 0 ]]
- # Check if the respective "hashed" units exist and are active (plugged)
- systemctl status --no-pager "$(readlink -f /sys/block/vda/vda1)"
- systemctl status --no-pager "$(readlink -f /sys/block/vda/vda2)"
- # Make sure we don't unnecessarily spam the log
- { journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile"
- [[ "$(wc -l <"$logfile")" -lt 10 ]]
-
- : >/etc/fstab
- rm -fr "${cursor:?}" "${logfile:?}" "${mpoint:?}"
-}
-
-testcase_mdadm_basic() {
- local i part_name raid_name raid_dev uuid
- local expected_symlinks=()
- local devices=(
- /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..4}
- )
-
- ls -l "${devices[@]}"
-
- echo "Mirror raid (RAID 1)"
- raid_name="mdmirror"
- raid_dev="/dev/md/$raid_name"
- part_name="${raid_name}_part"
- uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001"
- expected_symlinks=(
- "$raid_dev"
- "/dev/disk/by-id/md-name-H:$raid_name"
- "/dev/disk/by-id/md-uuid-$uuid"
- "/dev/disk/by-label/$part_name" # ext4 partition
- )
- # Create a simple RAID 1 with an ext4 filesystem
- echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2
- udevadm wait --settle --timeout=30 "$raid_dev"
- mkfs.ext4 -L "$part_name" "$raid_dev"
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- for i in {0..9}; do
- echo "Disassemble - reassemble loop, iteration #$i"
- mdadm -v --stop "$raid_dev"
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- mdadm --assemble "$raid_dev" --name "$raid_name" -v
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- done
- helper_check_device_symlinks
- helper_check_device_units
- # Cleanup
- mdadm -v --stop "$raid_dev"
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-
- echo "Parity raid (RAID 5)"
- raid_name="mdparity"
- raid_dev="/dev/md/$raid_name"
- part_name="${raid_name}_part"
- uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000101"
- expected_symlinks=(
- "$raid_dev"
- "/dev/disk/by-id/md-name-H:$raid_name"
- "/dev/disk/by-id/md-uuid-$uuid"
- "/dev/disk/by-label/$part_name" # ext4 partition
- )
- # Create a simple RAID 5 with an ext4 filesystem
- echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..2} -v -f --level=5 --raid-devices=3
- udevadm wait --settle --timeout=30 "$raid_dev"
- mkfs.ext4 -L "$part_name" "$raid_dev"
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- for i in {0..9}; do
- echo "Disassemble - reassemble loop, iteration #$i"
- mdadm -v --stop "$raid_dev"
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- mdadm --assemble "$raid_dev" --name "$raid_name" -v
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- done
- helper_check_device_symlinks
- helper_check_device_units
- # Cleanup
- mdadm -v --stop "$raid_dev"
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- helper_check_device_units
-
- echo "Mirror + parity raid (RAID 10) + multiple partitions"
- raid_name="mdmirpar"
- raid_dev="/dev/md/$raid_name"
- part_name="${raid_name}_part"
- uuid="aaaaaaaa:bbbbbbbb:cccccccc:00001010"
- expected_symlinks=(
- "$raid_dev"
- "/dev/disk/by-id/md-name-H:$raid_name"
- "/dev/disk/by-id/md-uuid-$uuid"
- "/dev/disk/by-label/$part_name" # ext4 partition
- # Partitions
- "${raid_dev}1"
- "${raid_dev}2"
- "${raid_dev}3"
- "/dev/disk/by-id/md-name-H:$raid_name-part1"
- "/dev/disk/by-id/md-name-H:$raid_name-part2"
- "/dev/disk/by-id/md-name-H:$raid_name-part3"
- "/dev/disk/by-id/md-uuid-$uuid-part1"
- "/dev/disk/by-id/md-uuid-$uuid-part2"
- "/dev/disk/by-id/md-uuid-$uuid-part3"
- )
- # Create a simple RAID 10 with an ext4 filesystem
- echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..3} -v -f --level=10 --raid-devices=4
- udevadm wait --settle --timeout=30 "$raid_dev"
- # Partition the raid device
- # Here, 'udevadm lock' is meaningless, as udevd does not lock MD devices.
- sfdisk --wipe=always "$raid_dev" <<EOF
-label: gpt
-
-uuid="deadbeef-dead-dead-beef-111111111111", name="mdpart1", size=8M
-uuid="deadbeef-dead-dead-beef-222222222222", name="mdpart2", size=32M
-uuid="deadbeef-dead-dead-beef-333333333333", name="mdpart3", size=16M
-EOF
- udevadm wait --settle --timeout=30 "/dev/disk/by-id/md-uuid-$uuid-part2"
- mkfs.ext4 -L "$part_name" "/dev/disk/by-id/md-uuid-$uuid-part2"
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- for i in {0..9}; do
- echo "Disassemble - reassemble loop, iteration #$i"
- mdadm -v --stop "$raid_dev"
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- mdadm --assemble "$raid_dev" --name "$raid_name" -v
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- done
- helper_check_device_symlinks
- helper_check_device_units
- # Cleanup
- mdadm -v --stop "$raid_dev"
- # Check if all expected symlinks were removed after the cleanup
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- helper_check_device_units
-}
-
-testcase_mdadm_lvm() {
- local part_name raid_name raid_dev uuid vgroup
- local expected_symlinks=()
- local devices=(
- /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..4}
- )
-
- ls -l "${devices[@]}"
-
- raid_name="mdlvm"
- raid_dev="/dev/md/$raid_name"
- part_name="${raid_name}_part"
- vgroup="${raid_name}_vg"
- uuid="aaaaaaaa:bbbbbbbb:ffffffff:00001010"
- expected_symlinks=(
- "$raid_dev"
- "/dev/$vgroup/mypart1" # LVM partition
- "/dev/$vgroup/mypart2" # LVM partition
- "/dev/disk/by-id/md-name-H:$raid_name"
- "/dev/disk/by-id/md-uuid-$uuid"
- "/dev/disk/by-label/$part_name" # ext4 partition
- )
- # Create a RAID 10 with LVM + ext4
- echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..3} -v -f --level=10 --raid-devices=4
- udevadm wait --settle --timeout=30 "$raid_dev"
- # Create an LVM on the MD
- lvm pvcreate -y "$raid_dev"
- lvm pvs
- lvm vgcreate "$vgroup" -y "$raid_dev"
- lvm vgs
- lvm vgchange -ay "$vgroup"
- lvm lvcreate -y -L 4M "$vgroup" -n mypart1
- lvm lvcreate -y -L 8M "$vgroup" -n mypart2
- lvm lvs
- udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
- mkfs.ext4 -L "$part_name" "/dev/$vgroup/mypart2"
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- # Disassemble the array
- lvm vgchange -an "$vgroup"
- mdadm -v --stop "$raid_dev"
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- helper_check_device_symlinks
- helper_check_device_units
- # Reassemble it and check if all required symlinks exist
- mdadm --assemble "$raid_dev" --name "$raid_name" -v
- udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
- helper_check_device_symlinks
- helper_check_device_units
- # Cleanup
- lvm vgchange -an "$vgroup"
- mdadm -v --stop "$raid_dev"
- # Check if all expected symlinks were removed after the cleanup
- udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
- helper_check_device_units
-}
-
-udevadm settle
-udevadm control --log-level debug
-lsblk -a
-
-echo "Check if all symlinks under /dev/disk/ are valid (pre-test)"
-helper_check_device_symlinks
-
-# TEST_FUNCTION_NAME is passed on the kernel command line via systemd.setenv=
-# in the respective test.sh file
-if ! command -v "${TEST_FUNCTION_NAME:?}"; then
- echo >&2 "Missing verification handler for test case '$TEST_FUNCTION_NAME'"
- exit 1
-fi
-
-echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME"
-"$TEST_FUNCTION_NAME"
-udevadm settle
-
-echo "Check if all symlinks under /dev/disk/ are valid (post-test)"
-helper_check_device_symlinks
-
-udevadm control --log-level info
-
-systemctl status systemd-udevd
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-65-ANALYZE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemctl log-level debug
-export SYSTEMD_LOG_LEVEL=debug
-
-# Sanity checks
-#
-# We can't really test time, critical-chain and plot verbs here, as
-# the testsuite service is a part of the boot transaction, so let's assume
-# they fail
-systemd-analyze || :
-systemd-analyze time || :
-systemd-analyze critical-chain || :
-# blame
-systemd-analyze blame
-systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame
-(! systemd-analyze blame --global)
-# plot
-systemd-analyze plot >/dev/null || :
-systemd-analyze plot --json=pretty >/dev/null || :
-systemd-analyze plot --json=short >/dev/null || :
-systemd-analyze plot --json=off >/dev/null || :
-systemd-analyze plot --json=pretty --no-legend >/dev/null || :
-systemd-analyze plot --json=short --no-legend >/dev/null || :
-systemd-analyze plot --json=off --no-legend >/dev/null || :
-systemd-analyze plot --table >/dev/null || :
-systemd-analyze plot --table --no-legend >/dev/null || :
-(! systemd-analyze plot --global)
-# legacy/deprecated options (moved to systemctl, but still usable from analyze)
-systemd-analyze log-level
-systemd-analyze log-level "$(systemctl log-level)"
-systemd-analyze get-log-level
-systemd-analyze set-log-level "$(systemctl log-level)"
-systemd-analyze log-target
-systemd-analyze log-target "$(systemctl log-target)"
-systemd-analyze get-log-target
-systemd-analyze set-log-target "$(systemctl log-target)"
-systemd-analyze service-watchdogs
-systemd-analyze service-watchdogs "$(systemctl service-watchdogs)"
-# dot
-systemd-analyze dot >/dev/null
-systemd-analyze dot systemd-journald.service >/dev/null
-systemd-analyze dot systemd-journald.service systemd-logind.service >/dev/null
-systemd-analyze dot --from-pattern="*" --from-pattern="*.service" systemd-journald.service >/dev/null
-systemd-analyze dot --to-pattern="*" --to-pattern="*.service" systemd-journald.service >/dev/null
-systemd-analyze dot --from-pattern="*.service" --to-pattern="*.service" systemd-journald.service >/dev/null
-systemd-analyze dot --order systemd-journald.service systemd-logind.service >/dev/null
-systemd-analyze dot --require systemd-journald.service systemd-logind.service >/dev/null
-systemd-analyze dot "systemd-*.service" >/dev/null
-(! systemd-analyze dot systemd-journald.service systemd-logind.service "*" bbb ccc)
-(! systemd-analyze dot --global systemd-journald.service)
-# dump
-# this should be rate limited to 10 calls in 10 minutes for unprivileged callers
-for _ in {1..10}; do
- runas testuser systemd-analyze dump systemd-journald.service >/dev/null
-done
-(! runas testuser systemd-analyze dump >/dev/null)
-# still limited after a reload
-systemctl daemon-reload
-(! runas testuser systemd-analyze dump >/dev/null)
-# and a re-exec
-systemctl daemon-reexec
-(! runas testuser systemd-analyze dump >/dev/null)
-# privileged call, so should not be rate limited
-for _ in {1..10}; do
- systemd-analyze dump systemd-journald.service >/dev/null
-done
-systemd-analyze dump >/dev/null
-systemd-analyze dump "*" >/dev/null
-systemd-analyze dump "*.socket" >/dev/null
-systemd-analyze dump "*.socket" "*.service" aaaaaaa ... >/dev/null
-systemd-analyze dump systemd-journald.service >/dev/null
-(! systemd-analyze dump "")
-(! systemd-analyze dump --global systemd-journald.service)
-# malloc
-systemd-analyze malloc >/dev/null
-(! systemd-analyze malloc --global)
-# unit-files
-systemd-analyze unit-files >/dev/null
-systemd-analyze unit-files systemd-journald.service >/dev/null
-systemd-analyze unit-files "*" >/dev/null
-systemd-analyze unit-files "*" aaaaaa "*.service" "*.target" >/dev/null
-systemd-analyze unit-files --user >/dev/null
-systemd-analyze unit-files --user "*" aaaaaa "*.service" "*.target" >/dev/null
-(! systemd-analyze unit-files --global)
-# unit-paths
-systemd-analyze unit-paths
-systemd-analyze unit-paths --user
-systemd-analyze unit-paths --global
-# exist-status
-systemd-analyze exit-status
-systemd-analyze exit-status STDOUT BPF
-systemd-analyze exit-status 0 1 {63..65}
-(! systemd-analyze exit-status STDOUT BPF "hello*")
-(! systemd-analyze exit-status --global)
-# capability
-systemd-analyze capability
-systemd-analyze capability cap_chown CAP_KILL
-systemd-analyze capability 0 1 {30..32}
-(! systemd-analyze capability cap_chown CAP_KILL "hello*")
-(! systemd-analyze capability --global)
-# condition
-mkdir -p /run/systemd/system
-UNIT_NAME="analyze-condition-$RANDOM.service"
-cat >"/run/systemd/system/$UNIT_NAME" <<EOF
-[Unit]
-AssertPathExists=/etc/os-release
-AssertEnvironment=!FOOBAR
-ConditionKernelVersion=>1.0
-ConditionPathExists=/etc/os-release
-
-[Service]
-ExecStart=true
-EOF
-systemctl daemon-reload
-systemd-analyze condition --unit="$UNIT_NAME"
-systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
- 'ConditionKernelVersion = >=3.1' \
- 'ConditionACPower=|false' \
- 'ConditionArchitecture=|!arm' \
- 'AssertPathExists=/etc/os-release'
-(! systemd-analyze condition 'ConditionArchitecture=|!arm' 'AssertXYZ=foo')
-(! systemd-analyze condition 'ConditionKernelVersion=<1.0')
-(! systemd-analyze condition 'AssertKernelVersion=<1.0')
-(! systemd-analyze condition --global 'ConditionKernelVersion = ! <4.0')
-# syscall-filter
-systemd-analyze syscall-filter >/dev/null
-systemd-analyze syscall-filter @chown @sync
-systemd-analyze syscall-filter @sync @sync @sync
-(! systemd-analyze syscall-filter @chown @sync @foobar)
-(! systemd-analyze syscall-filter --global)
-# filesystems (requires libbpf support)
-if systemctl --version | grep "+BPF_FRAMEWORK"; then
- systemd-analyze filesystems >/dev/null
- systemd-analyze filesystems @basic-api
- systemd-analyze filesystems @basic-api @basic-api @basic-api
- (! systemd-analyze filesystems @basic-api @basic-api @foobar @basic-api)
- (! systemd-analyze filesystems --global @basic-api)
-fi
-# calendar
-systemd-analyze calendar '*-2-29 0:0:0'
-systemd-analyze calendar --iterations=5 '*-2-29 0:0:0'
-systemd-analyze calendar '*-* *:*:*'
-systemd-analyze calendar --iterations=5 '*-* *:*:*'
-systemd-analyze calendar --iterations=50 '*-* *:*:*'
-systemd-analyze calendar --iterations=0 '*-* *:*:*'
-systemd-analyze calendar --iterations=5 '01-01-22 01:00:00'
-systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*'
-(! systemd-analyze calendar --iterations=0 '*-* 99:*:*')
-(! systemd-analyze calendar --base-time=never '*-* *:*:*')
-(! systemd-analyze calendar 1)
-(! systemd-analyze calendar "")
-(! systemd-analyze calendar --global '*-2-29 0:0:0')
-# timestamp
-systemd-analyze timestamp now
-systemd-analyze timestamp -- -1
-systemd-analyze timestamp yesterday now tomorrow
-(! systemd-analyze timestamp yesterday never tomorrow)
-(! systemd-analyze timestamp 1)
-(! systemd-analyze timestamp '*-2-29 0:0:0')
-(! systemd-analyze timestamp "")
-(! systemd-analyze timestamp --global now)
-# timespan
-systemd-analyze timespan 1
-systemd-analyze timespan 1s 300s '1year 0.000001s'
-(! systemd-analyze timespan 1s 300s aaaaaa '1year 0.000001s')
-(! systemd-analyze timespan -- -1)
-(! systemd-analyze timespan '*-2-29 0:0:0')
-(! systemd-analyze timespan "")
-(! systemd-analyze timespan --global 1)
-# cat-config
-systemd-analyze cat-config systemd/system.conf >/dev/null
-systemd-analyze cat-config /etc/systemd/system.conf >/dev/null
-systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null
-systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
-systemd-analyze cat-config foo/bar
-systemd-analyze cat-config --tldr systemd/system.conf >/dev/null
-systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null
-systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null
-systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null
-systemd-analyze cat-config --tldr foo/bar
-(! systemd-analyze cat-config --global systemd/system.conf)
-# security
-systemd-analyze security
-systemd-analyze security --json=off
-systemd-analyze security --json=pretty | jq
-systemd-analyze security --json=short | jq
-(! systemd-analyze security --global)
-
-if [[ ! -v ASAN_OPTIONS ]]; then
- # check that systemd-analyze cat-config paths work in a chroot
- mkdir -p /tmp/root
- mount --bind / /tmp/root
- if mountpoint -q /usr; then
- mount --bind /usr /tmp/root/usr
- fi
- systemd-analyze cat-config systemd/system-preset >/tmp/out1
- chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
- diff /tmp/out{1,2}
-fi
-
-# verify
-mkdir -p /tmp/img/usr/lib/systemd/system/
-mkdir -p /tmp/img/opt/
-
-touch /tmp/img/opt/script0.sh
-chmod +x /tmp/img/opt/script0.sh
-
-cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
-[Service]
-ExecStart = /opt/script0.sh
-EOF
-
-set +e
-# Default behaviour is to recurse through all dependencies when unit is loaded
-(! systemd-analyze verify --root=/tmp/img/ testfile.service)
-
-# As above, recurses through all dependencies when unit is loaded
-(! systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service)
-
-# Recurses through unit file and its direct dependencies when unit is loaded
-(! systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service)
-
-set -e
-
-# zero exit status since dependencies are ignored when unit is loaded
-systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service
-
-rm /tmp/img/usr/lib/systemd/system/testfile.service
-
-cat <<EOF >/tmp/testfile.service
-[Unit]
-foo = bar
-
-[Service]
-ExecStart = echo hello
-EOF
-
-cat <<EOF >/tmp/testfile2.service
-[Unit]
-Requires = testfile.service
-
-[Service]
-ExecStart = echo hello
-EOF
-
-# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
-systemd-analyze verify --recursive-errors=no /tmp/testfile2.service
-
-set +e
-# Non-zero exit status since all associated dependencies are recursively loaded when the unit file is loaded
-(! systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service)
-set -e
-
-rm /tmp/testfile.service
-rm /tmp/testfile2.service
-
-cat <<EOF >/tmp/sample.service
-[Unit]
-Description = A Sample Service
-
-[Service]
-ExecStart = echo hello
-Slice=support.slice
-EOF
-
-# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
-systemd-analyze verify --recursive-errors=no /tmp/sample.service
-
-cat <<EOF >/tmp/testfile.service
-[Service]
-ExecStart = echo hello
-DeviceAllow=/dev/sda
-EOF
-
-# Prevent regression from #13380 and #20859 where we can't verify hidden files
-cp /tmp/testfile.service /tmp/.testfile.service
-
-systemd-analyze verify /tmp/.testfile.service
-
-rm /tmp/.testfile.service
-
-# Alias a unit file's name on disk (see #20061)
-cp /tmp/testfile.service /tmp/testsrvc
-
-(! systemd-analyze verify /tmp/testsrvc)
-
-systemd-analyze verify /tmp/testsrvc:alias.service
-
-# Zero exit status since the value used for comparison determine exposure to security threats is by default 100
-systemd-analyze security --offline=true /tmp/testfile.service
-
-#The overall exposure level assigned to the unit is greater than the set threshold
-(! systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service)
-
-# Ensure we print the list of ACLs, see https://github.com/systemd/systemd/issues/23185
-systemd-analyze security --offline=true /tmp/testfile.service | grep -q -F "/dev/sda"
-
-rm /tmp/testfile.service
-
-cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
-[Service]
-ExecStart = echo hello
-PrivateNetwork = yes
-PrivateDevices = yes
-PrivateUsers = yes
-EOF
-
-# The new overall exposure level assigned to the unit is less than the set thresholds
-# Verifies that the --offline= option works with --root=
-systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service
-
-cat <<EOF >/tmp/foo@.service
-[Service]
-ExecStart=ls
-EOF
-
-cat <<EOF >/tmp/hoge@test.service
-[Service]
-ExecStart=ls
-EOF
-
-# issue #30357
-pushd /tmp
-systemd-analyze verify foo@bar.service
-systemd-analyze verify foo@.service
-systemd-analyze verify hoge@test.service
-(! systemd-analyze verify hoge@nonexist.service)
-(! systemd-analyze verify hoge@.service)
-popd
-pushd /
-systemd-analyze verify tmp/foo@bar.service
-systemd-analyze verify tmp/foo@.service
-systemd-analyze verify tmp/hoge@test.service
-(! systemd-analyze verify tmp/hoge@nonexist.service)
-(! systemd-analyze verify tmp/hoge@.service)
-popd
-pushd /usr
-systemd-analyze verify ../tmp/foo@bar.service
-systemd-analyze verify ../tmp/foo@.service
-systemd-analyze verify ../tmp/hoge@test.service
-(! systemd-analyze verify ../tmp/hoge@nonexist.service)
-(! systemd-analyze verify ../tmp/hoge@.service)
-popd
-systemd-analyze verify /tmp/foo@bar.service
-systemd-analyze verify /tmp/foo@.service
-systemd-analyze verify /tmp/hoge@test.service
-(! systemd-analyze verify /tmp/hoge@nonexist.service)
-(! systemd-analyze verify /tmp/hoge@.service)
-
-# test that all commands are verified.
-cat <<EOF >/tmp/multi-exec-start.service
-[Service]
-Type=oneshot
-ExecStart=true
-ExecStart=ls
-EOF
-systemd-analyze verify /tmp/multi-exec-start.service
-echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service
-(! systemd-analyze verify /tmp/multi-exec-start.service)
-
-# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
-# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
-# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
-# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is
-# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure
-# level for the unit file should decrease to account for that increased weight.
-cat <<EOF >/tmp/testfile.json
-{"UserOrDynamicUser":
- {"description_bad": "Service runs as root user",
- "weight": 0,
- "range": 10
- },
-"SupplementaryGroups":
- {"description_good": "Service has no supplementary groups",
- "description_bad": "Service runs with supplementary groups",
- "description_na": "Service runs as root, option does not matter",
- "weight": 200,
- "range": 1
- },
-"PrivateDevices":
- {"description_good": "Service has no access to hardware devices",
- "description_bad": "Service potentially has access to hardware devices",
- "weight": 1000,
- "range": 1
- },
-"PrivateMounts":
- {"description_good": "Service cannot install system mounts",
- "description_bad": "Service may install system mounts",
- "weight": 1000,
- "range": 1
- },
-"PrivateNetwork":
- {"description_good": "Service doesn't have access to the host's network",
- "description_bad": "Service has access to the host's network",
- "weight": 6000,
- "range": 1
- },
-"PrivateTmp":
- {"description_good": "Service has no access to other software's temporary files",
- "description_bad": "Service has access to other software's temporary files",
- "weight": 1000,
- "range": 1
- },
-"PrivateUsers":
- {"description_good": "Service does not have access to other users",
- "description_bad": "Service has access to other users",
- "weight": 1000,
- "range": 1
- },
-"ProtectControlGroups":
- {"description_good": "Service cannot modify the control group file system",
- "description_bad": "Service may modify the control group file system",
- "weight": 1000,
- "range": 1
- },
-"ProtectKernelModules":
- {"description_good": "Service cannot load or read kernel modules",
- "description_bad": "Service may load or read kernel modules",
- "weight": 1000,
- "range": 1
- },
-"ProtectKernelTunables":
- {"description_good": "Service cannot alter kernel tunables (/proc/sys, …)",
- "description_bad": "Service may alter kernel tunables",
- "weight": 1000,
- "range": 1
- },
-"ProtectKernelLogs":
- {"description_good": "Service cannot read from or write to the kernel log ring buffer",
- "description_bad": "Service may read from or write to the kernel log ring buffer",
- "weight": 1000,
- "range": 1
- },
-"ProtectClock":
- {"description_good": "Service cannot write to the hardware clock or system clock",
- "description_bad": "Service may write to the hardware clock or system clock",
- "weight": 1000,
- "range": 1
- },
-"ProtectHome":
- {"weight": 1000,
- "range": 10
- },
-"ProtectHostname":
- {"description_good": "Service cannot change system host/domainname",
- "description_bad": "Service may change system host/domainname",
- "weight": 50,
- "range": 1
- },
-"ProtectSystem":
- {"weight": 1000,
- "range": 10
- },
-"RootDirectoryOrRootImage":
- {"description_good": "Service has its own root directory/image",
- "description_bad": "Service runs within the host's root directory",
- "weight": 200,
- "range": 1
- },
-"LockPersonality":
- {"description_good": "Service cannot change ABI personality",
- "description_bad": "Service may change ABI personality",
- "weight": 100,
- "range": 1
- },
-"MemoryDenyWriteExecute":
- {"description_good": "Service cannot create writable executable memory mappings",
- "description_bad": "Service may create writable executable memory mappings",
- "weight": 100,
- "range": 1
- },
-"NoNewPrivileges":
- {"description_good": "Service processes cannot acquire new privileges",
- "description_bad": "Service processes may acquire new privileges",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_ADMIN":
- {"description_good": "Service has no administrator privileges",
- "description_bad": "Service has administrator privileges",
- "weight": 1500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP":
- {"description_good": "Service cannot change UID/GID identities/capabilities",
- "description_bad": "Service may change UID/GID identities/capabilities",
- "weight": 1500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_PTRACE":
- {"description_good": "Service has no ptrace() debugging abilities",
- "description_bad": "Service has ptrace() debugging abilities",
- "weight": 1500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_TIME":
- {"description_good": "Service processes cannot change the system clock",
- "description_bad": "Service processes may change the system clock",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_NET_ADMIN":
- {"description_good": "Service has no network configuration privileges",
- "description_bad": "Service has network configuration privileges",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_RAWIO":
- {"description_good": "Service has no raw I/O access",
- "description_bad": "Service has raw I/O access",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_MODULE":
- {"description_good": "Service cannot load kernel modules",
- "description_bad": "Service may load kernel modules",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_AUDIT":
- {"description_good": "Service has no audit subsystem access",
- "description_bad": "Service has audit subsystem access",
- "weight": 500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYSLOG":
- {"description_good": "Service has no access to kernel logging",
- "description_bad": "Service has access to kernel logging",
- "weight": 500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE":
- {"description_good": "Service has no privileges to change resource use parameters",
- "description_bad": "Service has privileges to change resource use parameters",
- "weight": 500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_MKNOD":
- {"description_good": "Service cannot create device nodes",
- "description_bad": "Service may create device nodes",
- "weight": 500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP":
- {"description_good": "Service cannot change file ownership/access mode/capabilities",
- "description_bad": "Service may change file ownership/access mode/capabilities unrestricted",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER":
- {"description_good": "Service cannot override UNIX file/IPC permission checks",
- "description_bad": "Service may override UNIX file/IPC permission checks",
- "weight": 1000,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_KILL":
- {"description_good": "Service cannot send UNIX signals to arbitrary processes",
- "description_bad": "Service may send UNIX signals to arbitrary processes",
- "weight": 500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW":
- {"description_good": "Service has no elevated networking privileges",
- "description_bad": "Service has elevated networking privileges",
- "weight": 500,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_BOOT":
- {"description_good": "Service cannot issue reboot()",
- "description_bad": "Service may issue reboot()",
- "weight": 100,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_MAC":
- {"description_good": "Service cannot adjust SMACK MAC",
- "description_bad": "Service may adjust SMACK MAC",
- "weight": 100,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE":
- {"description_good": "Service cannot mark files immutable",
- "description_bad": "Service may mark files immutable",
- "weight": 75,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_IPC_LOCK":
- {"description_good": "Service cannot lock memory into RAM",
- "description_bad": "Service may lock memory into RAM",
- "weight": 50,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_CHROOT":
- {"description_good": "Service cannot issue chroot()",
- "description_bad": "Service may issue chroot()",
- "weight": 50,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_BLOCK_SUSPEND":
- {"description_good": "Service cannot establish wake locks",
- "description_bad": "Service may establish wake locks",
- "weight": 25,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_WAKE_ALARM":
- {"description_good": "Service cannot program timers that wake up the system",
- "description_bad": "Service may program timers that wake up the system",
- "weight": 25,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_LEASE":
- {"description_good": "Service cannot create file leases",
- "description_bad": "Service may create file leases",
- "weight": 25,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG":
- {"description_good": "Service cannot issue vhangup()",
- "description_bad": "Service may issue vhangup()",
- "weight": 25,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_SYS_PACCT":
- {"description_good": "Service cannot use acct()",
- "description_bad": "Service may use acct()",
- "weight": 25,
- "range": 1
- },
-"CapabilityBoundingSet_CAP_BPF":
- {"description_good": "Service may load BPF programs",
- "description_bad": "Service may not load BPF programs",
- "weight": 25,
- "range": 1
- },
-"UMask":
- {"weight": 100,
- "range": 10
- },
-"KeyringMode":
- {"description_good": "Service doesn't share key material with other services",
- "description_bad": "Service shares key material with other service",
- "weight": 1000,
- "range": 1
- },
-"ProtectProc":
- {"description_good": "Service has restricted access to process tree(/proc hidepid=)",
- "description_bad": "Service has full access to process tree(/proc hidepid=)",
- "weight": 1000,
- "range": 3
- },
-"ProcSubset":
- {"description_good": "Service has no access to non-process/proc files(/proc subset=)",
- "description_bad": "Service has full access to non-process/proc files(/proc subset=)",
- "weight": 10,
- "range": 1
- },
-"NotifyAccess":
- {"description_good": "Service child processes cannot alter service state",
- "description_bad": "Service child processes may alter service state",
- "weight": 1000,
- "range": 1
- },
-"RemoveIPC":
- {"description_good": "Service user cannot leave SysV IPC objects around",
- "description_bad": "Service user may leave SysV IPC objects around",
- "description_na": "Service runs as root, option does not apply",
- "weight": 100,
- "range": 1
- },
-"Delegate":
- {"description_good": "Service does not maintain its own delegated control group subtree",
- "description_bad": "Service maintains its own delegated control group subtree",
- "weight": 100,
- "range": 1
- },
-"RestrictRealtime":
- {"description_good": "Service realtime scheduling access is restricted",
- "description_bad": "Service may acquire realtime scheduling",
- "weight": 500,
- "range": 1
- },
-"RestrictSUIDSGID":
- {"description_good": "SUID/SGIDfilecreationbyserviceisrestricted",
- "description_bad": "ServicemaycreateSUID/SGIDfiles",
- "weight": 1000,
- "range": 1
- },
-"RestrictNamespaces_user":
- {"description_good": "Servicecannotcreateusernamespaces",
- "description_bad": "Servicemaycreateusernamespaces",
- "weight": 1500,
- "range": 1
- },
-"RestrictNamespaces_mnt":
- {"description_good": "Service cannot create file system namespaces",
- "description_bad": "Service may create file system namespaces",
- "weight": 500,
- "range": 1
- },
-"RestrictNamespaces_ipc":
- {"description_good": "Service cannot create IPC namespaces",
- "description_bad": "Service may create IPC namespaces",
- "weight": 500,
- "range": 1
- },
-"RestrictNamespaces_pid":
- {"description_good": "Service cannot create process namespaces",
- "description_bad": "Service may create process namespaces",
- "weight": 500,
- "range": 1
- },
-"RestrictNamespaces_cgroup":
- {"description_good": "Service cannot create cgroup namespaces",
- "description_bad": "Service may create cgroup namespaces",
- "weight": 500,
- "range": 1
- },
-"RestrictNamespaces_net":
- {"description_good": "Service cannot create network namespaces",
- "description_bad": "Service may create network namespaces",
- "weight": 500,
- "range": 1
- },
-"RestrictNamespaces_uts":
- {"description_good": "Service cannot create hostname namespaces",
- "description_bad": "Service may create hostname namespaces",
- "weight": 100,
- "range": 1
- },
-"RestrictAddressFamilies_AF_INET_INET6":
- {"description_good": "Service cannot allocate Internet sockets",
- "description_bad": "Service may allocate Internet sockets",
- "weight": 1500,
- "range": 1
- },
-"RestrictAddressFamilies_AF_UNIX":
- {"description_good": "Service cannot allocate local sockets",
- "description_bad": "Service may allocate local sockets",
- "weight": 25,
- "range": 1
- },
-"RestrictAddressFamilies_AF_NETLINK":
- {"description_good": "Service cannot allocate netlink sockets",
- "description_bad": "Service may allocate netlink sockets",
- "weight": 200,
- "range": 1
- },
-"RestrictAddressFamilies_AF_PACKET":
- {"description_good": "Service cannot allocate packet sockets",
- "description_bad": "Service may allocate packet sockets",
- "weight": 1000,
- "range": 1
- },
-"RestrictAddressFamilies_OTHER":
- {"description_good": "Service cannot allocate exotic sockets",
- "description_bad": "Service may allocate exotic sockets",
- "weight": 1250,
- "range": 1
- },
-"SystemCallArchitectures":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_swap":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_obsolete":
- {"weight": 250,
- "range": 10
- },
-"SystemCallFilter_clock":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_cpu_emulation":
- {"weight": 250,
- "range": 10
- },
-"SystemCallFilter_debug":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_mount":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_module":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_raw_io":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_reboot":
- {"weight": 1000,
- "range": 10
- },
-"SystemCallFilter_privileged":
- {"weight": 700,
- "range": 10
- },
-"SystemCallFilter_resources":
- {"weight": 700,
- "range": 10
- },
-"IPAddressDeny":
- {"weight": 1000,
- "range": 10
- },
-"DeviceAllow":
- {"weight": 1000,
- "range": 10
- },
-"AmbientCapabilities":
- {"description_good": "Service process does not receive ambient capabilities",
- "description_bad": "Service process receives ambient capabilities",
- "weight": 500,
- "range": 1
- },
-"INVALID_ID":
- {"weight": 1000,
- "range": 10
- }
-}
-EOF
-
-# Reads in custom security requirements from the parsed .json file and uses these for comparison
-systemd-analyze security --threshold=90 --offline=true \
- --security-policy=/tmp/testfile.json \
- --root=/tmp/img/ testfile.service
-
-# The strict profile adds a lot of sanboxing options
-systemd-analyze security --threshold=25 --offline=true \
- --security-policy=/tmp/testfile.json \
- --profile=strict \
- --root=/tmp/img/ testfile.service
-
-# The trusted profile doesn't add any sanboxing options
-(! systemd-analyze security --threshold=25 --offline=true \
- --security-policy=/tmp/testfile.json \
- --profile=/usr/lib/systemd/portable/profile/trusted/service.conf \
- --root=/tmp/img/ testfile.service)
-
-(! systemd-analyze security --threshold=50 --offline=true \
- --security-policy=/tmp/testfile.json \
- --root=/tmp/img/ testfile.service)
-
-rm /tmp/img/usr/lib/systemd/system/testfile.service
-
-if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
- systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
-fi
-
-systemd-analyze --threshold=90 security systemd-journald.service
-
-# issue 23663
-check() {(
- set +x
- output=$(systemd-analyze security --offline="${2?}" "${3?}" | grep -F 'SystemCallFilter=')
- assert_in "System call ${1?} list" "$output"
- assert_in "[+✓] SystemCallFilter=~@swap" "$output"
- assert_in "[+✓] SystemCallFilter=~@resources" "$output"
- assert_in "[+✓] SystemCallFilter=~@reboot" "$output"
- assert_in "[+✓] SystemCallFilter=~@raw-io" "$output"
- assert_in "[-✗] SystemCallFilter=~@privileged" "$output"
- assert_in "[+✓] SystemCallFilter=~@obsolete" "$output"
- assert_in "[+✓] SystemCallFilter=~@mount" "$output"
- assert_in "[+✓] SystemCallFilter=~@module" "$output"
- assert_in "[+✓] SystemCallFilter=~@debug" "$output"
- assert_in "[+✓] SystemCallFilter=~@cpu-emulation" "$output"
- assert_in "[-✗] SystemCallFilter=~@clock" "$output"
-)}
-
-export -n SYSTEMD_LOG_LEVEL
-
-mkdir -p /run/systemd/system
-cat >/run/systemd/system/allow-list.service <<EOF
-[Service]
-ExecStart=false
-SystemCallFilter=@system-service
-SystemCallFilter=~@resources:ENOANO @privileged
-SystemCallFilter=@clock
-EOF
-
-cat >/run/systemd/system/deny-list.service <<EOF
-[Service]
-ExecStart=false
-SystemCallFilter=~@known
-SystemCallFilter=@system-service
-SystemCallFilter=~@resources:ENOANO @privileged
-SystemCallFilter=@clock
-EOF
-
-systemctl daemon-reload
-
-check allow yes /run/systemd/system/allow-list.service
-check allow no allow-list.service
-check deny yes /run/systemd/system/deny-list.service
-check deny no deny-list.service
-
-output=$(systemd-run -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
-name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
-
-check allow yes /run/systemd/transient/"$name"
-check allow no "$name"
-
-output=$(systemd-run -p "SystemCallFilter=~@known" -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
-name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
-
-check deny yes /run/systemd/transient/"$name"
-check deny no "$name"
-
-# Let's also test the "image-policy" verb
-
-systemd-analyze image-policy '*' 2>&1 | grep -q -F "Long form: =verity+signed+encrypted+unprotected+unused+absent"
-systemd-analyze image-policy '-' 2>&1 | grep -q -F "Long form: =unused+absent"
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -F "Long form: usr=verity:home=encrypted:=unused+absent"
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^home \+encrypted \+'
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr \+verity \+'
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^root \+ignore \+'
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr-verity \+unprotected \+'
-
-(! systemd-analyze image-policy 'doedel')
-
-# Output is very hard to predict, but let's run it for coverage anyway
-systemd-analyze pcrs
-systemd-analyze pcrs --json=pretty
-systemd-analyze pcrs 14 7 0 ima
-
-systemd-analyze architectures
-systemd-analyze architectures --json=pretty
-systemd-analyze architectures x86
-systemd-analyze architectures x86-64
-systemd-analyze architectures native
-systemd-analyze architectures uname
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Service that uses device isolation
-
-[Service]
-DevicePolicy=strict
-DeviceAllow=/dev/null r
-StandardOutput=file:/tmp/testsuite66serviceresults
-ExecStartPre=rm -f /tmp/testsuite66serviceresults
-ExecStart=bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done"
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-66-DEVICEISOLATION
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-RESULTS_FILE=/tmp/testsuite66serviceresults
-
-systemd-analyze log-level debug
-
-systemctl start testsuite-66-deviceisolation.service
-
-sleep 5
-grep -q "Operation not permitted" "$RESULTS_FILE"
-
-systemctl daemon-reload
-systemctl daemon-reexec
-
-systemctl stop testsuite-66-deviceisolation.service
-
-grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-67-INTEGRITY
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -euxo pipefail
-
-export DM_NAME="integrity_test"
-export FULL_DM_DEV_NAME="/dev/mapper/${DM_NAME}"
-export FS_UUID="01234567-ffff-eeee-eeee-0123456789ab"
-export GEN="/var/run/systemd/generator"
-
-image_dir=""
-
-cleanup()
-{
- if [ -z "${image_dir}" ]; then
- return
- fi
-
- if [ -f "${image_dir}/image" ]; then
- if [ -e "${FULL_DM_DEV_NAME}" ]; then
- integritysetup close "${DM_NAME}"
- fi
- losetup -d "${loop}"
- fi
-
- rm -rf "${image_dir}"
-}
-
-trap cleanup EXIT
-
-build_integrity_tab()
-{
-cat <<EOF >"/etc/integritytab"
-${DM_NAME} ${loop} - integrity-algorithm=$1
-EOF
-}
-
-image_dir="$(mktemp -d -t -p / integrity.tmp.XXXXXX)"
-if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
- echo "mktemp under / failed"
- exit 1
-fi
-
-dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1
-dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1
-loop="$(losetup --show -f "${image_dir}/image")"
-
-if [[ ! -e ${loop} ]]; then
- echo "Loopback device created not found!"
- exit 1
-fi
-
-# Do one iteration with a separate data device, to test those branches
-separate_data=1
-
-for algorithm in crc32c crc32 sha1 sha256
-do
- if [ "${separate_data}" -eq 1 ]; then
- data_option="--data-device=${image_dir}/data"
- else
- data_option=""
- fi
- integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1
- integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1
- mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1
-
- # Give userspace time to handle udev events for new FS showing up ...
- udevadm settle
-
- integritysetup close "${DM_NAME}" || exit 1
-
- # create integritytab, generate units, start service
- if [ "${separate_data}" -eq 1 ]; then
- data_option=",data-device=${image_dir}/data"
- else
- data_option=""
- fi
- build_integrity_tab "${algorithm}${data_option}"
-
- # Cause the generator to re-run
- systemctl daemon-reload || exit 1
-
- # Check for existence of unit files...
- if [[ ! -e "/run/systemd/generator/systemd-integritysetup@${DM_NAME}.service" ]]; then
- echo "Service file does not exist!"
- exit 1
- fi
-
- # Make sure we are in a consistent state, e.g. not already active before we start
- systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
- systemctl start systemd-integritysetup@"${DM_NAME}".service || exit 1
- # Reset the start-limit counters, as we're going to restart the service a couple of times
- systemctl reset-failed systemd-integritysetup@"${DM_NAME}".service
-
- # Check the signature on the FS to ensure we can retrieve it and that is matches
- if [ -e "${FULL_DM_DEV_NAME}" ]; then
- # If a separate device is used for the metadata storage, then blkid will return one of the loop devices
- if [ "${separate_data}" -eq 1 ]; then
- dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')"
- else
- dev_name="${FULL_DM_DEV_NAME}"
- fi
- if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then
- echo "Failed to locate FS with matching UUID!"
- exit 1
- fi
- else
- echo "Failed to bring up integrity device!"
- exit 1
- fi
-
- systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
-
- if [ -e "${FULL_DM_DEV_NAME}" ]; then
- echo "Expecting ${FULL_DM_DEV_NAME} to not exist after stopping unit!"
- exit 1
- fi
-
- separate_data=0
-done
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-68-PROPAGATE-EXIT-STATUS
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# Wait for a service to enter a state within a timeout period, if it doesn't
-# enter the desired state within the timeout period then this function will
-# exit the test case with a non zero exit code.
-wait_on_state_or_fail() {
- service=$1
- expected_state=$2
- timeout=$3
-
- state=$(systemctl show "$service" --property=ActiveState --value)
- while [ "$state" != "$expected_state" ]; do
- if [ "$timeout" = "0" ]; then
- systemd-analyze log-level info
- exit 1
- fi
- timeout=$((timeout - 1))
- sleep 1
- state=$(systemctl show "$service" --property=ActiveState --value)
- done
-}
-
-systemd-analyze log-level debug
-
-cat >/run/systemd/system/testservice-failure-68.service <<EOF
-[Unit]
-OnFailure=testservice-failure-exit-handler-68.service
-
-[Service]
-ExecStart=sh -c "exit 1"
-EOF
-
-cat >/run/systemd/system/testservice-failure-68-template.service <<EOF
-[Unit]
-OnFailure=testservice-failure-exit-handler-68-template@%n.service
-
-[Service]
-ExecStart=sh -c "exit 1"
-EOF
-
-cat >/run/systemd/system/testservice-success-68.service <<EOF
-[Unit]
-OnSuccess=testservice-success-exit-handler-68.service
-
-[Service]
-ExecStart=sh -c "exit 0"
-EOF
-
-cat >/run/systemd/system/testservice-success-68-template.service <<EOF
-[Unit]
-OnSuccess=testservice-success-exit-handler-68-template@%n.service
-
-[Service]
-ExecStart=sh -c "exit 0"
-EOF
-
-# Script to check that when an OnSuccess= dependency fires, the correct
-# MONITOR* env variables are passed.
-cat >/tmp/check_on_success.sh <<"EOF"
-#!/bin/sh
-
-set -ex
-env | sort
-if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then
- echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'"
- exit 1
-fi
-
-if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
- echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
- exit 1
-fi
-
-if [ "$MONITOR_EXIT_STATUS" != "0" ]; then
- echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'"
- exit 1
-fi
-
-if [ -z "$MONITOR_INVOCATION_ID" ]; then
- echo "MONITOR_INVOCATION_ID unset"
- exit 1
-fi
-
-if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] &&
- [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] &&
- [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then
-
- echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'"
- exit 1
-fi
-
-exit 0
-EOF
-chmod +x /tmp/check_on_success.sh
-
-cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF
-[Service]
-ExecStartPre=/tmp/check_on_success.sh
-ExecStart=/tmp/check_on_success.sh
-EOF
-
-cp /run/systemd/system/testservice-success-exit-handler-68.service \
- /run/systemd/system/testservice-transient-success-exit-handler-68.service
-
-# Template version.
-cat >/run/systemd/system/testservice-success-exit-handler-68-template@.service <<EOF
-[Service]
-ExecStartPre=echo "triggered by %i"
-ExecStartPre=/tmp/check_on_success.sh
-ExecStart=/tmp/check_on_success.sh
-EOF
-
-# Script to check that when an OnFailure= dependency fires, the correct
-# MONITOR* env variables are passed.
-cat >/tmp/check_on_failure.sh <<"EOF"
-#!/bin/sh
-
-set -ex
-env | sort
-if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then
- echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'"
- exit 1
-fi
-
-if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
- echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
- exit 1
-fi
-
-if [ "$MONITOR_EXIT_STATUS" != "1" ]; then
- echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'"
- exit 1
-fi
-
-if [ -z "$MONITOR_INVOCATION_ID" ]; then
- echo "MONITOR_INVOCATION_ID unset"
- exit 1
-fi
-
-if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] &&
- [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] &&
- [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then
-
- echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'"
- exit 1
-fi
-
-exit 0
-EOF
-chmod +x /tmp/check_on_failure.sh
-
-
-cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF
-[Service]
-# repeat the check to make sure that values are set correctly on repeated invocations
-Type=oneshot
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
-EOF
-
-cp /run/systemd/system/testservice-failure-exit-handler-68.service \
- /run/systemd/system/testservice-transient-failure-exit-handler-68.service
-
-# Template version.
-cat >/run/systemd/system/testservice-failure-exit-handler-68-template@.service <<EOF
-[Service]
-Type=oneshot
-ExecStartPre=echo "triggered by %i"
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
-EOF
-
-systemctl daemon-reload
-
-: "-------I----------------------------------------------------"
-systemctl start testservice-failure-68.service
-wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
-
-: "-------II---------------------------------------------------"
-systemctl start testservice-success-68.service
-wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
-
-# Test some transient units since these exit very quickly.
-: "-------III--------------------------------------------------"
-systemd-run --unit=testservice-transient-success-68 \
- --property=OnSuccess=testservice-transient-success-exit-handler-68.service \
- sh -c "exit 0"
-wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
-
-: "-------IIII-------------------------------------------------"
-systemd-run --unit=testservice-transient-failure-68 \
- --property=OnFailure=testservice-transient-failure-exit-handler-68.service \
- sh -c "exit 1"
-wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
-
-# Test template handlers too
-: "-------V---------------------------------------------------"
-systemctl start testservice-success-68-template.service
-wait_on_state_or_fail "testservice-success-exit-handler-68-template@testservice-success-68-template.service.service" "inactive" "10"
-
-: "-------VI----------------------------------------------------"
-systemctl start testservice-failure-68-template.service
-wait_on_state_or_fail "testservice-failure-exit-handler-68-template@testservice-failure-68-template.service.service" "inactive" "10"
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-69-SHUTDOWN
-
-[Service]
-Type=oneshot
-ExecStart=true
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-
-# Ensure that sandboxing doesn't stop creds from being accessible
-echo "test" > /tmp/testdata
-systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2
-# LoadCredentialEncrypted
-systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
-# SetCredentialEncrypted
-systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
-
-rm -f /tmp/testdata
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-cryptenroll_wipe_and_check() {(
- set +o pipefail
-
- : >/tmp/cryptenroll.out
- systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out
- grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out
-)}
-
-# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it
-if [[ "$(uname -m)" == "ppc64le" ]]; then
- echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716"
- exit 0
-fi
-
-export SYSTEMD_LOG_LEVEL=debug
-IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)"
-
-truncate -s 20M "$IMAGE"
-echo -n password >/tmp/password
-# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages
-chmod 0600 /tmp/password
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password
-
-# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff
-systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE"
-NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE"
-NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE"
-for _ in {0..9}; do
- systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE"
-done
-PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
-# Do some basic checks before we start wiping stuff
-systemd-cryptenroll "$IMAGE"
-systemd-cryptenroll "$IMAGE" | grep password
-systemd-cryptenroll "$IMAGE" | grep recovery
-# Let's start wiping
-cryptenroll_wipe_and_check "$IMAGE" --wipe=empty
-(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty)
-cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0
-PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password
-systemd-cryptenroll "$IMAGE" | grep password
-(! systemd-cryptenroll "$IMAGE" | grep recovery)
-# We shouldn't be able to wipe all keyslots without enrolling a new key first
-(! systemd-cryptenroll "$IMAGE" --wipe=all)
-PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all
-# Check if the newly (and only) enrolled password works
-(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE")
-(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE")
-PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE"
-
-systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE"
-systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE"
-systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE"
-systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE"
-systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE"
-
-# Unlocking using TPM2
-PASSWORD=foo systemd-cryptenroll --tpm2-device=auto "$IMAGE"
-systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
-systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --wipe-slot=tpm2 "$IMAGE"
-
-# Add PIN to TPM2 enrollment
-NEWPIN=1234 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
-
-# Change PIN on TPM2 enrollment
-PIN=1234 NEWPIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
-PIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
-
-(! systemd-cryptenroll --fido2-with-client-pin=false)
-(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo)
-(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE")
-(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE")
-(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE")
-(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE")
-(! systemd-cryptenroll --recovery-key --password "$IMAGE")
-(! systemd-cryptenroll --password --recovery-key "$IMAGE")
-(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE")
-(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE")
-(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE")
-(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
-(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE")
-(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE")
-(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE")
-(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE")
-(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE")
-(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE")
-(! systemd-cryptenroll --wipe-slot "$IMAGE")
-(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE")
-(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
-
-rm -f "$IMAGE"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-cryptsetup_has_token_plugin_support() {
- local plugin_path
-
- plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)"
- cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]]
-}
-
-tpm_check_failure_with_wrong_pin() {
- local testIMAGE="${1:?}"
- local badpin="${2:?}"
- local goodpin="${3:?}"
-
- # We need to be careful not to trigger DA lockout; allow 2 failures
- tpm2_dictionarylockout -s -n 2
- (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1)
- # Verify the correct PIN works, to be sure the failure wasn't a DA lockout
- PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1
- systemd-cryptsetup detach test-volume
- # Clear/reset the DA lockout counter
- tpm2_dictionarylockout -c
-}
-
-at_exit() {
- # Evict the TPM primary key that we persisted
- if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then
- tpm2_evictcontrol -c "$PERSISTENT_HANDLE"
- fi
-}
-
-trap at_exit EXIT
-
-# Prepare a fresh disk image
-IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)"
-
-truncate -s 20M "$IMAGE"
-echo -n passphrase >/tmp/passphrase
-# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages
-chmod 0600 /tmp/passphrase
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
-
-# Unlocking via keyfile
-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE"
-
-# Enroll unlock with default PCR policy
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Check with wrong PCR
-tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-
-# Enroll unlock with PCR+PIN policy
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
-PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout
-for _ in {0..3}; do
- tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
-done
-
-# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
-if cryptsetup_has_token_plugin_support; then
- PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1
- systemd-cryptsetup detach test-volume
-
- # Check failure with wrong PIN
- for _ in {0..3}; do
- tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
- done
-else
- echo 'cryptsetup has no LUKS2 token plugin support, skipping'
-fi
-
-# Check failure with wrong PCR (and correct PIN)
-tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-
-# Enroll unlock with PCR 0+7
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Check with wrong PCR 0
-tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-
-if tpm_has_pcr sha256 12; then
- # Enroll using an explicit PCR value (that does match current PCR value)
- systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
- EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
- PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
- systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
- systemd-cryptsetup detach test-volume
-
- # Same as above plus more PCRs without the value or alg specified
- systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
- EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
- PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
- systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
- systemd-cryptsetup detach test-volume
-
- # Same as above plus more PCRs with hash alg specified but hash value not specified
- systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
- EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
- PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
- systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
- systemd-cryptsetup detach test-volume
-
- # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value
- systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
- tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
- CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
- EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1)
- PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
- (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
- tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE"
- systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
- systemd-cryptsetup detach test-volume
-
- # enroll TPM using device key instead of direct access, then verify unlock using TPM
- tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
- CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
- tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
- systemd-analyze srk > /tmp/srk2.pub
- cmp /tmp/srk.pub /tmp/srk2.pub
- if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then
- cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public
- fi
-
- # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
- if openssl_supports_kdf SSKDF; then
- PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE"
- systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
- systemd-cryptsetup detach test-volume
- fi
-
- rm -f /tmp/pcr.dat /tmp/srk.pub
-fi
-
-# Use default (0) seal key handle
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Use SRK seal key handle
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Test invalid ranges: pcr, nv, session, permanent
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE") # PCR
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent
-
-# Use non-SRK persistent seal key handle (by creating/persisting new key)
-PRIMARY=/tmp/primary.ctx
-tpm2_createprimary -c "$PRIMARY"
-PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle)
-PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}"
-tpm2_flushcontext -t
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
-if openssl_supports_kdf SSKDF; then
- # Make sure that --tpm2-device-key= also works with systemd-repart
- tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
- mkdir /tmp/dditest
- cat > /tmp/dditest/50-root.conf <<EOF
-[Partition]
-Type=root
-Format=ext4
-CopyFiles=/tmp/dditest:/
-Encrypt=tpm2
-EOF
- PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=50M /tmp/dditest.raw --tpm2-pcrs=
- DEVICE="$(systemd-dissect --attach /tmp/dditest.raw)"
- udevadm wait --settle --timeout=10 "$DEVICE"
- systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes
- mkdir /tmp/dditest.mnt
- mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt
- cmp /tmp/dditest.mnt/50-root.conf /tmp/dditest/50-root.conf
- umount /tmp/dditest.mnt
- rmdir /tmp/dditest.mnt
- rm /tmp/dditest.raw
- rm /tmp/dditest/50-root.conf
- rmdir /tmp/dditest
-fi
-
-rm -f "$IMAGE" "$PRIMARY"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-SD_MEASURE="/usr/lib/systemd/systemd-measure"
-
-if [[ ! -x "${SD_MEASURE:?}" ]]; then
- echo "$SD_MEASURE not found, skipping the test"
- exit 0
-fi
-
-IMAGE="$(mktemp /tmp/systemd-measure-XXX.image)"
-
-echo HALLO >/tmp/tpmdata1
-echo foobar >/tmp/tpmdata2
-
-cat >/tmp/result <<EOF
-11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
-11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
-11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
-11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: | cmp - /tmp/result
-
-cat >/tmp/result.json <<EOF
-{"sha1":[{"pcr":11,"hash":"5177e4ad69db92192c10e5f80402bf81bfec8a81"}],"sha256":[{"pcr":11,"hash":"37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13"}],"sha384":[{"pcr":11,"hash":"5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087"}],"sha512":[{"pcr":11,"hash":"961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b"}]}
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: -j | diff -u - /tmp/result.json
-
-cat >/tmp/result <<EOF
-11:sha1=6765ee305db063040c454d32697d922b3d4f232b
-11:sha256=21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699
-11:sha384=08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2
-11:sha512=65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo | cmp - /tmp/result
-
-cat >/tmp/result.json <<EOF
-{"sha1":[{"phase":"foo","pcr":11,"hash":"6765ee305db063040c454d32697d922b3d4f232b"}],"sha256":[{"phase":"foo","pcr":11,"hash":"21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699"}],"sha384":[{"phase":"foo","pcr":11,"hash":"08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2"}],"sha512":[{"phase":"foo","pcr":11,"hash":"65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0"}]}
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo -j | diff -u - /tmp/result.json
-
-rm /tmp/result /tmp/result.json
-
-if ! tpm_has_pcr sha1 11 || ! tpm_has_pcr sha256 11; then
- echo "PCR sysfs files not found, skipping signed PCR policy tests"
- exit 0
-fi
-
-# Generate key pair
-openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "/tmp/pcrsign-private.pem"
-openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
-
-MEASURE_BANKS=("--bank=sha256")
-# Check if SHA1 signatures are supported
-#
-# Some distros have started phasing out SHA1, so make sure the SHA1
-# signatures are supported before trying to use them.
-if echo hello | openssl dgst -sign /tmp/pcrsign-private.pem -sha1 >/dev/null; then
- MEASURE_BANKS+=("--bank=sha1")
-fi
-
-# Sign current PCR state with it
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
-dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
-systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
-systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
-
-# Invalidate PCR, decrypting should fail now
-tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null)
-
-# Sign new PCR state, decrypting should work now.
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2"
-systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
-
-# Now, do the same, but with a cryptsetup binding
-truncate -s 20M "$IMAGE"
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
-# Ensure that an unrelated signature, when not requested, is not used
-touch /run/systemd/tpm2-pcr-signature.json
-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE"
-# Reset and use the signature now
-rm -f /run/systemd/tpm2-pcr-signature.json
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE"
-
-# Check if we can activate that (without the token module stuff)
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2
-
-# Check if we can activate that (and a second time with the token module stuff enabled)
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2
-
-# After extending the PCR things should fail
-tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
-(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
-
-# But once we sign the current PCRs, we should be able to unlock again
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3"
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
-systemd-cryptsetup detach test-volume2
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
-systemd-cryptsetup detach test-volume2
-
-# Test --append mode and de-duplication. With the same parameters signing should not add a new entry
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4"
-cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4"
-
-# Sign one more phase, this should
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5"
-(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5")
-
-# Should still be good to unlock, given the old entry still exists
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1
-systemd-cryptsetup detach test-volume2
-
-# Adding both signatures once more should not change anything, due to the deduplication
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6"
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7"
-cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7"
-
-rm -f "$IMAGE"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
-
-if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then
- echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests"
- exit 0
-fi
-
-at_exit() {
- if [[ $? -ne 0 ]]; then
- # Dump the event log on fail, to make debugging a bit easier
- jq --seq --slurp </run/log/systemd/tpm2-measure.log
- fi
-}
-
-trap at_exit EXIT
-
-# Temporarily override sd-pcrextend's sanity checks
-export SYSTEMD_FORCE_MEASURE=1
-
-"$SD_PCREXTEND" --help
-"$SD_PCREXTEND" --version
-"$SD_PCREXTEND" foo
-"$SD_PCREXTEND" --machine-id
-"$SD_PCREXTEND" --tpm2-device=list
-"$SD_PCREXTEND" --tpm2-device=auto foo
-"$SD_PCREXTEND" --tpm2-device=/dev/tpm0 foo
-"$SD_PCREXTEND" --bank=sha256 foo
-"$SD_PCREXTEND" --bank=sha256 --bank=sha256 foo
-"$SD_PCREXTEND" --graceful foo
-"$SD_PCREXTEND" --pcr=15 foo
-"$SD_PCREXTEND" --file-system=/
-"$SD_PCREXTEND" --file-system=/tmp --file-system=/
-"$SD_PCREXTEND" --file-system=/tmp --file-system=/ --pcr=15 --pcr=11
-
-if tpm_has_pcr sha1 11; then
- "$SD_PCREXTEND" --bank=sha1 --pcr=11 foo
-fi
-
-(! "$SD_PCREXTEND")
-(! "$SD_PCREXTEND" "")
-(! "$SD_PCREXTEND" foo bar)
-(! "$SD_PCREXTEND" --bank= foo)
-(! "$SD_PCREXTEND" --tpm2-device= foo)
-(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
-(! "$SD_PCREXTEND" --pcr= foo)
-(! "$SD_PCREXTEND" --pcr=-1 foo)
-(! "$SD_PCREXTEND" --pcr=1024 foo)
-(! "$SD_PCREXTEND" --foo=bar)
-
-unset SYSTEMD_FORCE_MEASURE
-
-# Note: since we're reading the TPM event log as json-seq, the same rules apply to the output
-# as well, i.e. each record is prefixed by RS (0x1E, 036) and suffixed by LF (0x0A, 012).
-# LF is usually eaten by bash, but RS needs special handling.
-
-# Save the number of events in the current event log, so we can skip them when
-# checking changes caused by following tests
-RECORD_COUNT="$(jq --seq --slurp '. | length' </run/log/systemd/tpm2-measure.log | tr -d '\036')"
-
-# Let's measure the machine ID
-tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
-mv /etc/machine-id /etc/machine-id.save
-echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
-SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id
-mv /etc/machine-id.save /etc/machine-id
-tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
-
-# And check it matches expectations
-diff /tmp/newpcr15 \
- <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
-
-# Check that the event log record was properly written
-test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')"
-DIGEST_EXPECTED="$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)"
-DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
-test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
-
-RECORD_COUNT=$((RECORD_COUNT + 1))
-# And similar for the boot phase measurement into PCR 11
-tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
-# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...)
-mkdir -p /run/systemd/system/systemd-pcrextend.socket.d
-cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf <<EOF
-[Unit]
-# Turn off all conditions */
-ConditionSecurity=
-EOF
-systemctl daemon-reload
-systemctl restart systemd-pcrextend.socket
-varlinkctl call /run/systemd/io.systemd.PCRExtend io.systemd.PCRExtend.Extend '{"pcr":11,"text":"foobar"}'
-tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
-
-diff /tmp/newpcr11 \
- <(cat /tmp/oldpcr11 <(echo -n "foobar" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
-
-# Check the event log for the 2nd new record since $RECORD_COUNT
-test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e11')"
-DIGEST_EXPECTED="$(echo -n "foobar" | openssl dgst -hex -sha256 -r)"
-DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
-test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
-
-# Measure a file system into PCR 15
-tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
-SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --file-system=/
-# Put together the "file system word" we just sent to the TPM
-# file-system:MOUNTPOINT:TYPE:UUID:LABEL:PART_ENTRY_UUID:PART_ENTRY_TYPE:PART_ENTRY_NAME
-ROOT_DEVICE="$(findmnt -n -o SOURCE /)"
-FS_WORD="$(lsblk -n -o MOUNTPOINT,FSTYPE,UUID,LABEL,PARTUUID,PARTTYPE,PARTLABEL "$ROOT_DEVICE" | sed -r 's/[ ]+/:/g')"
-tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
-
-# And check if it matches with the current PCR 15 state
-diff /tmp/newpcr15 \
- <(cat /tmp/oldpcr15 <(echo -n "file-system:$FS_WORD" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
-
-rm -f /tmp/oldpcr{11,15} /tmp/newpcr{11,15}
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-export PAGER=
-SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
-SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
-
-if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] ; then
- echo "$SD_PCREXTEND or $SD_PCRLOCK not found, skipping pcrlock tests"
- exit 0
-fi
-
-at_exit() {
- if [[ $? -ne 0 ]]; then
- # Dump the event log on fail, to make debugging a bit easier
- [[ -e /run/log/systemd/tpm2-measure.log ]] && jq --seq --slurp </run/log/systemd/tpm2-measure.log
- fi
-
- return 0
-}
-
-trap at_exit EXIT
-
-# Temporarily override sd-pcrextend's sanity checks
-export SYSTEMD_FORCE_MEASURE=1
-
-# The PCRs we are going to lock to. We exclude the various PCRs we touched
-# above where no event log record was written, because we cannot analyze
-# things without event log. We include debug PCR 16, see below.
-PCRS="1+2+3+4+5+16"
-
-# Remove the old measurement log, as it contains all kinds of nonsense from the
-# previous test, which will fail our consistency checks. Removing the file also
-# means we'll fail consistency check, but at least we'll fail them consistently
-# (as the PCR values simply won't match the log).
-rm -f /run/log/systemd/tpm2-measure.log
-
-# Ensure a truncated log doesn't crash pcrlock
-echo -n -e \\x1e >/tmp/borked
-set +e
-SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty
-ret=$?
-set -e
-# If it crashes the exit code will be 149
-test $ret -eq 1
-
-SYSTEMD_COLORS=256 "$SD_PCRLOCK"
-"$SD_PCRLOCK" cel --no-pager --json=pretty
-"$SD_PCRLOCK" log --pcr="$PCRS"
-"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"
-"$SD_PCRLOCK" list-components
-"$SD_PCRLOCK" list-components --location=250-
-"$SD_PCRLOCK" list-components --location=250-:350-
-"$SD_PCRLOCK" lock-firmware-config
-"$SD_PCRLOCK" lock-gpt
-"$SD_PCRLOCK" lock-machine-id
-"$SD_PCRLOCK" lock-file-system
-"$SD_PCRLOCK" lock-file-system /
-"$SD_PCRLOCK" predict --pcr="$PCRS"
-"$SD_PCRLOCK" predict --pcr="0x1+0x3+4"
-"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS"
-
-SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
-if [[ -n "$SD_STUB" ]]; then
- "$SD_PCRLOCK" lock-pe "$SD_STUB"
- "$SD_PCRLOCK" lock-pe <"$SD_STUB"
- "$SD_PCRLOCK" lock-uki "$SD_STUB"
- "$SD_PCRLOCK" lock-uki <"$SD_STUB"
-fi
-
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
-# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
-"$SD_PCRLOCK" make-policy --pcr="$PCRS"
-"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
-
-img="/tmp/pcrlock.img"
-truncate -s 20M "$img"
-echo -n hoho >/tmp/pcrlockpwd
-chmod 0600 /tmp/pcrlockpwd
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd
-
-systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --wipe-slot=tpm2 "$img"
-systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
-systemd-cryptsetup detach pcrlock
-
-# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail
-"$SD_PCREXTEND" --pcr=16 test70
-
-"$SD_PCRLOCK" cel --json=pretty
-
-(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless )
-
-# Now add a component for it, rebuild policy and it should work (we'll rebuild
-# once like that, but don't provide the recovery pin. This should fail, since
-# the PCR is hosed after all. But then we'll use recovery pin, and it should
-# work.
-echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
-(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
-
-systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
-systemd-cryptsetup detach pcrlock
-
-# And now let's do it the clean way, and generate the right policy ahead of time.
-echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
-"$SD_PCRLOCK" make-policy --pcr="$PCRS"
-# the next one should be skipped because redundant
-"$SD_PCRLOCK" make-policy --pcr="$PCRS"
-# but this one should not be skipped, even if redundant, because we force it
-"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
-
-"$SD_PCREXTEND" --pcr=16 test70-take-two
-
-"$SD_PCRLOCK" cel --json=pretty
-
-systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
-systemd-cryptsetup detach pcrlock
-
-# Now use the root fs support, i.e. make the tool write a copy of the pcrlock
-# file as service credential to some temporary dir and remove the local copy, so that
-# it has to use the credential version.
-mkdir /tmp/fakexbootldr
-SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
-mv /var/lib/systemd/pcrlock.json /var/lib/systemd/pcrlock.json.gone
-
-systemd-creds decrypt /tmp/fakexbootldr/loader/credentials/pcrlock.*.cred
-
-SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY=/tmp/fakexbootldr/loader/credentials systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,headless
-systemd-cryptsetup detach pcrlock
-
-mv /var/lib/systemd/pcrlock.json.gone /var/lib/systemd/pcrlock.json
-SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" remove-policy
-
-"$SD_PCRLOCK" unlock-firmware-config
-"$SD_PCRLOCK" unlock-gpt
-"$SD_PCRLOCK" unlock-machine-id
-"$SD_PCRLOCK" unlock-file-system
-"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock
-"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock
-
-(! "$SD_PCRLOCK" "")
-(! "$SD_PCRLOCK" predict --pcr=-1)
-(! "$SD_PCRLOCK" predict --pcr=foo)
-(! "$SD_PCRLOCK" predict --pcr=1+1)
-(! "$SD_PCRLOCK" predict --pcr=1+++++1)
-(! "$SD_PCRLOCK" make-policy --nv-index=0)
-(! "$SD_PCRLOCK" make-policy --nv-index=foo)
-(! "$SD_PCRLOCK" list-components --location=:)
-(! "$SD_PCRLOCK" lock-gpt "")
-(! "$SD_PCRLOCK" lock-gpt /dev/sr0)
-(! "$SD_PCRLOCK" lock-pe /dev/full)
-(! "$SD_PCRLOCK" lock-pe /bin/true)
-(! "$SD_PCRLOCK" lock-uki /dev/full)
-(! "$SD_PCRLOCK" lock-uki /bin/true)
-(! "$SD_PCRLOCK" lock-file-system "")
-
-# Exercise Varlink API a bit (but first turn off condition)
-
-mkdir -p /run/systemd/system/systemd-pcrlock.socket.d
-cat > /run/systemd/system/systemd-pcrlock.socket.d/50-no-condition.conf <<EOF
-[Unit]
-# Turn off all conditions
-ConditionSecurity=
-EOF
-
-systemctl daemon-reload
-systemctl restart systemd-pcrlock.socket
-
-varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.RemovePolicy '{}'
-varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.MakePolicy '{}'
-varlinkctl call --collect --json=pretty /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.ReadEventLog '{}'
-
-rm "$img" /tmp/pcrlockpwd
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-70-TPM2
-Wants=tpm2.target
-After=tpm2.target
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
-
-if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then
- echo "$SD_TPM2SETUP not found, skipping the test"
- exit 0
-fi
-
-"$SD_TPM2SETUP" --help
-"$SD_TPM2SETUP" --version
-"$SD_TPM2SETUP" --tpm2-device=list
-"$SD_TPM2SETUP" --tpm2-device=auto
-"$SD_TPM2SETUP" --tpm2-device=/dev/tpm0
-"$SD_TPM2SETUP" --early=yes
-"$SD_TPM2SETUP" --early=yes
-"$SD_TPM2SETUP" --early=no
-"$SD_TPM2SETUP" --early=no
-
-(! "$SD_TPM2SETUP" "")
-(! "$SD_TPM2SETUP" --tpm2-device=)
-(! "$SD_TPM2SETUP" --tpm2-device=/dev/null)
-(! "$SD_TPM2SETUP" --foo=bar)
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-71-HOSTNAME
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-restore_hostname() {
- if [[ -e /tmp/hostname.bak ]]; then
- mv /tmp/hostname.bak /etc/hostname
- else
- rm -f /etc/hostname
- fi
-}
-
-testcase_hostname() {
- local orig=
-
- if [[ -f /etc/hostname ]]; then
- cp /etc/hostname /tmp/hostname.bak
- orig=$(cat /etc/hostname)
- fi
-
- trap restore_hostname RETURN
-
- # should activate daemon and work
- if [[ -n "$orig" ]]; then
- assert_in "Static hostname: $orig" "$(hostnamectl)"
- fi
- assert_in "Kernel: $(uname -s) $(uname -r)" "$(hostnamectl)"
-
- # change hostname
- assert_rc 0 hostnamectl set-hostname testhost
- assert_eq "$(cat /etc/hostname)" "testhost"
- assert_in "Static hostname: testhost" "$(hostnamectl)"
-
- if [[ -n "$orig" ]]; then
- # reset to original
- assert_rc 0 hostnamectl set-hostname "$orig"
- assert_eq "$(cat /etc/hostname)" "$orig"
- assert_in "Static hostname: $orig" "$(hostnamectl)"
- fi
-}
-
-restore_machine_info() {
- if [[ -e /tmp/machine-info.bak ]]; then
- mv /tmp/machine-info.bak /etc/machine-info
- else
- rm -f /etc/machine-info
- fi
-}
-
-get_chassis() (
- # shellcheck source=/dev/null
- . /etc/machine-info
-
- echo "$CHASSIS"
-)
-
-stop_hostnamed() {
- systemctl stop systemd-hostnamed.service
- # Reset trigger limit. This might fail if the unit was unloaded already, so ignore any errors.
- systemctl reset-failed systemd-hostnamed || :
-}
-
-testcase_chassis() {
- local i
-
- if [[ -f /etc/machine-info ]]; then
- cp /etc/machine-info /tmp/machine-info.bak
- fi
-
- trap restore_machine_info RETURN
-
- # Invalid chassis type is refused
- assert_rc 1 hostnamectl chassis hoge
-
- # Valid chassis types
- for i in vm container desktop laptop convertible server tablet handset watch embedded; do
- hostnamectl chassis "$i"
- assert_eq "$(hostnamectl chassis)" "$i"
- assert_eq "$(get_chassis)" "$i"
- done
-
- stop_hostnamed
- rm -f /etc/machine-info
-
- # fallback chassis type
- if systemd-detect-virt --quiet --container; then
- assert_eq "$(hostnamectl chassis)" container
- elif systemd-detect-virt --quiet --vm; then
- assert_eq "$(hostnamectl chassis)" vm
- fi
-}
-
-restore_sysfs_dmi() {
- umount /sys/class/dmi/id
- rm -rf /run/systemd/system/systemd-hostnamed.service.d
- systemctl daemon-reload
- stop_hostnamed
-}
-
-testcase_firmware_date() {
- # No DMI on s390x or ppc
- if [[ ! -d /sys/class/dmi/id ]]; then
- echo "/sys/class/dmi/id not found, skipping firmware date tests."
- return 0
- fi
-
- trap restore_sysfs_dmi RETURN
-
- # Ignore /sys being mounted as tmpfs
- mkdir -p /run/systemd/system/systemd-hostnamed.service.d/
- cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
-[Service]
-Environment="SYSTEMD_DEVICE_VERIFY_SYSFS=0"
-Environment="SYSTEMD_HOSTNAME_FORCE_DMI=1"
-EOF
- systemctl daemon-reload
-
- mount -t tmpfs none /sys/class/dmi/id
- echo '1' >/sys/class/dmi/id/uevent
-
- echo '09/08/2000' >/sys/class/dmi/id/bios_date
- stop_hostnamed
- assert_in '2000-09-08' "$(hostnamectl)"
-
- echo '2022' >/sys/class/dmi/id/bios_date
- stop_hostnamed
- assert_not_in 'Firmware Date' "$(hostnamectl)"
-
- echo 'garbage' >/sys/class/dmi/id/bios_date
- stop_hostnamed
- assert_not_in 'Firmware Date' "$(hostnamectl)"
-}
-
-testcase_nss-myhostname() {
- local database host i
-
- HOSTNAME="$(hostnamectl hostname)"
-
- # Set up a dummy network for _gateway and _outbound labels
- ip link add foo type dummy
- ip link set up dev foo
- ip addr add 10.0.0.2/24 dev foo
- for i in {128..150}; do
- ip addr add "10.0.0.$i/24" dev foo
- done
- ip route add 10.0.0.1 dev foo
- ip route add default via 10.0.0.1 dev foo
-
- # Note: `getent hosts` probes gethostbyname2(), whereas `getent ahosts` probes gethostbyname3()
- # and gethostbyname4() (through getaddrinfo() -> gaih_inet() -> get_nss_addresses())
- getent hosts -s myhostname
- getent ahosts -s myhostname
-
- # With IPv6 disabled
- sysctl -w net.ipv6.conf.all.disable_ipv6=1
- # Everything under .localhost and .localhost.localdomain should resolve to localhost
- for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
- run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname "$host"
- run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahosts -s myhostname "$host"
- run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahostsv4 -s myhostname "$host"
- (! getent ahostsv6 -s myhostname localhost)
- done
- for i in 2 {128..150}; do
- run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
- run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
- run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
- run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "10.0.0.$i"
- run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
- run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
- done
- for database in hosts ahosts ahostsv4 ahostsv6; do
- (! getent "$database" -s myhostname ::1)
- done
- (! getent ahostsv6 -s myhostname "$HOSTNAME")
- run_and_grep -n "^fe80:[^ ]+\s+STREAM$" getent ahosts -s myhostname "$HOSTNAME"
-
- # With IPv6 enabled
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- # Everything under .localhost and .localhost.localdomain should resolve to localhost
- for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
- run_and_grep "^::1\s+localhost$" getent hosts -s myhostname "$host"
- run_and_grep "^::1\s+STREAM" getent ahosts -s myhostname "$host"
- run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
- run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahostsv4 -s myhostname "$host"
- run_and_grep -n "^::1\s+STREAM" getent ahostsv4 -s myhostname "$host"
- run_and_grep "^::1\s+STREAM" getent ahostsv6 -s myhostname "$host"
- run_and_grep -n "^127\.0\.0\.1\s+STREAM" getent ahostsv6 -s myhostname "$host"
- done
- for i in 2 {128..150}; do
- run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
- run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
- run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
- run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
- done
- run_and_grep "^fe80:[^ ]+\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
- run_and_grep "^fe80:[^ ]+\s+STREAM" getent ahosts -s myhostname "$HOSTNAME"
- run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname 127.0.0.1
- run_and_grep "^127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahosts -s myhostname 127.0.0.1
- run_and_grep "^::ffff:127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahostsv6 -s myhostname 127.0.0.1
- run_and_grep "^127\.0\.0\.2\s+$HOSTNAME$" getent hosts -s myhostname 127.0.0.2
- run_and_grep "^::1\s+localhost $HOSTNAME$" getent hosts -s myhostname ::1
- run_and_grep "^::1\s+STREAM\s+::1$" getent ahosts -s myhostname ::1
- (! getent ahostsv4 -s myhostname ::1)
-
- # _gateway
- for host in _gateway{,.} 10.0.0.1; do
- run_and_grep "^10\.0\.0\.1\s+_gateway$" getent hosts -s myhostname "$host"
- run_and_grep "^10\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
- done
-
- # _outbound
- for host in _outbound{,.} 10.0.0.2; do
- run_and_grep "^10\.0\.0\.2\s+" getent hosts -s myhostname "$host"
- run_and_grep "^10\.0\.0\.2\s+STREAM" getent ahosts -s myhostname "$host"
- done
-
- # Non-existent records
- for database in hosts ahosts ahostsv4 ahostsv6; do
- (! getent "$database" -s myhostname this.should.not.exist)
- done
- (! getent hosts -s myhostname 10.254.254.1)
- (! getent hosts -s myhostname fd00:dead:beef:cafe::1)
-}
-
-test_varlink() {
- A="$(mktemp -u)"
- B="$(mktemp -u)"
- varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A"
- hostnamectl --json=short > "$B"
- cmp "$A" "$B"
-}
-
-run_testcases
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-72-SYSUPDATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-SYSUPDATE=/lib/systemd/systemd-sysupdate
-SECTOR_SIZES=(512 4096)
-WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
-BACKING_FILE="$WORKDIR/joined.raw"
-export SYSTEMD_ESP_PATH="$WORKDIR/esp"
-export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr"
-export SYSTEMD_PAGER=cat
-export SYSTEMD_LOG_LEVEL=debug
-
-if [[ ! -x "$SYSUPDATE" ]]; then
- echo "no systemd-sysupdate" >/skipped
- exit 77
-fi
-
-# Loopback devices may not be supported. They are used because sfdisk cannot
-# change the sector size of a file, and we want to test both 512 and 4096 byte
-# sectors. If loopback devices are not supported, we can only test one sector
-# size, and the underlying device is likely to have a sector size of 512 bytes.
-if [[ ! -e /dev/loop-control ]]; then
- echo "No loopback device support"
- SECTOR_SIZES=(512)
-fi
-
-at_exit() {
- set +e
-
- losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
- losetup --detach "$loop_dev"
- done
-
- rm -rf "$WORKDIR"
-}
-
-trap at_exit EXIT
-
-new_version() {
- local sector_size="${1:?}"
- local version="${2:?}"
-
- # Create a pair of random partition payloads, and compress one
- dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048
- dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048
- gzip -k -f "$WORKDIR/source/part2-$version.raw"
-
- # Create a random "UKI" payload
- echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
-
- # Create a random extra payload
- echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
-
- # Create tarball of a directory
- mkdir -p "$WORKDIR/source/dir-$version"
- echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt"
- echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt"
- tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" .
-
- (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
-}
-
-update_now() {
- # Update to newest version. First there should be an update ready, then we
- # do the update, and then there should not be any ready anymore
-
- "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new
- "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update
- (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new)
-}
-
-verify_version() {
- local block_device="${1:?}"
- local sector_size="${2:?}"
- local version="${3:?}"
- local part1_number="${4:?}"
- local part2_number="${5:?}"
- local gpt_reserved_sectors part1_offset part2_offset
-
- gpt_reserved_sectors=$((1024 * 1024 / sector_size))
- part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors))
- part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors))
-
- # Check the partitions
- dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw"
- dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw"
-
- # Check the UKI
- cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi"
- test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")"
-
- # Check the extra efi
- cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
-
- # Check the directories
- cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt"
- cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
-}
-
-for sector_size in "${SECTOR_SIZES[@]}"; do
- # Disk size of:
- # - 1MB for GPT
- # - 4 partitions of 2048 sectors each
- # - 1MB for backup GPT
- disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2))
- rm -f "$BACKING_FILE"
- truncate -s "$disk_size" "$BACKING_FILE"
-
- if [[ -e /dev/loop-control ]]; then
- blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
- else
- blockdev="$BACKING_FILE"
- fi
-
- sfdisk "$blockdev" <<EOF
-label: gpt
-unit: sectors
-sector-size: $sector_size
-
-size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
-size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
-size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
-size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
-EOF
-
- for d in "dirs" "defs"; do
- rm -rf "${WORKDIR:?}/$d"
- mkdir -p "$WORKDIR/$d"
- done
-
- cat >"$WORKDIR/defs/01-first.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=part1-@v.raw
-
-[Target]
-Type=partition
-Path=$blockdev
-MatchPattern=part1-@v
-MatchPartitionType=root-x86-64
-EOF
-
- cat >"$WORKDIR/defs/02-second.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=part2-@v.raw.gz
-
-[Target]
-Type=partition
-Path=$blockdev
-MatchPattern=part2-@v
-MatchPartitionType=root-x86-64-verity
-EOF
-
- cat >"$WORKDIR/defs/03-third.conf" <<EOF
-[Source]
-Type=directory
-Path=$WORKDIR/source
-MatchPattern=dir-@v
-
-[Target]
-Type=directory
-Path=$WORKDIR/dirs
-CurrentSymlink=$WORKDIR/dirs/current
-MatchPattern=dir-@v
-InstancesMax=3
-EOF
-
- cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=uki-@v.efi
-
-[Target]
-Type=regular-file
-Path=/EFI/Linux
-PathRelativeTo=boot
-MatchPattern=uki_@v+@l-@d.efi \
- uki_@v+@l.efi \
- uki_@v.efi
-Mode=0444
-TriesLeft=3
-TriesDone=0
-InstancesMax=2
-EOF
-
- cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=uki-extra-@v.efi
-
-[Target]
-Type=regular-file
-Path=/EFI/Linux
-PathRelativeTo=boot
-MatchPattern=uki_@v.efi.extra.d/extra.addon.efi
-Mode=0444
-InstancesMax=2
-EOF
-
- rm -rf "${WORKDIR:?}"/{esp,xbootldr,source}
- mkdir -p "$WORKDIR"/{source,esp/EFI/Linux,xbootldr/EFI/Linux}
-
- # Install initial version and verify
- new_version "$sector_size" v1
- update_now
- verify_version "$blockdev" "$sector_size" v1 1 3
-
- # Create second version, update and verify that it is added
- new_version "$sector_size" v2
- update_now
- verify_version "$blockdev" "$sector_size" v2 2 4
-
- # Create third version, update and verify it replaced the first version
- new_version "$sector_size" v3
- update_now
- verify_version "$blockdev" "$sector_size" v3 1 3
- test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
- test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
- test ! -d "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d"
-
- # Create fourth version, and update through a file:// URL. This should be
- # almost as good as testing HTTP, but is simpler for us to set up. file:// is
- # abstracted in curl for us, and since our main goal is to test our own code
- # (and not curl) this test should be quite good even if not comprehensive. This
- # will test the SHA256SUMS logic at least (we turn off GPG validation though,
- # see above)
- new_version "$sector_size" v4
-
- cat >"$WORKDIR/defs/02-second.conf" <<EOF
-[Source]
-Type=url-file
-Path=file://$WORKDIR/source
-MatchPattern=part2-@v.raw.gz
-
-[Target]
-Type=partition
-Path=$blockdev
-MatchPattern=part2-@v
-MatchPartitionType=root-x86-64-verity
-EOF
-
- cat >"$WORKDIR/defs/03-third.conf" <<EOF
-[Source]
-Type=url-tar
-Path=file://$WORKDIR/source
-MatchPattern=dir-@v.tar.gz
-
-[Target]
-Type=directory
-Path=$WORKDIR/dirs
-CurrentSymlink=$WORKDIR/dirs/current
-MatchPattern=dir-@v
-InstancesMax=3
-EOF
-
- update_now
- verify_version "$blockdev" "$sector_size" v4 2 4
-
- # Cleanup
- [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
- rm "$BACKING_FILE"
-done
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-73-LOCALE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-enable_debug() {
- mkdir -p /run/systemd/system/systemd-localed.service.d
- cat >>/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
- mkdir -p /run/systemd/system/systemd-vconsole-setup.service.d
- cat >>/run/systemd/system/systemd-vconsole-setup.service.d/override.conf <<EOF
-[Unit]
-StartLimitIntervalSec=0
-
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
- systemctl daemon-reload
-}
-
-testcase_locale() {
- local i output
-
- if [[ -f /etc/locale.conf ]]; then
- cp /etc/locale.conf /tmp/locale.conf.bak
- fi
-
- # Debian/Ubuntu specific file
- if [[ -f /etc/default/locale ]]; then
- cp /etc/default/locale /tmp/default-locale.bak
- fi
-
- if [[ -f /etc/locale.gen ]]; then
- cp /etc/locale.gen /tmp/locale.gen.bak
- fi
-
- # remove locale.conf to make /etc/default/locale used by Debian/Ubuntu
- rm -f /etc/locale.conf
- # also remove /etc/default/locale
- rm -f /etc/default/locale
- # and create /etc/default to make /etc/default/locale created by localed
- mkdir -p /etc/default
-
- trap restore_locale RETURN
- # Ensure at least one UTF-8 locale exists.
- generate_locale en_US.UTF-8
-
- # create invalid locale
- mkdir -p /usr/lib/locale/xx_XX.UTF-8
- assert_not_in "xx_XX.UTF-8" "$(localectl list-locales)"
-
- if [[ -z "$(localectl list-locales)" ]]; then
- echo "No locale installed, skipping test."
- return
- fi
-
- # start with a known default environment and make sure to also give a
- # default value to LC_CTYPE= since we're about to also set/unset it. We
- # also reload PID1 configuration to make sure that PID1 environment itself
- # is updated as it's not always been the case.
- assert_rc 0 localectl set-locale "LANG=en_US.UTF-8" "LC_CTYPE=C"
- systemctl daemon-reload
- output=$(localectl)
- assert_in "System Locale: LANG=en_US.UTF-8" "$output"
- assert_in "LC_CTYPE=C" "$output"
- output=$(systemctl show-environment)
- assert_in "LANG=en_US.UTF-8" "$output"
- assert_in "LC_CTYPE=C" "$output"
-
- # warn when kernel command line has locale settings
- output=$(SYSTEMD_PROC_CMDLINE="locale.LANG=C.UTF-8 locale.LC_CTYPE=ja_JP.UTF-8" localectl 2>&1)
- assert_in "Warning:" "$output"
- assert_in "Command Line: LANG=C.UTF-8" "$output"
- assert_in "LC_CTYPE=ja_JP.UTF-8" "$output"
- assert_in "System Locale:" "$output"
-
- # change locale
- for i in $(localectl list-locales); do
- assert_rc 0 localectl set-locale "LANG=C" "LC_CTYPE=$i"
- if [[ -f /etc/default/locale ]]; then
- assert_eq "$(cat /etc/default/locale)" "LANG=C
-LC_CTYPE=$i"
- else
- assert_eq "$(cat /etc/locale.conf)" "LANG=C
-LC_CTYPE=$i"
- fi
- output=$(localectl)
- assert_in "System Locale: LANG=C" "$output"
- assert_in "LC_CTYPE=$i" "$output"
- output=$(systemctl show-environment)
- assert_in "LANG=C" "$output"
- assert_in "LC_CTYPE=$i" "$output"
-
- assert_rc 0 localectl set-locale "$i"
- if [[ -f /etc/default/locale ]]; then
- assert_eq "$(cat /etc/default/locale)" "LANG=$i"
- else
- assert_eq "$(cat /etc/locale.conf)" "LANG=$i"
- fi
- output=$(localectl)
- assert_in "System Locale: LANG=$i" "$output"
- assert_not_in "LC_CTYPE=" "$output"
- output=$(systemctl show-environment)
- assert_in "LANG=$i" "$output"
- assert_not_in "LC_CTYPE=" "$output"
- done
-
- # test if localed auto-runs locale-gen
- if command -v locale-gen >/dev/null 2>&1 &&
- ! localectl list-locales | grep -F "de_DE.UTF-8"; then
-
- # clear previous locale
- systemctl stop systemd-localed.service
- rm -f /etc/locale.conf /etc/default/locale
-
- # change locale
- assert_rc 0 localectl set-locale de_DE.UTF-8
- if [[ -f /etc/default/locale ]]; then
- assert_eq "$(cat /etc/default/locale)" "LANG=de_DE.UTF-8"
- else
- assert_eq "$(cat /etc/locale.conf)" "LANG=de_DE.UTF-8"
- fi
- assert_in "System Locale: LANG=de_DE.UTF-8" "$(localectl)"
- assert_in "LANG=de_DE.UTF-8" "$(systemctl show-environment)"
-
- # ensure tested locale exists and works now
- assert_in "de_DE.UTF-8" "$(localectl list-locales)"
- fi
-}
-
-backup_keymap() {
- if [[ -f /etc/vconsole.conf ]]; then
- cp /etc/vconsole.conf /tmp/vconsole.conf.bak
- fi
-
- if [[ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]]; then
- cp /etc/X11/xorg.conf.d/00-keyboard.conf /tmp/00-keyboard.conf.bak
- fi
-
- # Debian/Ubuntu specific file
- if [[ -f /etc/default/keyboard ]]; then
- cp /etc/default/keyboard /tmp/default-keyboard.bak
- fi
-
- mkdir -p /etc/default
-}
-
-restore_keymap() {
- if [[ -f /tmp/vconsole.conf.bak ]]; then
- mv /tmp/vconsole.conf.bak /etc/vconsole.conf
- else
- rm -f /etc/vconsole.conf
- fi
-
- if [[ -f /tmp/00-keyboard.conf.bak ]]; then
- mv /tmp/00-keyboard.conf.bak /etc/X11/xorg.conf.d/00-keyboard.conf
- else
- rm -f /etc/X11/xorg.conf.d/00-keyboard.conf
- fi
-
- if [[ -f /tmp/default-keyboard.bak ]]; then
- mv /tmp/default-keyboard.bak /etc/default/keyboard
- else
- rm -f /etc/default/keyboard
- rmdir --ignore-fail-on-non-empty /etc/default
- fi
-}
-
-wait_vconsole_setup() {
- local i ss
- for i in {1..20}; do
- (( i > 1 )) && sleep 0.5
- ss="$(systemctl --property SubState --value show systemd-vconsole-setup.service)"
- if [[ "$ss" == "exited" || "$ss" == "dead" || "$ss" == "condition" ]]; then
- return 0
- elif [[ "$ss" == "failed" ]]; then
- echo "WARNING: systemd-vconsole-setup.service failed, ignoring." >&2
- systemctl reset-failed systemd-vconsole-setup.service
- return 0
- fi
- done
-
- systemctl status systemd-vconsole-setup.service
- return 1
-}
-
-testcase_vc_keymap() {
- local i output vc
-
- if [[ -z "$(localectl list-keymaps)" ]]; then
- echo "No vconsole keymap installed, skipping test."
- return
- fi
-
- backup_keymap
- trap restore_keymap RETURN
-
- # should activate daemon and work
- assert_in "VC Keymap:" "$(localectl)"
-
- for i in $(localectl list-keymaps); do
- # clear previous conversion from VC -> X11 keymap
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
- # set VC keymap
- assert_rc 0 localectl set-keymap "$i"
- output=$(localectl)
-
- # check VC keymap
- vc=$(cat /etc/vconsole.conf)
- assert_in "KEYMAP=$i" "$vc"
- assert_in "VC Keymap: $i" "$output"
-
- # check VC -> X11 keymap conversion
- if [[ "$i" == "us" ]]; then
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: pc105\+inet" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
- assert_in "XKBLAYOUT=us" "$vc"
- assert_in "XKBMODEL=pc105\+inet" "$vc"
- assert_not_in "XKBVARIANT" "$vc"
- assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
- elif [[ "$i" == "us-acentos" ]]; then
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: pc105" "$output"
- assert_in "X11 Variant: intl" "$output"
- assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
- assert_in "XKBLAYOUT=us" "$vc"
- assert_in "XKBMODEL=pc105" "$vc"
- assert_in "XKBVARIANT=intl" "$vc"
- assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
- elif [[ "$i" =~ ^us-.* ]]; then
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: microsoftpro" "$output"
- assert_in "X11 Variant:" "$output"
- assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
- assert_in "XKBLAYOUT=us" "$vc"
- assert_in "XKBMODEL=microsoftpro" "$vc"
- assert_in "XKBVARIANT=" "$vc"
- assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
- fi
- done
-
- # gets along without config file
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/vconsole.conf
- assert_in "VC Keymap: .unset." "$(localectl)"
-}
-
-testcase_x11_keymap() {
- local output
-
- if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
- echo "No x11 keymap installed, skipping test."
- return
- fi
-
- backup_keymap
- trap restore_keymap RETURN
-
- # should activate daemon and work
- assert_in "X11 Layout:" "$(localectl)"
-
- # set x11 keymap (layout, model, variant, options)
- assert_rc 0 localectl set-x11-keymap us pc105+inet intl terminate:ctrl_alt_bksp
-
- if [[ -f /etc/default/keyboard ]]; then
- assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
-XKBMODEL=pc105+inet
-XKBVARIANT=intl
-XKBOPTIONS=terminate:ctrl_alt_bksp"
- else
- output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
- assert_in 'Option "XkbLayout" "us"' "$output"
- assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
- assert_in 'Option "XkbVariant" "intl"' "$output"
- assert_in 'Option "XkbOptions" "terminate:ctrl_alt_bksp"' "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in 'XKBLAYOUT=us' "$output"
- assert_in 'XKBMODEL=pc105\+inet' "$output"
- assert_in 'XKBVARIANT=intl' "$output"
- assert_in 'XKBOPTIONS=terminate:ctrl_alt_bksp' "$output"
- fi
-
- output=$(localectl)
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: pc105\+inet" "$output"
- assert_in "X11 Variant: intl" "$output"
- assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
- # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
- rm -f /etc/default/keyboard
-
- # set x11 keymap (layout, model, variant)
- assert_rc 0 localectl set-x11-keymap us pc105+inet intl
-
- if [[ -f /etc/default/keyboard ]]; then
- assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
-XKBMODEL=pc105+inet
-XKBVARIANT=intl"
- else
- output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
- assert_in 'Option "XkbLayout" "us"' "$output"
- assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
- assert_in 'Option "XkbVariant" "intl"' "$output"
- assert_not_in 'Option "XkbOptions"' "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in 'XKBLAYOUT=us' "$output"
- assert_in 'XKBMODEL=pc105\+inet' "$output"
- assert_in 'XKBVARIANT=intl' "$output"
- assert_not_in 'XKBOPTIONS' "$output"
- fi
-
- output=$(localectl)
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: pc105\+inet" "$output"
- assert_in "X11 Variant: intl" "$output"
- assert_not_in "X11 Options:" "$output"
-
- # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
- rm -f /etc/default/keyboard
-
- # set x11 keymap (layout, model)
- assert_rc 0 localectl set-x11-keymap us pc105+inet
-
- if [[ -f /etc/default/keyboard ]]; then
- assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
-XKBMODEL=pc105+inet"
- else
- output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
- assert_in 'Option "XkbLayout" "us"' "$output"
- assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
- assert_not_in 'Option "XkbVariant"' "$output"
- assert_not_in 'Option "XkbOptions"' "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in 'XKBLAYOUT=us' "$output"
- assert_in 'XKBMODEL=pc105\+inet' "$output"
- assert_not_in 'XKBVARIANT' "$output"
- assert_not_in 'XKBOPTIONS' "$output"
- fi
-
- output=$(localectl)
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: pc105\+inet" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_not_in "X11 Options:" "$output"
-
- # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
- rm -f /etc/default/keyboard
-
- # set x11 keymap (layout)
- assert_rc 0 localectl set-x11-keymap us
-
- if [[ -f /etc/default/keyboard ]]; then
- assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
- else
- output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
- assert_in 'Option "XkbLayout" "us"' "$output"
- assert_not_in 'Option "XkbModel"' "$output"
- assert_not_in 'Option "XkbVariant"' "$output"
- assert_not_in 'Option "XkbOptions"' "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in 'XKBLAYOUT=us' "$output"
- assert_not_in 'XKBMODEL' "$output"
- assert_not_in 'XKBVARIANT' "$output"
- assert_not_in 'XKBOPTIONS' "$output"
- fi
-
- output=$(localectl)
- assert_in "X11 Layout: us" "$output"
- assert_not_in "X11 Model:" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_not_in "X11 Options:" "$output"
-
- # gets along without config file
- systemctl stop systemd-localed.service
- rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
- output=$(localectl)
- assert_in "X11 Layout: .unset." "$output"
- assert_not_in "X11 Model:" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_not_in "X11 Options:" "$output"
-}
-
-testcase_convert() {
- if [[ -z "$(localectl list-keymaps)" ]]; then
- echo "No vconsole keymap installed, skipping test."
- return
- fi
-
- if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
- echo "No x11 keymap installed, skipping test."
- return
- fi
-
- backup_keymap
- trap restore_keymap RETURN
-
- # clear previous settings
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
- # set VC keymap without conversion
- assert_rc 0 localectl --no-convert set-keymap us
- output=$(localectl)
-
- # check VC keymap
- vc=$(cat /etc/vconsole.conf)
- assert_in "KEYMAP=us" "$vc"
- assert_in "VC Keymap: us" "$output"
-
- # check VC -> X11 keymap conversion (nothing set)
- assert_in "X11 Layout: .unset." "$output"
- assert_not_in "X11 Model:" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_not_in "X11 Options:" "$output"
-
- assert_not_in "XKBLAYOUT=" "$vc"
- assert_not_in "XKBMODEL=" "$vc"
- assert_not_in "XKBVARIANT=" "$vc"
- assert_not_in "XKBOPTIONS=" "$vc"
-
- # set VC keymap with conversion
- assert_rc 0 localectl set-keymap us
- output=$(localectl)
-
- # check VC keymap
- vc=$(cat /etc/vconsole.conf)
- assert_in "KEYMAP=us" "$vc"
- assert_in "VC Keymap: us" "$output"
-
- # check VC -> X11 keymap conversion
- assert_in "X11 Layout: us" "$output"
- assert_in "X11 Model: pc105\+inet" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
- assert_in "XKBLAYOUT=us" "$vc"
- assert_in "XKBMODEL=pc105\+inet" "$vc"
- assert_not_in "XKBVARIANT" "$vc"
- assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
-
- # clear previous settings
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
- # set x11 keymap (layout) without conversion
- assert_rc 0 localectl --no-convert set-x11-keymap us
-
- assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)"
- assert_in "VC Keymap: .unset." "$(localectl)"
-
- if [[ -f /etc/default/keyboard ]]; then
- assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
- else
- output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
- assert_in 'Option "XkbLayout" "us"' "$output"
- assert_not_in 'Option "XkbModel"' "$output"
- assert_not_in 'Option "XkbVariant"' "$output"
- assert_not_in 'Option "XkbOptions"' "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in 'XKBLAYOUT=us' "$output"
- assert_not_in 'XKBMODEL=' "$output"
- assert_not_in 'XKBVARIANT=' "$output"
- assert_not_in 'XKBOPTIONS=' "$output"
- fi
-
- output=$(localectl)
- assert_in "X11 Layout: us" "$output"
- assert_not_in "X11 Model:" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_not_in "X11 Options:" "$output"
-
- # set x11 keymap (layout, model) with conversion
- assert_rc 0 localectl set-x11-keymap us
-
- assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)"
- assert_in "VC Keymap: us" "$(localectl)"
-
- if [[ -f /etc/default/keyboard ]]; then
- assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
- else
- output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
- assert_in 'Option "XkbLayout" "us"' "$output"
- assert_not_in 'Option "XkbModel"' "$output"
- assert_not_in 'Option "XkbVariant"' "$output"
- assert_not_in 'Option "XkbOptions"' "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in 'XKBLAYOUT=us' "$output"
- assert_not_in 'XKBMODEL=' "$output"
- assert_not_in 'XKBVARIANT=' "$output"
- assert_not_in 'XKBOPTIONS=' "$output"
- fi
-
- output=$(localectl)
- assert_in "X11 Layout: us" "$output"
- assert_not_in "X11 Model:" "$output"
- assert_not_in "X11 Variant:" "$output"
- assert_not_in "X11 Options:" "$output"
-}
-
-testcase_validate() {
- if [[ -z "$(localectl list-keymaps)" ]]; then
- echo "No vconsole keymap installed, skipping test."
- return
- fi
-
- if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
- echo "No x11 keymap installed, skipping test."
- return
- fi
-
- backup_keymap
- trap restore_keymap RETURN
-
- # clear previous settings
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
- # create invalid configs
- cat >/etc/vconsole.conf <<EOF
-KEYMAP=foobar
-XKBLAYOUT=hogehoge
-EOF
-
- # confirm that the invalid settings are not shown
- output=$(localectl)
- assert_in "VC Keymap: .unset." "$output"
- if [[ "$output" =~ "X11 Layout: hogehoge" ]]; then
- # Debian/Ubuntu build systemd without xkbcommon.
- echo "systemd built without xkbcommon, skipping test."
- return
- fi
- assert_in "X11 Layout: .unset." "$output"
-
- # only update the virtual console keymap
- assert_rc 0 localectl --no-convert set-keymap us
-
- output=$(localectl)
- assert_in "VC Keymap: us" "$output"
- assert_in "X11 Layout: .unset." "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in "KEYMAP=us" "$output"
- assert_not_in "XKBLAYOUT=" "$output"
-
- # clear previous settings
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
- # create invalid configs
- cat >/etc/vconsole.conf <<EOF
-KEYMAP=foobar
-XKBLAYOUT=hogehoge
-EOF
-
- # confirm that the invalid settings are not shown
- output=$(localectl)
- assert_in "VC Keymap: .unset." "$output"
- assert_in "X11 Layout: .unset." "$output"
-
- # only update the X11 keyboard layout
- assert_rc 0 localectl --no-convert set-x11-keymap us
-
- output=$(localectl)
- assert_in "VC Keymap: .unset." "$output"
- assert_in "X11 Layout: us" "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_not_in "KEYMAP=" "$output"
- assert_in "XKBLAYOUT=us" "$output"
-
- # clear previous settings
- systemctl stop systemd-localed.service
- wait_vconsole_setup
- rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
- # create invalid configs
- cat >/etc/vconsole.conf <<EOF
-KEYMAP=foobar
-XKBLAYOUT=hogehoge
-EOF
-
- # update the virtual console keymap with conversion
- assert_rc 0 localectl set-keymap us
-
- output=$(localectl)
- assert_in "VC Keymap: us" "$output"
- assert_in "X11 Layout: us" "$output"
-
- output=$(cat /etc/vconsole.conf)
- assert_in "KEYMAP=us" "$output"
- assert_in "XKBLAYOUT=us" "$output"
-}
-
-locale_gen_cleanup() {
- # Some running apps might keep the mount point busy, hence the lazy unmount
- mountpoint -q /usr/lib/locale && umount --lazy /usr/lib/locale
- [[ -e /tmp/locale.gen.bak ]] && mv -f /tmp/locale.gen.bak /etc/locale.gen
-
- return 0
-}
-
-# Issue: https://github.com/systemd/systemd/pull/27179
-testcase_locale_gen_leading_space() {
- if ! command -v locale-gen >/dev/null; then
- echo "No locale-gen support, skipping test."
- return 0
- fi
-
- [[ -e /etc/locale.gen ]] && cp -f /etc/locale.gen /tmp/locale.gen.bak
- trap locale_gen_cleanup RETURN
- # Overmount the existing locale-gen database with an empty directory
- # to force it to regenerate locales
- mount -t tmpfs tmpfs /usr/lib/locale
-
- {
- echo -e "en_US.UTF-8 UTF-8"
- echo -e " en_US.UTF-8 UTF-8"
- echo -e "\ten_US.UTF-8 UTF-8"
- echo -e " \t en_US.UTF-8 UTF-8 \t"
- } >/etc/locale.gen
-
- localectl set-locale de_DE.UTF-8
- localectl set-locale en_US.UTF-8
-}
-
-# Make sure the content of kbd-model-map is the one that the tests expect
-# regardless of the version installed on the distro where the testsuite is
-# running on.
-export SYSTEMD_KBD_MODEL_MAP=/usr/lib/systemd/tests/testdata/test-keymap-util/kbd-model-map
-
-enable_debug
-run_testcases
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-/usr/lib/systemd/systemd-battery-check --help
-/usr/lib/systemd/systemd-battery-check --version
-
-/usr/lib/systemd/systemd-battery-check || :
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if systemd-detect-virt --quiet --container; then
- echo "running on container, skipping."
- exit 0
-fi
-
-if ! command -v bootctl >/dev/null; then
- echo "bootctl not found, skipping."
- exit 0
-fi
-
-if [[ ! -d /usr/lib/systemd/boot/efi ]]; then
- echo "sd-boot is not installed, skipping."
- exit 0
-fi
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-basic_tests() {
- bootctl "$@" --help
- bootctl "$@" --version
-
- bootctl "$@" install --make-entry-directory=yes
- bootctl "$@" remove --make-entry-directory=yes
-
- bootctl "$@" install --all-architectures
- bootctl "$@" remove --all-architectures
-
- bootctl "$@" install --make-entry-directory=yes --all-architectures
- bootctl "$@" remove --make-entry-directory=yes --all-architectures
-
- bootctl "$@" install
- (! bootctl "$@" update)
- bootctl "$@" update --graceful
-
- bootctl "$@" is-installed
- bootctl "$@" is-installed --graceful
- bootctl "$@" random-seed
-
- bootctl "$@"
- bootctl "$@" status
- bootctl "$@" status --quiet
- bootctl "$@" list
- bootctl "$@" list --quiet
- bootctl "$@" list --json=short
- bootctl "$@" list --json=pretty
-
- bootctl "$@" remove
- (! bootctl "$@" is-installed)
- (! bootctl "$@" is-installed --graceful)
-}
-
-testcase_bootctl_basic() {
- assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$"
- assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$"
- bootctl --print-root-device
-
- basic_tests
-}
-
-cleanup_image() (
- set +e
-
- if [[ -z "${IMAGE_DIR:-}" ]]; then
- return 0
- fi
-
- umount "${IMAGE_DIR}/root"
-
- if [[ -n "${LOOPDEV:-}" ]]; then
- losetup -d "${LOOPDEV}"
- unset LOOPDEV
- fi
-
- udevadm settle
-
- rm -rf "${IMAGE_DIR}"
- unset IMAGE_DIR
-
- return 0
-)
-
-testcase_bootctl_image() {
- IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
- trap cleanup_image RETURN
-
- truncate -s 256m "${IMAGE_DIR}/image"
-
- cat >"${IMAGE_DIR}/partscript" <<EOF
-label: gpt
-type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=64M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
-type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
-EOF
-
- LOOPDEV="$(losetup --show -P -f "${IMAGE_DIR}/image")"
- sfdisk "$LOOPDEV" <"${IMAGE_DIR}/partscript"
-
- udevadm settle
-
- mkfs.vfat -n esp "${LOOPDEV}p1"
- mkfs.ext4 -L root "${LOOPDEV}p2"
- mkfs.ext4 -L boot "${LOOPDEV}p3"
-
- mkdir -p "${IMAGE_DIR}/root"
- mount -t ext4 "${LOOPDEV}p2" "${IMAGE_DIR}/root"
-
- mkdir -p "${IMAGE_DIR}/root/efi"
- mkdir -p "${IMAGE_DIR}/root/boot"
- mkdir -p "${IMAGE_DIR}/root/etc"
- mkdir -p "${IMAGE_DIR}/root/usr/lib"
- if [[ -f /usr/lib/os-release ]]; then
- cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
- ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
- else
- cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
- fi
-
- umount "${IMAGE_DIR}/root"
-
- assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path)" "/run/systemd/mount-rootfs/efi"
- assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path --esp-path=/efi)" "/run/systemd/mount-rootfs/efi"
- assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path)" "/run/systemd/mount-rootfs/boot"
- assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path --boot-path=/boot)" "/run/systemd/mount-rootfs/boot"
-
- # FIXME: This provides spurious result.
- bootctl --image "${IMAGE_DIR}/image" --print-root-device || :
-
- basic_tests --image "${IMAGE_DIR}/image"
-}
-
-cleanup_raid() (
- set +e
-
- if [[ -z "${IMAGE_DIR:-}" ]]; then
- return 0
- fi
-
- systemd-umount "${IMAGE_DIR}/root/efi"
- systemd-umount "${IMAGE_DIR}/root/boot"
- systemd-umount "${IMAGE_DIR}/root"
-
- mdadm --misc --stop /dev/md/raid-esp
- mdadm --misc --stop /dev/md/raid-root
-
- if [[ -n "${LOOPDEV1:-}" ]]; then
- mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
- mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
- fi
-
- if [[ -n "${LOOPDEV2:-}" ]]; then
- mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
- mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
- fi
-
- udevadm settle
-
- if [[ -n "${LOOPDEV1:-}" ]]; then
- mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
- mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
- losetup -d "${LOOPDEV1}"
- unset LOOPDEV1
- fi
-
- if [[ -n "${LOOPDEV2:-}" ]]; then
- mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
- mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
- losetup -d "${LOOPDEV2}"
- unset LOOPDEV2
- fi
-
- udevadm settle
-
- rm -rf "${IMAGE_DIR}"
-
- return 0
-)
-
-testcase_bootctl_raid() {
- if ! command -v mdadm >/dev/null; then
- echo "mdadm not found, skipping."
- return 0
- fi
-
- if ! command -v mkfs.btrfs >/dev/null; then
- echo "mkfs.btrfs not found, skipping."
- return 0
- fi
-
- IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
- trap cleanup_raid RETURN
-
- truncate -s 256m "${IMAGE_DIR}/image1"
- truncate -s 256m "${IMAGE_DIR}/image2"
-
- cat >"${IMAGE_DIR}/partscript" <<EOF
-label: gpt
-type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=64M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
-type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
-EOF
-
- LOOPDEV1="$(losetup --show -P -f "${IMAGE_DIR}/image1")"
- LOOPDEV2="$(losetup --show -P -f "${IMAGE_DIR}/image2")"
- sfdisk "$LOOPDEV1" <"${IMAGE_DIR}/partscript"
- sfdisk "$LOOPDEV2" <"${IMAGE_DIR}/partscript"
-
- udevadm settle
-
- echo y | mdadm --create /dev/md/raid-esp --name "raid-esp" "${LOOPDEV1}p1" "${LOOPDEV2}p1" -v -f --level=1 --raid-devices=2
- mkfs.vfat /dev/md/raid-esp
- echo y | mdadm --create /dev/md/raid-root --name "raid-root" "${LOOPDEV1}p2" "${LOOPDEV2}p2" -v -f --level=1 --raid-devices=2
- mkfs.ext4 /dev/md/raid-root
- mkfs.btrfs -f -M -d raid1 -m raid1 -L "raid-boot" "${LOOPDEV1}p3" "${LOOPDEV2}p3"
-
- mkdir -p "${IMAGE_DIR}/root"
- mount -t ext4 /dev/md/raid-root "${IMAGE_DIR}/root"
- mkdir -p "${IMAGE_DIR}/root/efi"
- mount -t vfat /dev/md/raid-esp "${IMAGE_DIR}/root/efi"
- mkdir -p "${IMAGE_DIR}/root/boot"
- mount -t btrfs "${LOOPDEV1}p3" "${IMAGE_DIR}/root/boot"
-
- mkdir -p "${IMAGE_DIR}/root/etc"
- mkdir -p "${IMAGE_DIR}/root/usr/lib"
- if [[ -f /usr/lib/os-release ]]; then
- cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
- ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
- else
- cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
- fi
-
- # find_esp() does not support md RAID partition.
- (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path)
- (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)
-
- # If the verification is relaxed, it accepts md RAID partition.
- assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path)" "${IMAGE_DIR}/root/efi"
- assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)" "${IMAGE_DIR}/root/efi"
-
- # find_xbootldr() does not support btrfs RAID, and bootctl tries to fall back to use ESP.
- # (but as in the above, the ESP verification is also failed in this case).
- (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path)
- (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)
-
- # If the verification for ESP is relaxed, bootctl falls back to use ESP.
- assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/efi"
-
- # If the verification is relaxed, it accepts the xbootldr partition.
- assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/boot"
- assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)" "${IMAGE_DIR}/root/boot"
-
- # FIXME: This provides spurious result.
- bootctl --root "${IMAGE_DIR}/root" --print-root-device || :
-
- SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
-}
-
-testcase_bootctl_varlink() {
- varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}'
-
- # We may have UEFI in the test environment.
- # If we don't have UEFI then we can test whether bootctl's varlink API fails cleanly.
- # If we do have UEFI then the rest of the clean fail tests should be skipped.
- if ! (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported; then
- return 0
- fi
- (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
- (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
-}
-
-run_testcases
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Unset $PAGER so we don't have to use --no-pager everywhere
-export PAGER=
-
-busctl --help
-busctl help
-busctl --version
-busctl
-busctl list --no-pager --allow-interactive-authorization=no
-busctl list
-busctl list --unique --show-machine --full
-# Pass the JSON output (-j) through jq to check if it's valid
-busctl list --acquired --activatable --no-legend -j | jq
-busctl status
-busctl status --machine=.host --augment-creds=no
-busctl status --user --machine=testuser@.host
-busctl status org.freedesktop.systemd1
-# Ignore the exit code here, since this runs during machine bootup, so busctl
-# might attempt to introspect a job that already finished and fail, i.e.:
-# Failed to introspect object /org/freedesktop/systemd1/job/335 of service org.freedesktop.systemd1: Unknown object '/org/freedesktop/systemd1/job/335'.
-busctl tree || :
-busctl tree org.freedesktop.login1
-busctl tree --list org.freedesktop.login1
-busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
-busctl introspect --watch-bind=yes --xml-interface org.freedesktop.systemd1 /org/freedesktop/LogControl1
-busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager
-
-busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- GetDefaultTarget
-# Pass both JSON outputs through jq to check if the response JSON is valid
-busctl call --json=pretty \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
-busctl call --json=short \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
-# Get all properties on the org.freedesktop.systemd1.Manager interface and dump
-# them as JSON to exercise the internal JSON transformations
-busctl call -j \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Properties \
- GetAll s "org.freedesktop.systemd1.Manager" | jq -c
-busctl call --verbose --timeout=60 --expect-reply=yes \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"
-
-busctl emit /org/freedesktop/login1 org.freedesktop.login1.Manager \
- PrepareForSleep b false
-busctl emit --auto-start=no --destination=systemd-logind.service \
- /org/freedesktop/login1 org.freedesktop.login1.Manager \
- PrepareForShutdown b false
-
-busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- Version
-busctl get-property --verbose \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- LogLevel LogTarget SystemState Version
-# Pass both JSON outputs through jq to check if the response JSON is valid
-busctl get-property --json=pretty \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- LogLevel LogTarget SystemState Version | jq
-busctl get-property --json=short \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- LogLevel LogTarget SystemState Version | jq
-
-# Set a property and check if it was indeed set
-busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- KExecWatchdogUSec t 666
-busctl get-property -j \
- org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- KExecWatchdogUSec | jq -e '.data == 666'
-
-(! busctl status org.freedesktop.systemd2)
-(! busctl tree org.freedesktop.systemd2)
-(! busctl introspect org.freedesktop.systemd1)
-(! busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd2)
-(! busctl introspect org.freedesktop.systemd2 /org/freedesktop/systemd1)
-
-# Invalid method
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- ThisMethodDoesntExist)
-# Invalid signature
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- ListUnitsByNames ab 1 false)
-# Invalid arguments
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- GetUnitByPID u "hello")
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- -- ListUnitsByNames as -1 "systemd-journald.service")
-# Not enough arguments
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- ListUnitsByNames as 99 "systemd-journald.service")
-
-(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- NonexistentProperty)
-(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- Version NonexistentProperty Version)
-
-# Invalid property
-(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- NonexistentProperty t 666)
-# Invalid signature
-(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- KExecWatchdogUSec s 666)
-# Invalid argument
-(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
- KExecWatchdogUSec t "foo")
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-at_exit() {
- set +e
- systemctl --no-block stop capsule@foobar.service
- rm -rf /run/capsules/foobar
- rm -rf /var/lib/capsules/foobar
- rm -f /run/systemd/system/capsule@.service.d/99-asan.conf
-}
-
-trap at_exit EXIT
-
-# Appease ASan, since the capsule@.service uses DynamicUser=yes
-systemctl edit --runtime --stdin capsule@.service --drop-in=99-asan.conf <<EOF
-[Service]
-EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
-EOF
-
-(! test -f /run/capsules/foobar )
-(! test -f /var/lib/capsules/foobar )
-(! id -u c-foobar )
-
-systemctl start capsule@foobar.service
-
-test -d /run/capsules/foobar
-test -d /var/lib/capsules/foobar
-id -u c-foobar
-
-systemctl status capsule@foobar.service
-
-busctl -C foobar
-
-systemctl -C foobar
-
-systemd-run -C foobar -u sleepinfinity /bin/sleep infinity
-
-systemctl -C foobar status sleepinfinity
-
-systemctl -C foobar stop sleepinfinity
-
-(! systemctl clean capsule@foobar.service )
-
-systemctl stop capsule@foobar.service
-
-systemctl clean capsule@foobar.service --what=all
-
-(! test -f /run/capsules/foobar )
-(! test -f /var/lib/capsules/foobar )
-(! id -u c-foobar )
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-cgls
-systemd-cgls --all --full
-systemd-cgls -k
-systemd-cgls --xattr=yes
-systemd-cgls --xattr=no
-systemd-cgls --cgroup-id=yes
-systemd-cgls --cgroup-id=no
-
-systemd-cgls /system.slice/systemd-journald.service
-systemd-cgls /system.slice/systemd-journald.service /init.scope
-systemd-cgls /sys/fs/cgroup/system.slice/systemd-journald.service /init.scope
-[[ -d /sys/fs/cgroup/init.scope ]] && init_scope="init.scope" || init_scope="systemd/init.scope"
-(cd "/sys/fs/cgroup/$init_scope" && systemd-cgls)
-systemd-cgls --unit=systemd-journald.service
-# There's most likely no user session running, so we need to create one
-systemd-run --user --wait --pipe -M testuser@.host systemd-cgls --user-unit=app.slice
-
-(! systemd-cgls /foo/bar)
-(! systemd-cgls --unit=hello.world)
-(! systemd-cgls --user-unit=hello.world)
-(! systemd-cgls --xattr=foo)
-(! systemd-cgls --cgroup-id=foo)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Without tty attached cgtop should default to --iterations=1
-systemd-cgtop
-systemd-cgtop --iterations=1
-# Same as --iterations=1
-systemd-cgtop -1
-systemd-cgtop --delay=1ms
-systemd-cgtop --raw
-systemd-cgtop --batch
-systemd-cgtop --cpu=percentage
-systemd-cgtop --cpu=time
-systemd-cgtop -P
-systemd-cgtop -k
-systemd-cgtop --recursive=no -P
-systemd-cgtop --recursive=no -k
-systemd-cgtop --depth=0
-systemd-cgtop --depth=100
-
-for order in path tasks cpu memory io; do
- systemd-cgtop --order="$order"
-done
-systemd-cgtop -p -t -c -m -i
-
-(! systemd-cgtop --cpu=foo)
-(! systemd-cgtop --order=foo)
-(! systemd-cgtop --depth=-1)
-(! systemd-cgtop --recursive=foo)
-(! systemd-cgtop --delay=1foo)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
- . "$(dirname "$0")"/util.sh
-
-# Make sure the binary name fits into 15 characters
-CORE_TEST_BIN="/tmp/test-dump"
-CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
-MAKE_DUMP_SCRIPT="/tmp/make-dump"
-# Unset $PAGER so we don't have to use --no-pager everywhere
-export PAGER=
-
-at_exit() {
- rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
-}
-
-trap at_exit EXIT
-
-if systemd-detect-virt -cq; then
- echo "Running in a container, skipping the systemd-coredump test..."
- exit 0
-fi
-
-# To make all coredump entries stored in system.journal.
-journalctl --rotate
-
-# Check that we're the ones to receive coredumps
-sysctl kernel.core_pattern | grep systemd-coredump
-
-# Prepare "fake" binaries for coredumps, so we can properly exercise
-# the matching stuff too
-cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
-cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
-# Simple script that spawns given "fake" binary and then kills it with
-# given signal
-cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF
-#!/bin/bash -ex
-
-bin="${1:?}"
-sig="${2:?}"
-
-ulimit -c unlimited
-"$bin" infinity &
-pid=$!
-# Sync with the "fake" binary, so we kill it once it's fully forked off,
-# otherwise we might kill it during fork and kernel would then report
-# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN).
-# In this case, wait until the "fake" binary (sleep in this case) enters
-# the "interruptible sleep" state, at which point it should be ready
-# to be sacrificed.
-for _ in {0..9}; do
- read -ra self_stat <"/proc/$pid/stat"
- [[ "${self_stat[2]}" == S ]] && break
- sleep .5
-done
-kill -s "$sig" "$pid"
-# This should always fail
-! wait "$pid"
-EOF
-chmod +x "$MAKE_DUMP_SCRIPT"
-
-# Privileged stuff
-[[ "$(id -u)" -eq 0 ]]
-# Trigger a couple of coredumps
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
-# In the tests we store the coredumps in journals, so let's generate a couple
-# with Storage=external as well
-mkdir -p /run/systemd/coredump.conf.d/
-printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
-rm -fv /run/systemd/coredump.conf.d/99-external.conf
-# Wait a bit for the coredumps to get processed
-timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
-
-if cgroupfs_supports_user_xattrs; then
- # Make sure we can forward crashes back to containers
- CONTAINER="testsuite-74-container"
-
- mkdir -p "/var/lib/machines/$CONTAINER"
- mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
- # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
- # but we need the ASan-related overrides scattered across /etc
- cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
-[Service]
-ExecStart=
-ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
- --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
-EOF
- systemctl daemon-reload
-
- [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
-
- machinectl start "$CONTAINER"
- timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
-
- [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
- machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
- systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT"
- systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
- # Wait a bit for the coredumps to get processed
- timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"
-
- rm -rf "/var/lib/machines/$CONTAINER"
-fi
-
-coredumpctl
-SYSTEMD_LOG_LEVEL=debug coredumpctl
-coredumpctl --help
-coredumpctl --version
-coredumpctl --no-pager --no-legend
-coredumpctl --all
-coredumpctl -1
-coredumpctl -n 1
-coredumpctl --reverse
-coredumpctl -F COREDUMP_EXE
-coredumpctl --json=short | jq
-coredumpctl --json=pretty | jq
-coredumpctl --json=off
-coredumpctl --root=/
-coredumpctl --directory=/var/log/journal
-coredumpctl --file="/var/log/journal/$(</etc/machine-id)"/*.journal
-coredumpctl --since=@0
-coredumpctl --since=yesterday --until=tomorrow
-# We should have a couple of externally stored coredumps
-coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
-grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
-rm -f /tmp/coredumpctl.out
-
-coredumpctl info
-coredumpctl info "$CORE_TEST_BIN"
-coredumpctl info /foo /bar/ /baz "$CORE_TEST_BIN"
-coredumpctl info "${CORE_TEST_BIN##*/}"
-coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}"
-coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN"
-coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN"
-
-coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN"
-SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN"
-coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"
-
-coredumpctl dump "$CORE_TEST_BIN" >/tmp/core.redirected
-test -s /tmp/core.redirected
-coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}"
-test -s /tmp/core.output
-rm -f /tmp/core.{output,redirected}
-
-# Unprivileged stuff
-# Related issue: https://github.com/systemd/systemd/issues/26912
-UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
-# Trigger a couple of coredumps as an unprivileged user
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
-# In the tests we store the coredumps in journals, so let's generate a couple
-# with Storage=external as well
-mkdir -p /run/systemd/coredump.conf.d/
-printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
-rm -fv /run/systemd/coredump.conf.d/99-external.conf
-# Wait a bit for the coredumps to get processed
-timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
-
-# root should see coredumps from both binaries
-coredumpctl info "$CORE_TEST_UNPRIV_BIN"
-coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
-# The test user should see only their own coredumps
-"${UNPRIV_CMD[@]}" coredumpctl
-"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN"
-"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
-(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN")
-(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}")
-# We should have a couple of externally stored coredumps
-"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
-grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
-rm -f /tmp/coredumpctl.out
-
-"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN"
-"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}"
-
-"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected
-test -s /tmp/core.redirected
-"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}"
-test -s /tmp/core.output
-rm -f /tmp/core.{output,redirected}
-(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null)
-
-# --backtrace mode
-# Pass one of the existing journal coredump records to systemd-coredump and
-# use our PID as the source to make matching the coredump later easier
-# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME
-journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
- /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
-# Wait a bit for the coredump to get processed
-timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
-coredumpctl info "$$"
-coredumpctl info COREDUMP_HOSTNAME="mymachine"
-
-# This used to cause a stack overflow
-systemd-run -t --property CoredumpFilter=all ls /tmp
-systemd-run -t --property CoredumpFilter=default ls /tmp
-
-(! coredumpctl --hello-world)
-(! coredumpctl -n 0)
-(! coredumpctl -n -1)
-(! coredumpctl --file=/dev/null)
-(! coredumpctl --since=0)
-(! coredumpctl --until='')
-(! coredumpctl --since=today --until=yesterday)
-(! coredumpctl --directory=/ --root=/)
-(! coredumpctl --json=foo)
-(! coredumpctl -F foo -F bar)
-(! coredumpctl list 0)
-(! coredumpctl list -- -1)
-(! coredumpctl list '')
-(! coredumpctl info /../.~=)
-(! coredumpctl info '')
-(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN")
-(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN")
-(! coredumpctl debug --debugger=/bin/false)
-(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-at_exit() {
- rm -rfv /{run,etc}/systemd/system/delta-test*
-}
-
-trap at_exit EXIT
-
-# Create a couple of supporting units with overrides
-#
-# Extended unit
-cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF
-[Service]
-ExecStart=true
-EOF
-mkdir -p "/run/systemd/system/delta-test-unit-extended.service.d"
-cat >"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <<EOF
-[Unit]
-Description=Foo Bar
-[Service]
-ExecStartPre=true
-EOF
-# Masked unit
-cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service
-systemctl mask delta-test-unit-masked.service
-# Overridden unit
-cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-overridden.service
-cp -fv /run/systemd/system/delta-test-unit-overridden.service /etc/systemd/system/delta-test-unit-overridden.service
-echo "ExecStartPost=/bin/true" >>/etc/systemd/system/delta-test-unit-overridden.service
-# Overridden but equivalent unit
-ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-equivalent.service
-ln -sfv /run/systemd/system/delta-test-unit-extended.service /etc/systemd/system/delta-test-unit-equivalent.service
-# Redirected unit
-ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-redirected.service
-ln -sfv /run/systemd/system/delta-test-unit-overidden.service /etc/systemd/system/delta-test-unit-extended.service
-
-systemctl daemon-reload
-
-systemd-delta
-systemd-delta /run
-systemd-delta systemd/system
-systemd-delta /run systemd/system /run
-systemd-delta /run foo/bar hello/world systemd/system /run
-systemd-delta foo/bar
-systemd-delta --diff=true
-systemd-delta --diff=false
-
-for type in masked equivalent redirected overridden extended unchanged; do
- systemd-delta --type="$type"
- systemd-delta --type="$type" /run
-done
-systemd-delta --type=equivalent,redirected
-
-(! systemd-delta --diff=foo)
-(! systemd-delta --type=foo)
-(! systemd-delta --type=equivalent,redirected,foo)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Simple wrapper to check both escaping and unescaping of given strings
-# Arguments:
-# $1 - expected unescaped string
-# $2 - expected escaped string
-# $3 - optional arguments for systemd-escape
-check_escape() {
- unescaped="${1?}"
- escaped="${2?}"
- shift 2
-
- assert_eq "$(systemd-escape "$@" -- "$unescaped")" "$escaped"
- assert_eq "$(systemd-escape "$@" --unescape -- "$escaped")" "$unescaped"
-}
-
-systemd-escape --help
-systemd-escape --version
-
-check_escape '' ''
-check_escape 'hello' 'hello'
-check_escape 'hello-world' 'hello\x2dworld'
-check_escape '-+ěščřž---🤔' '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94'
-check_escape '/this/is/a/path/a b c' '-this-is-a-path-a\x20b\x20c'
-
-# Multiple strings to escape/unescape
-assert_eq "$(systemd-escape 'hello-world' '/dev/loop1' 'template@🐍')" \
- 'hello\x2dworld -dev-loop1 template\x40\xf0\x9f\x90\x8d'
-assert_eq "$(systemd-escape --unescape -- 'hello\x2dworld' '-dev-loop1' 'template\x40\xf0\x9f\x90\x8d')" \
- 'hello-world /dev/loop1 template@🐍'
-
-# --suffix= is not compatible with --unescape
-assert_eq "$(systemd-escape --suffix=mount -- '-+ěščřž---🤔')" \
- '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94.mount'
-assert_eq "$(systemd-escape --suffix=timer 'this has spaces')" \
- 'this\x20has\x20spaces.timer'
-assert_eq "$(systemd-escape --suffix=service 'trailing-spaces ')" \
- 'trailing\x2dspaces\x20\x20.service'
-assert_eq "$(systemd-escape --suffix=automount ' leading-spaces')" \
- '\x20\x20\x20leading\x2dspaces.automount'
-
-# --template=
-check_escape 'hello' 'hello@hello.service' --template=hello@.service
-check_escape ' what - is _ love? 🤔 ¯\_(ツ)_/¯' \
- 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service' \
- --template=hello@.service
-check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
- 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
- --template=mount-my-stuff@.service
-check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
- 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
- --template=mount-my-stuff@.service --path
-
-# --instance (must be used with --unescape)
-assert_eq "$(systemd-escape --unescape --instance 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service')" \
- ' what - is _ love? 🤔 ¯\_(ツ)_/¯'
-assert_eq "$(systemd-escape --unescape --instance 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
- '/this/is/where/my/stuff/is/ with spaces though '
-assert_eq "$(systemd-escape --unescape --instance --path 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
- '/this/is/where/my/stuff/is/ with spaces though '
-
-# --path, reversible cases
-check_escape / '-' --path
-check_escape '/hello/world' 'hello-world' --path
-check_escape '/mnt/smb/おにぎり' \
- 'mnt-smb-\xe3\x81\x8a\xe3\x81\xab\xe3\x81\x8e\xe3\x82\x8a' \
- --path
-
-# --path, non-reversible cases
-assert_eq "$(systemd-escape --path ///////////////)" '-'
-assert_eq "$(systemd-escape --path /..)" '-'
-assert_eq "$(systemd-escape --path /../.././../.././)" '-'
-assert_eq "$(systemd-escape --path /../.././../.././foo)" 'foo'
-
-# --mangle
-assert_eq "$(systemd-escape --mangle 'hello-world')" 'hello-world.service'
-assert_eq "$(systemd-escape --mangle '/mount/this')" 'mount-this.mount'
-assert_eq "$(systemd-escape --mangle 'my-service@ 🐱 ')" 'my-service@\x20\xf0\x9f\x90\xb1\x20.service'
-assert_eq "$(systemd-escape --mangle '/dev/disk/by-emoji/🍎')" 'dev-disk-by\x2demoji-\xf0\x9f\x8d\x8e.device'
-assert_eq "$(systemd-escape --mangle 'daily-existential-crisis .timer')" 'daily-existential-crisis\x20.timer'
-assert_eq "$(systemd-escape --mangle 'trailing-whitespace.mount ')" 'trailing-whitespace.mount\x20.service'
-
-(! systemd-escape)
-(! systemd-escape --suffix='' hello)
-(! systemd-escape --suffix=invalid hello)
-(! systemd-escape --suffix=mount --template=hello@.service hello)
-(! systemd-escape --suffix=mount --mangle)
-(! systemd-escape --template='')
-(! systemd-escape --template=@)
-(! systemd-escape --template='hello@.service' '')
-(! systemd-escape --unescape --template='hello@.service' '@hello.service')
-(! systemd-escape --unescape --template='hello@.service' 'hello@.service')
-(! systemd-escape --mangle --template=hello@.service hello)
-(! systemd-escape --instance 'hello@hello.service')
-(! systemd-escape --instance --template=hello@.service 'hello@hello.service')
-(! systemd-escape --unescape --instance --path 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')
-(! systemd-escape --path '/../hello/..')
-(! systemd-escape --path '.')
-(! systemd-escape --path '..')
-(! systemd-escape --path "$(set +x; printf '%0.sa' {0..256})")
-(! systemd-escape --unescape --path '')
-(! systemd-escape --mangle '')
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if ! command -v systemd-firstboot >/dev/null; then
- echo "systemd-firstboot not found, skipping the test"
- exit 0
-fi
-
-at_exit() {
- if [[ -n "${ROOT:-}" ]]; then
- ls -lR "$ROOT"
- rm -fr "$ROOT"
- fi
-
- restore_locale
-}
-
-trap at_exit EXIT
-
-# Generated via `mkpasswd -m sha-512 -S foobarsalt password1`
-# shellcheck disable=SC2016
-ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41'
-# Generated via `mkpasswd -m sha-512 -S foobarsalt password2`
-# shellcheck disable=SC2016
-ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.'
-
-if [[ -f /etc/locale.conf ]]; then
- cp /etc/locale.conf /tmp/locale.conf.bak
-fi
-
-# Debian/Ubuntu specific file
-if [[ -f /etc/default/locale ]]; then
- cp /etc/default/locale /tmp/default-locale.bak
-fi
-
-if [[ -f /etc/locale.gen ]]; then
- cp /etc/locale.gen /tmp/locale.gen.bak
-fi
-
-# Make sure at least two locales exist (C.UTF-8 and en_US.UTF-8) as systemd-firstboot --prompt-locale will
-# skip writing the locale if it detects only one is installed.
-generate_locale en_US.UTF-8
-
-# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make
-# sure we use the appropriate path for locale configuration.
-LOCALE_PATH="/etc/locale.conf"
-[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale"
-[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8
-
-# Create a minimal root so we don't modify the testbed
-ROOT=test-root
-mkdir -p "$ROOT/bin"
-# Dummy shell for --root-shell=
-touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
-
-systemd-firstboot --root="$ROOT" --locale=foo
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-rm -fv "$ROOT$LOCALE_PATH"
-systemd-firstboot --root="$ROOT" --locale-messages=foo
-grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH"
-rm -fv "$ROOT$LOCALE_PATH"
-systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
-
-systemd-firstboot --root="$ROOT" --keymap=foo
-grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
-
-systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin
-readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin"
-
-systemd-firstboot --root="$ROOT" --hostname "foobar"
-grep -q "foobar" "$ROOT/etc/hostname"
-
-systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
-
-rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
-systemd-firstboot --root="$ROOT" --root-password=foo
-grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
-grep -q "^root:" "$ROOT/etc/shadow"
-rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
-echo "foo" >root.passwd
-systemd-firstboot --root="$ROOT" --root-password-file=root.passwd
-grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
-grep -q "^root:" "$ROOT/etc/shadow"
-rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd
-# Set the shell together with the password, as firstboot won't touch
-# /etc/passwd if it already exists
-systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell
-grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
-
-systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42"
-grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
-
-# Configs should not get overwritten if they exist unless --force is used
-systemd-firstboot --root="$ROOT" \
- --locale=locale-overwrite \
- --locale-messages=messages-overwrite \
- --keymap=keymap-overwrite \
- --timezone=CET \
- --hostname=hostname-overwrite \
- --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
- --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
- --root-shell=/bin/barshell \
- --kernel-command-line="hello.world=0"
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
-grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
-readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
-grep -q "foobar" "$ROOT/etc/hostname"
-grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
-grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
-grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
-
-# The same thing, but now with --force
-systemd-firstboot --root="$ROOT" --force \
- --locale=locale-overwrite \
- --locale-messages=messages-overwrite \
- --keymap=keymap-overwrite \
- --timezone=CET \
- --hostname=hostname-overwrite \
- --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
- --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
- --root-shell=/bin/barshell \
- --kernel-command-line="hello.world=0"
-grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH"
-grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf"
-readlink "$ROOT/etc/localtime" | grep -q "/CET$"
-grep -q "hostname-overwrite" "$ROOT/etc/hostname"
-grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id"
-grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
-grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow"
-grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline"
-
-# Test that --reset removes all files configured by firstboot.
-systemd-firstboot --root="$ROOT" --reset
-[[ ! -e "$ROOT/etc/locale.conf" ]]
-[[ ! -e "$ROOT/etc/vconsole.conf" ]]
-[[ ! -e "$ROOT/etc/localtime" ]]
-[[ ! -e "$ROOT/etc/hostname" ]]
-[[ ! -e "$ROOT/etc/machine-id" ]]
-[[ ! -e "$ROOT/etc/kernel/cmdline" ]]
-
-# --copy-* options
-rm -fr "$ROOT"
-mkdir "$ROOT"
-# Copy everything at once (--copy)
-systemd-firstboot --root="$ROOT" --copy
-diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
-diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
-diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
-[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
-[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
-rm -fr "$ROOT"
-mkdir "$ROOT"
-# Copy everything at once, but now by using separate switches
-systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell
-diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
-diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
-diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
-[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
-[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
-
-# --prompt-* options
-rm -fr "$ROOT"
-mkdir -p "$ROOT/bin"
-touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
-# Temporarily disable pipefail to avoid `echo: write error: Broken pipe
-set +o pipefail
-# We can do only limited testing here, since it's all an interactive stuff,
-# so --prompt and --prompt-root-password are skipped on purpose
-echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
-# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so
-# it can present them as a list to the user. As Debian does not ship/provide
-# compatible keymaps (from the kbd package), skip this test if the keymaps are
-# missing.
-if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then
- echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
- grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
-fi
-echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
-readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
-echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
-grep -q "foobar" "$ROOT/etc/hostname"
-echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
-grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-# Existing files should not get overwritten
-echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
-grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-# Now without the welcome screen but with force
-echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no
-grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
-# Re-enable pipefail
-set -o pipefail
-
-# Assorted tests
-rm -fr "$ROOT"
-mkdir "$ROOT"
-
-systemd-firstboot --root="$ROOT" --setup-machine-id
-grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id"
-
-systemd-firstboot --root="$ROOT" --delete-root-password
-diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
-
-(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell)
-(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
-(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-id128 --help
-systemd-id128 help
-systemd-id128 show
-systemd-id128 show --pretty | tail
-systemd-id128 show --value | tail
-systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709 # root-x86-64
-systemd-id128 show --pretty 4f68bce3e8cd4db196e7fbcaf984b709
-systemd-id128 show root-x86-64
-systemd-id128 show --pretty root-x86-64
-[[ "$(systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
-[[ "$(systemd-id128 show 4f68bce3-e8cd-4db1-96e7-fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
-
-systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-[[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]]
-
-systemd-id128 show -j
-systemd-id128 show --no-pager
-systemd-id128 show --json=short
-systemd-id128 show --no-legend
-systemd-id128 show --no-pager --no-legend
-systemd-id128 show root -P -u
-
-[[ "$(systemd-id128 new | wc -c)" -eq 33 ]]
-systemd-id128 new -p
-systemd-id128 new -u
-systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709
-
-systemd-id128 machine-id
-systemd-id128 machine-id --pretty
-systemd-id128 machine-id --uuid
-systemd-id128 machine-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-assert_eq "$(systemd-id128 machine-id)" "$(</etc/machine-id)"
-
-systemd-id128 boot-id
-systemd-id128 boot-id --pretty
-systemd-id128 boot-id --uuid
-systemd-id128 boot-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-assert_eq "$(systemd-id128 boot-id --uuid)" "$(</proc/sys/kernel/random/boot_id)"
-
-# shellcheck disable=SC2016
-systemd-run --wait --pipe bash -euxc '[[ $INVOCATION_ID == "$(systemd-id128 invocation-id)" ]]'
-
-(! systemd-id128)
-(! systemd-id128 new -a '')
-(! systemd-id128 new -a '0')
-(! systemd-id128 invocation-id -a 4f68bce3e8cd4db196e7fbcaf984b709)
-(! systemd-id128 show '')
-(! systemd-id128 show "$(set +x; printf '%0.s0' {0..64})")
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2064
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-root_mock() {
- local root="${1:?}"
-
- mkdir -p "$root"
- # Put a tmpfs over the "root", so we're able to remount it as read-only
- # when needed
- mount -t tmpfs tmpfs "$root"
- mkdir "$root/etc" "$root/run"
-}
-
-root_cleanup() {
- local root="${1:?}"
-
- umount --recursive "$root"
- rm -fr "$root"
-}
-
-testcase_sanity() {
- systemd-machine-id-setup
- systemd-machine-id-setup --help
- systemd-machine-id-setup --version
- systemd-machine-id-setup --print
- systemd-machine-id-setup --root= --print
- systemd-machine-id-setup --root=/ --print
-
- (! systemd-machine-id-setup "")
- (! systemd-machine-id-setup --foo)
-}
-
-testcase_invalid() {
- local root machine_id
-
- root="$(mktemp -d)"
- trap "root_cleanup $root" RETURN
- root_mock "$root"
-
- systemd-machine-id-setup --print --root "$root"
- echo abc >>"$root/etc/machine-id"
- machine_id="$(systemd-machine-id-setup --print --root "$root")"
- diff <(echo "$machine_id") "$root/etc/machine-id"
-}
-
-testcase_transient() {
- local root transient_id committed_id
-
- root="$(mktemp -d)"
- trap "root_cleanup $root" RETURN
- root_mock "$root"
-
- systemd-machine-id-setup --print --root "$root"
- echo abc >>"$root/etc/machine-id"
- mount -o remount,ro "$root"
- mount -t tmpfs tmpfs "$root/run"
- transient_id="$(systemd-machine-id-setup --print --root "$root")"
- mount -o remount,rw "$root"
- committed_id="$(systemd-machine-id-setup --print --commit --root "$root")"
- [[ "$transient_id" == "$committed_id" ]]
- diff "$root/etc/machine-id" "$root/run/machine-id"
-}
-
-# Check if we correctly processed the invalid machine ID we set up in the respective
-# test.sh file
-systemctl --state=failed --no-legend --no-pager >/failed
-test ! -s /failed
-
-run_testcases
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-MODULES_LOAD_BIN="/usr/lib/systemd/systemd-modules-load"
-CONFIG_FILE="/run/modules-load.d/99-test.conf"
-
-at_exit() {
- rm -rfv "${CONFIG_FILE:?}"
-}
-
-trap at_exit EXIT
-
-if systemd-detect-virt -cq; then
- echo "Running in a container, skipping the systemd-modules-load test..."
- exit 0
-fi
-
-ORIG_MODULES_LOAD_CONFIG="$(systemd-analyze cat-config modules-load.d)"
-
-# Check if we have required kernel modules
-modprobe --all --resolve-alias dummy
-
-mkdir -p /run/modules-load.d/
-
-"$MODULES_LOAD_BIN"
-"$MODULES_LOAD_BIN" --help
-"$MODULES_LOAD_BIN" --version
-
-# Explicit config file
-modprobe -v --all --remove dummy
-printf "dummy" >"$CONFIG_FILE"
-"$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log
-grep -E "Inserted module .*dummy" /tmp/out.log
-
-# Implicit config file
-modprobe -v --all --remove dummy
-printf "dummy" >"$CONFIG_FILE"
-"$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "Inserted module .*dummy" /tmp/out.log
-
-# Valid & invalid data mixed together
-modprobe -v --all --remove dummy
-cat >"$CONFIG_FILE" <<EOF
-
-dummy
-dummy
-dummy
- dummy
-dummy
- \\n\n\n\\\\\\
-
-dumm!@@123##2455
-# This is a comment
-$(printf "%.0sx" {0..4096})
-dummy
-dummy
-foo-bar-baz
-1
-"
-'
-EOF
-"$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "^Inserted module .*dummy" /tmp/out.log
-grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
-(! grep -E "This is a comment" /tmp/out.log)
-# Each module should be loaded only once, even if specified multiple times
-[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
-[[ "$(grep -Ec "^Failed to find module" /tmp/out.log)" -eq 7 ]]
-
-# Command line arguments
-modprobe -v --all --remove dummy
-# Make sure we have no config files left over that might interfere with
-# following tests
-rm -fv "$CONFIG_FILE"
-[[ "$ORIG_MODULES_LOAD_CONFIG" == "$(systemd-analyze cat-config modules-load.d)" ]]
-CMDLINE="ro root= modules_load= modules_load=, / = modules_load=foo-bar-baz,dummy modules_load=dummy,dummy,dummy"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" "$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "^Inserted module .*dummy" /tmp/out.log
-grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
-# Each module should be loaded only once, even if specified multiple times
-[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
-
-(! "$MODULES_LOAD_BIN" --nope)
-(! "$MODULES_LOAD_BIN" /foo/bar/baz)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# We're going to play around with block/loop devices, so bail out early
-# if we're running in nspawn
-if systemd-detect-virt --container >/dev/null; then
- echo "Container detected, skipping the test"
- exit 0
-fi
-
-at_exit() {
- set +e
-
- [[ -n "${LOOP:-}" ]] && losetup -d "$LOOP"
- [[ -n "${WORK_DIR:-}" ]] && rm -fr "$WORK_DIR"
-}
-
-trap at_exit EXIT
-
-WORK_DIR="$(mktemp -d)"
-
-systemd-mount --list
-systemd-mount --list --full
-systemd-mount --list --no-legend
-systemd-mount --list --no-pager
-systemd-mount --list --quiet
-
-# Set up a simple block device for further tests
-dd if=/dev/zero of="$WORK_DIR/simple.img" bs=1M count=16
-mkfs.ext4 -L sd-mount-test "$WORK_DIR/simple.img"
-LOOP="$(losetup --show --find "$WORK_DIR/simple.img")"
-udevadm wait --timeout 60 --settle "$LOOP"
-mkdir "$WORK_DIR/mnt"
-mount "$LOOP" "$WORK_DIR/mnt"
-touch "$WORK_DIR/mnt/foo.bar"
-umount "$LOOP"
-(! mountpoint "$WORK_DIR/mnt")
-
-# Mount with both source and destination set
-systemd-mount "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$WORK_DIR/mnt"
-systemd-mount --list --full
-test -e "$WORK_DIR/mnt/foo.bar"
-systemd-umount "$WORK_DIR/mnt"
-# Same thing, but with explicitly specified filesystem and disabled filesystem check
-systemd-mount --type=ext4 --fsck=no --collect "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").mount"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemd-mount --umount "$LOOP"
-# Discover additional metadata (unit description should now contain filesystem label)
-systemd-mount --no-ask-password --discover "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Description "$WORK_DIR/mnt" | grep -q sd-mount-test
-systemd-umount "$WORK_DIR/mnt"
-# Set a unit description
-systemd-mount --description="Very Important Unit" "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Very Important Unit"
-systemd-umount "$WORK_DIR/mnt"
-# Set a property
-systemd-mount --property="Description=Foo Bar" "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Foo Bar"
-systemd-umount "$WORK_DIR/mnt"
-# Set mount options
-systemd-mount --options=ro,x-foo-bar "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Options "$WORK_DIR/mnt" | grep -Eq "(^ro|,ro)"
-systemctl show -P Options "$WORK_DIR/mnt" | grep -q "x-foo-bar"
-systemd-umount "$WORK_DIR/mnt"
-
-# Mount with only source set
-systemd-mount "$LOOP"
-systemctl status /run/media/system/sd-mount-test
-systemd-mount --list --full
-test -e /run/media/system/sd-mount-test/foo.bar
-systemd-umount LABEL=sd-mount-test
-
-# Automount
-systemd-mount --automount=yes "$LOOP" "$WORK_DIR/mnt"
-systemd-mount --list --full
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
-[[ "$(systemctl show -P ActiveState "$WORK_DIR/mnt")" == inactive ]]
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl status "$WORK_DIR/mnt"
-systemd-umount "$WORK_DIR/mnt"
-# Automount + automount-specific property
-systemd-mount -A --automount-property="Description=Bar Baz" "$LOOP" "$WORK_DIR/mnt"
-systemctl show -P Description "$(systemd-escape --path "$WORK_DIR/mnt").automount" | grep -q "Bar Baz"
-test -e "$WORK_DIR/mnt/foo.bar"
-# Call --umount via --machine=, first with a relative path (bad) and then with
-# an absolute one (good)
-(! systemd-umount --machine=.host "$(realpath --relative-to=. "$WORK_DIR/mnt")")
-systemd-umount --machine=.host "$WORK_DIR/mnt"
-
-# ext4 doesn't support uid=/gid=
-(! systemd-mount -t ext4 --owner=testuser "$LOOP" "$WORK_DIR/mnt")
-
-# Automount + --bind-device
-systemd-mount --automount=yes --bind-device --timeout-idle-sec=1 "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
-# Trigger the automount
-test -e "$WORK_DIR/mnt/foo.bar"
-# Wait until it's idle again
-sleep 1.5
-# Safety net for slower/overloaded systems
-timeout 10s bash -c "while systemctl is-active -q $WORK_DIR/mnt; do sleep .2; done"
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
-# Disassemble the underlying block device
-losetup -d "$LOOP"
-unset LOOP
-# The automount unit should disappear once the underlying blockdev is gone
-timeout 10s bash -c "while systemctl status '$(systemd-escape --path "$WORK_DIR/mnt".automount)'; do sleep .2; done"
-
-# Mount a disk image
-systemd-mount --discover "$WORK_DIR/simple.img"
-# We can access files in the image even if the loopback block device is not initialized by udevd.
-test -e /run/media/system/simple.img/foo.bar
-# systemd-mount --list and systemd-umount require the loopback block device is initialized by udevd.
-udevadm settle --timeout 30
-assert_in "/dev/loop.* ext4 +sd-mount-test" "$(systemd-mount --list --full)"
-systemd-umount "$WORK_DIR/simple.img"
-
-# --owner + vfat
-#
-# Create a vfat image, as ext4 doesn't support uid=/gid= fixating for all
-# files/directories
-dd if=/dev/zero of="$WORK_DIR/owner-vfat.img" bs=1M count=16
-mkfs.vfat -n owner-vfat "$WORK_DIR/owner-vfat.img"
-LOOP="$(losetup --show --find "$WORK_DIR/owner-vfat.img")"
-udevadm wait --timeout 60 --settle "$LOOP"
-# Mount it and check the UID/GID
-[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]]
-systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$WORK_DIR/mnt"
-[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "testuser:testuser" ]]
-touch "$WORK_DIR/mnt/hello"
-[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]]
-systemd-umount LABEL=owner-vfat
-
-# tmpfs
-mkdir -p "$WORK_DIR/mnt/foo/bar"
-systemd-mount --tmpfs "$WORK_DIR/mnt/foo"
-test ! -d "$WORK_DIR/mnt/foo/bar"
-touch "$WORK_DIR/mnt/foo/baz"
-systemd-umount "$WORK_DIR/mnt/foo"
-test -d "$WORK_DIR/mnt/foo/bar"
-test ! -e "$WORK_DIR/mnt/foo/baz"
-
-# overlay
-systemd-mount --type=overlay --options="lowerdir=/etc,upperdir=$WORK_DIR/upper,workdir=$WORK_DIR/work" /etc "$WORK_DIR/overlay"
-touch "$WORK_DIR/overlay/foo"
-test -e "$WORK_DIR/upper/foo"
-systemd-umount "$WORK_DIR/overlay"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-at_exit() {
- rm -f /run/credstore/network.conf.50-testme
- rm -f /run/credstore/network.network.50-testme
- rm -f /run/systemd/networkd.conf.d/50-testme.conf
- rm -f /run/systemd/network/50-testme.network
- rm -f /run/systemd/system/systemd-network-generator.service.d/50-testme.conf
-}
-
-trap at_exit EXIT
-
-mkdir -p /run/credstore
-cat > /run/credstore/network.conf.50-testme <<EOF
-[Network]
-SpeedMeter=yes
-EOF
-
-cat > /run/credstore/network.network.50-testme <<EOF
-[Match]
-Property=IDONTEXIST
-EOF
-
-systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
-[Service]
-LoadCredential=network.conf.50-testme
-LoadCredential=network.network.50-testme
-EOF
-
-systemctl restart systemd-network-generator
-
-diff /run/credstore/network.conf.50-testme /run/systemd/networkd.conf.d/50-testme.conf
-diff /run/credstore/network.network.50-testme /run/systemd/network/50-testme.network
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
- systemctl stop systemd-networkd
-
- if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then
- rm -fvr {/usr/lib,/etc,/run}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \
- {/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
- "new" "+4"
- fi
-
- rm -f /run/systemd/networkd.conf.d/10-hoge.conf
-}
-
-trap at_exit EXIT
-
-export NETWORK_NAME="10-networkctl-test-$RANDOM.network"
-export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev"
-export LINK_NAME="10-networkctl-test-$RANDOM.link"
-cat >"/usr/lib/systemd/network/$NETWORK_NAME" <<EOF
-[Match]
-Name=test
-EOF
-
-# Test files
-
-networkctl mask --runtime "donotexist.network"
-assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
-networkctl unmask "donotexist.network" # unmask should work even without --runtime
-[[ ! -e /run/systemd/network/donotexist.network ]]
-
-touch /usr/lib/systemd/network/donotexist.network
-(! networkctl unmask "donotexist.network")
-rm /usr/lib/systemd/network/donotexist.network
-
-networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
-
-cat >new <<EOF
-[Match]
-Name=test2
-EOF
-
-EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
-(! networkctl mask --runtime "$NETWORK_NAME")
-printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
-
-networkctl mask "$NETWORK_NAME"
-assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
-(! networkctl edit "$NETWORK_NAME")
-(! networkctl edit --runtime "$NETWORK_NAME")
-(! networkctl cat "$NETWORK_NAME")
-networkctl unmask "$NETWORK_NAME"
-
-EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
-printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
-
-(! networkctl mask "$NETWORK_NAME")
-(! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
-
-cat >"+4" <<EOF
-[Network]
-IPv6AcceptRA=no
-EOF
-
-EDITOR='cp' script -ec 'networkctl edit "$NETWORK_NAME" --drop-in test' /dev/null
-cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf"
-
-networkctl cat "$NETWORK_NAME" | grep '^# ' |
- cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
-
-cat >"/usr/lib/systemd/network/$NETDEV_NAME" <<EOF
-[NetDev]
-Name=test2
-Kind=dummy
-EOF
-
-networkctl cat "$NETDEV_NAME"
-
-cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
-[Match]
-OriginalName=test2
-
-[Link]
-Alias=test_alias
-EOF
-
-SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null
-cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
-
-# Test links
-systemctl unmask systemd-networkd
-systemctl stop systemd-networkd
-(! networkctl cat @test2)
-
-systemctl start systemd-networkd
-SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
-networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
-
-EDITOR='cp' script -ec 'networkctl edit @test2 --drop-in test2.conf' /dev/null
-cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test2.conf"
-
-SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
-(! EDITOR='true' script -ec 'networkctl edit @test2 --runtime --drop-in test2.conf' /dev/null)
-
-ip_link="$(ip link show test2)"
-if systemctl --quiet is-active systemd-udevd; then
- assert_in 'alias test_alias' "$ip_link"
-fi
-
-mkdir -p /run/systemd/networkd.conf.d
-cat >/run/systemd/networkd.conf.d/10-hoge.conf <<EOF
-# TEST DROP-IN FILE
-[Network]
-SpeedMeter=yes
-EOF
-
-assert_in '# TEST DROP-IN FILE' "$(networkctl cat)"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-USER_DIRS_CONF="/root/.config/user-dirs.dirs"
-
-at_exit() {
- set +e
-
- rm -fv "${USER_DIRS_CONF:?}"
-}
-
-trap at_exit EXIT
-
-# Check that we indeed run under root to make the rest of the test work
-[[ "$(id -u)" -eq 0 ]]
-
-# Create a custom user-dirs.dir file to exercise the xdg-user-dirs part
-# of sd-path/from_user_dir()
-mkdir -p "/root/.config"
-cat >"${USER_DIRS_CONF:?}" <<\EOF
-XDG_DESKTOP_DIR="$HOME/my-fancy-desktop"
-XDG_INVALID
-
-XDG_DOWNLOAD_DIR = "$HOME"
-XDG_TEMPLATES_DIR="/templates"
-# Invalid records
-XDG_TEMPLATES_DIR=/not-templates"
-XDG_TEMPLATES_DIR="/also-not-teplates
-XDG_TEMPLATES_DIR=""
-XDG_TEMPLATES_DIR="../"
-
-XDG_PUBLICSHARE_DIR="$HOME/cat-pictures"
-XDG_DOCUMENTS_DIR="$HOME/top/secret/documents"
-XDG_MUSIC_DIR="/tmp/vaporwave"
-XDG_PICTURES_DIR="$HOME/Pictures"
-XDG_VIDEOS_DIR="$HOME/🤔"
-EOF
-
-systemd-path --help
-systemd-path --version
-systemd-path
-systemd-path temporary system-binaries user binfmt
-
-assert_eq "$(systemd-path system-runtime)" "/run"
-assert_eq "$(systemd-path --suffix='' system-runtime)" "/run"
-assert_eq "$(systemd-path --suffix='🤔' system-runtime)" "/run/🤔"
-assert_eq "$(systemd-path --suffix=hello system-runtime)" "/run/hello"
-
-# Note for the stuff below: everything defaults to $HOME, only the desktop
-# directory defaults to $HOME/Desktop.
-#
-# Check the user-dirs.dir stuff from above
-assert_eq "$(systemd-path user)" "/root"
-assert_eq "$(systemd-path user-desktop)" "/root/my-fancy-desktop"
-assert_eq "$(systemd-path user-documents)" "/root/top/secret/documents"
-assert_eq "$(systemd-path user-download)" "/root"
-assert_eq "$(systemd-path user-music)" "/tmp/vaporwave"
-assert_eq "$(systemd-path user-pictures)" "/root/Pictures"
-assert_eq "$(systemd-path user-public)" "/root/cat-pictures"
-assert_eq "$(systemd-path user-templates)" "/templates"
-assert_eq "$(systemd-path user-videos)" "/root/🤔"
-
-# Remove the user-dirs.dir file and check the defaults
-rm -fv "$USER_DIRS_CONF"
-[[ ! -e "$USER_DIRS_CONF" ]]
-assert_eq "$(systemd-path user-desktop)" "/root/Desktop"
-for dir in "" documents download music pictures public templates videos; do
- assert_eq "$(systemd-path "user${dir:+-$dir}")" "/root"
-done
-
-# sd-path should consider only absolute $HOME
-assert_eq "$(HOME=/hello-world systemd-path user)" "/hello-world"
-assert_eq "$(HOME=hello-world systemd-path user)" "/root"
-assert_eq "$(HOME=/hello systemd-path --suffix=world user)" "/hello/world"
-assert_eq "$(HOME=hello systemd-path --suffix=world user)" "/root/world"
-# Same with some other env variables
-assert_in "/my-config" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path search-configuration)"
-assert_in "/my-config/foo" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path --suffix=foo search-configuration)"
-assert_in "/my-home/.config/foo" "$(HOME=/my-home XDG_CONFIG_HOME=my-config systemd-path --suffix=foo search-configuration)"
-assert_not_in "my-config" "$(HOME=my-config XDG_CONFIG_HOME=my-config systemd-path search-configuration)"
-
-(! systemd-path '')
-(! systemd-path system-binaries 🤔 user)
-(! systemd-path --xyz)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemctl log-level info
-
-if systemd-detect-virt -cq; then
- echo "Running in a container, skipping the systemd-pstore test..."
- exit 0
-fi
-
-DUMMY_DMESG_0="$(mktemp)"
-cat >"$DUMMY_DMESG_0" <<\EOF
-6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124
-6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293
-6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728
-6,17162,5343126766065,-; loop0: p1 p2
-6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
-6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem.
-6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728
-6,17166,5343158073563,-; loop0: p1 p2
-6,17167,5343158074325,-; loop0: p1 p2
-6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
-6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem.
-6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728
-6,17171,5343158700439,-; loop0: p1 p2
-6,17172,5343158701120,-; loop0: p1 p2
-EOF
-
-DUMMY_DMESG_1="$(mktemp)"
-cat >"$DUMMY_DMESG_1" <<\EOF
-Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu.
-Příliš žluťoučký kůň úpěl ďábelské ódy.
-Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů.
-Vyciď křišťálový nůž, ó učiň úděsné líbivým!
-Loď čeří kýlem tůň obzvlášť v Grónské úžině
-Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům.
-Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk.
-Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso.
-Stróż pchnął kość w quiz gędźb vel fax myjń.
-Portez ce vieux whisky au juge blond qui fume!
-EOF
-
-file_count() { find "${1:?}" -type f | wc -l; }
-file_size() { wc -l <"${1:?}"; }
-random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; }
-
-# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
-# record id, a 64-bit number.
-#
-# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.
-
-prepare_efi_logs() {
- local file="${1:?}"
- local timestamp="${2:?}"
- local chunk count filename
-
- # For the EFI backend, the 3 least significant digits of record id encodes a
- # "count" number, the next 2 least significant digits for the dmesg part
- # (chunk) number, and the remaining digits as the timestamp. See
- # linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write().
- count="$(file_size "$file")"
- chunk=0
- # The sed in the process substitution below just reverses the file
- while read -r line; do
- filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")"
- echo "$line" >"/sys/fs/pstore/$filename"
- chunk=$((chunk + 1))
- done < <(sed '1!G;h;$!d' "$file")
-
- if [[ "$chunk" -eq 0 ]]; then
- echo >&2 "No dmesg-efi files were created"
- exit 1
- fi
-}
-
-prepare_erst_logs() {
- local file="${1:?}"
- local start_id="${2:?}"
- local id filename
-
- # For the ERST backend, the record is a monotonically increasing number, seeded as
- # a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer().
- id="$start_id"
- # The sed in the process substitution below just reverses the file
- while read -r line; do
- filename="$(printf "dmesg-erst-%0.16d" "$id")"
- echo "$line" >"/sys/fs/pstore/$filename"
- id=$((id + 1))
- done < <(sed '1!G;h;$!d' "$file")
-
- if [[ "$id" -eq "$start_id" ]]; then
- echo >&2 "No dmesg-erst files were created"
- exit 1
- fi
-
- # ID of the last dmesg file will be the ID of the erst subfolder
- echo "$((id - 1))"
-}
-
-prepare_pstore_config() {
- local storage="${1:?}"
- local unlink="${2:?}"
-
- systemctl stop systemd-pstore
-
- rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/*
-
- mkdir -p /run/systemd/pstore.conf.d
- cat >/run/systemd/pstore.conf.d/99-test.conf <<EOF
-[PStore]
-Storage=$storage
-Unlink=$unlink
-EOF
-
- systemd-analyze cat-config systemd/pstore.conf | grep "$storage"
- systemd-analyze cat-config systemd/pstore.conf | grep "$unlink"
-}
-
-start_pstore() {
- rm -f /tmp/journal.cursor
- journalctl -q -n 0 --cursor-file=/tmp/journal.cursor
- systemctl restart systemd-pstore
- journalctl --sync
-}
-
-at_exit() {
- set +e
-
- mountpoint -q /sys/fs/pstore && umount /sys/fs/pstore
- rm -fr /var/lib/systemd/pstore/*
- rm -f /run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf
- rm -f /run/systemd/pstore.conf.d/99-test.conf
-}
-
-trap at_exit EXIT
-
-# To avoid having to depend on the VM providing the pstore, let's simulate
-# it using a simple bind mount
-PSTORE_DIR="$(mktemp -d)"
-mount --bind "${PSTORE_DIR:?}" "/sys/fs/pstore"
-
-# Disable the start limit since we're going to restart the systemd-pstore
-# service quite a lot in a short time span
-mkdir -p /run/systemd/system/systemd-pstore.service.d
-cat >/run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf <<EOF
-[Unit]
-StartLimitInterval=0
-EOF
-systemctl daemon-reload
-
-# systemd-pstore is a no-op with Storage=none
-for unlink in yes no; do
- : "Backend: N/A; Storage: none; Unlink: $unlink"
- timestamp="$(random_efi_timestamp)"
- prepare_pstore_config "none" "$unlink"
- prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
- old_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$old_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
-
- : "Backend: EFI; Storage: external; Unlink: $unlink"
- timestamp="$(random_efi_timestamp)"
- prepare_pstore_config "external" "$unlink"
- prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
- [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
- # We always log to journal
- diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
- filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "$DUMMY_DMESG_0")")"
- diff "$DUMMY_DMESG_0" "$filename"
-
- : "Backend: EFI; Storage: external; Unlink: $unlink; multiple dmesg files"
- timestamps=()
- timestamp="$(random_efi_timestamp)"
- prepare_pstore_config "external" "$unlink"
- for i in {0..6}; do
- timestamp="$((timestamp + (i * 10)))"
- timestamps+=("$timestamp")
- # Create a name reference to one of the $DUMMY_DMESG_X variables
- dmesg="DUMMY_DMESG_$((i % 2))"
- prepare_efi_logs "${!dmesg}" "$timestamp"
- # Add one "random" (non-dmesg) file as well
- echo "hello world" >/sys/fs/pstore/foo.bar
- [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
- filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "${!dmesg}")")"
- diff "${!dmesg}" "$filename"
- grep "hello world" "/var/lib/systemd/pstore/foo.bar"
- done
- # Check that we kept all previous records as well
- for timestamp in "${timestamps[@]}"; do
- [[ -d "/var/lib/systemd/pstore/$timestamp" ]]
- [[ "$(file_count "/var/lib/systemd/pstore/$timestamp/")" -gt 0 ]]
- done
-
- : "Backend: EFI; Storage: journal; Unlink: $unlink"
- timestamp="$(random_efi_timestamp)"
- prepare_pstore_config "journal" "$unlink"
- prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
- [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
- diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
-
- : "Backend: ERST; Storage: external; Unlink: $unlink"
- prepare_pstore_config "external" "$unlink"
- last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
- [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
- # We always log to journal
- diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
- filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
- diff "$DUMMY_DMESG_0" "$filename"
-
- : "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files"
- last_ids=()
- prepare_pstore_config "external" "$unlink"
- for i in {0..9}; do
- # Create a name reference to one of the $DUMMY_DMESG_X variables
- dmesg="DUMMY_DMESG_$((i % 2))"
- last_id="$(prepare_erst_logs "${!dmesg}" "$((i * 100))")"
- last_ids+=("$last_id")
- # Add one "random" (non-dmesg) file as well
- echo "hello world" >/sys/fs/pstore/foo.bar
- [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
- filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
- diff "${!dmesg}" "$filename"
- grep "hello world" "/var/lib/systemd/pstore/foo.bar"
- done
- # Check that we kept all previous records as well
- for last_id in "${last_ids[@]}"; do
- directory="$(printf "/var/lib/systemd/pstore/%0.16d" "$last_id")"
- [[ -d "$directory" ]]
- [[ "$(file_count "$directory")" -gt 0 ]]
- done
-
- : "Backend: ERST; Storage: journal; Unlink: $unlink"
- prepare_pstore_config "journal" "$unlink"
- last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
- [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
- start_pstore
- [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
- [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
- diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
-done
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-run --help --no-pager
-systemd-run --version
-systemd-run --no-ask-password true
-systemd-run --no-block --collect true
-
-export PARENT_FOO=bar
-touch /tmp/public-marker
-
-: "Transient service (system daemon)"
-systemd-run --wait --pipe \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --system \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice=foo \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice=foo.slice \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice-inherit \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice-inherit --slice=foo \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.service$ ]]'
-# We should not inherit caller's environment
-systemd-run --wait --pipe bash -xec '[[ -z "$PARENT_FOO" ]]'
-systemd-run --wait --pipe bash -xec '[[ "$PWD" == / && -n "$INVOCATION_ID" ]]'
-systemd-run --wait --pipe \
- --send-sighup \
- --working-directory="" \
- --working-directory=/tmp \
- bash -xec '[[ "$PWD" == /tmp ]]'
-systemd-run --wait --pipe --same-dir bash -xec "[[ \"\$PWD\" == $PWD ]]"
-systemd-run --wait --pipe \
- --property=LimitCORE=1M:2M \
- --property=LimitCORE=16M:32M \
- --property=PrivateTmp=yes \
- bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
-systemd-run --wait --pipe \
- --uid=testuser \
- bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
-systemd-run --wait --pipe \
- --gid=testuser \
- bash -xec '[[ "$(id -nu)" == root && "$(id -ng)" == testuser ]]'
-systemd-run --wait --pipe \
- --uid=testuser \
- --gid=root \
- bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == root ]]'
-systemd-run --wait --pipe --expand-environment=no \
- --nice=10 \
- bash -xec 'read -r -a SELF_STAT </proc/self/stat && [[ "${SELF_STAT[18]}" -eq 10 ]]'
-systemd-run --wait --pipe \
- --setenv=ENV_HELLO="nope" \
- --setenv=ENV_HELLO="env world" \
- --setenv=EMPTY= \
- --setenv=PARENT_FOO \
- --property=Environment="ALSO_HELLO='also world'" \
- bash -xec '[[ "$ENV_HELLO" == "env world" && -z "$EMPTY" && "$PARENT_FOO" == bar && "$ALSO_HELLO" == "also world" ]]'
-
-UNIT="service-0-$RANDOM"
-systemd-run --remain-after-exit --unit="$UNIT" \
- --service-type=simple \
- --service-type=oneshot \
- true
-systemctl cat "$UNIT"
-grep -q "^Type=oneshot" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT"
-(! systemctl cat "$UNIT")
-
-: "Transient service (user daemon)"
-systemd-run --wait --pipe --user --machine=testuser@ \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/.+/run-.+\.service$ ]]'
-systemd-run --wait --pipe --user --machine=testuser@ \
- bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
-systemd-run --wait --pipe --user --machine=testuser@ \
- bash -xec '[[ "$PWD" == /home/testuser && -n "$INVOCATION_ID" ]]'
-
-# PrivateTmp=yes implies PrivateUsers=yes for user manager, so skip this if we
-# don't have unprivileged user namespaces.
-if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -ne 1 ]]; then
- systemd-run --wait --pipe --user --machine=testuser@ \
- --property=LimitCORE=1M:2M \
- --property=LimitCORE=16M:32M \
- --property=PrivateTmp=yes \
- bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
-fi
-
-: "Transient scope (system daemon)"
-systemd-run --scope \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --system \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice=foo \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice=foo.slice \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice-inherit \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice-inherit --slice=foo \
- bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.scope$ ]]'
-# We should inherit caller's environment
-systemd-run --scope bash -xec '[[ "$PARENT_FOO" == bar ]]'
-systemd-run --scope \
- --property=RuntimeMaxSec=10 \
- --property=RuntimeMaxSec=infinity \
- true
-
-: "Transient scope (user daemon)"
-# FIXME: https://github.com/systemd/systemd/issues/27883
-#systemd-run --scope --user --machine=testuser@ \
-# bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/run-.+\.scope$ ]]'
-# We should inherit caller's environment
-#systemd-run --scope --user --machine=testuser@ bash -xec '[[ "$PARENT_FOO" == bar ]]'
-
-: "Transient timer unit"
-UNIT="timer-0-$RANDOM"
-systemd-run --remain-after-exit \
- --unit="$UNIT" \
- --timer-property=OnUnitInactiveSec=16h \
- true
-systemctl cat "$UNIT.service" "$UNIT.timer"
-grep -q "^OnUnitInactiveSec=16h$" "/run/systemd/transient/$UNIT.timer"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT.timer" "$UNIT.service" || :
-
-UNIT="timer-1-$RANDOM"
-systemd-run --remain-after-exit \
- --unit="$UNIT" \
- --on-active=10 \
- --on-active=30s \
- --on-boot=1s \
- --on-startup=2m \
- --on-unit-active=3h20m \
- --on-unit-inactive="5d 4m 32s" \
- --on-calendar="mon,fri *-1/2-1,3 *:30:45" \
- --on-clock-change \
- --on-clock-change \
- --on-timezone-change \
- --timer-property=After=systemd-journald.service \
- --description="Hello world" \
- --description="My Fancy Timer" \
- true
-systemctl cat "$UNIT.service" "$UNIT.timer"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.timer"
-grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnActiveSec=10s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnActiveSec=30s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnBootSec=1s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnStartupSec=2min$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnUnitActiveSec=3h 20min$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnUnitInactiveSec=5d 4min 32s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnCalendar=mon,fri \*\-1/2\-1,3 \*:30:45$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnClockChange=yes$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnTimezoneChange=yes$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.service"
-grep -q "^RemainAfterExit=yes$" "/run/systemd/transient/$UNIT.service"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-(! grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.service")
-systemctl stop "$UNIT.timer" "$UNIT.service" || :
-
-: "Transient path unit"
-UNIT="path-0-$RANDOM"
-systemd-run --remain-after-exit \
- --unit="$UNIT" \
- --path-property=PathExists=/tmp \
- --path-property=PathExists=/tmp/foo \
- --path-property=PathChanged=/root/bar \
- true
-systemctl cat "$UNIT.service" "$UNIT.path"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.path"
-grep -q "^PathExists=/tmp$" "/run/systemd/transient/$UNIT.path"
-grep -q "^PathExists=/tmp/foo$" "/run/systemd/transient/$UNIT.path"
-grep -q "^PathChanged=/root/bar$" "/run/systemd/transient/$UNIT.path"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT.path" "$UNIT.service" || :
-
-: "Transient socket unit"
-UNIT="socket-0-$RANDOM"
-systemd-run --remain-after-exit \
- --unit="$UNIT" \
- --socket-property=ListenFIFO=/tmp/socket.fifo \
- --socket-property=SocketMode=0666 \
- --socket-property=SocketMode=0644 \
- true
-systemctl cat "$UNIT.service" "$UNIT.socket"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.socket"
-grep -q "^ListenFIFO=/tmp/socket.fifo$" "/run/systemd/transient/$UNIT.socket"
-grep -q "^SocketMode=0666$" "/run/systemd/transient/$UNIT.socket"
-grep -q "^SocketMode=0644$" "/run/systemd/transient/$UNIT.socket"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT.socket" "$UNIT.service" || :
-
-: "Interactive options"
-SHELL=/bin/true systemd-run --shell
-SHELL=/bin/true systemd-run --scope --shell
-systemd-run --wait --pty true
-systemd-run --wait --machine=.host --pty true
-(! SHELL=/bin/false systemd-run --quiet --shell)
-
-(! systemd-run)
-(! systemd-run "")
-(! systemd-run --foo=bar)
-(! systemd-run --wait --pipe --slice=foo.service true)
-
-for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} property service-type setenv; do
- (! systemd-run "--$opt=" true)
- (! systemd-run "--$opt=''" true)
-done
-
-# Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc
-BOOT_ID="$(</proc/sys/kernel/random/boot_id)"
-UNIT_BOOT_ID="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id)"
-assert_eq "$BOOT_ID" "$UNIT_BOOT_ID"
-
-TMP_KVER="/tmp/version.$RANDOM"
-KVER="$(</proc/version).piff"
-echo "$KVER" >"$TMP_KVER"
-mount --bind "$TMP_KVER" /proc/version
-UNIT_KVER="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)"
-assert_eq "$KVER" "$UNIT_KVER"
-umount /proc/version
-rm -f "$TMP_KVER"
-
-if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; then
- # Check that invoking the tool under the run0 alias name works
- run0 ls /
- assert_eq "$(run0 echo foo)" "foo"
- # Check if we set some expected environment variables
- for arg in "" "--user=root" "--user=testuser"; do
- assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
- assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
- assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
- done
- # Let's chain a couple of run0 calls together, for fun
- readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
- assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER"
-fi
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-74-AUX-UTILS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
- systemctl stop per-source-limit.socket
- rm -f /run/systemd/system/per-source-limit{@.service,.socket} /run/foo.conn{1..4}
- systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-cat >/run/systemd/system/per-source-limit.socket <<EOF
-[Socket]
-ListenStream=/run/per-source-limit.sk
-MaxConnectionsPerSource=2
-Accept=yes
-EOF
-
-cat >/run/systemd/system/per-source-limit@.service <<EOF
-[Unit]
-BindsTo=per-source-limit.socket
-After=per-source-limit.socket
-
-[Service]
-ExecStartPre=echo waldo
-ExecStart=sleep infinity
-StandardOutput=socket
-EOF
-
-systemctl daemon-reload
-systemctl start per-source-limit.socket
-systemctl status per-source-limit.socket
-
-# So these two should take up the first two connection slots
-socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn1 &
-J1="$!"
-socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn2 &
-J2="$!"
-
-waitfor() {
- local file="${1:?}"
-
- for _ in {0..20}; do
- if grep -q waldo "$file"; then
- return 0
- fi
-
- sleep .5
- done
-
- echo >&2 "Timeout while waiting for the expected output"
- return 1
-}
-
-# Wait until the word "waldo" shows in the output files
-waitfor /tmp/foo.conn1
-waitfor /tmp/foo.conn2
-
-# The next connection should fail, because the limit is hit
-socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn3 &
-J3="$!"
-
-# But this one should work, because done under a different UID
-setpriv --reuid=1 socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn4 &
-J4="$!"
-
-waitfor /tmp/foo.conn4
-
-# The third job should fail quickly, wait for it
-wait "$J3"
-
-# The other jobs will hang forever, since we run "sleep infinity" on the server side. Let's kill the jobs now.
-kill "$J1"
-kill "$J2"
-kill "$J4"
-
-# The 3rd connection should not have seen "waldo", since it should have been refused too early
-(! grep -q "waldo" /tmp/foo.conn3 )
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if ! command -v ssh &> /dev/null || ! command -v sshd &> /dev/null ; then
- echo "ssh/sshd not found, skipping test." >&2
- exit 0
-fi
-
-systemctl -q is-active sshd-unix-local.socket
-
-if test -e /dev/vsock ; then
- systemctl -q is-active sshd-vsock.socket
-fi
-
-if test -d /run/host/unix-export ; then
- systemctl -q is-active sshd-unix-export.socket
-fi
-
-# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
-if [[ -v ASAN_OPTIONS ]] ; then
- exit 0
-fi
-
-ROOTID=$(mktemp -u)
-
-removesshid() {
- rm -f "$ROOTID" "$ROOTID".pub
-}
-
-ssh-keygen -N '' -C '' -t rsa -f "$ROOTID"
-
-mkdir -p 0700 /root/.ssh
-# Add a newline in case authorized_keys wasn't terminated correctly.
-echo >>/root/.ssh/authorized_keys
-cat "$ROOTID".pub >>/root/.ssh/authorized_keys
-
-# set root pw to "foo", just to set it to something valid
-# shellcheck disable=SC2016
-usermod -p '$5$AAy6BYJ6rzz.QELv$6LpVEU3/RQmVz.svHu/33qoJWWWzZuJ3DM2fo9JgcUD' root
-usermod -U root
-
-mkdir -p /etc/ssh
-test -f /etc/ssh/ssh_host_rsa_key || ssh-keygen -t rsa -C '' -N '' -f /etc/ssh/ssh_host_rsa_key
-echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
-echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
-
-test -f /etc/ssh/ssh_config || echo 'Include /etc/ssh/ssh_config.d/*.conf' > /etc/ssh/ssh_config
-
-# ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that are aware of distros use
-mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
-
-ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" .host cat /etc/machine-id | cmp - /etc/machine-id
-ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" unix/run/ssh-unix-local/socket cat /etc/machine-id | cmp - /etc/machine-id
-
-modprobe vsock_loopback ||:
-if test -e /dev/vsock -a -d /sys/module/vsock_loopback ; then
- ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" vsock/1 cat /etc/machine-id | cmp - /etc/machine-id
-fi
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Unset $PAGER so we don't have to use --no-pager everywhere
-export PAGER=
-
-varlinkctl --help
-varlinkctl help --no-pager
-varlinkctl --version
-varlinkctl --json=help
-
-# TODO: abstract namespace sockets (@...)
-# Path to a socket
-varlinkctl info /run/systemd/journal/io.systemd.journal
-varlinkctl info /run/systemd/../systemd/../../run/systemd/journal/io.systemd.journal
-varlinkctl info "./$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)"
-varlinkctl info unix:/run/systemd/journal/io.systemd.journal
-varlinkctl info --json=off /run/systemd/journal/io.systemd.journal
-varlinkctl info --json=pretty /run/systemd/journal/io.systemd.journal | jq .
-varlinkctl info --json=short /run/systemd/journal/io.systemd.journal | jq .
-varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq .
-
-varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal
-varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq .
-
-varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal
-varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
-
-if command -v userdbctl >/dev/null; then
- systemctl start systemd-userdbd
- varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'
- varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq .
- varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
- varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | jq --seq .
- varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
- (! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .)
-fi
-
-IDL_FILE="$(mktemp)"
-varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal | tee "${IDL_FILE:?}"
-varlinkctl validate-idl "$IDL_FILE"
-varlinkctl validate-idl "$IDL_FILE"
-cat /bin/sh >"$IDL_FILE"
-(! varlinkctl validate-idl "$IDL_FILE")
-
-if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
- # Path to an executable
- varlinkctl info /usr/lib/systemd/systemd-pcrextend
- varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend
- varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend
- varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
-fi
-
-# SSH transport
-SSHBINDIR="$(mktemp -d)"
-
-rm_rf_sshbindir() {
- rm -rf "$SSHBINDIR"
-}
-
-trap rm_rf_sshbindir EXIT
-
-# Create a fake "ssh" binary that validates everything works as expected
-cat > "$SSHBINDIR"/ssh <<'EOF'
-#!/bin/sh
-
-set -xe
-
-test "$1" = "-W"
-test "$2" = "/run/systemd/journal/io.systemd.journal"
-test "$3" = "foobar"
-
-exec socat - UNIX-CONNECT:/run/systemd/journal/io.systemd.journal
-EOF
-
-chmod +x "$SSHBINDIR"/ssh
-
-SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.systemd.journal
-
-# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage
-find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do
- varlinkctl info "$socket"
-
- varlinkctl list-interfaces "$socket" | while read -r interface; do
- varlinkctl introspect "$socket" "$interface"
- done
-done
-
-(! varlinkctl)
-(! varlinkctl "")
-(! varlinkctl info)
-(! varlinkctl info "")
-(! varlinkctl info /run/systemd/notify)
-(! varlinkctl info /run/systemd/private)
-# Relative paths must begin with ./
-(! varlinkctl info "$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)")
-(! varlinkctl info unix:)
-(! varlinkctl info unix:"")
-(! varlinkctl info exec:)
-(! varlinkctl info exec:"")
-(! varlinkctl list-interfaces)
-(! varlinkctl list-interfaces "")
-(! varlinkctl introspect)
-(! varlinkctl introspect /run/systemd/journal/io.systemd.journal)
-(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "")
-(! varlinkctl introspect "" "")
-(! varlinkctl call)
-(! varlinkctl call "")
-(! varlinkctl call "" "")
-(! varlinkctl call "" "" "")
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord </dev/null)
-(! varlinkctl validate-idl "")
-(! varlinkctl validate-idl </dev/null)
-
-varlinkctl info /run/systemd/io.systemd.Hostname
-varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
-varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-at_exit() {
- set +e
- rm -rf /var/lib/machines/mymachine.raw.v
- rm -rf /var/lib/machines/mytree.v
- rm -rf /var/lib/machines/testroot.v
- umount -l /tmp/dotvroot
- rmdir /tmp/dotvroot
-}
-
-trap at_exit EXIT
-
-mkdir -p /var/lib/machines/mymachine.raw.v
-
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
-
-mkdir -p /var/lib/machines/mytree.v
-
-mkdir /var/lib/machines/mytree.v/mytree_33.4
-mkdir /var/lib/machines/mytree.v/mytree_33.5
-mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
-mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
-mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
-
-ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
-
-export SYSTEMD_LOG_LEVEL=debug
-
-if [ "$ARCH" = "x86-64" ] ; then
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
- (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
-
- systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-elif [ "$ARCH" = "arm64" ] ; then
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
- (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
- (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
-
- systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-else
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-
- test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
- (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
- (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
- (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
-
- systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-fi
-
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
-
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
-
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
-
-if [ "$ARCH" != "arm64" ] ; then
- test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
- test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
-else
- test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
- test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
-fi
-
-(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
-
-mkdir /tmp/dotvroot
-mount --bind / /tmp/dotvroot
-
-mkdir /var/lib/machines/testroot.v
-mkdir /var/lib/machines/testroot.v/testroot_32
-ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
-mkdir /var/lib/machines/testroot.v/testroot_34
-
-ls -l /var/lib/machines/testroot.v
-
-test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
-test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
-(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
-
-find /var/lib/machines/testroot.v/testroot_34
-rm -rf /var/lib/machines/testroot.v/testroot_34
-test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
-test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
-systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
-
-rm /var/lib/machines/testroot.v/testroot_33
-test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
-test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
-(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
-
-rm -rf /var/lib/machines/testroot.v/testroot_32
-(! systemd-vpick /var/lib/machines/testroot.v)
-(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Tests for systemd-resolved
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# vi: ts=4 sw=4 tw=0 et:
-
-# TODO:
-# - IPv6-only stack
-# - mDNS
-# - LLMNR
-# - DoT/DoH
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# We need at least Knot 3.0 which support (among others) the ds-push directive
-if ! knotc -c /usr/lib/systemd/tests/testdata/knot-data/knot.conf conf-check; then
- echo "This test requires at least Knot 3.0. skipping..." | tee --append /skipped
- exit 77
-fi
-
-RUN_OUT="$(mktemp)"
-
-run() {
- "$@" |& tee "$RUN_OUT"
-}
-
-run_delv() {
- # Since [0] delv no longer loads /etc/(bind/)bind.keys by default, so we
- # have to do that explicitly for each invocation
- run delv -a /etc/bind.keys "$@"
-}
-
-disable_ipv6() {
- sysctl -w net.ipv6.conf.all.disable_ipv6=1
-}
-
-enable_ipv6() {
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- networkctl reconfigure dns0
- /usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=dns0:routable --timeout=30
-}
-
-monitor_check_rr() (
- set +x
- set +o pipefail
- local since="${1:?}"
- local match="${2:?}"
-
- # Wait until the first mention of the specified log message is
- # displayed. We turn off pipefail for this, since we don't care about the
- # lhs of this pipe expression, we only care about the rhs' result to be
- # clean
- timeout -v 30s journalctl -u resolvectl-monitor.service --since "$since" -f --full | grep -m1 "$match"
-)
-
-restart_resolved() {
- systemctl stop systemd-resolved.service
- (! systemctl is-failed systemd-resolved.service)
- # Reset the restart counter since we call this method a bunch of times
- # and can occasionally hit the default rate limit
- systemctl reset-failed systemd-resolved.service
- systemctl start systemd-resolved.service
- systemctl service-log-level systemd-resolved.service debug
-}
-
-# Test for resolvectl, resolvconf
-systemctl unmask systemd-resolved.service
-systemctl enable --now systemd-resolved.service
-systemctl service-log-level systemd-resolved.service debug
-ip link add hoge type dummy
-ip link add hoge.foo type dummy
-resolvectl dns hoge 10.0.0.1 10.0.0.2
-resolvectl dns hoge.foo 10.0.0.3 10.0.0.4
-assert_in '10.0.0.1 10.0.0.2' "$(resolvectl dns hoge)"
-assert_in '10.0.0.3 10.0.0.4' "$(resolvectl dns hoge.foo)"
-resolvectl dns hoge 10.0.1.1 10.0.1.2
-resolvectl dns hoge.foo 10.0.1.3 10.0.1.4
-assert_in '10.0.1.1 10.0.1.2' "$(resolvectl dns hoge)"
-assert_in '10.0.1.3 10.0.1.4' "$(resolvectl dns hoge.foo)"
-if ! RESOLVCONF=$(command -v resolvconf 2>/dev/null); then
- TMPDIR=$(mktemp -d -p /tmp resolvconf-tests.XXXXXX)
- RESOLVCONF="$TMPDIR"/resolvconf
- ln -s "$(command -v resolvectl 2>/dev/null)" "$RESOLVCONF"
-fi
-echo nameserver 10.0.2.1 10.0.2.2 | "$RESOLVCONF" -a hoge
-echo nameserver 10.0.2.3 10.0.2.4 | "$RESOLVCONF" -a hoge.foo
-assert_in '10.0.2.1 10.0.2.2' "$(resolvectl dns hoge)"
-assert_in '10.0.2.3 10.0.2.4' "$(resolvectl dns hoge.foo)"
-echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35
-echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp
-assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
-assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
-
-# Tests for _localdnsstub and _localdnsproxy
-assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)"
-assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)"
-assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)"
-assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)"
-
-assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)"
-assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)"
-assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)"
-assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
-
-# Tests for mDNS and LLMNR settings
-mkdir -p /run/systemd/resolved.conf.d
-{
- echo "[Resolve]"
- echo "MulticastDNS=no"
- echo "LLMNR=no"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-restart_resolved
-# make sure networkd is not running.
-systemctl stop systemd-networkd.service
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-# Tests that reloading works
-{
- echo "[Resolve]"
- echo "MulticastDNS=yes"
- echo "LLMNR=yes"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-systemctl reload systemd-resolved.service
-# defaults to yes (both the global and per-link settings are yes)
-assert_in 'yes' "$(resolvectl mdns hoge)"
-assert_in 'yes' "$(resolvectl llmnr hoge)"
-# set per-link setting
-resolvectl mdns hoge yes
-resolvectl llmnr hoge yes
-assert_in 'yes' "$(resolvectl mdns hoge)"
-assert_in 'yes' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge resolve
-resolvectl llmnr hoge resolve
-assert_in 'resolve' "$(resolvectl mdns hoge)"
-assert_in 'resolve' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge no
-resolvectl llmnr hoge no
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-# downgrade global setting to resolve
-{
- echo "[Resolve]"
- echo "MulticastDNS=resolve"
- echo "LLMNR=resolve"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-systemctl reload systemd-resolved.service
-# set per-link setting
-resolvectl mdns hoge yes
-resolvectl llmnr hoge yes
-assert_in 'resolve' "$(resolvectl mdns hoge)"
-assert_in 'resolve' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge resolve
-resolvectl llmnr hoge resolve
-assert_in 'resolve' "$(resolvectl mdns hoge)"
-assert_in 'resolve' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge no
-resolvectl llmnr hoge no
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-# downgrade global setting to no
-{
- echo "[Resolve]"
- echo "MulticastDNS=no"
- echo "LLMNR=no"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-systemctl reload systemd-resolved.service
-# set per-link setting
-resolvectl mdns hoge yes
-resolvectl llmnr hoge yes
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge resolve
-resolvectl llmnr hoge resolve
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge no
-resolvectl llmnr hoge no
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-
-# Cleanup
-rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf
-ip link del hoge
-ip link del hoge.foo
-
-### SETUP ###
-# Configure network
-hostnamectl hostname ns1.unsigned.test
-cat >>/etc/hosts <<EOF
-10.0.0.1 ns1.unsigned.test
-fd00:dead:beef:cafe::1 ns1.unsigned.test
-
-127.128.0.5 localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5
-EOF
-
-mkdir -p /run/systemd/network
-cat >/run/systemd/network/10-dns0.netdev <<EOF
-[NetDev]
-Name=dns0
-Kind=dummy
-EOF
-cat >/run/systemd/network/10-dns0.network <<EOF
-[Match]
-Name=dns0
-
-[Network]
-IPv6AcceptRA=no
-Address=10.0.0.1/24
-Address=fd00:dead:beef:cafe::1/64
-DNSSEC=allow-downgrade
-DNS=10.0.0.1
-DNS=fd00:dead:beef:cafe::1
-EOF
-cat >/run/systemd/network/10-dns1.netdev <<EOF
-[NetDev]
-Name=dns1
-Kind=dummy
-EOF
-cat >/run/systemd/network/10-dns1.network <<EOF
-[Match]
-Name=dns1
-
-[Network]
-IPv6AcceptRA=no
-Address=10.99.0.1/24
-DNSSEC=no
-EOF
-systemctl edit --stdin --full --runtime --force "resolved-dummy-server.service" <<EOF
-[Service]
-Type=notify
-Environment=SYSTEMD_LOG_LEVEL=debug
-ExecStart=/usr/lib/systemd/tests/unit-tests/manual/test-resolved-dummy-server 10.99.0.1:53
-EOF
-
-DNS_ADDRESSES=(
- "10.0.0.1"
- "fd00:dead:beef:cafe::1"
-)
-
-mkdir -p /run/systemd/resolved.conf.d
-{
- echo "[Resolve]"
- echo "FallbackDNS="
- echo "DNSSEC=allow-downgrade"
- echo "DNSOverTLS=opportunistic"
-} >/run/systemd/resolved.conf.d/test.conf
-ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-# Override the default NTA list, which turns off DNSSEC validation for (among
-# others) the test. domain
-mkdir -p "/etc/dnssec-trust-anchors.d/"
-echo local >/etc/dnssec-trust-anchors.d/local.negative
-
-# Copy over our knot configuration
-mkdir -p /var/lib/knot/zones/ /etc/knot/
-cp -rfv /usr/lib/systemd/tests/testdata/knot-data/zones/* /var/lib/knot/zones/
-cp -fv /usr/lib/systemd/tests/testdata/knot-data/knot.conf /etc/knot/knot.conf
-chgrp -R knot /etc/knot/ /var/lib/knot/
-chmod -R ug+rwX /var/lib/knot/
-chmod -R g+r /etc/knot/
-
-# Sign the root zone
-keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
-# Create a trust anchor for resolved with our root zone
-keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive
-# Create a bind-compatible trust anchor (for delv)
-# Note: the trust-anchors directive is relatively new, so use the original
-# managed-keys one until it's widespread enough
-{
- echo 'managed-keys {'
- keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g'
- echo '};'
-} >/etc/bind.keys
-# Create an /etc/bind/bind.keys symlink, which is used by delv on Ubuntu
-mkdir -p /etc/bind
-ln -svf /etc/bind.keys /etc/bind/bind.keys
-
-# Start the services
-systemctl unmask systemd-networkd
-systemctl restart systemd-networkd
-/usr/lib/systemd/systemd-networkd-wait-online --interface=dns1:routable --timeout=60
-systemctl reload systemd-resolved
-systemctl start resolved-dummy-server
-
-# Create knot's runtime dir, since from certain version it's provided only by
-# the package and not created by tmpfiles/systemd
-if [[ ! -d /run/knot ]]; then
- mkdir -p /run/knot
- chown -R knot:knot /run/knot
-fi
-systemctl start knot
-# Wait a bit for the keys to propagate
-sleep 4
-
-systemctl status resolved-dummy-server
-networkctl status
-resolvectl status
-resolvectl log-level debug
-
-# Start monitoring queries
-systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor
-systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short
-
-# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones
-# that are forwarded using the `dnsproxy` module. Until the issue is resolved,
-# let's fall back to pre-processing the `zone-check` output a bit before checking it
-#
-# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913
-run knotc zone-check || :
-sed -i '/forwarded.test./d' "$RUN_OUT"
-[[ ! -s "$RUN_OUT" ]]
-# We need to manually propagate the DS records of onlinesign.test. to the parent
-# zone, since they're generated online
-knotc zone-begin test.
-if knotc zone-get test. onlinesign.test. ds | grep .; then
- # Drop any old DS records, if present (e.g. on test re-run)
- knotc zone-unset test. onlinesign.test. ds
-fi
-# Propagate the new DS records
-while read -ra line; do
- knotc zone-set test. "${line[0]}" 600 "${line[@]:1}"
-done < <(keymgr onlinesign.test. ds)
-knotc zone-commit test.
-
-knotc reload
-sleep 2
-
-### SETUP END ###
-
-: "--- nss-resolve/nss-myhostname tests"
-# Sanity check
-TIMESTAMP=$(date '+%F %T')
-# Issue: https://github.com/systemd/systemd/issues/23951
-# With IPv6 enabled
-run getent -s resolve ahosts ns1.unsigned.test
-grep -qE "^fd00:dead:beef:cafe::1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
-monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN AAAA fd00:dead:beef:cafe::1"
-# With IPv6 disabled
-# Issue: https://github.com/systemd/systemd/issues/23951
-disable_ipv6
-run getent -s resolve ahosts ns1.unsigned.test
-grep -qE "^10\.0\.0\.1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
-(! grep -qE "fd00:dead:beef:cafe::1" "$RUN_OUT")
-monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN A 10.0.0.1"
-enable_ipv6
-
-# Issue: https://github.com/systemd/systemd/issues/18812
-# PR: https://github.com/systemd/systemd/pull/18896
-# Follow-up issue: https://github.com/systemd/systemd/issues/23152
-# Follow-up PR: https://github.com/systemd/systemd/pull/23161
-# With IPv6 enabled
-run getent -s resolve ahosts localhost
-grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
-run getent -s myhostname ahosts localhost
-grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
-# With IPv6 disabled
-disable_ipv6
-run getent -s resolve ahosts localhost
-grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
-(! grep -qE "::1" "$RUN_OUT")
-run getent -s myhostname ahosts localhost
-grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
-enable_ipv6
-
-# Issue: https://github.com/systemd/systemd/issues/25088
-run getent -s resolve hosts 127.128.0.5
-grep -qEx '127\.128\.0\.5\s+localhost5(\s+localhost5?\.localdomain[45]?){4}' "$RUN_OUT"
-[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
-
-# Issue: https://github.com/systemd/systemd/issues/20158
-run dig +noall +answer +additional localhost5.
-grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
-[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
-run dig +noall +answer +additional localhost5.localdomain4.
-grep -qEx 'localhost5\.localdomain4\.\s+0\s+IN\s+CNAME\s+localhost5\.' "$RUN_OUT"
-grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
-[ "$(wc -l <"$RUN_OUT")" -eq 2 ]
-
-: "--- Basic resolved tests ---"
-# Issue: https://github.com/systemd/systemd/issues/22229
-# PR: https://github.com/systemd/systemd/pull/22231
-FILTERED_NAMES=(
- "0.in-addr.arpa"
- "255.255.255.255.in-addr.arpa"
- "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"
- "hello.invalid"
- "hello.alt"
-)
-
-for name in "${FILTERED_NAMES[@]}"; do
- (! run host "$name")
- grep -qF "NXDOMAIN" "$RUN_OUT"
-done
-
-# Follow-up
-# Issue: https://github.com/systemd/systemd/issues/22401
-# PR: https://github.com/systemd/systemd/pull/22414
-run dig +noall +authority +comments SRV .
-grep -qF "status: NOERROR" "$RUN_OUT"
-grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT"
-
-run resolvectl query -t SVCB svcb.test
-grep -qF 'alpn="dot"' "$RUN_OUT"
-grep -qF "ipv4hint=10.0.0.1" "$RUN_OUT"
-
-run resolvectl query -t HTTPS https.test
-grep -qF 'alpn="h2,h3"' "$RUN_OUT"
-
-: "--- ZONE: unsigned.test. ---"
-run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA
-grep -qF "10.0.0.101" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
-run resolvectl query unsigned.test
-grep -qF "10.0.0.10" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
-grep -qF "authenticated: no" "$RUN_OUT"
-run dig @ns1.unsigned.test +short MX unsigned.test
-grep -qF "15 mail.unsigned.test." "$RUN_OUT"
-run resolvectl query --legend=no -t MX unsigned.test
-grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT"
-
-
-: "--- ZONE: signed.test (static DNSSEC) ---"
-# Check the trust chain (with and without systemd-resolved in between
-# Issue: https://github.com/systemd/systemd/issues/22002
-# PR: https://github.com/systemd/systemd/pull/23289
-run_delv @ns1.unsigned.test signed.test
-grep -qF "; fully validated" "$RUN_OUT"
-run_delv signed.test
-grep -qF "; fully validated" "$RUN_OUT"
-
-for addr in "${DNS_ADDRESSES[@]}"; do
- run_delv "@$addr" -t A mail.signed.test
- grep -qF "; fully validated" "$RUN_OUT"
- run_delv "@$addr" -t AAAA mail.signed.test
- grep -qF "; fully validated" "$RUN_OUT"
-done
-run resolvectl query mail.signed.test
-grep -qF "10.0.0.11" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::11" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-run dig +short signed.test
-grep -qF "10.0.0.10" "$RUN_OUT"
-run resolvectl query signed.test
-grep -qF "signed.test: 10.0.0.10" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-run dig @ns1.unsigned.test +short MX signed.test
-grep -qF "10 mail.signed.test." "$RUN_OUT"
-run resolvectl query --legend=no -t MX signed.test
-grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT"
-# Check a non-existent domain
-run dig +dnssec this.does.not.exist.signed.test
-grep -qF "status: NXDOMAIN" "$RUN_OUT"
-# Check a wildcard record
-run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test
-grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-# Check SRV support
-run resolvectl service _mysvc._tcp signed.test
-grep -qF "myservice.signed.test:1234" "$RUN_OUT"
-grep -qF "10.0.0.20" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-# Test service resolve over Varlink
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}'
-grep -qF '"services":[{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test","canonicalName":"myservice.signed.test","addresses":[{"ifindex":' "$RUN_OUT"
-grep -qF '"family":10,"address":[253,0,222,173,190,239,202,254,0,0,0,0,0,0,0,23]' "$RUN_OUT"
-grep -qF '"family":2,"address":[10,0,0,20]' "$RUN_OUT"
-grep -qF '}]}],"txt":["This is TXT for myservice"],"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"},"flags":' "$RUN_OUT"
-
-# without name
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test"}'
-# without txt (SD_RESOLVE_NO_TXT)
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":64}'
-(! grep -qF '"txt"' "$RUN_OUT")
-# without address (SD_RESOLVE_NO_ADDRESS)
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":128}'
-(! grep -qF '"addresses"' "$RUN_OUT")
-# without txt and address
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":192}'
-(! grep -qF '"txt"' "$RUN_OUT")
-(! grep -qF '"addresses"' "$RUN_OUT")
-
-(! run resolvectl service _invalidsvc._udp signed.test)
-grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT"
-run resolvectl service _untrustedsvc._udp signed.test
-grep -qF "myservice.untrusted.test:1111" "$RUN_OUT"
-grep -qF "10.0.0.123" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-# Check OPENPGPKEY support
-run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
-grep -qF "; fully validated" "$RUN_OUT"
-run resolvectl openpgp mr.smith@signed.test
-grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-# Check zone transfers (AXFR/IXFR)
-# Note: since resolved doesn't support zone transfers, let's just make sure it
-# simply refuses such requests without choking on them
-# See: https://github.com/systemd/systemd/pull/30809#issuecomment-1880102804
-run dig @ns1.unsigned.test AXFR signed.test
-grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
-run dig AXFR signed.test
-grep -qF "; Transfer failed" "$RUN_OUT"
-run dig @ns1.unsigned.test IXFR=43 signed.test
-grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
-run dig IXFR=43 signed.test
-grep -qF "; Transfer failed" "$RUN_OUT"
-
-# DNSSEC validation with multiple records of the same type for the same name
-# Issue: https://github.com/systemd/systemd/issues/22002
-# PR: https://github.com/systemd/systemd/pull/23289
-check_domain() {
- local domain="${1:?}"
- local record="${2:?}"
- local message="${3:?}"
- local addr
-
- for addr in "${DNS_ADDRESSES[@]}"; do
- run_delv "@$addr" -t "$record" "$domain"
- grep -qF "$message" "$RUN_OUT"
- done
-
- run_delv -t "$record" "$domain"
- grep -qF "$message" "$RUN_OUT"
-
- run resolvectl query "$domain"
- grep -qF "authenticated: yes" "$RUN_OUT"
-}
-
-check_domain "dupe.signed.test" "A" "; fully validated"
-check_domain "dupe.signed.test" "AAAA" "; negative response, fully validated"
-check_domain "dupe-ipv6.signed.test" "AAAA" "; fully validated"
-check_domain "dupe-ipv6.signed.test" "A" "; negative response, fully validated"
-check_domain "dupe-mixed.signed.test" "A" "; fully validated"
-check_domain "dupe-mixed.signed.test" "AAAA" "; fully validated"
-
-# Test resolution of CNAME chains
-TIMESTAMP=$(date '+%F %T')
-run resolvectl query -t A cname-chain.signed.test
-grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-monitor_check_rr "$TIMESTAMP" "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14"
-
-# Non-existing RR + CNAME chain
-#run dig +dnssec AAAA cname-chain.signed.test
-#grep -qF "status: NOERROR" "$RUN_OUT"
-#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
-
-
-: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---"
-# Check the trust chain (with and without systemd-resolved in between
-# Issue: https://github.com/systemd/systemd/issues/22002
-# PR: https://github.com/systemd/systemd/pull/23289
-run_delv @ns1.unsigned.test sub.onlinesign.test
-grep -qF "; fully validated" "$RUN_OUT"
-run_delv sub.onlinesign.test
-grep -qF "; fully validated" "$RUN_OUT"
-
-run dig +short sub.onlinesign.test
-grep -qF "10.0.0.133" "$RUN_OUT"
-run resolvectl query sub.onlinesign.test
-grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-run dig @ns1.unsigned.test +short TXT onlinesign.test
-grep -qF '"hello from onlinesign"' "$RUN_OUT"
-run resolvectl query --legend=no -t TXT onlinesign.test
-grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
-
-for addr in "${DNS_ADDRESSES[@]}"; do
- run_delv "@$addr" -t A dual.onlinesign.test
- grep -qF "10.0.0.135" "$RUN_OUT"
- run_delv "@$addr" -t AAAA dual.onlinesign.test
- grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
- run_delv "@$addr" -t ANY ipv6.onlinesign.test
- grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
-done
-run resolvectl query dual.onlinesign.test
-grep -qF "10.0.0.135" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-run resolvectl query ipv6.onlinesign.test
-grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-# Check a non-existent domain
-# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the
-# different response than with "standard" DNSSEC
-run dig +dnssec this.does.not.exist.onlinesign.test
-grep -qF "status: NOERROR" "$RUN_OUT"
-grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT"
-# Check a wildcard record
-run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test
-grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-# Resolve via dbus method
-TIMESTAMP=$(date '+%F %T')
-run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0
-grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT"
-monitor_check_rr "$TIMESTAMP" "secondsub.onlinesign.test IN A 10.0.0.134"
-
-
-: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
-# Issue: https://github.com/systemd/systemd/issues/23955
-# FIXME
-resolvectl flush-caches
-#run dig +short untrusted.test A untrusted.test AAAA
-#grep -qF "10.0.0.121" "$RUN_OUT"
-#grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
-run resolvectl query untrusted.test
-grep -qF "untrusted.test:" "$RUN_OUT"
-grep -qF "10.0.0.121" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
-grep -qF "authenticated: no" "$RUN_OUT"
-run resolvectl service _mysvc._tcp untrusted.test
-grep -qF "myservice.untrusted.test:1234" "$RUN_OUT"
-grep -qF "10.0.0.123" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
-
-# Issue: https://github.com/systemd/systemd/issues/19472
-# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN
-# FIXME: re-enable once the issue is resolved
-#run dig +dnssec AAAA untrusted.test
-#grep -qF "status: NOERROR" "$RUN_OUT"
-#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
-## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL
-#run dig +dnssec this.does.not.exist.untrusted.test
-#grep -qF "status: NXDOMAIN" "$RUN_OUT"
-
-: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---"
-JOURNAL_CURSOR="$(mktemp)"
-journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR"
-
-# See "test-resolved-dummy-server.c" for the server part
-(! run resolvectl query nope.forwarded.test)
-grep -qF "nope.forwarded.test" "$RUN_OUT"
-grep -qF "not found" "$RUN_OUT"
-
-# SERVFAIL + EDE code 6: DNSSEC Bogus
-(! run resolvectl query edns-bogus-dnssec.forwarded.test)
-grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT"
-# Same thing, but over Varlink
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT"
-grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed."
-
-# SERVFAIL + EDE code 16: Censored + extra text
-(! run resolvectl query edns-extra-text.forwarded.test)
-grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)"
-
-# SERVFAIL + EDE code 0: Other + extra text
-(! run resolvectl query edns-code-zero.forwarded.test)
-grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)"
-
-# SERVFAIL + invalid EDE code
-(! run resolvectl query edns-invalid-code.forwarded.test)
-grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)"
-
-# SERVFAIL + invalid EDE code + extra text
-(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test)
-grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)"
-
-### Test resolvectl show-cache
-run resolvectl show-cache
-run resolvectl show-cache --json=short
-run resolvectl show-cache --json=pretty
-
-# Issue: https://github.com/systemd/systemd/issues/29580 (part #1)
-dig @127.0.0.54 signed.test
-
-systemctl stop resolvectl-monitor.service
-systemctl stop resolvectl-monitor-json.service
-
-# Issue: https://github.com/systemd/systemd/issues/29580 (part #2)
-#
-# Check for any warnings regarding malformed messages
-(! journalctl -u resolvectl-monitor.service -u reseolvectl-monitor-json.service -p warning --grep malformed)
-# Verify that all queries recorded by `resolvectl monitor --json` produced a valid JSON
-# with expected fields
-journalctl -p info -o cat _SYSTEMD_UNIT="resolvectl-monitor-json.service" | while read -r line; do
- # Check that both "question" and "answer" fields are arrays
- #
- # The expression is slightly more complicated due to the fact that the "answer" field is optional,
- # so we need to select it only if it's present, otherwise the type == "array" check would fail
- echo "$line" | jq -e '[. | .question, (select(has("answer")) | .answer) | type == "array"] | all'
-done
-
-# Test serve stale feature and NFTSet= if nftables is installed
-if command -v nft >/dev/null; then
- ### Test without serve stale feature ###
- NFT_FILTER_NAME=dns_port_filter
-
- drop_dns_outbound_traffic() {
- nft add table inet $NFT_FILTER_NAME
- nft add chain inet $NFT_FILTER_NAME output \{ type filter hook output priority 0 \; \}
- nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 udp dport 53 drop
- nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 tcp dport 53 drop
- nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 udp dport 53 drop
- nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 tcp dport 53 drop
- }
-
- run dig stale1.unsigned.test -t A
- grep -qE "NOERROR" "$RUN_OUT"
- sleep 2
- drop_dns_outbound_traffic
- set +e
- # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up
- # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617
- run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
- set -eux
- grep -qE "no servers could be reached" "$RUN_OUT"
- nft flush ruleset
-
- ### Test TIMEOUT with serve stale feature ###
-
- mkdir -p /run/systemd/resolved.conf.d
- {
- echo "[Resolve]"
- echo "StaleRetentionSec=1d"
- } >/run/systemd/resolved.conf.d/test.conf
- ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
- systemctl reload systemd-resolved.service
-
- run dig stale1.unsigned.test -t A
- grep -qE "NOERROR" "$RUN_OUT"
- sleep 2
- drop_dns_outbound_traffic
- # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above)
- run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
- grep -qE "NOERROR" "$RUN_OUT"
- grep -qE "10.0.0.112" "$RUN_OUT"
-
- nft flush ruleset
-
- ### Test NXDOMAIN with serve stale feature ###
- # NXDOMAIN response should replace the cache with NXDOMAIN response
- run dig stale1.unsigned.test -t A
- grep -qE "NOERROR" "$RUN_OUT"
- # Delete stale1 record from zone
- knotc zone-begin unsigned.test
- knotc zone-unset unsigned.test stale1 A
- knotc zone-commit unsigned.test
- knotc reload
- sleep 2
- run dig stale1.unsigned.test -t A
- grep -qE "NXDOMAIN" "$RUN_OUT"
-
- nft flush ruleset
-
- ### NFTSet= test
- nft add table inet sd_test
- nft add set inet sd_test c '{ type cgroupsv2; }'
- nft add set inet sd_test u '{ typeof meta skuid; }'
- nft add set inet sd_test g '{ typeof meta skgid; }'
-
- # service
- systemd-run --unit test-nft.service --service-type=exec -p DynamicUser=yes \
- -p 'NFTSet=cgroup:inet:sd_test:c user:inet:sd_test:u group:inet:sd_test:g' sleep 10000
- run nft list set inet sd_test c
- grep -qF "test-nft.service" "$RUN_OUT"
- uid=$(getent passwd test-nft | cut -d':' -f3)
- run nft list set inet sd_test u
- grep -qF "$uid" "$RUN_OUT"
- gid=$(getent passwd test-nft | cut -d':' -f4)
- run nft list set inet sd_test g
- grep -qF "$gid" "$RUN_OUT"
- systemctl stop test-nft.service
-
- # scope
- run systemd-run --scope -u test-nft.scope -p 'NFTSet=cgroup:inet:sd_test:c' nft list set inet sd_test c
- grep -qF "test-nft.scope" "$RUN_OUT"
-
- mkdir -p /run/systemd/system
- # socket
- {
- echo "[Socket]"
- echo "ListenStream=12345"
- echo "BindToDevice=lo"
- echo "NFTSet=cgroup:inet:sd_test:c"
- } >/run/systemd/system/test-nft.socket
- {
- echo "[Service]"
- echo "ExecStart=/usr/bin/sleep 10000"
- } >/run/systemd/system/test-nft.service
- systemctl daemon-reload
- systemctl start test-nft.socket
- systemctl status test-nft.socket
- run nft list set inet sd_test c
- grep -qF "test-nft.socket" "$RUN_OUT"
- systemctl stop test-nft.socket
- rm -f /run/systemd/system/test-nft.{socket,service}
-
- # slice
- mkdir /run/systemd/system/system.slice.d
- {
- echo "[Slice]"
- echo "NFTSet=cgroup:inet:sd_test:c"
- } >/run/systemd/system/system.slice.d/00-test-nft.conf
- systemctl daemon-reload
- run nft list set inet sd_test c
- grep -qF "system.slice" "$RUN_OUT"
- rm -rf /run/systemd/system/system.slice.d
-
- nft flush ruleset
-else
- echo "nftables is not installed. Skipped serve stale feature and NFTSet= tests."
-fi
-
-### Test resolvectl show-server-state ###
-run resolvectl show-server-state
-grep -qF "10.0.0.1" "$RUN_OUT"
-grep -qF "Interface" "$RUN_OUT"
-
-run resolvectl show-server-state --json=short
-grep -qF "10.0.0.1" "$RUN_OUT"
-grep -qF "Interface" "$RUN_OUT"
-
-run resolvectl show-server-state --json=pretty
-grep -qF "10.0.0.1" "$RUN_OUT"
-grep -qF "Interface" "$RUN_OUT"
-
-### Test resolvectl statistics ###
-run resolvectl statistics
-grep -qF "Transactions" "$RUN_OUT"
-grep -qF "Cache" "$RUN_OUT"
-grep -qF "Failure Transactions" "$RUN_OUT"
-grep -qF "DNSSEC Verdicts" "$RUN_OUT"
-
-run resolvectl statistics --json=short
-grep -qF "transactions" "$RUN_OUT"
-grep -qF "cache" "$RUN_OUT"
-grep -qF "dnssec" "$RUN_OUT"
-
-run resolvectl statistics --json=pretty
-grep -qF "transactions" "$RUN_OUT"
-grep -qF "cache" "$RUN_OUT"
-grep -qF "dnssec" "$RUN_OUT"
-
-### Test resolvectl reset-statistics ###
-run resolvectl reset-statistics
-
-run resolvectl reset-statistics --json=pretty
-
-run resolvectl reset-statistics --json=short
-
-test "$(resolvectl --json=short query -t AAAA localhost)" == '{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}'
-test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]}'
-
-# Test ResolveRecord RR resolving via Varlink
-test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}'
-test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}'
-
-# Ensure that reloading keeps the manually configured address
-{
- echo "[Resolve]"
- echo "DNS=8.8.8.8"
-} >/run/systemd/resolved.conf.d/reload.conf
-resolvectl dns dns0 1.1.1.1
-systemctl reload systemd-resolved.service
-resolvectl status
-resolvectl dns dns0 | grep -qF "1.1.1.1"
-# For some reason piping this last command to grep fails with:
-# 'resolvectl[1378]: Failed to print table: Broken pipe'
-# so use an intermediate file in /tmp/
-resolvectl >/tmp/output
-grep -qF "DNS Servers: 8.8.8.8" /tmp/output
-
-# Check if resolved exits cleanly.
-restart_resolved
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-76-SYSCTL
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-echo "foo.bar=42" >/tmp/foo.conf
-assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
-assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
-
-echo "-foo.foo=42" >/tmp/foo.conf
-assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
-assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
-
-if ! systemd-detect-virt --quiet --container; then
- ip link add hoge type dummy
- udevadm wait /sys/class/net/hoge
-
- cat >/tmp/foo.conf <<EOF
-net.ipv4.conf.*.drop_gratuitous_arp=1
-net.ipv4.*.*.bootp_relay=1
-net.ipv4.aaa.*.disable_policy=1
-EOF
-
- echo 0 >/proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
- echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay
- echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy
-
- assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
- assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
- assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
- assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
-fi
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-78-SIGQUEUE
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then
- echo "env tool too old, can't block signals, skipping test." >&2
- echo OK >/testok
- exit 0
-fi
-
-systemd-analyze log-level debug
-
-UNIT="test-sigqueue-$RANDOM.service"
-
-systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity
-
-systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
-
-# We simply check that six signals are queued now. There's no easy way to check
-# from shell which ones those are, hence we don't check that.
-P=$(systemctl show -P MainPID "$UNIT")
-
-test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6
-
-systemctl stop $UNIT
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-79-MEMPRESS
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-MemoryAccounting=1
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# We not just test if the file exists, but try to read from it, since if
-# CONFIG_PSI_DEFAULT_DISABLED is set in the kernel the file will exist and can
-# be opened, but any read()s will fail with EOPNOTSUPP, which we want to
-# detect.
-if ! cat /proc/pressure/memory >/dev/null ; then
- echo "kernel too old, has no PSI." >&2
- echo OK >/testok
- exit 0
-fi
-
-systemd-analyze log-level debug
-
-CGROUP=/sys/fs/cgroup/"$(systemctl show testsuite-79.service -P ControlGroup)"
-test -d "$CGROUP"
-
-if ! test -f "$CGROUP"/memory.pressure ; then
- echo "No memory accounting/PSI delegated via cgroup, can't test." >&2
- echo OK >/testok
- exit 0
-fi
-
-UNIT="test-mempress-$RANDOM.service"
-SCRIPT="/tmp/mempress-$RANDOM.sh"
-
-cat >"$SCRIPT" <<'EOF'
-#!/bin/bash
-
-set -ex
-
-export
-id
-
-test -n "$MEMORY_PRESSURE_WATCH"
-test "$MEMORY_PRESSURE_WATCH" != /dev/null
-test -w "$MEMORY_PRESSURE_WATCH"
-
-ls -al "$MEMORY_PRESSURE_WATCH"
-
-EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)"
-
-test "$EXPECTED" = "$MEMORY_PRESSURE_WRITE"
-
-EOF
-
-chmod +x "$SCRIPT"
-
-systemd-run -u "$UNIT" -p Type=exec -p ProtectControlGroups=1 -p DynamicUser=1 -p MemoryPressureWatch=on -p MemoryPressureThresholdSec=123ms -p BindPaths=$SCRIPT --wait "$SCRIPT"
-
-rm "$SCRIPT"
-
-systemd-analyze log-level info
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-80-NOTIFYACCESS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-mkfifo /tmp/syncfifo1 /tmp/syncfifo2
-
-sync_in() {
- read -r x < /tmp/syncfifo1
- test "$x" = "$1"
-}
-
-sync_out() {
- echo "$1" > /tmp/syncfifo2
-}
-
-export SYSTEMD_LOG_LEVEL=debug
-
-systemctl --no-block start notify.service
-
-sync_in a
-
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
-assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts"
-
-sync_out b
-sync_in c
-
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main"
-assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process"
-assert_rc 3 systemctl --quiet is-active notify.service
-
-sync_out d
-sync_in e
-
-systemctl --quiet is-active notify.service
-assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK"
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none"
-
-systemctl stop notify.service
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
-
-rm /tmp/syncfifo1 /tmp/syncfifo2
-
-# Now test basic fdstore behaviour
-
-MYSCRIPT="/tmp/myscript$RANDOM.sh"
-cat >> "$MYSCRIPT" <<'EOF'
-#!/usr/bin/env bash
-set -eux
-set -o pipefail
-test "$FDSTORE" -eq 7
-N="/tmp/$RANDOM"
-echo $RANDOM > "$N"
-systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N"
-rm "$N"
-systemd-notify --ready
-exec sleep infinity
-EOF
-
-chmod +x "$MYSCRIPT"
-
-MYUNIT="myunit$RANDOM.service"
-systemd-run -u "$MYUNIT" -p Type=notify -p FileDescriptorStoreMax=7 "$MYSCRIPT"
-
-test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2
-systemd-analyze fdstore "$MYUNIT" --json=short
-systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]'
-
-systemctl stop "$MYUNIT"
-rm "$MYSCRIPT"
-
-systemd-analyze log-level debug
-
-# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service)
-systemctl start fdstore-pin.target
-
-assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes
-assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
-
-# The file descriptor store should survive service restarts
-systemctl restart fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
-
-# It should not survive the service stop plus a later start (unless pinned)
-systemctl stop fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
-
-systemctl start fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
-
-systemctl stop fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
-
-systemctl clean fdstore-pin.service --what=fdstore
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-debug-generator"
-OUT_DIR="$(mktemp -d /tmp/debug-generator.XXX)"
-
-at_exit() {
- rm -frv "${OUT_DIR:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-# Potential FIXME:
-# - debug-generator should gracefully handle duplicated mask/wants
-# - also, handle gracefully empty mask/wants
-ARGS=(
- "systemd.mask=masked-no-suffix"
- "systemd.mask=masked.service"
- "systemd.mask=masked.socket"
- "systemd.wants=wanted-no-suffix"
- "systemd.wants=wanted.service"
- "systemd.wants=wanted.mount"
- "rd.systemd.mask=masked-initrd.service"
- "rd.systemd.wants=wanted-initrd.service"
-)
-
-# Regular (non-initrd) scenario
-#
-: "debug-shell: regular"
-CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
-link_eq "$OUT_DIR/early/masked.service" /dev/null
-link_eq "$OUT_DIR/early/masked.socket" /dev/null
-link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
-link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service
-link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
-# Following stuff should be ignored, as it's prefixed with rd.
-test ! -h "$OUT_DIR/early/masked-initrd.service"
-test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service"
-test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service"
-test ! -d "$OUT_DIR/early/initrd.target.wants"
-
-# Let's re-run the generator with systemd.debug_shell that should be honored
-: "debug-shell: regular + systemd.debug_shell"
-CMDLINE="$CMDLINE systemd.debug_shell"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-
-# Same thing, but with custom tty
-: "debug-shell: regular + systemd.debug_shell=/dev/tty666"
-CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
-
-# Same thing, but with custom tty using systemd.default_debug_tty
-: "debug-shell: regular + systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
-CMDLINE="$CMDLINE systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
-
-# Now override the default target via systemd.unit=
-: "debug-shell: regular + systemd.unit="
-CMDLINE="$CMDLINE systemd.unit=my-fancy.target"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
-link_eq "$OUT_DIR/early/masked.service" /dev/null
-link_eq "$OUT_DIR/early/masked.socket" /dev/null
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-test ! -d "$OUT_DIR/early/default.target.wants"
-
-
-# Initrd scenario
-: "debug-shell: initrd"
-CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell"
-SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
-link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
-# The non-initrd stuff (i.e. without the rd. suffix) should be ignored in
-# this case
-test ! -h "$OUT_DIR/early/masked-no-suffix.service"
-test ! -h "$OUT_DIR/early/masked.service"
-test ! -h "$OUT_DIR/early/masked.socket"
-test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service"
-test ! -d "$OUT_DIR/early/default.target.wants"
-
-# Again, but with rd.systemd.debug_shell
-: "debug-shell: initrd + rd.systemd.debug_shell"
-CMDLINE="$CMDLINE rd.systemd.debug_shell"
-SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-
-# Override the default target
-: "debug-shell: initrd + rd.systemd.unit"
-CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target"
-SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
-link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
-test ! -d "$OUT_DIR/early/initrd.target.wants"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator"
-CONFIG_FILE="/run/environment.d/99-test.conf"
-OUT_FILE="$(mktemp)"
-
-at_exit() {
- set +e
- rm -frv "${CONFIG_FILE:?}" "${OUT_FILE:?}"
- systemctl -M testuser@.host --user daemon-reload
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-mkdir -p /run/environment.d/
-
-cat >"$CONFIG_FILE" <<EOF
-
-\t\n\t
-3
-=
- =
-INVALID
-ALSO_INVALID=
-EMPTY_INVALID=""
-3_INVALID=foo
-xxxx xx xxxxxx
-# This is a comment
-$(printf "%.0sx" {0..4096})=
-SIMPLE=foo
-REF=\$SIMPLE
-ALSO_REF=\${SIMPLE}
-DEFAULT="\${NONEXISTENT:-default value}"
-ALTERNATE="\${SIMPLE:+alternate value}"
-LIST=foo,bar,baz
-SIMPLE=redefined
-UNASSIGNED=\$FOO_BAR_BAZ
-VERY_LONG="very $(printf "%.0sx" {0..4096})= long string"
-EOF
-
-# Source env assignments from a file and check them - do this in a subshell
-# to not pollute the test environment
-check_environment() {(
- # shellcheck source=/dev/null
- source "${1:?}"
-
- [[ "$SIMPLE" == "redefined" ]]
- [[ "$REF" == "foo" ]]
- [[ "$ALSO_REF" == "foo" ]]
- [[ "$DEFAULT" == "default value" ]]
- [[ "$ALTERNATE" == "alternate value" ]]
- [[ "$LIST" == "foo,bar,baz" ]]
- [[ "$VERY_LONG" =~ ^very\ ]]
- [[ "$VERY_LONG" =~ \ long\ string$ ]]
- [[ -z "$UNASSIGNED" ]]
- [[ ! -v INVALID ]]
- [[ ! -v ALSO_INVALID ]]
- [[ ! -v EMPTY_INVALID ]]
- [[ ! -v 3_INVALID ]]
-)}
-
-# Check the output by directly calling the generator
-"$GENERATOR_BIN" | tee "$OUT_FILE"
-check_environment "$OUT_FILE"
-: >"$OUT_FILE"
-
-# Check if the generator is correctly called in a user session
-systemctl -M testuser@.host --user daemon-reload
-systemctl -M testuser@.host --user show-environment | tee "$OUT_FILE"
-check_environment "$OUT_FILE"
-
-(! "$GENERATOR_BIN" foo)
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235,SC2233
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator"
-NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$"
-OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)"
-FSTAB="$(mktemp)"
-
-at_exit() {
- mountpoint -q /proc/cmdline && umount /proc/cmdline
- rm -fr "${OUT_DIR:?}" "${FSTAB:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-FSTAB_GENERAL=(
- # Valid entries
- "/dev/test2 /nofail ext4 nofail 0 0"
- "/dev/test3 /regular btrfs defaults 0 0"
- "/dev/test4 /x-systemd.requires xfs x-systemd.requires=foo.service 0 0"
- "/dev/test5 /x-systemd.before-after xfs x-systemd.before=foo.service,x-systemd.after=bar.mount 0 0"
- "/dev/test6 /x-systemd.wanted-required-by xfs x-systemd.wanted-by=foo.service,x-systemd.required-by=bar.device 0 0"
- "/dev/test7 /x-systemd.requires-mounts-for xfs x-systemd.requires-mounts-for=/foo/bar/baz 0 0"
- "/dev/test8 /x-systemd.automount-idle-timeout vfat x-systemd.automount,x-systemd.idle-timeout=50s 0 0"
- "/dev/test9 /x-systemd.makefs xfs x-systemd.makefs 0 0"
- "/dev/test10 /x-systemd.growfs xfs x-systemd.growfs 0 0"
- "/dev/test11 /_netdev ext4 defaults,_netdev 0 0"
- "/dev/test12 /_rwonly ext4 x-systemd.rw-only 0 0"
- "/dev/test13 /chaos1 zfs x-systemd.rw-only,x-systemd.requires=hello.service,x-systemd.after=my.device 0 0"
- "/dev/test14 /chaos2 zfs x.systemd.wanted-by=foo.service,x-systemd.growfs,x-systemd.makefs 0 0"
- "/dev/test15 /fstype/auto auto defaults 0 0"
- "/dev/test16 /fsck/me ext4 defaults 0 1"
- "/dev/test17 /also/fsck/me ext4 defaults,x-systemd.requires-mounts-for=/var/lib/foo 0 99"
- "/dev/test18 /swap swap defaults 0 0"
- "/dev/test19 /swap/makefs swap defaults,x-systemd.makefs 0 0"
- "/dev/test20 /var xfs defaults,x-systemd.device-timeout=1h 0 0"
- "/dev/test21 /usr ext4 defaults 0 1"
- "/dev/test22 /initrd/mount ext2 defaults,x-systemd.rw-only,x-initrd.mount 0 1"
- "/dev/test23 /initrd/mount/nofail ext3 defaults,nofail,x-initrd.mount 0 1"
- "/dev/test24 /initrd/mount/deps ext4 x-initrd.mount,x-systemd.before=early.service,x-systemd.after=late.service 0 1"
-
- # Incomplete, but valid entries
- "/dev/incomplete1 /incomplete1"
- "/dev/incomplete2 /incomplete2 ext4"
- "/dev/incomplete3 /incomplete3 ext4 defaults"
- "/dev/incomplete4 /incomplete4 ext4 defaults 0"
-
- # Remote filesystems
- "/dev/remote1 /nfs nfs bg 0 0"
- "/dev/remote2 /nfs4 nfs4 bg 0 0"
- "bar.tld:/store /remote/storage nfs ro,x-systemd.wanted-by=store.service 0 0"
- "user@host.tld:/remote/dir /remote/top-secret sshfs rw,x-systemd.before=naughty.service 0 0"
- "foo.tld:/hello /hello/world ceph defaults 0 0"
- "//192.168.0.1/storage /cifs-storage cifs automount,nofail 0 0"
-)
-
-FSTAB_GENERAL_ROOT=(
- # rootfs with bunch of options we should ignore and fsck enabled
- "/dev/test1 / ext4 noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1"
- "${FSTAB_GENERAL[@]}"
-)
-
-FSTAB_MINIMAL=(
- "/dev/loop1 /foo/bar ext3 defaults 0 0"
-)
-
-FSTAB_DUPLICATE=(
- "/dev/dup1 / ext4 defaults 0 1"
- "/dev/dup2 / ext4 defaults,x-systemd.requires=foo.mount 0 2"
-)
-
-FSTAB_INVALID=(
- # Ignored entries
- "/dev/ignored1 /sys/fs/cgroup/foo ext4 defaults 0 0"
- "/dev/ignored2 /sys/fs/selinux ext4 defaults 0 0"
- "/dev/ignored3 /dev/console ext4 defaults 0 0"
- "/dev/ignored4 /proc/kmsg ext4 defaults 0 0"
- "/dev/ignored5 /proc/sys ext4 defaults 0 0"
- "/dev/ignored6 /proc/sys/kernel/random/boot_id ext4 defaults 0 0"
- "/dev/ignored7 /run/host ext4 defaults 0 0"
- "/dev/ignored8 /run/host/foo ext4 defaults 0 0"
- "/dev/ignored9 /autofs autofs defaults 0 0"
- "/dev/invalid1 not-a-path ext4 defaults 0 0"
- ""
- "/dev/invalid1"
- " "
- "\\"
- "$"
-)
-
-check_fstab_mount_units() {
- local what where fstype opts passno unit
- local item opt split_options filtered_options supp service device arg
- local array_name="${1:?}"
- local out_dir="${2:?}/normal"
- # Get a reference to the array from its name
- local -n fstab_entries="$array_name"
-
- # Running the checks in a container is pretty much useless, since we don't
- # generate any mounts, but don't skip the whole test to test the "skip"
- # paths as well
- in_container && return 0
-
- for item in "${fstab_entries[@]}"; do
- # Don't use a pipe here, as it would make the variables out of scope
- read -r what where fstype opts _ passno <<< "$item"
-
- # Skip non-initrd mounts in initrd
- if in_initrd_host && ! [[ "$opts" =~ x-initrd.mount ]]; then
- continue
- fi
-
- if [[ "$fstype" == swap ]]; then
- unit="$(systemd-escape --suffix=swap --path "${what:?}")"
- cat "$out_dir/$unit"
-
- grep -qE "^What=$what$" "$out_dir/$unit"
- if [[ "$opts" != defaults ]]; then
- grep -qE "^Options=$opts$" "$out_dir/$unit"
- fi
-
- if [[ "$opts" =~ x-systemd.makefs ]]; then
- service="$(systemd-escape --template=systemd-mkswap@.service --path "$what")"
- test -e "$out_dir/$service"
- fi
-
- continue
- fi
-
- # If we're parsing host's fstab in initrd, prefix all mount targets
- # with /sysroot
- in_initrd_host && where="/sysroot${where:?}"
- unit="$(systemd-escape --suffix=mount --path "${where:?}")"
- cat "$out_dir/$unit"
-
- # Check the general stuff
- grep -qE "^What=$what$" "$out_dir/$unit"
- grep -qE "^Where=$where$" "$out_dir/$unit"
- if [[ -n "$fstype" ]] && [[ "$fstype" != auto ]]; then
- grep -qE "^Type=$fstype$" "$out_dir/$unit"
- fi
- if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then
- # Some options are not propagated to the generated unit
- if [[ "$where" == / || "$where" == /usr ]]; then
- filtered_options="$(opt_filter "$opts" "(noauto|nofail|x-systemd.(wanted-by=|required-by=|automount|device-timeout=))")"
- else
- filtered_options="$(opt_filter "$opts" "^x-systemd.device-timeout=")"
- fi
-
- if [[ "${filtered_options[*]}" != defaults ]]; then
- grep -qE "^Options=.*$filtered_options.*$" "$out_dir/$unit"
- fi
- fi
-
- if ! [[ "$opts" =~ (noauto|x-systemd.(wanted-by=|required-by=|automount)) ]]; then
- # We don't create the Requires=/Wants= symlinks for noauto/automount mounts
- # and for mounts that use x-systemd.wanted-by=/required-by=
- if in_initrd_host; then
- if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
- link_eq "$out_dir/initrd-fs.target.requires/$unit" "../$unit"
- else
- link_eq "$out_dir/initrd-fs.target.wants/$unit" "../$unit"
- fi
- elif [[ "$fstype" =~ $NETWORK_FS_RX || "$opts" =~ _netdev ]]; then
- # Units with network filesystems should have a Requires= dependency
- # on the remote-fs.target, unless they use nofail or are an nfs "bg"
- # mounts, in which case the dependency is downgraded to Wants=
- if [[ "$opts" =~ nofail ]] || [[ "$fstype" =~ ^(nfs|nfs4) && "$opts" =~ bg ]]; then
- link_eq "$out_dir/remote-fs.target.wants/$unit" "../$unit"
- else
- link_eq "$out_dir/remote-fs.target.requires/$unit" "../$unit"
- fi
- else
- # Similarly, local filesystems should have a Requires= dependency on
- # the local-fs.target, unless they use nofail, in which case the
- # dependency is downgraded to Wants=. Rootfs is a special case,
- # since we always ignore nofail there
- if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
- link_eq "$out_dir/local-fs.target.requires/$unit" "../$unit"
- else
- link_eq "$out_dir/local-fs.target.wants/$unit" "../$unit"
- fi
- fi
- fi
-
- if [[ "${passno:=0}" -ne 0 ]]; then
- # Generate systemd-fsck@.service dependencies, if applicable
- if in_initrd && [[ "$where" == / || "$where" == /usr ]]; then
- continue
- fi
-
- if [[ "$where" == / ]]; then
- link_endswith "$out_dir/local-fs.target.wants/systemd-fsck-root.service" "/lib/systemd/system/systemd-fsck-root.service"
- else
- service="$(systemd-escape --template=systemd-fsck@.service --path "$what")"
- grep -qE "^After=$service$" "$out_dir/$unit"
- if [[ "$where" == /usr ]]; then
- grep -qE "^Wants=$service$" "$out_dir/$unit"
- else
- grep -qE "^Requires=$service$" "$out_dir/$unit"
- fi
- fi
- fi
-
- # Check various x-systemd options
- #
- # First, split them into an array to make splitting them even further
- # easier
- IFS="," read -ra split_options <<< "$opts"
- # and process them one by one.
- #
- # Note: the "machinery" below might (and probably does) miss some
- # combinations of supported options, so tread carefully
- for opt in "${split_options[@]}"; do
- if [[ "$opt" =~ ^x-systemd.requires= ]]; then
- service="$(opt_get_arg "$opt")"
- grep -qE "^Requires=$service$" "$out_dir/$unit"
- grep -qE "^After=$service$" "$out_dir/$unit"
- elif [[ "$opt" =~ ^x-systemd.before= ]]; then
- service="$(opt_get_arg "$opt")"
- grep -qE "^Before=$service$" "$out_dir/$unit"
- elif [[ "$opt" =~ ^x-systemd.after= ]]; then
- service="$(opt_get_arg "$opt")"
- grep -qE "^After=$service$" "$out_dir/$unit"
- elif [[ "$opt" =~ ^x-systemd.wanted-by= ]]; then
- service="$(opt_get_arg "$opt")"
- if [[ "$where" == / ]]; then
- # This option is ignored for rootfs mounts
- (! link_eq "$out_dir/$service.wants/$unit" "../$unit")
- else
- link_eq "$out_dir/$service.wants/$unit" "../$unit"
- fi
- elif [[ "$opt" =~ ^x-systemd.required-by= ]]; then
- service="$(opt_get_arg "$opt")"
- if [[ "$where" == / ]]; then
- # This option is ignored for rootfs mounts
- (! link_eq "$out_dir/$service.requires/$unit" "../$unit")
- else
- link_eq "$out_dir/$service.requires/$unit" "../$unit"
- fi
- elif [[ "$opt" =~ ^x-systemd.requires-mounts-for= ]]; then
- arg="$(opt_get_arg "$opt")"
- grep -qE "^RequiresMountsFor=$arg$" "$out_dir/$unit"
- elif [[ "$opt" == x-systemd.device-bound ]]; then
- # This is implied for fstab mounts
- :
- elif [[ "$opt" == x-systemd.automount ]]; then
- # The $unit should have an accompanying automount unit
- supp="$(systemd-escape --suffix=automount --path "$where")"
- if [[ "$where" == / ]]; then
- # This option is ignored for rootfs mounts
- test ! -e "$out_dir/$supp"
- (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp")
- else
- test -e "$out_dir/$supp"
- link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp"
- fi
- elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then
- # The timeout applies to the automount unit, not the original
- # mount one
- arg="$(opt_get_arg "$opt")"
- supp="$(systemd-escape --suffix=automount --path "$where")"
- grep -qE "^TimeoutIdleSec=$arg$" "$out_dir/$supp"
- elif [[ "$opt" =~ ^x-systemd.device-timeout= ]]; then
- arg="$(opt_get_arg "$opt")"
- device="$(systemd-escape --suffix=device --path "$what")"
- grep -qE "^JobRunningTimeoutSec=$arg$" "$out_dir/${device}.d/50-device-timeout.conf"
- elif [[ "$opt" == x-systemd.makefs ]]; then
- service="$(systemd-escape --template=systemd-makefs@.service --path "$what")"
- test -e "$out_dir/$service"
- link_eq "$out_dir/${unit}.requires/$service" "../$service"
- elif [[ "$opt" == x-systemd.rw-only ]]; then
- grep -qE "^ReadWriteOnly=yes$" "$out_dir/$unit"
- elif [[ "$opt" == x-systemd.growfs ]]; then
- service="$(systemd-escape --template=systemd-growfs@.service --path "$where")"
- link_endswith "$out_dir/${unit}.wants/$service" "/lib/systemd/system/systemd-growfs@.service"
- elif [[ "$opt" == bg ]] && [[ "$fstype" =~ ^(nfs|nfs4)$ ]]; then
- # We "convert" nfs bg mounts to fg, so we can do the job-control
- # ourselves
- grep -qE "^Options=.*\bx-systemd.mount-timeout=infinity\b" "$out_dir/$unit"
- grep -qE "^Options=.*\bfg\b.*" "$out_dir/$unit"
- elif [[ "$opt" =~ ^x-systemd\. ]]; then
- echo >&2 "Unhandled mount option: $opt"
- exit 1
- fi
- done
- done
-}
-
-: "fstab-generator: regular"
-printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
-
-# Skip the rest when running in a container, as it makes little sense to check
-# initrd-related stuff there and fstab-generator might have a bit strange
-# behavior during certain tests, like https://github.com/systemd/systemd/issues/27156
-if in_container; then
- echo "Running in a container, skipping the rest of the fstab-generator tests..."
- exit 0
-fi
-
-# In this mode we treat the entries as "regular" ones
-: "fstab-generator: initrd - initrd fstab"
-printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null check_fstab_mount_units FSTAB_GENERAL "$OUT_DIR"
-
-# In this mode we prefix the mount target with /sysroot and ignore all mounts
-# that don't have the x-initrd.mount flag
-: "fstab-generator: initrd - host fstab"
-printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
-
-# Check the default stuff that we (almost) always create in initrd
-: "fstab-generator: initrd default"
-SYSTEMD_PROC_CMDLINE="root=/dev/sda2" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-test -e "$OUT_DIR/normal/sysroot.mount"
-test -e "$OUT_DIR/normal/systemd-fsck-root.service"
-link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
-link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
-
-: "fstab-generator: run as systemd-sysroot-fstab-check in initrd"
-ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check
-(! /tmp/systemd-sysroot-fstab-check foo)
-(! SYSTEMD_IN_INITRD=0 /tmp/systemd-sysroot-fstab-check)
-printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
-SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB="$FSTAB" /tmp/systemd-sysroot-fstab-check
-
-: "fstab-generator: duplicate"
-printf "%s\n" "${FSTAB_DUPLICATE[@]}" >"$FSTAB"
-cat "$FSTAB"
-(! SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR")
-
-: "fstab-generator: invalid"
-printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB"
-cat "$FSTAB"
-# Don't care about the exit code here
-SYSTEMD_PROC_CMDLINE="" SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || :
-# No mounts should get created here
-[[ "$(find "$OUT_DIR" -name "*.mount" | wc -l)" -eq 0 ]]
-
-: "fstab-generator: kernel args - fstab=0"
-printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-(! SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
-
-: "fstab-generator: kernel args - rd.fstab=0"
-printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
-
-: "fstab-generator: kernel args - systemd.swap=0"
-printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-# No swap units should get created here
-[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]]
-
-# Possible TODO
-# - combine the rootfs & usrfs arguments and mix them with fstab entries
-# - systemd.volatile=
-: "fstab-generator: kernel args - root= + rootfstype= + rootflags="
-# shellcheck disable=SC2034
-EXPECTED_FSTAB=(
- "/dev/disk/by-label/rootfs / ext4 noexec,ro 0 1"
-)
-CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function
-# we're parsing host's fstab, but it's all on the kernel cmdline instead
-SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR"
-
-# This is a very basic sanity test that involves manual checks, since adding it
-# to the check_fstab_mount_units() function would make it way too complex
-# (yet another possible TODO)
-: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags="
-CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount"
-# The general idea here is to mount the device to /sysusr/usr and then
-# bind-mount /sysusr/usr to /sysroot/usr
-grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount"
-grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount"
-grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount"
-grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount"
-link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount"
-grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
-grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
-grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount"
-link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-# Disable history expansion so we don't have to escape ! in strings below
-set +o histexpand
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator"
-OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)"
-
-at_exit() {
- rm -frv "${OUT_DIR:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-if in_container; then
- # Do a limited test in a container, as writing to /dev is usually restrited
- : "getty-generator: \$container_ttys env (container)"
- # In a container we allow only /dev/pts/* ptys
- PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-
- # console-getty.service is always pulled in in containers
- link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service"
- link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service"
- test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
- test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
-
- exit 0
-fi
-
-DUMMY_ACTIVE_CONSOLES=(
- "hvc99"
- "xvc99"
- "hvsi99"
- "sclp_line99"
- "ttysclp99"
- "3270!tty99"
- "dummy99"
-)
-DUMMY_INACTIVE_CONSOLES=(
- "inactive99"
- "xvc199"
-)
-DUMMY_CONSOLES=(
- "${DUMMY_ACTIVE_CONSOLES[@]}"
- "${DUMMY_INACTIVE_CONSOLES[@]}"
-)
-# Create a bunch of dummy consoles
-for console in "${DUMMY_CONSOLES[@]}"; do
- mknod "/dev/$console" c 4 0
-done
-# Sneak in one "not-a-tty" console
-touch /dev/notatty99
-# Temporarily replace /sys/class/tty/console/active with our list of dummy
-# consoles so getty-generator can process them
-echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles
-mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active
-
-: "getty-generator: no arguments"
-# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out
-PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do
- unit="$(systemd-escape --template serial-getty@.service "$console")"
- link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
-done
-for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do
- unit="$(systemd-escape --template serial-getty@.service "$console")"
- test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
- test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
-done
-
-: "getty-generator: systemd.getty_auto=0 on kernel cmdline"
-SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment"
-PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-# Cleanup
-umount /sys/class/tty/console/active --lazy
-rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator"
-OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)"
-
-at_exit() {
- rm -frv "${OUT_DIR:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-check_kernel_cmdline_target() {
- local out_dir="${1:?}/normal"
-
- cat "$out_dir/kernel-command-line.target"
- grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
- grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
-
- link_eq "$out_dir/default.target" "kernel-command-line.target"
-}
-
-: "run-generator: empty cmdline"
-SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-: "run-generator: single command"
-CMDLINE="systemd.run='echo hello world'"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-check_kernel_cmdline_target "$OUT_DIR"
-UNIT="$OUT_DIR/normal/kernel-command-line.service"
-cat "$UNIT"
-systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
-grep -qE "^SuccessAction=exit$" "$UNIT"
-grep -qE "^FailureAction=exit$" "$UNIT"
-grep -qE "^ExecStart=echo hello world$" "$UNIT"
-
-: "run-generator: multiple commands + success/failure actions"
-ARGS=(
- # These should be ignored
- "systemd.run"
- "systemd.run_success_action"
- "systemd.run_failure_action"
-
- # Set actions which we will overwrite later
- "systemd.run_success_action="
- "systemd.run_failure_action="
-
- "systemd.run=/bin/false"
- "systemd.run="
- "systemd.run=/bin/true"
- "systemd.run='echo this is a long string'"
-
- "systemd.run_success_action=reboot"
- "systemd.run_failure_action=poweroff-force"
-)
-CMDLINE="${ARGS[*]}"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-check_kernel_cmdline_target "$OUT_DIR"
-UNIT="$OUT_DIR/normal/kernel-command-line.service"
-cat "$UNIT"
-systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
-grep -qE "^SuccessAction=reboot$" "$UNIT"
-grep -qE "^FailureAction=poweroff-force$" "$UNIT"
-grep -qE "^ExecStart=/bin/false$" "$UNIT"
-grep -qE "^ExecStart=$" "$UNIT"
-grep -qE "^ExecStart=/bin/true$" "$UNIT"
-grep -qE "^ExecStart=echo this is a long string$" "$UNIT"
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-81-GENERATORS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator"
-OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)"
-
-at_exit() {
- rm -frv "${OUT_DIR:?}" /system-update
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-rm -f /system-update
-
-: "system-update-generator: no /system-update flag"
-run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-: "system-update-generator: with /system-update flag"
-touch /system-update
-run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
-
-: "system-update-generator: kernel cmdline warnings"
-# We should warn if the default target is overridden on the kernel cmdline
-# by a runlevel or systemd.unit=, but still generate the symlink
-SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log
-link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
-grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log
-grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-82-SOFTREBOOT
-DefaultDependencies=no
-After=basic.target
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-FileDescriptorStoreMax=3
-NotifyAccess=all
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
- # Since the soft-reboot drops the enqueued end.service, we won't shutdown
- # the test VM if the test fails and have to wait for the watchdog to kill
- # us (which may take quite a long time). Let's just forcibly kill the machine
- # instead to save CI resources.
- if [[ $? -ne 0 ]]; then
- echo >&2 "Test failed, shutting down the machine..."
- systemctl poweroff -ff
- fi
-}
-
-trap at_exit EXIT
-
-systemd-analyze log-level debug
-
-export SYSTEMD_LOG_LEVEL=debug
-
-if [ -f /run/testsuite82.touch3 ]; then
- echo "This is the fourth boot!"
- systemd-notify --status="Fourth Boot"
-
- test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3
-
- rm /run/testsuite82.touch3
- mount
- rmdir /original-root /run/nextroot
-
- # Check that the fdstore entry still exists
- test "$LISTEN_FDS" -eq 3
- read -r x <&5
- test "$x" = "oinkoink"
-
- # Check that the surviving services are still around
- test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
- test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active"
- test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active"
- test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
-
- # Check journals
- journalctl -o short-monotonic --no-hostname --grep '(will soft-reboot|KILL|corrupt)'
- assert_eq "$(journalctl -q -o short-monotonic -u systemd-journald.service --grep 'corrupt')" ""
-
- # All succeeded, exit cleanly now
-
-elif [ -f /run/testsuite82.touch2 ]; then
- echo "This is the third boot!"
- systemd-notify --status="Third Boot"
-
- test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2
-
- rm /run/testsuite82.touch2
-
- # Check that the fdstore entry still exists
- test "$LISTEN_FDS" -eq 2
- read -r x <&4
- test "$x" = "miaumiau"
-
- # Upload another entry
- T="/dev/shm/fdstore.$RANDOM"
- echo "oinkoink" >"$T"
- systemd-notify --fd=3 --pid=parent 3<"$T"
- rm "$T"
-
- # Check that the surviving services are still around
- test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
- test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active"
- test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active"
- test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
-
- # Test that we really are in the new overlayfs root fs
- read -r x </lower
- test "$x" = "miep"
- cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
- grep -q MARKER=1 /etc/os-release
-
- # Switch back to the original root, away from the overlayfs
- mount --bind /original-root /run/nextroot
- mount
-
- # Restart the unit that is not supposed to survive
- systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
-
- # Now issue the soft reboot. We should be right back soon.
- touch /run/testsuite82.touch3
- systemctl --no-block soft-reboot
-
- # Now block until the soft-boot killing spree kills us
- exec sleep infinity
-
-elif [ -f /run/testsuite82.touch ]; then
- echo "This is the second boot!"
- systemd-notify --status="Second Boot"
-
- test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1
-
- # Clean up what we created earlier
- rm /run/testsuite82.touch
-
- # Check that the fdstore entry still exists
- test "$LISTEN_FDS" -eq 1
- read -r x <&3
- test "$x" = "wuffwuff"
-
- # Check that we got a PrepareForShutdownWithMetadata signal with the right type
- cat /run/testsuite82.signal
- test "$(jq -r '.payload.data[1].type.data' </run/testsuite82.signal)" = "soft-reboot"
-
- # Check that the system credentials survived the soft reboot.
- test "$(systemd-creds cat --system kernelcmdlinecred)" = "uff"
-
- # Upload another entry
- T="/dev/shm/fdstore.$RANDOM"
- echo "miaumiau" >"$T"
- systemd-notify --fd=3 --pid=parent 3<"$T"
- rm "$T"
-
- # Check that the surviving services are still around
- test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
- test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active"
- test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active"
- test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
-
- # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs)
- mkdir -p /run/nextroot /tmp/nextroot-lower /original-root
- mount -t tmpfs tmpfs /tmp/nextroot-lower
- echo miep >/tmp/nextroot-lower/lower
-
- # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate
- # directory across soft reboots. Try to cover corner cases by truncating it.
- mkdir -p /tmp/nextroot-lower/usr/lib
- grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release
- echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release
- cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
- (! grep -q MARKER=1 /etc/os-release)
-
- mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro
-
- # Bind our current root into the target so that we later can return to it
- mount --bind / /run/nextroot/original-root
-
- # Restart the unit that is not supposed to survive
- systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
-
- # Now ensure there are no naming clashes and a bunch of transient units all succeed
- for _ in $(seq 1 25); do
- systemd-run --wait true
- done
-
- # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should
- # automatically do a softreboot instead of normal reboot.
- touch /run/testsuite82.touch2
- systemctl --no-block reboot
-
- # Now block until the soft-boot killing spree kills us
- exec sleep infinity
-else
- # This is the first boot
- systemd-notify --status="First Boot"
-
- test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0
-
- # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly
- T="/dev/shm/fdstore.$RANDOM"
- echo "wuffwuff" >"$T"
- systemd-notify --fd=3 --pid=parent 3<"$T"
- rm "$T"
-
- survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh"
- cat >"$survive_sigterm" <<EOF
-#!/bin/bash
-trap "" TERM
-systemd-notify --ready
-rm "$survive_sigterm"
-exec sleep infinity
-EOF
- chmod +x "$survive_sigterm"
-
- survive_argv="/dev/shm/survive-argv-$RANDOM.sh"
- cat >"$survive_argv" <<EOF
-#!/bin/bash
-systemd-notify --ready
-rm "$survive_argv"
-exec -a @sleep sleep infinity
-EOF
- chmod +x "$survive_argv"
- # This sets DefaultDependencies=no so that they remain running until the very end, and
- # IgnoreOnIsolate=yes so that they aren't stopped via the "testsuite.target" isolation we do on next boot,
- # and will be killed by the final sigterm/sigkill spree.
- systemd-run --collect --service-type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive-sigterm.service "$survive_sigterm"
- systemd-run --collect --service-type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive.service sleep infinity
-
- # Ensure that the unit doesn't get deactivated by dependencies on the source file. Given it's a verity
- # image that is already open, even if the tmpfs with the image goes away, the file will be pinned by the
- # kernel and will keep working.
- cp /usr/share/minimal_0.* /tmp/
-
- # Configure these transient units to survive the soft reboot - they will not conflict with shutdown.target
- # and it will be ignored on the isolate that happens in the next boot. The first will use argv[0][0] =
- # '@', and the second will use SurviveFinalKillSignal=yes. Both should survive.
- systemd-run --service-type=notify --unit=testsuite-82-survive-argv.service \
- --property SurviveFinalKillSignal=no \
- --property IgnoreOnIsolate=yes \
- --property DefaultDependencies=no \
- --property After=basic.target \
- --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
- --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
- "$survive_argv"
- systemd-run --service-type=exec --unit=testsuite-82-survive.service \
- --property TemporaryFileSystem="/run /tmp /var" \
- --property RootImage=/tmp/minimal_0.raw \
- --property BindReadOnlyPaths=/dev/log \
- --property BindReadOnlyPaths=/run/systemd/journal/socket \
- --property BindReadOnlyPaths=/run/systemd/journal/stdout \
- --property SurviveFinalKillSignal=yes \
- --property IgnoreOnIsolate=yes \
- --property DefaultDependencies=no \
- --property After=basic.target \
- --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
- --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
- sleep infinity
-
- # Check that we can set up an inhibitor, and that busctl monitor sees the
- # PrepareForShutdownWithMetadata signal and that it says 'soft-reboot'.
- systemd-run --unit busctl.service --service-type=exec --property StandardOutput=file:/run/testsuite82.signal \
- busctl monitor --json=pretty --match 'sender=org.freedesktop.login1,path=/org/freedesktop/login1,interface=org.freedesktop.login1.Manager,member=PrepareForShutdownWithMetadata,type=signal'
- systemd-run --unit inhibit.service --service-type=exec \
- systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
- sleep infinity
-
- # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus
- # being restarted and the "unique" bus IDs not being unique across restarts
- for _ in $(seq 1 25); do
- # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is
- # used instead of the bus ID)
- systemd-run --wait false || true
- done
-
- # Now issue the soft reboot. We should be right back soon.
- touch /run/testsuite82.touch
- systemctl --no-block --check-inhibitors=yes soft-reboot
-
- # Now block until the soft-boot killing spree kills us
- exec sleep infinity
-fi
-
-systemd-analyze log-level info
-
-touch /testok
-systemctl --no-block exit 123
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-83-BTRFS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-FSTYPE="$(stat --file-system --format "%T" /)"
-
-if [[ "$FSTYPE" != "btrfs" ]]; then
- echo "Root filesystem is $FSTYPE instead of btrfs, skipping"
- exit 77
-fi
-
-TEST_BTRFS_OFFSET=/usr/lib/systemd/tests/unit-tests/manual/test-btrfs-physical-offset
-
-SWAPFILE=/var/tmp/swapfile
-
-btrfs filesystem mkswapfile -s 10m "$SWAPFILE"
-sync -f "$SWAPFILE"
-
-offset_btrfs_progs="$(btrfs inspect-internal map-swapfile -r "$SWAPFILE")"
-echo "btrfs-progs: $offset_btrfs_progs"
-
-offset_btrfs_util="$("$TEST_BTRFS_OFFSET" "$SWAPFILE")"
-echo "btrfs-util: $offset_btrfs_util"
-
-(( offset_btrfs_progs == offset_btrfs_util ))
-
-rm -f "$SWAPFILE"
-
-/usr/lib/systemd/tests/unit-tests/manual/test-btrfs
-
-touch /testok
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-84-STORAGETM
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-modprobe -v nvmet-tcp
-modprobe -v nvme-tcp
-
-systemctl start sys-kernel-config.mount
-
-dd if=/dev/urandom of=/var/tmp/storagetm.test bs=1024 count=10240
-
-NVME_UUID="$(cat /proc/sys/kernel/random/uuid)"
-systemd-run -u teststoragetm.service -p Type=notify -p "Environment=SYSTEMD_NVME_UUID=${NVME_UUID:?}" /usr/lib/systemd/systemd-storagetm /var/tmp/storagetm.test --nqn=quux
-NVME_DEVICE="/dev/disk/by-id/nvme-uuid.${NVME_UUID:?}"
-
-nvme connect-all -t tcp -a 127.0.0.1 -s 16858 --hostid="$(cat /proc/sys/kernel/random/uuid)"
-udevadm wait --settle "$NVME_DEVICE"
-
-dd if="$NVME_DEVICE" bs=1024 | cmp /var/tmp/storagetm.test -
-
-nvme disconnect-all
-systemctl stop teststoragetm.service
-rm /var/tmp/storagetm.test
-
-touch /testok
create_dummy_container() {
local root="${1:?}"
- if [[ ! -d /usr/share/testsuite-13-container-template ]]; then
+ if [[ ! -d /usr/share/TEST-13-NSPAWN-container-template ]]; then
echo >&2 "Missing container template, probably not running in TEST-13-NSPAWN?"
exit 1
fi
mkdir -p "$root"
- cp -a /usr/share/testsuite-13-container-template/* "$root"
+ cp -a /usr/share/TEST-13-NSPAWN-container-template/* "$root"
coverage_create_nspawn_dropin "$root"
}