diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
commit | fc53809803cd2bc2434e312b19a18fa36776da12 (patch) | |
tree | b4b43bd6538f51965ce32856e9c053d0f90919c8 /test | |
parent | Adding upstream version 255.5. (diff) | |
download | systemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip |
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
798 files changed, 12276 insertions, 4925 deletions
diff --git a/test/testsuite-07.units/issue2467.socket b/mkosi.images/minimal-0/mkosi.extra/opt/some_file index af1317b..bd4fba4 100644 --- a/test/testsuite-07.units/issue2467.socket +++ b/mkosi.images/minimal-0/mkosi.extra/opt/some_file @@ -1,3 +1 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -[Socket] -ListenStream=/run/test.ctl diff --git a/test/README.testsuite b/test/README.testsuite index 9174e16..13ba157 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -1,10 +1,72 @@ +# Integration tests + +## Running the integration tests with meson + mkosi + +To run the integration tests with meson + mkosi, make sure you're running the +latest version of mkosi. See +[`docs/HACKING.md`](https://github.com/systemd/systemd/blob/main/test/README.md) +for more specific details. Make sure `mkosi` is available in `$PATH` when +reconfiguring meson to make sure it is picked up properly. + +We also need to make sure the required meson options are enabled: + +```shell +$ meson setup --reconfigure build -Dremote=enabled +``` + +Next, we can build the integration test image: + +```shell +$ meson compile -C build mkosi +``` + +After the image has been built, the integration tests can be run with: + +```shell +$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build/ --suite integration-tests --num-processes "$(($(nproc) / 4))" +``` + +As usual, specific tests can be run in meson by appending the name of the test +which is usually the name of the directory e.g. + +```shell +$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build/ -v TEST-01-BASIC +``` + +See `meson introspect build --tests` for a list of tests. + +To interactively debug a failing integration test, the `--interactive` option +(`-i`) for `meson test` can be used. Note that this requires meson v1.5.0 or +newer: + +```shell +$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build/ -i TEST-01-BASIC +``` + +Due to limitations in meson, the integration tests do not yet depend on the +mkosi target, which means the mkosi target has to be manually rebuilt before +running the integration tests. To rebuild the image and rerun a test, the +following command can be used: + +```shell +$ meson compile -C build mkosi && SYSTEMD_INTEGRATION_TESTS=1 meson test -C build -v TEST-01-BASIC +``` + +The integration tests use the same mkosi configuration that's used when you run +mkosi in the systemd reposistory, so any local modifications to the mkosi +configuration (e.g. in `mkosi.local.conf`) are automatically picked up and used +by the integration tests as well. + +## Running the integration tests the old fashioned way + The extended testsuite only works with UID=0. It consists of the subdirectories -named "test/TEST-??-*", each of which contains a description of an OS image and +named `test/TEST-??-*`, each of which contains a description of an OS image and a test which consists of systemd units and scripts to execute in this image. The same image is used for execution under `systemd-nspawn` and `qemu`. To run the extended testsuite do the following: +```shell $ ninja -C build # Avoid building anything as root later $ sudo test/run-integration-tests.sh ninja: Entering directory `/home/zbyszek/src/systemd/build' @@ -20,111 +82,107 @@ make: Leaving directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC' --x-- Result of TEST-01-BASIC: 0 --x-- --x-- Running TEST-02-CRYPTSETUP --x-- + make -C TEST-02-CRYPTSETUP clean setup run +``` If one of the tests fails, then $subdir/test.log contains the log file of the test. To run just one of the cases: +```shell $ sudo make -C test/TEST-01-BASIC clean setup run +``` -Specifying the build directory -============================== +### Specifying the build directory If the build directory is not detected automatically, it can be specified with BUILD_DIR=: +```shell $ sudo BUILD_DIR=some-other-build/ test/run-integration-tests +``` or +```shell $ sudo make -C test/TEST-01-BASIC BUILD_DIR=../../some-other-build/ ... +``` Note that in the second case, the path is relative to the test case directory. An absolute path may also be used in both cases. -Testing installed binaries instead of built -=========================================== +### Testing installed binaries instead of built To run the extended testsuite using the systemd installed on the system instead of the systemd from a build, use the NO_BUILD=1: +```shell $ sudo NO_BUILD=1 test/run-integration-tests +``` -Configuration variables -======================= +### Configuration variables -TEST_NO_QEMU=1 - Don't run tests under qemu +`TEST_NO_QEMU=1`: Don't run tests under qemu. -TEST_QEMU_ONLY=1 - Run only tests that require qemu +`TEST_QEMU_ONLY=1`: Run only tests that require qemu. -TEST_NO_NSPAWN=1 - Don't run tests under systemd-nspawn +`TEST_NO_NSPAWN=1`: Don't run tests under systemd-nspawn. -TEST_PREFER_NSPAWN=1 - Run all tests that do not require qemu under systemd-nspawn +`TEST_PREFER_NSPAWN=1`: Run all tests that do not require qemu under +systemd-nspawn. -TEST_NO_KVM=1 - Disable qemu KVM auto-detection (may be necessary when you're trying to run the - *vanilla* qemu and have both qemu and qemu-kvm installed) +`TEST_NO_KVM=1`: Disable qemu KVM auto-detection (may be necessary when you're +trying to run the *vanilla* qemu and have both qemu and qemu-kvm installed) -TEST_NESTED_KVM=1 - Allow tests to run with nested KVM. By default, the testsuite disables - nested KVM if the host machine already runs under KVM. Setting this - variable disables such checks +`TEST_NESTED_KVM=1`: Allow tests to run with nested KVM. By default, the +testsuite disables nested KVM if the host machine already runs under KVM. +Setting this variable disables such checks. -QEMU_MEM=512M - Configure amount of memory for qemu VMs (defaults to 512M) +`QEMU_MEM=512M`: Configure amount of memory for qemu VMs (defaults to 512M). -QEMU_SMP=1 - Configure number of CPUs for qemu VMs (defaults to 1) +`QEMU_SMP=1`: Configure number of CPUs for qemu VMs (defaults to 1). -KERNEL_APPEND='...' - Append additional parameters to the kernel command line +`KERNEL_APPEND='...'`: Append additional parameters to the kernel command line. -NSPAWN_ARGUMENTS='...' - Specify additional arguments for systemd-nspawn +`NSPAWN_ARGUMENTS='...'`: Specify additional arguments for systemd-nspawn. -QEMU_TIMEOUT=infinity - Set a timeout for tests under qemu (defaults to 1800 sec) +`QEMU_TIMEOUT=infinity`: Set a timeout for tests under qemu (defaults to 1800 +sec). -NSPAWN_TIMEOUT=infinity - Set a timeout for tests under systemd-nspawn (defaults to 1800 sec) +`NSPAWN_TIMEOUT=infinity`: Set a timeout for tests under systemd-nspawn +(defaults to 1800 sec). -INTERACTIVE_DEBUG=1 - Configure the machine to be more *user-friendly* for interactive debuggung - (e.g. by setting a usable default terminal, suppressing the shutdown after - the test, etc.) +`INTERACTIVE_DEBUG=1`: Configure the machine to be more *user-friendly* for +interactive debugging (e.g. by setting a usable default terminal, suppressing +the shutdown after the test, etc.). -TEST_MATCH_SUBTEST=subtest - If the test makes use of `run_subtests` use this variable to provide - a POSIX extended regex to run only subtests matching the expression +`TEST_MATCH_SUBTEST=subtest`: If the test makes use of `run_subtests` use this +variable to provide a POSIX extended regex to run only subtests matching the +expression. -TEST_MATCH_TESTCASE=testcase - Same as $TEST_MATCH_SUBTEST but for subtests that make use of `run_testcases` +`TEST_MATCH_TESTCASE=testcase`: Same as $TEST_MATCH_SUBTEST but for subtests +that make use of `run_testcases`. The kernel and initrd can be specified with $KERNEL_BIN and $INITRD. (Fedora's or Debian's default kernel path and initrd are used by default.) A script will try to find your qemu binary. If you want to specify a different -one with $QEMU_BIN. +one with `$QEMU_BIN`. -Debugging the qemu image -======================== +### Debugging the qemu image -If you want to log in the testsuite virtual machine, use INTERACTIVE_DEBUG=1 +If you want to log in the testsuite virtual machine, use `INTERACTIVE_DEBUG=1` and log in as root: +```shell $ sudo make -C test/TEST-01-BASIC INTERACTIVE_DEBUG=1 run +``` The root password is empty. -Ubuntu CI -========= +## Ubuntu CI -New PR submitted to the project are run through regression tests, and one set +New PRs submitted to the project are run through regression tests, and one set of those is the 'autopkgtest' runs for several different architectures, called 'Ubuntu CI'. Part of that testing is to run all these tests. Sometimes these tests are temporarily deny-listed from running in the 'autopkgtest' tests while @@ -149,6 +207,28 @@ failed run, can be downloaded from the artifacts.tar.gz archive which will be reachable in the same URL parent directory as the logs.gz that gets linked on the Github CI status. +The log URL can be derived following a simple algorithm, however the test +completion timestamp is needed and it's not easy to find without access to the +log itself. For example, a noble s390x job started on 2024-03-23 at 02:09:11 +will be stored at the following URL: + +https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/noble/s390x/s/systemd-upstream/20240323_020911_e8e88@/log.gz + +Fortunately a list of URLs listing file paths for recently completed test runs +is available at: + +https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/ + +paths listed at this URL can be appended to the URL to download them. + +The 5 characters at the end of the last directory are not random, but the first +5 characters of a SHA1 hash generated based on the set of parameters given to +the build plus the completion timestamp, such as: + +```shell +$ echo -n 'systemd-upstream {"build-git": "https://salsa.debian.org/systemd-team/systemd.git#debian/master", "env": ["UPSTREAM_REPO=https://github.com/systemd/systemd.git", "CFLAGS=-O0", "DEB_BUILD_PROFILES=pkg.systemd.upstream noudeb", "TEST_UPSTREAM=1", "CONFFLAGS_UPSTREAM=--werror -Dslow-tests=true", "UPSTREAM_PULL_REQUEST=31444", "GITHUB_STATUSES_URL=https://api.github.com/repos/systemd/systemd/statuses/c27f600a1c47f10b22964eaedfb5e9f0d4279cd9"], "ppas": ["upstream-systemd-ci/systemd-ci"], "submit-time": "2024-02-27 17:06:27", "uuid": "02cd262f-af22-4f82-ac91-53fa5a9e7811"}' | sha1sum | cut -c1-5 +``` + To add new dependencies or new binaries to the packages used during the tests, a merge request can be sent to: https://salsa.debian.org/systemd-team/systemd targeting the 'upstream-ci' branch. @@ -158,6 +238,10 @@ located at: https://git.launchpad.net/autopkgtest-cloud/ +A generic description of the testing infrastructure can be found at: + +https://wiki.ubuntu.com/ProposedMigration/AutopkgtestInfrastructure + In case of infrastructure issues with this CI, things might go wrong in two places: @@ -166,61 +250,92 @@ places: - running a job: all currently running jobs are listed at https://autopkgtest.ubuntu.com/running#pkg-systemd-upstream in case the PR does not show the status for some reason -- reporting the job result: this is done on Canonical's cloud infrastructure, - if jobs are started and running but no status is visible on the PR, then it is +- reporting the job result: this is done on Canonical's cloud infrastructure, if + jobs are started and running but no status is visible on the PR, then it is likely that reporting back is not working -For infrastructure help, reaching out to Canonical via the #ubuntu-devel channel -on libera.chat is an effective way to receive support in general. +The CI job needs a PPA in order to be accepted, and the +upstream-systemd-ci/systemd-ci PPA is used. Note that this is necessary even +when there are no packages to backport, but by default a PPA won't have a +repository for a release if there are no packages built for it. To work around +this problem, when a new empty release is needed the mark-suite-dirty tool from +the https://git.launchpad.net/ubuntu-archive-tools can be used to force the PPA +to publish an empty repository, for example: + +```shell +$ ./mark-suite-dirty -A ppa:upstream-systemd-ci/ubuntu/systemd-ci -s noble +``` + +will create an empty 'noble' repository that can be used for 'noble' CI jobs. + +For infrastructure help, reaching out to 'qa-help' via the #ubuntu-quality +channel on libera.chat is an effective way to receive support in general. -Manually running a part of the Ubuntu CI test suite -=================================================== +Given access to the shared secret, tests can be re-run using the generic +retry-github-test tool: + +https://git.launchpad.net/autopkgtest-cloud/tree/charms/focal/autopkgtest-cloud-worker/autopkgtest-cloud/tools/retry-github-test + +A wrapper script that makes it easier to use is also available: + +https://piware.de/gitweb/?p=bin.git;a=blob;f=retry-gh-systemd-Test + +## Manually running a part of the Ubuntu CI test suite In some situations one may want/need to run one of the tests run by Ubuntu CI locally for debugging purposes. For this, you need a machine (or a VM) with the same Ubuntu release as is used by Ubuntu CI (Jammy ATTOW). First of all, clone the Debian systemd repository and sync it with the code of -the PR (set by the $UPSTREAM_PULL_REQUEST env variable) you'd like to debug: +the PR (set by the `$UPSTREAM_PULL_REQUEST` env variable) you'd like to debug: -# git clone https://salsa.debian.org/systemd-team/systemd.git -# cd systemd -# git checkout upstream-ci -# TEST_UPSTREAM=1 UPSTREAM_PULL_REQUEST=12345 ./debian/extra/checkout-upstream +```shell +$ git clone https://salsa.debian.org/systemd-team/systemd.git +$ cd systemd +$ git checkout upstream-ci +$ TEST_UPSTREAM=1 UPSTREAM_PULL_REQUEST=12345 ./debian/extra/checkout-upstream +``` Now install necessary build & test dependencies: -## PPA with some newer Ubuntu packages required by upstream systemd -# add-apt-repository -y --enable-source ppa:upstream-systemd-ci/systemd-ci -# apt build-dep -y systemd -# apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 \ +```shell +# PPA with some newer Ubuntu packages required by upstream systemd +$ add-apt-repository -y --enable-source ppa:upstream-systemd-ci/systemd-ci +$ apt build-dep -y systemd +$ apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 \ libcurl4-openssl-dev libfdisk-dev libtss2-dev libfido2-dev \ libssl-dev python3-pefile +``` Build systemd deb packages with debug info: -# TEST_UPSTREAM=1 DEB_BUILD_OPTIONS="nocheck nostrip noopt" dpkg-buildpackage -us -uc -# cd .. +```shell +$ TEST_UPSTREAM=1 DEB_BUILD_OPTIONS="nocheck nostrip noopt" dpkg-buildpackage -us -uc +$ cd .. +``` Prepare a testbed image for autopkgtest (tweak the release as necessary): -# autopkgtest-buildvm-ubuntu-cloud --ram-size 1024 -v -a amd64 -r jammy +```shell +$ autopkgtest-buildvm-ubuntu-cloud --ram-size 1024 -v -a amd64 -r jammy +``` And finally run the autopkgtest itself: -# autopkgtest -o logs *.deb systemd/ \ +```shell +$ autopkgtest -o logs *.deb systemd/ \ --env=TEST_UPSTREAM=1 \ --timeout-factor=3 \ --test-name=boot-and-services \ --shell-fail \ -- autopkgtest-virt-qemu --cpus 4 --ram-size 2048 autopkgtest-jammy-amd64.img +``` -where --test-name= is the name of the test you want to run/debug. The ---shell-fail option will pause the execution in case the test fails and shows +where `--test-name=` is the name of the test you want to run/debug. The +`--shell-fail` option will pause the execution in case the test fails and shows you the information how to connect to the testbed for further debugging. -Manually running CodeQL analysis -===================================== +## Manually running CodeQL analysis This is mostly useful for debugging various CodeQL quirks. @@ -230,31 +345,40 @@ binary from the unpacked archive in $PATH for brevity. Switch to the systemd repository if not already: +```shell $ cd <systemd-repo> +``` Create an initial CodeQL database: +```shell $ CCACHE_DISABLE=1 codeql database create codeqldb --language=cpp -vvv +``` Disabling ccache is important, otherwise you might see CodeQL complaining: -No source code was seen and extracted to /home/mrc0mmand/repos/@ci-incubator/systemd/codeqldb. -This can occur if the specified build commands failed to compile or process any code. - - Confirm that there is some source code for the specified language in the project. - - For codebases written in Go, JavaScript, TypeScript, and Python, do not specify - an explicit --command. - - For other languages, the --command must specify a "clean" build which compiles - all the source code files without reusing existing build artefacts. +No source code was seen and extracted to +/home/mrc0mmand/repos/@ci-incubator/systemd/codeqldb. This can occur if the +specified build commands failed to compile or process any code. + - Confirm that there is some source code for the specified language in the + project. + - For codebases written in Go, JavaScript, TypeScript, and Python, do not + specify an explicit --command. + - For other languages, the --command must specify a "clean" build which + compiles all the source code files without reusing existing build artefacts. If you want to run all queries systemd uses in CodeQL, run: +```shell $ codeql database analyze codeqldb/ --format csv --output results.csv .github/codeql-custom.qls .github/codeql-queries/*.ql -vvv +``` Note: this will take a while. If you're interested in a specific check, the easiest way (without hunting down the specific CodeQL query file) is to create a custom query suite. For example: +```shell $ cat >test.qls <<EOF - queries: . from: codeql/cpp-queries @@ -262,10 +386,13 @@ $ cat >test.qls <<EOF id: - cpp/missing-return EOF +``` And then execute it in the same way as above: +```shell $ codeql database analyze codeqldb/ --format csv --output results.csv test.qls -vvv +``` More about query suites here: https://codeql.github.com/docs/codeql-cli/creating-codeql-query-suites/ @@ -273,13 +400,63 @@ The results are then located in the `results.csv` file as a comma separated values list (obviously), which is the most human-friendly output format the CodeQL utility provides (so far). -Code coverage -============= +## Running Coverity locally + +Note: this requires a Coverity license, as the public tool +[tarball](https://scan.coverity.com/download) doesn't contain cov-analyze and +friends, so the usefulness of this guide is somewhat limited. + +Debugging certain pesky Coverity defects can be painful, especially since the +OSS Coverity instance has a very strict limit on how many builds we can send it +per day/week, so if you have an access to a non-OSS Coverity license, knowing +how to debug defects locally might come in handy. + +After installing the necessary tooling we need to populate the emit DB first: + +```shell +$ rm -rf build cov +$ meson setup build -Dman=false +$ cov-build --dir=./cov ninja -C build +``` + +From there it depends if you're interested in a specific defect or all of them. +For the latter run: + +```shell +$ cov-analyze --dir=./cov --wait-for-license +``` + +If you want to debug a specific defect, telling that to cov-analyze speeds +things up a bit: + +```shell +$ cov-analyze --dir=./cov --wait-for-license --disable-default --enable ASSERT_SIDE_EFFECT +``` + +The final step is getting the actual report which can be generated in multiple +formats, for example: + +```shell +$ cov-format-errors --dir ./cov --text-output-style multiline +$ cov-format-errors --dir=./cov --emacs-style +$ cov-format-errors --dir=./cov --html-output html-out +``` + +Which generate a text report, an emacs-compatible text report, and an HTML +report respectively. + +Other useful options for cov-format-error include `--file <file>` to filter out +defects for a specific file, `--checker-regex DEFECT_TYPE` to filter our only a +specific defect (if this wasn't done already by cov-analyze), and many others, +see `--help` for an exhaustive list. + +## Code coverage We have a daily cron job in CentOS CI which runs all unit and integration tests, -collects coverage using gcov/lcov, and uploads the report to Coveralls[0]. In -order to collect the most accurate coverage information, some measures have -to be taken regarding sandboxing, namely: +collects coverage using gcov/lcov, and uploads the report to +[Coveralls](https://coveralls.io/github/systemd/systemd). In order to collect +the most accurate coverage information, some measures have to be taken regarding +sandboxing, namely: - ProtectSystem= and ProtectHome= need to be turned off - the $BUILD_DIR with necessary .gcno files needs to be present in the image @@ -288,23 +465,21 @@ to be taken regarding sandboxing, namely: The first point is relatively easy to handle and is handled automagically by our test "framework" by creating necessary dropins. -Making the $BUILD_DIR accessible to _everything_ is slightly more complicated. -First, and foremost, the $BUILD_DIR has a POSIX ACL that makes it writable +Making the `$BUILD_DIR` accessible to _everything_ is slightly more complicated. +First, and foremost, the `$BUILD_DIR` has a POSIX ACL that makes it writable to everyone. However, this is not enough in some cases, like for services that use DynamicUser=yes, since that implies ProtectSystem=strict that can't -be turned off. A solution to this is to use ReadWritePaths=$BUILD_DIR, which +be turned off. A solution to this is to use `ReadWritePaths=$BUILD_DIR`, which works for the majority of cases, but can't be turned on globally, since ReadWritePaths= creates its own mount namespace which might break some -services. Hence, the ReadWritePaths=$BUILD_DIR is enabled for all services +services. Hence, the `ReadWritePaths=$BUILD_DIR` is enabled for all services with the `test-` prefix (i.e. test-foo.service or test-foo-bar.service), both in the system and the user managers. -So, if you're considering writing an integration test that makes use -of DynamicUser=yes, or other sandboxing stuff that implies it, please prefix -the test unit (be it a static one or a transient one created via systemd-run), -with `test-`, unless the test unit needs to be able to install mount points -in the main mount namespace - in that case use IGNORE_MISSING_COVERAGE=yes -in the test definition (i.e. TEST-*-NAME/test.sh), which will skip the post-test +So, if you're considering writing an integration test that makes use of +DynamicUser=yes, or other sandboxing stuff that implies it, please prefix the +test unit (be it a static one or a transient one created via systemd-run), with +`test-`, unless the test unit needs to be able to install mount points in the +main mount namespace - in that case use `IGNORE_MISSING_COVERAGE=yes` in the +test definition (i.e. `TEST-*-NAME/test.sh`), which will skip the post-test check for missing coverage for the respective test. - -[0] https://coveralls.io/github/systemd/systemd diff --git a/test/TEST-01-BASIC/meson.build b/test/TEST-01-BASIC/meson.build new file mode 100644 index 0000000..257dadd --- /dev/null +++ b/test/TEST-01-BASIC/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : 'TEST-01-BASIC', + }, +] diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh index 93185cf..d961973 100755 --- a/test/TEST-01-BASIC/test.sh +++ b/test/TEST-01-BASIC/test.sh @@ -16,14 +16,4 @@ KERNEL_APPEND="foo -- -z bar --- baz $KERNEL_APPEND" # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" -test_append_files() { - local workspace="${1:?}" - local dst - - # Install tests manually so the test is functional even when -Dinstall-tests=false - dst="$workspace/usr/lib/systemd/tests/testdata/units/" - mkdir -p "$dst" - cp -v "$TEST_UNITS_DIR"/{testsuite-01,end}.service "$TEST_UNITS_DIR/testsuite.target" "$dst" -} - do_test "$@" diff --git a/test/TEST-02-UNITTESTS/meson.build b/test/TEST-02-UNITTESTS/meson.build new file mode 100644 index 0000000..b000339 --- /dev/null +++ b/test/TEST-02-UNITTESTS/meson.build @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'cmdline' : integration_test_template['cmdline'] + [ + ''' + +systemd.setenv=TEST_CMDLINE_NEWLINE=foo + ''', + ''' +systemd.setenv=TEST_CMDLINE_NEWLINE=bar + ''', + ], + }, +] diff --git a/test/testsuite-03.units/always-activating.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service index 93ddb85..93ddb85 100644 --- a/test/testsuite-03.units/always-activating.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service diff --git a/test/testsuite-03.units/always-activating.socket b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket index c76fed2..c76fed2 100644 --- a/test/testsuite-03.units/always-activating.socket +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service index 60ffd7a..60ffd7a 100644 --- a/test/testsuite-03.units/fails-on-restart-restartdirect.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target index 58e2561..58e2561 100644 --- a/test/testsuite-03.units/fails-on-restart-restartdirect.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target diff --git a/test/testsuite-03.units/fails-on-restart.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service index fb7e7ae..fb7e7ae 100644 --- a/test/testsuite-03.units/fails-on-restart.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service diff --git a/test/testsuite-03.units/fails-on-restart.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target index 865fb2a..865fb2a 100644 --- a/test/testsuite-03.units/fails-on-restart.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target diff --git a/test/testsuite-03.units/hello-after-sleep.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target index b0ddb30..b0ddb30 100644 --- a/test/testsuite-03.units/hello-after-sleep.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target diff --git a/test/testsuite-03.units/hello.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service index 0c3f2f8..0c3f2f8 100644 --- a/test/testsuite-03.units/hello.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service diff --git a/test/testsuite-03.units/propagatestopto-and-pullin.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target index 8e409af..8e409af 100644 --- a/test/testsuite-03.units/propagatestopto-and-pullin.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target diff --git a/test/testsuite-03.units/propagatestopto-indirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target index e8229a7..e8229a7 100644 --- a/test/testsuite-03.units/propagatestopto-indirect.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target diff --git a/test/testsuite-03.units/propagatestopto-only.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target index 327b7c1..327b7c1 100644 --- a/test/testsuite-03.units/propagatestopto-only.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target diff --git a/test/testsuite-03.units/sleep-infinity-simple.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service index 211346d..211346d 100644 --- a/test/testsuite-03.units/sleep-infinity-simple.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service diff --git a/test/testsuite-03.units/sleep.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service index 32c2037..32c2037 100644 --- a/test/testsuite-03.units/sleep.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service index b05f2f8..a2b9c33 100644 --- a/test/testsuite-03.units/succeeds-on-restart-restartdirect.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] Type=oneshot -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh +ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh Restart=on-failure RestartMode=direct diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target index 2cf3c60..2cf3c60 100644 --- a/test/testsuite-03.units/succeeds-on-restart-restartdirect.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target diff --git a/test/testsuite-03.units/succeeds-on-restart.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service index d7b3c7a..d0e9c2b 100644 --- a/test/testsuite-03.units/succeeds-on-restart.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] Type=oneshot -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh +ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh Restart=on-failure RestartMode=normal diff --git a/test/testsuite-03.units/succeeds-on-restart.sh b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh index 1428b18..1428b18 100755 --- a/test/testsuite-03.units/succeeds-on-restart.sh +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh diff --git a/test/testsuite-03.units/succeeds-on-restart.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target index eb82f47..eb82f47 100644 --- a/test/testsuite-03.units/succeeds-on-restart.target +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target diff --git a/test/testsuite-03.units/unstoppable.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service index 6eb7c19..6eb7c19 100644 --- a/test/testsuite-03.units/unstoppable.service +++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service diff --git a/test/TEST-03-JOBS/meson.build b/test/TEST-03-JOBS/meson.build new file mode 100644 index 0000000..3484d21 --- /dev/null +++ b/test/TEST-03-JOBS/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-03-JOBS.units'] diff --git a/test/testsuite-04.units/delegated-cgroup-filtering.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service index 2c4201a..7ecd358 100644 --- a/test/testsuite-04.units/delegated-cgroup-filtering.service +++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service @@ -2,7 +2,8 @@ Description=Test service for delegated logs filtering [Service] -Type=simple +Type=oneshot ExecStart=/usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload.sh Delegate=yes SyslogLevel=notice +LogLevelMax=info diff --git a/test/testsuite-04.units/forever-print-hola.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service index 9273d6d..9273d6d 100644 --- a/test/testsuite-04.units/forever-print-hola.service +++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service new file mode 100644 index 0000000..4c43005 --- /dev/null +++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service @@ -0,0 +1,11 @@ +[Unit] +Description=Log filtering unit + +[Service] +Type=oneshot +ExecStart=sh -c 'echo "Logging from the service, and ~more~ foo bar"' +# If the service finishes extremely fast, journald cannot find the source of the +# stream. Hence, we need to call 'journalctl --sync' before service finishes. +ExecStart=journalctl --sync +SyslogLevel=notice +LogLevelMax=info diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service new file mode 100644 index 0000000..d4541af --- /dev/null +++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Silent successful service + +[Service] +Type=oneshot +LogLevelMax=notice +ExecStart=/bin/true +# If the service finishes extremely fast, journald cannot find the source of the +# stream. Hence, we need to call 'journalctl --sync' before service finishes. +ExecStart=journalctl --sync diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service new file mode 100644 index 0000000..004693b --- /dev/null +++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Verbose successful service + +[Service] +Type=oneshot +ExecStart=echo success +# If the service finishes extremely fast, journald cannot find the source of the +# stream. Hence, we need to call 'journalctl --sync' before service finishes. +ExecStart=journalctl --sync +# Suppress debugging logs from PID1 or sd-executor. Otherwise, the client context +# may be outdated when the stream from 'echo' command in the above comes. +LogLevelMax=info diff --git a/test/TEST-04-JOURNAL/meson.build b/test/TEST-04-JOURNAL/meson.build new file mode 100644 index 0000000..5a0b073 --- /dev/null +++ b/test/TEST-04-JOURNAL/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'storage' : 'persistent', + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-04-JOURNAL.units'] diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh index 42574ce..a7aa71f 100755 --- a/test/TEST-04-JOURNAL/test.sh +++ b/test/TEST-04-JOURNAL/test.sh @@ -11,9 +11,6 @@ test_append_files() { local workspace="${1:?}" local dropin_dir - mkdir -p "$workspace/test-journals/" - cp -av "${TEST_BASE_DIR:?}/test-journals/"* "$workspace/test-journals/" - image_install curl setterm unzstd image_install -o openssl # Necessary for RH-based systems, otherwise MHD fails with: @@ -23,7 +20,7 @@ test_append_files() { # 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 diff --git a/test/TEST-05-RLIMITS/meson.build b/test/TEST-05-RLIMITS/meson.build new file mode 100644 index 0000000..d8198fd --- /dev/null +++ b/test/TEST-05-RLIMITS/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : 'TEST-05-RLIMITS', + }, +] diff --git a/test/testsuite-06.units/hola.service b/test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service index 94f9b47..94f9b47 100644 --- a/test/testsuite-06.units/hola.service +++ b/test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service diff --git a/test/TEST-06-SELINUX/meson.build b/test/TEST-06-SELINUX/meson.build new file mode 100644 index 0000000..7a850be --- /dev/null +++ b/test/TEST-06-SELINUX/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'cmdline' : integration_test_template['cmdline'] + ['selinux=1', 'lsm=selinux'], + # FIXME; Figure out why reboot sometimes hangs with 'linux' firmware. + 'firmware' : 'uefi', + 'vm' : true, + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-06-SELINUX.units'] diff --git a/test/units/testsuite-07.service b/test/TEST-07-PID1/TEST-07-PID1.service index 92302bf..53dd54c 100644 --- a/test/units/testsuite-07.service +++ b/test/TEST-07-PID1/TEST-07-PID1.service @@ -1,6 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=TEST-07-PID1 +Wants=basic.target multi-user.target +After=basic.target +Before=getty-pre.target [Service] Type=oneshot diff --git a/test/testsuite-07.units/issue14566-repro.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service index 5680596..757f978 100644 --- a/test/testsuite-07.units/issue14566-repro.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service @@ -3,6 +3,6 @@ Description=Issue 14566 Repro [Service] -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-07.units/%N.sh +ExecStart=/usr/lib/systemd/tests/testdata/TEST-07-PID1.units/%N.sh ExecStopPost=/bin/true KillMode=mixed diff --git a/test/testsuite-07.units/issue14566-repro.sh b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh index 74fa760..74fa760 100755 --- a/test/testsuite-07.units/issue14566-repro.sh +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh diff --git a/test/testsuite-07.units/issue16115-repro-1.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service index 90252b3..90252b3 100644 --- a/test/testsuite-07.units/issue16115-repro-1.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service diff --git a/test/testsuite-07.units/issue16115-repro-2.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service index 7c65691..7c65691 100644 --- a/test/testsuite-07.units/issue16115-repro-2.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service diff --git a/test/testsuite-07.units/issue16115-repro-3.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service index c68f93d..c68f93d 100644 --- a/test/testsuite-07.units/issue16115-repro-3.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service diff --git a/test/testsuite-07.units/issue2467.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service index 99d886f..99d886f 100644 --- a/test/testsuite-07.units/issue2467.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket new file mode 100644 index 0000000..209b6bb --- /dev/null +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket @@ -0,0 +1,7 @@ +# 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 diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount new file mode 120000 index 0000000..802f026 --- /dev/null +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount @@ -0,0 +1 @@ +issue2730.mount
\ No newline at end of file diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount new file mode 100644 index 0000000..2ac76c0 --- /dev/null +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount @@ -0,0 +1,8 @@ +[Mount] +What=tmpfs +Where=/issue2730 +Type=tmpfs + +[Install] +WantedBy=local-fs.target +Alias=issue2730-alias.mount diff --git a/test/testsuite-07.units/issue27953.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service index f441067..f441067 100644 --- a/test/testsuite-07.units/issue27953.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service diff --git a/test/testsuite-07.units/issue3166-fail-on-restart.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service index b8695d8..b8695d8 100644 --- a/test/testsuite-07.units/issue3166-fail-on-restart.service +++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount b/test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount new file mode 120000 index 0000000..70a8534 --- /dev/null +++ b/test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount @@ -0,0 +1 @@ +../issue2730.mount
\ No newline at end of file diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket new file mode 100644 index 0000000..8b7964b --- /dev/null +++ b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket @@ -0,0 +1,35 @@ +# 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 diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket new file mode 100644 index 0000000..bff192d --- /dev/null +++ b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket @@ -0,0 +1,36 @@ +# 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 diff --git a/test/TEST-07-PID1/meson.build b/test/TEST-07-PID1/meson.build new file mode 100644 index 0000000..311860d --- /dev/null +++ b/test/TEST-07-PID1/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'unit' : files('TEST-07-PID1.service'), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-07-PID1.units'] diff --git a/test/TEST-07-PID1/test.sh b/test/TEST-07-PID1/test.sh index cc8a81f..2513406 100755 --- a/test/TEST-07-PID1/test.sh +++ b/test/TEST-07-PID1/test.sh @@ -11,31 +11,6 @@ NSPAWN_ARGUMENTS="--capability=CAP_NET_ADMIN" . "${TEST_BASE_DIR:?}/test-functions" test_append_files() { - local workspace="${1:?}" - - # We might not be fast enough to hit the 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 case. - if ! get_bool "$QEMU_KVM" || get_bool "$IS_BUILT_WITH_COVERAGE"; then - mkdir -p "$workspace/etc/systemd/system/issue2467.socket.d" - printf "[Socket]\nTriggerLimitIntervalSec=10\n" >"$workspace/etc/systemd/system/issue2467.socket.d/TriggerLimitInterval.conf" - fi - - # Issue: https://github.com/systemd/systemd/issues/2730 - mkdir -p "$workspace/etc/systemd/system/" - cat >"$workspace/etc/systemd/system/issue2730.mount" <<EOF -[Mount] -What=tmpfs -Where=/issue2730 -Type=tmpfs - -[Install] -WantedBy=local-fs.target -Alias=issue2730-alias.mount -EOF - "${SYSTEMCTL:?}" enable --root="$workspace" issue2730.mount - ln -svrf "$workspace/etc/systemd/system/issue2730.mount" "$workspace/etc/systemd/system/issue2730-alias.mount" - image_install logger socat } diff --git a/test/TEST-08-INITRD/meson.build b/test/TEST-08-INITRD/meson.build new file mode 100644 index 0000000..dcbfe20 --- /dev/null +++ b/test/TEST-08-INITRD/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'cmdline' : integration_test_template['cmdline'] + [ + 'rd.systemd.wants=initrd-run-mount.service', + ], + 'exit-code' : 124, + 'vm' : true, + }, +] diff --git a/test/TEST-08-INITRD/test.sh b/test/TEST-08-INITRD/test.sh index e8dbb2c..73f743c 100755 --- a/test/TEST-08-INITRD/test.sh +++ b/test/TEST-08-INITRD/test.sh @@ -14,11 +14,11 @@ TEST_NO_NSPAWN=1 test_append_files() { local workspace="${1:?}" - local sd_initrd file dir + local exitrd file dir - # Create a shutdown initrd + # Create an exitrd # - # This should provide coverage for shutdown initrd related issues, see: + # This should provide coverage for exitrd related issues, see: # - https://github.com/systemd/systemd/issues/28645 # - https://github.com/systemd/systemd/pull/28648 # - https://github.com/systemd/systemd/pull/28793 @@ -26,25 +26,25 @@ test_append_files() { # This is a bit messier than I originally anticipated, as installing our own libraries # is handled implicitly by install_systemd() which I don't want to use here, since # I need only the systemd-shutdown binary - sd_initrd="$workspace/shutdown-initrd" - mkdir -p "$sd_initrd/etc" "$sd_initrd/usr" - initdir="$sd_initrd" image_install bash /usr/lib/os-release - ln -srf "$sd_initrd/usr/lib/os-release" "$sd_initrd/etc/initrd-release" - initdir="$sd_initrd" inst_binary "$workspace/usr/lib/systemd/systemd-shutdown" "/usr/lib/systemd/systemd-shutdown" - initdir="$sd_initrd" inst_libs "$sd_initrd/usr/lib/systemd/systemd-shutdown" + exitrd="$workspace/exitrd" + mkdir -p "$exitrd/etc" "$exitrd/usr" + initdir="$exitrd" image_install bash /usr/lib/os-release + ln -srf "$exitrd/usr/lib/os-release" "$exitrd/etc/initrd-release" + initdir="$exitrd" inst_binary "$workspace/usr/lib/systemd/systemd-shutdown" "/usr/lib/systemd/systemd-shutdown" + initdir="$exitrd" inst_libs "$exitrd/usr/lib/systemd/systemd-shutdown" # We need to deal with libsystemd stuff explicitly, as we don't call install_systemd() here while read -r file; do - initdir="$sd_initrd" inst_library "$file" "${file##"$workspace"}" - initdir="$sd_initrd" inst_libs "$file" + initdir="$exitrd" inst_library "$file" "${file##"$workspace"}" + initdir="$exitrd" inst_libs "$file" done < <(find "$workspace/usr/" -name "libsystemd*.so*") # Call systemd-shutdown indirectly, so we can show a message that we can check for - # later to make sure the shutdown initrd was actually executed - cat >"$sd_initrd/shutdown" <<\EOF + # later to make sure the exitrd was actually executed + cat >"$exitrd/shutdown" <<\EOF #!/usr/bin/bash -eu -echo "Hello from shutdown initrd" +echo "Hello from exitrd" exec /usr/lib/systemd/systemd-shutdown "$@" EOF - chmod +x "$sd_initrd/shutdown" + chmod +x "$exitrd/shutdown" } check_result_qemu_hook() { @@ -65,13 +65,13 @@ check_result_qemu_hook() { # [ 6.245955] systemd-shutdown[1]: Failed to switch root to "/run/initramfs": Invalid argument if grep -qE "systemd-shutdown.+: Failed to move /run/initramfs" "$console_log" || grep -qE "systemd-shutdown.+: Failed to switch root" "$console_log"; then - derror "sd-shutdown failed to switch root in shutdown initrd" + derror "sd-shutdown failed to switch root in exitrd" return 1 fi - # Check if the shutdown initrd was executed at all - if ! grep -qE "^Hello from shutdown initrd\s*$" "$console_log"; then - derror "Missing 'hello' message from shutdown initrd" + # Check if the exitrd was executed at all + if ! grep -q "Hello from exitrd" "$console_log"; then + derror "Missing 'hello' message from exitrd" return 1 fi @@ -80,7 +80,7 @@ check_result_qemu_hook() { # 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() { diff --git a/test/TEST-09-REBOOT/meson.build b/test/TEST-09-REBOOT/meson.build new file mode 100644 index 0000000..c4b41bc --- /dev/null +++ b/test/TEST-09-REBOOT/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'storage' : 'persistent', + # FIXME; Figure out why reboot sometimes hangs with 'linux' firmware. + 'firmware' : 'uefi', + }, +] diff --git a/test/TEST-13-NSPAWN/meson.build b/test/TEST-13-NSPAWN/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-13-NSPAWN/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-13-NSPAWN/test.sh b/test/TEST-13-NSPAWN/test.sh index 93af59e..9a0404f 100755 --- a/test/TEST-13-NSPAWN/test.sh +++ b/test/TEST-13-NSPAWN/test.sh @@ -5,13 +5,19 @@ set -e TEST_DESCRIPTION="systemd-nspawn tests" IMAGE_NAME="nspawn" TEST_NO_NSPAWN=1 +IMAGE_ADDITIONAL_ROOT_SIZE=500 +TEST_FORCE_NEWIMAGE=1 # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" test_append_files() { local workspace="${1:?}" - local container="$workspace/testsuite-13-container-template" + local container="$workspace/usr/share/TEST-13-NSPAWN-container-template" + + # For virtual wlan interface. + instmods mac80211_hwsim + generate_module_dependencies # Create a dummy container "template" with a minimal toolset, which we can # then use as a base for our nspawn/machinectl tests diff --git a/test/TEST-15-DROPIN/meson.build b/test/TEST-15-DROPIN/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-15-DROPIN/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/units/testsuite-16.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.service index d5494ae..3be8ef5 100644 --- a/test/units/testsuite-16.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.service @@ -3,6 +3,10 @@ Description=TEST-16-EXTEND-TIMEOUT # Testsuite: Assess all other testsuite-*.services worked as expected +Wants=basic.target multi-user.target +After=basic.target +Before=getty-pre.target + Wants=success-all.service Wants=success-start.service Wants=success-runtime.service @@ -14,7 +18,5 @@ StopWhenUnneeded=yes [Service] ExecStartPre=rm -f /failed /testok -Type=exec -TimeoutStartSec=infinity -ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh -ExecStart=true +Type=oneshot +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh diff --git a/test/testsuite-16.units/extend-timeout.sh b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh index 45a18b9..45a18b9 100755 --- a/test/testsuite-16.units/extend-timeout.sh +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/testsuite-16.units/fail-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service index 1d74963..7380994 100644 --- a/test/testsuite-16.units/fail-runtime.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service @@ -10,4 +10,4 @@ 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 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/testsuite-16.units/fail-start.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service index 38aebf2..984d8f9 100644 --- a/test/testsuite-16.units/fail-start.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service @@ -11,4 +11,4 @@ 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 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/testsuite-16.units/fail-stop.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service index 1910ac9..4293d7e 100644 --- a/test/testsuite-16.units/fail-stop.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service @@ -10,7 +10,7 @@ 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 +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' diff --git a/test/testsuite-16.units/success-all.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service index 1fdc363..7f26084 100644 --- a/test/testsuite-16.units/success-all.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service @@ -12,4 +12,4 @@ 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 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/testsuite-16.units/success-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service index 2f5744d..33a36f3 100644 --- a/test/testsuite-16.units/success-runtime.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service @@ -11,4 +11,4 @@ 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 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/testsuite-16.units/success-start.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service index be518e9..0453b34 100644 --- a/test/testsuite-16.units/success-start.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service @@ -10,4 +10,4 @@ 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 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/testsuite-16.units/success-stop.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service index 2bd3e3e..3719f54 100644 --- a/test/testsuite-16.units/success-stop.service +++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service @@ -10,4 +10,4 @@ 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 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh diff --git a/test/TEST-16-EXTEND-TIMEOUT/meson.build b/test/TEST-16-EXTEND-TIMEOUT/meson.build new file mode 100644 index 0000000..15986ac --- /dev/null +++ b/test/TEST-16-EXTEND-TIMEOUT/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'unit' : files('TEST-16-EXTEND-TIMEOUT.service'), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-16-EXTEND-TIMEOUT.units'] diff --git a/test/TEST-17-UDEV/meson.build b/test/TEST-17-UDEV/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-17-UDEV/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-18-FAILUREACTION/meson.build b/test/TEST-18-FAILUREACTION/meson.build new file mode 100644 index 0000000..5edfbca --- /dev/null +++ b/test/TEST-18-FAILUREACTION/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + # FIXME; Figure out why reboot sometimes hangs with 'linux' firmware. + 'firmware' : 'uefi', + }, +] diff --git a/test/TEST-19-CGROUP/meson.build b/test/TEST-19-CGROUP/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-19-CGROUP/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-21-DFUZZER/meson.build b/test/TEST-21-DFUZZER/meson.build new file mode 100644 index 0000000..f57be63 --- /dev/null +++ b/test/TEST-21-DFUZZER/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'timeout' : 3600, + 'priority' : 50, + 'vm' : true, + 'enabled' : false, + }, +] diff --git a/test/TEST-22-TMPFILES/meson.build b/test/TEST-22-TMPFILES/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-22-TMPFILES/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service new file mode 100644 index 0000000..022c37e --- /dev/null +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service @@ -0,0 +1,10 @@ +# 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 diff --git a/test/testsuite-23.units/testsuite-23-bound-by.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service index a2df5a1..c999c2e 100644 --- a/test/testsuite-23.units/testsuite-23-bound-by.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service @@ -3,4 +3,4 @@ Description=Unit with BoundBy= [Service] -ExecStart=/bin/sleep 0.7 +ExecStart=sleep 0.7 diff --git a/test/testsuite-23.units/testsuite-23-fail.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service index 36f8baa..b78e6b4 100644 --- a/test/testsuite-23.units/testsuite-23-fail.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=Failing unit -OnFailure=testsuite-23-uphold.service +OnFailure=TEST-23-UNIT-FILE-uphold.service [Service] -ExecStart=/bin/false +ExecStart=false diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-1.service index 9919a9f..47f0452 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-1.service @@ -4,4 +4,4 @@ Type=notify NotifyAccess=all MountAPIVFS=yes PrivateTmp=yes -ExecStart=/bin/bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity' +ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity' diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-2.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service index 36b4c27..42dd445 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-2.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service +JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service [Service] Type=oneshot diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-3.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-3.service index 9094445..f745481 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-3.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-3.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service +JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service [Service] Type=oneshot diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service new file mode 100644 index 0000000..a0ff403 --- /dev/null +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service @@ -0,0 +1,10 @@ +# 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' diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-5.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-5.service index c3d316b..c3d316b 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-5.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-5.service diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service new file mode 100644 index 0000000..9227bda --- /dev/null +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service @@ -0,0 +1,10 @@ +# 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' diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-7.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-7.service index 60c083a..a5d62c6 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-7.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-7.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service +JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service [Service] Type=oneshot diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-8.service index dac1cea..8e4944a 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-8.service @@ -6,4 +6,4 @@ MountAPIVFS=yes PrivateTmp=yes ExecStartPre=test -e /tmp/shared-private-file-x ExecStartPre=test -e /tmp/hoge -ExecStart=/bin/bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity' +ExecStart=bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity' diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-9.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service index 6c64873..a30f92a 100644 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-9.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service +JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service [Service] Type=oneshot diff --git a/test/testsuite-23.units/testsuite-23-namespaced.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service index 5a6f5cd..82eb74c 100644 --- a/test/testsuite-23.units/testsuite-23-namespaced.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service @@ -7,7 +7,7 @@ Type=notify RemainAfterExit=yes MountAPIVFS=yes PrivateTmp=yes -BindPaths=/run/testsuite-23-marker-fixed:/tmp/testfile-marker-fixed +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=/bin/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' +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' diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service new file mode 100644 index 0000000..699b608 --- /dev/null +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service @@ -0,0 +1,6 @@ +# 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' diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh new file mode 100755 index 0000000..4c9e10b --- /dev/null +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh @@ -0,0 +1,11 @@ +#!/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 diff --git a/test/units/testsuite-77-server.socket b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket index 4305077..c8f7595 100644 --- a/test/units/testsuite-77-server.socket +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket @@ -1,5 +1,5 @@ [Unit] -Description=TEST-77-OPENFILE server socket +Description=OpenFile= test server socket [Socket] ListenStream=/tmp/test.sock diff --git a/test/units/testsuite-77-server@.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service index 8e99ac8..a57c804 100644 --- a/test/units/testsuite-77-server@.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service @@ -1,5 +1,5 @@ [Unit] -Description=TEST-77-OPENFILE server +Description=OpenFile= test server service [Service] ExecStart=echo "Socket" diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service new file mode 100644 index 0000000..0739aaf --- /dev/null +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service @@ -0,0 +1,10 @@ +# 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 diff --git a/test/testsuite-23.units/testsuite-23-prop-stop-two.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service index 2bcd209..b2bb869 100644 --- a/test/testsuite-23.units/testsuite-23-prop-stop-two.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service @@ -3,4 +3,4 @@ Description=Stop Propagation Sender [Service] -ExecStart=/bin/sleep 1.5 +ExecStart=sleep 1.5 diff --git a/test/testsuite-23.units/testsuite-23-retry-fail.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service index 0fc27c4..319757a 100644 --- a/test/testsuite-23.units/testsuite-23-retry-fail.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service @@ -5,5 +5,5 @@ Description=Failed Dependency Unit [Service] Type=oneshot RemainAfterExit=yes -ExecStart=/bin/sh -c "if [ -f /tmp/testsuite-23-retry-fail ]; then exit 0; else exit 1; fi" +ExecStart=sh -c "if [ -f /tmp/TEST-23-UNIT-FILE-retry-fail ]; then exit 0; else exit 1; fi" Restart=no diff --git a/test/testsuite-23.units/testsuite-23-retry-upheld.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service index 0426d76..722a3b4 100644 --- a/test/testsuite-23.units/testsuite-23-retry-upheld.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service @@ -1,10 +1,10 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=Upheld Unit -Requires=testsuite-23-retry-fail.service -After=testsuite-23-retry-fail.service +Requires=TEST-23-UNIT-FILE-retry-fail.service +After=TEST-23-UNIT-FILE-retry-fail.service [Service] Type=oneshot RemainAfterExit=yes -ExecStart=/bin/echo ok +ExecStart=echo ok diff --git a/test/testsuite-23.units/testsuite-23-retry-uphold.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service index f35e842..829ea5c 100644 --- a/test/testsuite-23.units/testsuite-23-retry-uphold.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=Upholding Unit -Upholds=testsuite-23-retry-upheld.service +Upholds=TEST-23-UNIT-FILE-retry-upheld.service [Service] -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity diff --git a/test/testsuite-23.units/testsuite-23-short-lived.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service index 2dcb2ae..ac9583f 100644 --- a/test/testsuite-23.units/testsuite-23-short-lived.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service @@ -8,4 +8,4 @@ StartLimitBurst=15 StartLimitIntervalSec=1h [Service] -ExecStart=/usr/lib/systemd/tests/testdata/units/testsuite-23-short-lived.sh +ExecStart=/usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-short-lived.sh diff --git a/test/testsuite-23.units/testsuite-23-specifier-j-depends-wants.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-depends-wants.service index c45edd9..c45edd9 100644 --- a/test/testsuite-23.units/testsuite-23-specifier-j-depends-wants.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-depends-wants.service diff --git a/test/testsuite-23.units/testsuite-23-specifier-j-wants.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service index 9abb257..5aeee9f 100644 --- a/test/testsuite-23.units/testsuite-23-specifier-j-wants.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service @@ -1,8 +1,8 @@ # 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 +Wants=TEST-23-UNIT-FILE-specifier-j-depends-%j.service +After=TEST-23-UNIT-FILE-specifier-j-depends-%j.service [Service] Type=oneshot diff --git a/test/testsuite-23.units/testsuite-23-success.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service index 410d4f8..1ae6271 100644 --- a/test/testsuite-23.units/testsuite-23-success.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=Succeeding unit -OnSuccess=testsuite-23-fail.service +OnSuccess=TEST-23-UNIT-FILE-fail.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/testsuite-23.units/testsuite-23-upheldby-install.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service index a456207..edb8fcb 100644 --- a/test/testsuite-23.units/testsuite-23-upheldby-install.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service @@ -3,7 +3,7 @@ Description=Unit that sets UpheldBy= through [Install] [Service] -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity [Install] -UpheldBy=testsuite-23-retry-uphold.service +UpheldBy=TEST-23-UNIT-FILE-retry-uphold.service diff --git a/test/testsuite-23.units/testsuite-23-uphold.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service index 3549d6a..36c5dae 100644 --- a/test/testsuite-23.units/testsuite-23-uphold.service +++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=Upholding Unit -Upholds=testsuite-23-short-lived.service +Upholds=TEST-23-UNIT-FILE-short-lived.service [Service] -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity diff --git a/test/TEST-23-UNIT-FILE/meson.build b/test/TEST-23-UNIT-FILE/meson.build new file mode 100644 index 0000000..3f44662 --- /dev/null +++ b/test/TEST-23-UNIT-FILE/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-23-UNIT-FILE.units'] diff --git a/test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf b/test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf new file mode 100644 index 0000000..d6cdad0 --- /dev/null +++ b/test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +[Partition] +Type=linux-generic +UUID=0fc63daf-8483-4772-8e79-3d69d8477de4 +Label=varcrypt_keydev +SizeMinBytes=16M +Format=ext4 +CopyFiles=/keyfile:/keyfile diff --git a/test/TEST-24-CRYPTSETUP/keyfile b/test/TEST-24-CRYPTSETUP/keyfile new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/test/TEST-24-CRYPTSETUP/keyfile @@ -0,0 +1 @@ +test diff --git a/test/TEST-24-CRYPTSETUP/meson.build b/test/TEST-24-CRYPTSETUP/meson.build new file mode 100644 index 0000000..af41f16 --- /dev/null +++ b/test/TEST-24-CRYPTSETUP/meson.build @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'credentials' : integration_test_template['credentials'] + [ + files('keyfile'), + 'fstab.extra="/dev/mapper/test24_varcrypt /var ext4 defaults 0 1"', + ], + 'cmdline' : [ + 'rd.systemd.wants=encrypted-var.service', + 'rd.luks=1', + 'luks.name=0d318174-56b0-4d6e-a324-ac1e7e7d235d=test24_varcrypt', + 'luks.key=0d318174-56b0-4d6e-a324-ac1e7e7d235d=/keyfile:LABEL=varcrypt_keydev', + 'luks.options=0d318174-56b0-4d6e-a324-ac1e7e7d235d=x-initrd.attach', + ], + 'qemu-args' : [ + '-drive', 'id=keydev,if=none,format=raw,cache=unsafe,file=@0@'.format(project_build_root / 'mkosi.output/keydev.raw'), + '-device', 'scsi-hd,drive=keydev', + ], + 'mkosi-args' : integration_test_template['mkosi-args'] + [ + '--runtime-size=11G', + ], + 'vm' : true, + }, +] diff --git a/test/TEST-24-CRYPTSETUP/template.cfg b/test/TEST-24-CRYPTSETUP/template.cfg new file mode 100644 index 0000000..42f8b9d --- /dev/null +++ b/test/TEST-24-CRYPTSETUP/template.cfg @@ -0,0 +1,8 @@ +dn = "cn = systemd" +expiration_days = 30 + +signing_key +encryption_key + +tls_www_client +email_protection_key diff --git a/test/TEST-24-CRYPTSETUP/test.sh b/test/TEST-24-CRYPTSETUP/test.sh index 4ace177..a7e118c 100755 --- a/test/TEST-24-CRYPTSETUP/test.sh +++ b/test/TEST-24-CRYPTSETUP/test.sh @@ -18,6 +18,9 @@ KERNEL_OPTIONS=( "luks.name=$PART_UUID=$DM_NAME" "luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev" "luks.options=$PART_UUID=x-initrd.attach" + # Forward journal to console to make debugging easier (or possible at all) if we fail to bring the + # encrypted /var up during boot + "systemd.journald.forward_to_console=1" ) KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}" QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img" @@ -39,6 +42,125 @@ check_result_qemu() { return $ret } +can_test_pkcs11() { + if ! command -v "softhsm2-util" >/dev/null; then + ddebug "softhsm2-util not available, skipping the PKCS#11 test" + return 1 + fi + if ! command -v "pkcs11-tool" >/dev/null; then + ddebug "pkcs11-tool not available, skipping the PKCS#11 test" + return 1 + fi + if ! command -v "certtool" >/dev/null; then + ddebug "certtool not available, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+P11KIT"; then + ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+OPENSSL"; then + ddebug "Support for openssl is disabled, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP\b"; then + ddebug "Support for libcryptsetup is disabled, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP_PLUGINS"; then + ddebug "Support for libcryptsetup plugins is disabled, skipping the PKCS#11 test" + return 1 + fi + + return 0 +} + +setup_pkcs11_token() { + dinfo "Setup PKCS#11 token" + local P11_MODULE_CONFIGS_DIR P11_MODULE_DIR SOFTHSM_MODULE + + export SOFTHSM2_CONF="/tmp/softhsm2.conf" + mkdir -p "$initdir/usr/lib/softhsm/tokens/" + cat >${SOFTHSM2_CONF} <<EOF +directories.tokendir = $initdir/usr/lib/softhsm/tokens/ +objectstore.backend = file +slots.removable = false +slots.mechanisms = ALL +EOF + export GNUTLS_PIN="1234" + export GNUTLS_SO_PIN="12345678" + softhsm2-util --init-token --free --label "TestToken" --pin ${GNUTLS_PIN} --so-pin ${GNUTLS_SO_PIN} + + if ! P11_MODULE_CONFIGS_DIR=$(pkg-config --variable=p11_module_configs p11-kit-1); then + echo "WARNING! Cannot get p11_module_configs from p11-kit-1.pc, assuming /usr/share/p11-kit/modules" >&2 + P11_MODULE_CONFIGS_DIR="/usr/share/p11-kit/modules" + fi + + if ! P11_MODULE_DIR=$(pkg-config --variable=p11_module_path p11-kit-1); then + echo "WARNING! Cannot get p11_module_path from p11-kit-1.pc, assuming /usr/lib/pkcs11" >&2 + P11_MODULE_DIR="/usr/lib/pkcs11" + fi + + SOFTHSM_MODULE=$(grep -F 'module:' "$P11_MODULE_CONFIGS_DIR/softhsm2.module"| cut -d ':' -f 2| xargs) + if [[ "$SOFTHSM_MODULE" =~ ^[^/] ]]; then + SOFTHSM_MODULE="$P11_MODULE_DIR/$SOFTHSM_MODULE" + fi + + # RSA ##################################################### + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "RSA:2048" --label "RSATestKey" --usage-decrypt + + certtool --generate-self-signed \ + --load-privkey="pkcs11:token=TestToken;object=RSATestKey;type=private" \ + --load-pubkey="pkcs11:token=TestToken;object=RSATestKey;type=public" \ + --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \ + --outder --outfile "/tmp/rsa_test.crt" + + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/rsa_test.crt" --type cert --label "RSATestKey" + rm "/tmp/rsa_test.crt" + + # prime256v1 ############################################## + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "EC:prime256v1" --label "ECTestKey" --usage-derive + + certtool --generate-self-signed \ + --load-privkey="pkcs11:token=TestToken;object=ECTestKey;type=private" \ + --load-pubkey="pkcs11:token=TestToken;object=ECTestKey;type=public" \ + --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \ + --outder --outfile "/tmp/ec_test.crt" + + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/ec_test.crt" --type cert --label "ECTestKey" + rm "/tmp/ec_test.crt" + + ########################################################### + rm ${SOFTHSM2_CONF} + unset SOFTHSM2_CONF + + inst_libs "$SOFTHSM_MODULE" + inst_library "$SOFTHSM_MODULE" + inst_simple "$P11_MODULE_CONFIGS_DIR/softhsm2.module" + + cat >"$initdir/etc/softhsm2.conf" <<EOF +directories.tokendir = /usr/lib/softhsm/tokens/ +objectstore.backend = file +slots.removable = false +slots.mechanisms = ALL +log.level = INFO +EOF + + mkdir -p "$initdir/etc/systemd/system/systemd-cryptsetup@.service.d" + cat >"$initdir/etc/systemd/system/systemd-cryptsetup@.service.d/PKCS11.conf" <<EOF +[Unit] +# Make sure we can start systemd-cryptsetup@empty_pkcs11_auto.service many times +StartLimitBurst=10 + +[Service] +Environment="SOFTHSM2_CONF=/etc/softhsm2.conf" +Environment="PIN=$GNUTLS_PIN" +EOF + + unset GNUTLS_PIN + unset GNUTLS_SO_PIN +} + test_create_image() { create_empty_image_rootdir @@ -57,6 +179,10 @@ test_create_image() { install_dmevent generate_module_dependencies + if can_test_pkcs11; then + setup_pkcs11_token + fi + # Create a keydev dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16 mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img" @@ -84,7 +210,7 @@ EOF if command -v dracut >/dev/null; then dracut --force --verbose --add crypt "$INITRD" elif command -v mkinitcpio >/dev/null; then - mkinitcpio --addhooks sd-encrypt --generate "$INITRD" + mkinitcpio -S autodetect --addhooks sd-encrypt --generate "$INITRD" elif command -v mkinitramfs >/dev/null; then # The cryptroot hook is provided by the cryptsetup-initramfs package if ! dpkg-query -s cryptsetup-initramfs; then diff --git a/test/TEST-25-IMPORT/meson.build b/test/TEST-25-IMPORT/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-25-IMPORT/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-26-SYSTEMCTL/meson.build b/test/TEST-26-SYSTEMCTL/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-26-SYSTEMCTL/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-29-PORTABLE/meson.build b/test/TEST-29-PORTABLE/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-29-PORTABLE/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf b/test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf index e2e25d5..e2e25d5 100644 --- a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf +++ b/test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf diff --git a/test/TEST-30-ONCLOCKCHANGE/meson.build b/test/TEST-30-ONCLOCKCHANGE/meson.build new file mode 100644 index 0000000..cd0f1f6 --- /dev/null +++ b/test/TEST-30-ONCLOCKCHANGE/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-30-ONCLOCKCHANGE.units'] diff --git a/test/TEST-31-DEVICE-ENUMERATION/meson.build b/test/TEST-31-DEVICE-ENUMERATION/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-31-DEVICE-ENUMERATION/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-32-OOMPOLICY/meson.build b/test/TEST-32-OOMPOLICY/meson.build new file mode 100644 index 0000000..b715c0d --- /dev/null +++ b/test/TEST-32-OOMPOLICY/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'configuration' : integration_test_template['configuration'] + { + 'memory-accounting' : 'yes', + }, + 'vm' : true, + }, +] diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/meson.build b/test/TEST-34-DYNAMICUSERMIGRATE/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-34-DYNAMICUSERMIGRATE/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-35-LOGIN/meson.build b/test/TEST-35-LOGIN/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-35-LOGIN/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-36-NUMAPOLICY/meson.build b/test/TEST-36-NUMAPOLICY/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-36-NUMAPOLICY/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-38-FREEZER/meson.build b/test/TEST-38-FREEZER/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-38-FREEZER/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/meson.build b/test/TEST-43-PRIVATEUSER-UNPRIV/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-43-PRIVATEUSER-UNPRIV/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh b/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh index 1d1dab4..c055735 100755 --- a/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh +++ b/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh @@ -12,6 +12,7 @@ has_user_dbus_socket || exit 0 test_require_bin mksquashfs test_append_files() { + inst_binary mksquashfs inst_binary unsquashfs install_verity_minimal } diff --git a/test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service b/test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service new file mode 100644 index 0000000..1b1dd98 --- /dev/null +++ b/test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-44-LOG-NAMESPACE +Wants=basic.target multi-user.target systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket +After=basic.target systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket +Before=getty-pre.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-44-LOG-NAMESPACE/meson.build b/test/TEST-44-LOG-NAMESPACE/meson.build new file mode 100644 index 0000000..555ba7b --- /dev/null +++ b/test/TEST-44-LOG-NAMESPACE/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'unit' : files('TEST-44-LOG-NAMESPACE.service'), + }, +] diff --git a/test/TEST-45-TIMEDATE/meson.build b/test/TEST-45-TIMEDATE/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-45-TIMEDATE/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-46-HOMED/meson.build b/test/TEST-46-HOMED/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-46-HOMED/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh index 3bf3891..923e002 100755 --- a/test/TEST-46-HOMED/test.sh +++ b/test/TEST-46-HOMED/test.sh @@ -21,6 +21,9 @@ test_append_files() { install_btrfs generate_module_dependencies fi + inst_binary ssh + inst_binary sshd + inst_binary ssh-keygen } do_test "$@" diff --git a/test/TEST-50-DISSECT/meson.build b/test/TEST-50-DISSECT/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-50-DISSECT/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh index f1abce8..a0cd677 100755 --- a/test/TEST-50-DISSECT/test.sh +++ b/test/TEST-50-DISSECT/test.sh @@ -12,6 +12,15 @@ TEST_INSTALL_VERITY_MINIMAL=1 # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" +# On Ubuntu the BPF LSM is not enabled by default, so we need to do it via the +# kernel command line on boot +if [ "$LOOKS_LIKE_UBUNTU" = "yes" ]; then + KERNEL_OPTIONS=( + "lsm=lockdown,capability,landlock,yama,apparmor,bpf" + ) + KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}" +fi + test_require_bin mksquashfs veritysetup sfdisk test_append_files() { @@ -21,11 +30,13 @@ test_append_files() { generate_module_dependencies inst_binary wc inst_binary sha256sum + inst_binary tar if command -v openssl >/dev/null 2>&1; then inst_binary openssl fi inst_binary mksquashfs inst_binary unsquashfs + inst_binary pkcheck install_verity_minimal } diff --git a/test/testsuite-52.units/test-honor-first-shutdown.service b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service index abcf578..bbeac3a 100644 --- a/test/testsuite-52.units/test-honor-first-shutdown.service +++ b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service @@ -4,7 +4,7 @@ Description=Honor First Shutdown feature After=multi-user.target [Service] -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-52.units/%N.sh +ExecStart=/usr/lib/systemd/tests/testdata/TEST-52-HONORFIRSTSHUTDOWN.units/%N.sh ExecStop=sh -c 'kill -KILL $MAINPID' FailureAction=reboot diff --git a/test/testsuite-52.units/test-honor-first-shutdown.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh index 0cb574f..0cb574f 100755 --- a/test/testsuite-52.units/test-honor-first-shutdown.sh +++ b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/meson.build b/test/TEST-52-HONORFIRSTSHUTDOWN/meson.build new file mode 100644 index 0000000..08503e8 --- /dev/null +++ b/test/TEST-52-HONORFIRSTSHUTDOWN/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-52-HONORFIRSTSHUTDOWN.units'] diff --git a/test/TEST-53-ISSUE-16347/meson.build b/test/TEST-53-ISSUE-16347/meson.build new file mode 100644 index 0000000..4c764a2 --- /dev/null +++ b/test/TEST-53-ISSUE-16347/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'mkosi-args' : integration_test_template['mkosi-args'] + [ + '--configure-script', meson.current_source_dir() / 'mkosi.configure', + ], + 'vm' : true, + }, +] diff --git a/test/TEST-53-ISSUE-16347/mkosi.configure b/test/TEST-53-ISSUE-16347/mkosi.configure new file mode 100755 index 0000000..d754fe4 --- /dev/null +++ b/test/TEST-53-ISSUE-16347/mkosi.configure @@ -0,0 +1,6 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -e + +RTC="$(date -u +%Y-%m-%dT%H:%M:%S -d "+3 days")" +jq ".QemuArgs += [\"-rtc\", \"base=$RTC\"]" diff --git a/test/TEST-54-CREDS/meson.build b/test/TEST-54-CREDS/meson.build new file mode 100644 index 0000000..8edb043 --- /dev/null +++ b/test/TEST-54-CREDS/meson.build @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'cmdline' : integration_test_template['cmdline'] + [ + 'systemd.set_credential=kernelcmdlinecred:uff', + 'systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest', + 'systemd.set_credential=login.motd:hello', + 'systemd.set_credential=login.issue:welcome', + 'systemd.set_credential_binary=waldi:d29vb29mZmZ3dWZmZnd1ZmYK', + 'rd.systemd.import_credentials=no', + 'rd.systemd.wants=initrdcred.service', + ], + 'credentials' : integration_test_template['credentials'] + [ + 'mynspawncredential=strangevalue', + files('systemd.extra-unit.my-service.service'), + files('systemd.unit-dropin.my-service.service'), + files('systemd.unit-dropin.my-service.service~30-named.service'), + ], + 'qemu-args' : integration_test_template['qemu-args'] + [ + '-fw_cfg', 'name=opt/io.systemd.credentials/myqemucredential,string=othervalue', + '-smbios', 'type=11,value=io.systemd.credential:smbioscredential=magicdata', + '-smbios', 'type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh', + '-smbios', 'type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK', + '-smbios', 'type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg==', + '-smbios', 'type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg==', + '-smbios', 'type=11,value=io.systemd.credential:getty.ttys.container=idontexist', + ], + }, +] diff --git a/test/units/testsuite-69.service b/test/TEST-54-CREDS/systemd.extra-unit.my-service.service index 7aa0664..e29d0ee 100644 --- a/test/units/testsuite-69.service +++ b/test/TEST-54-CREDS/systemd.extra-unit.my-service.service @@ -1,7 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=TEST-69-SHUTDOWN [Service] Type=oneshot -ExecStart=/bin/true +ExecStart=touch /tmp/unit-cred diff --git a/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service new file mode 100644 index 0000000..279a941 --- /dev/null +++ b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +[Service] +ExecStart=touch /tmp/unit-dropin diff --git a/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service new file mode 100644 index 0000000..67d87ca --- /dev/null +++ b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +[Service] +ExecStart=touch /tmp/unit-named-dropin diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh index c0a9d7a..99c44e3 100755 --- a/test/TEST-54-CREDS/test.sh +++ b/test/TEST-54-CREDS/test.sh @@ -9,6 +9,23 @@ NSPAWN_CREDS=( ) NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}" +UNIT_CRED=$(base64 -w 0 <<EOF +[Service] +Type=oneshot +ExecStart=touch /tmp/unit-cred +EOF +) +DROPIN_CRED=$(base64 -w 0 <<EOF +[Service] +ExecStart=touch /tmp/unit-dropin +EOF +) +NAMED_DROPIN_CRED=$(base64 -w 0 <<EOF +[Service] +ExecStart=touch /tmp/unit-named-dropin +EOF +) + QEMU_CREDS=( "-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue" "-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata" @@ -17,6 +34,9 @@ QEMU_CREDS=( "-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg==" "-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg==" "-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist" + "-smbios type=11,value=io.systemd.credential.binary:systemd.extra-unit.my-service.service=$UNIT_CRED" + "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service=$DROPIN_CRED" + "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service~30-named=$NAMED_DROPIN_CRED" ) QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}" diff --git a/test/TEST-55-OOMD/meson.build b/test/TEST-55-OOMD/meson.build new file mode 100644 index 0000000..adc0509 --- /dev/null +++ b/test/TEST-55-OOMD/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'credentials' : integration_test_template['credentials'] + [ + files('systemd.unit-dropin.init.scope'), + ], + 'vm' : true, + }, +] diff --git a/test/TEST-55-OOMD/systemd.unit-dropin.init.scope b/test/TEST-55-OOMD/systemd.unit-dropin.init.scope new file mode 100644 index 0000000..31b7f90 --- /dev/null +++ b/test/TEST-55-OOMD/systemd.unit-dropin.init.scope @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +[Scope] +MemoryHigh=infinity +StartupMemoryHigh=10G diff --git a/test/TEST-58-REPART/meson.build b/test/TEST-58-REPART/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-58-REPART/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-59-RELOADING-RESTART/meson.build b/test/TEST-59-RELOADING-RESTART/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-59-RELOADING-RESTART/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-60-MOUNT-RATELIMIT/meson.build b/test/TEST-60-MOUNT-RATELIMIT/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-60-MOUNT-RATELIMIT/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-62-RESTRICT-IFACES/meson.build b/test/TEST-62-RESTRICT-IFACES/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-62-RESTRICT-IFACES/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/testsuite-63.units/test63-glob.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path index 5f237a9..5f237a9 100644 --- a/test/testsuite-63.units/test63-glob.path +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path diff --git a/test/testsuite-63.units/test63-glob.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service index 3f49dd4..3f49dd4 100644 --- a/test/testsuite-63.units/test63-glob.service +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service diff --git a/test/testsuite-63.units/test63-issue-24577-dep.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service index e332ea4..e332ea4 100644 --- a/test/testsuite-63.units/test63-issue-24577-dep.service +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service diff --git a/test/testsuite-63.units/test63-issue-24577.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path index 80ba1db..80ba1db 100644 --- a/test/testsuite-63.units/test63-issue-24577.path +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path diff --git a/test/testsuite-63.units/test63-issue-24577.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service index 568518b..568518b 100644 --- a/test/testsuite-63.units/test63-issue-24577.service +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service diff --git a/test/testsuite-63.units/test63-pr-30768.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path index b541358..b541358 100644 --- a/test/testsuite-63.units/test63-pr-30768.path +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path diff --git a/test/testsuite-63.units/test63-pr-30768.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service index 5739084..5739084 100644 --- a/test/testsuite-63.units/test63-pr-30768.service +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service diff --git a/test/testsuite-63.units/test63.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63.path index 64d5ed6..64d5ed6 100644 --- a/test/testsuite-63.units/test63.path +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63.path diff --git a/test/testsuite-63.units/test63.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63.service index 01a928b..01a928b 100644 --- a/test/testsuite-63.units/test63.service +++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63.service diff --git a/test/TEST-63-PATH/meson.build b/test/TEST-63-PATH/meson.build new file mode 100644 index 0000000..4aa3afd --- /dev/null +++ b/test/TEST-63-PATH/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-63-PATH.units'] diff --git a/test/TEST-64-UDEV-STORAGE/btrfs_basic.configure b/test/TEST-64-UDEV-STORAGE/btrfs_basic.configure new file mode 100755 index 0000000..8ef58a9 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/btrfs_basic.configure @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"] + +for i in range(4): + id = f"drivebtrfsbasic{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "350M" if i == 0 else "128M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += [ + "-device", + f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefbtrfs{i}", + ] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/iscsi_lvm.configure b/test/TEST-64-UDEV-STORAGE/iscsi_lvm.configure new file mode 100755 index 0000000..ca23e33 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/iscsi_lvm.configure @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"] + +for i in range(4): + id = f"driveiscsibasic{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "150M" if i == 0 else "70M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += [ + "-device", + f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefiscsi{i}", + ] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure b/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure new file mode 100755 index 0000000..6108bdb --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +config["QemuDrives"] += [ + { + "Id": "drive0", + "Size": "64M", + "Options": "cache=unsafe", + } +] +config["QemuArgs"] += ["-device", "pci-bridge,id=pci_bridge0,chassis_nr=64"] + +# Create 25 additional PCI bridges, each one connected to the previous one +# (basically a really long extension cable), and attach a virtio drive to +# the last one. This should force udev into attempting to create a device +# unit with a _really_ long name. +for bridge in range(1, 26): + config["QemuArgs"] += [ + "-device", + f"pci-bridge,id=pci_bridge{bridge},bus=pci_bridge{bridge - 1}," + f"chassis_nr={64 + bridge}" + ] + +config["QemuArgs"] += ["-device", f"virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge25"] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/lvm_basic.configure b/test/TEST-64-UDEV-STORAGE/lvm_basic.configure new file mode 100755 index 0000000..9387b83 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/lvm_basic.configure @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"] + +for i in range(4): + id = f"drivelvmbasic{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "32M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += [ + "-device", f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeeflvm{i}", + ] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/mdadm_basic.configure b/test/TEST-64-UDEV-STORAGE/mdadm_basic.configure new file mode 100755 index 0000000..3f00afa --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/mdadm_basic.configure @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"] + +for i in range(5): + id = f"drivemdadmbasic{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "64M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += [ + "-device", f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefmdadm{i}", + ] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/mdadm_lvm.configure b/test/TEST-64-UDEV-STORAGE/mdadm_lvm.configure new file mode 100755 index 0000000..b7661d9 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/mdadm_lvm.configure @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"] + +for i in range(5): + id = f"drivemdadmlvm{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "64M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += [ + "-device", f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefmdadmlvm{i}", + ] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/meson.build b/test/TEST-64-UDEV-STORAGE/meson.build new file mode 100644 index 0000000..15981ce --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/meson.build @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +name = fs.name(meson.current_source_dir()) +unit = configure_file( + input : files('../test.service.in'), + output : '@0@.service'.format(name), + # Disable user service manager by default for performance. + configuration : integration_test_template['configuration'] + { + 'wants' : '', + 'after' : '', + }, +) + +foreach testcase : [ + 'btrfs_basic', + 'iscsi_lvm', + 'long_sysfs_path', + 'lvm_basic', + 'mdadm_basic', + 'mdadm_lvm', + 'multipath_basic_failover', + 'nvme_basic', + 'nvme_subsystem', + 'simultaneous_events', + 'virtio_scsi_basic', + 'virtio_scsi_identically_named_partitions', +] + integration_tests += [ + integration_test_template + { + 'name' : '@0@-@1@'.format(name, testcase), + # Make sure the service is still named TEST-64-UDEV-STORAGE.service. + 'unit' : unit, + 'cmdline' : integration_test_template['cmdline'] + [ + 'systemd.setenv=TEST_FUNCTION_NAME=testcase_@0@'.format(testcase) + ], + 'mkosi-args' : integration_test_template['mkosi-args'] + [ + '--configure-script', files('@0@.configure'.format(testcase)), + ], + 'priority' : 10, + 'vm' : true, + }, + ] +endforeach diff --git a/test/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure b/test/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure new file mode 100755 index 0000000..5f323b8 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure @@ -0,0 +1,31 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +# Add 16 multipath devices, each backed by 4 paths +# We don't use --qemu-drive for this since they have to share the file. +for ndisk in range(16): + wwn = f"0xDEADDEADBEEF{ndisk:04d}" + if ndisk == 0: + size = "16M" + else: + size = "1M" + + for nback in range(4): + id = f"drive{ndisk}x{nback}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": size, + "Options": "cache=unsafe", + "FileId": str(ndisk), + } + ] + config["QemuArgs"] += ["-device", f"scsi-hd,drive={id},serial=MPIO{ndisk},wwn={wwn}"] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/nvme_basic.configure b/test/TEST-64-UDEV-STORAGE/nvme_basic.configure new file mode 100755 index 0000000..37d0d35 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/nvme_basic.configure @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import os +import subprocess +import sys + + +config = json.load(sys.stdin) + +qemu = f"qemu-system-{os.environ["QEMU_ARCHITECTURE"]}" +result = subprocess.run([qemu, '-device', 'help'], check=True, text=True, stdout=subprocess.PIPE) +if 'name "nvme"' not in result.stdout: + print("nvme device driver is not available, skipping test...", file=sys.stderr) + exit(77) + +def add_drive(i: int, serial: str) -> None: + global config + id = f"nvme{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "1M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += ["-device", f"nvme,drive={id},serial={serial},num_queues=8"] + +for i in range(5): + add_drive(i, serial=f"deadbeef{i}") +for i in range(5, 10): + add_drive(i, serial=f" deadbeef {i} ") +for i in range(10, 15): + add_drive(i, serial=f" dead/beef/{i} ") +for i in range(15, 20): + add_drive(i, serial=f"dead/../../beef/{i}") + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/nvme_subsystem.configure b/test/TEST-64-UDEV-STORAGE/nvme_subsystem.configure new file mode 100755 index 0000000..eb601a6 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/nvme_subsystem.configure @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import os +import subprocess +import sys + + +config = json.load(sys.stdin) + +qemu = f"qemu-system-{os.environ["QEMU_ARCHITECTURE"]}" +result = subprocess.run([qemu, '-device', 'help'], check=True, text=True, stdout=subprocess.PIPE) +if 'name "nvme"' not in result.stdout: + print("nvme device driver is not available, skipping test...", file=sys.stderr) + exit(77) + +for id in ("nvme0", "nvme1"): + config["QemuDrives"] += [ + { + "Id": id, + "Size": "1M", + "Options": "cache=unsafe", + } + + ] + +config["QemuArgs"] += [ + # Create an NVM Subsystem Device + "-device", "nvme-subsys,id=nvme-subsys-64,nqn=subsys64", + # Attach two NVM controllers to it + "-device", "nvme,subsys=nvme-subsys-64,serial=deadbeef", + "-device", "nvme,subsys=nvme-subsys-64,serial=deadbeef", + # And create two shared namespaces attached to both controllers + "-device", "nvme-ns,drive=nvme0,nsid=16,shared=on", + "-device", "nvme-ns,drive=nvme1,nsid=17,shared=on", +] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/simultaneous_events.configure b/test/TEST-64-UDEV-STORAGE/simultaneous_events.configure new file mode 100755 index 0000000..a0edb01 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/simultaneous_events.configure @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +for i in range(10): + id = f"drivesimultaneousevents{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "128M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += ["-device", f"scsi-hd,drive={id},serial=deadbeeftest{i}"] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index b9e7bdf..b5a70ca 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -159,18 +159,18 @@ test_run() { return 0 } -testcase_megasas2_basic() { - if ! "${QEMU_BIN:?}" -device help | grep 'name "megasas-gen2"'; then - echo "megasas-gen2 device driver is not available, skipping test..." +testcase_virtio_scsi_basic() { + if ! "${QEMU_BIN:?}" -device help | grep 'name "virtio-scsi-pci"'; then + echo "virtio-scsi-pci device driver is not available, skipping test..." return 77 fi local i local qemu_opts=( - "-device megasas-gen2,id=scsi0" - "-device megasas-gen2,id=scsi1" - "-device megasas-gen2,id=scsi2" - "-device megasas-gen2,id=scsi3" + "-device virtio-scsi-pci,id=scsi0" + "-device virtio-scsi-pci,id=scsi1" + "-device virtio-scsi-pci,id=scsi2" + "-device virtio-scsi-pci,id=scsi3" ) for i in {0..127}; do @@ -268,24 +268,15 @@ testcase_virtio_scsi_identically_named_partitions() { # and attach them to a virtio-scsi controller local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4") local diskpath="${TESTDIR:?}/namedpart0.img" - local i lodev num_disk num_part qemu_timeout + local i num_disk qemu_timeout if get_bool "${IS_BUILT_WITH_ASAN:=}" || ! get_bool "$QEMU_KVM"; then num_disk=4 - num_part=4 else num_disk=16 - num_part=8 fi dd if=/dev/zero of="$diskpath" bs=1M count=18 - lodev="$(losetup --show -f -P "$diskpath")" - sfdisk "${lodev:?}" <<EOF -label: gpt - -$(for ((i = 1; i <= num_part; i++)); do echo 'name="Hello world", size=2M'; done) -EOF - losetup -d "$lodev" for ((i = 0; i < num_disk; i++)); do diskpath="${TESTDIR:?}/namedpart$i.img" @@ -325,19 +316,9 @@ testcase_multipath_basic_failover() { local qemu_opts=("-device virtio-scsi-pci,id=scsi") local partdisk="${TESTDIR:?}/multipathpartitioned.img" - local image lodev nback ndisk wwn + local image nback ndisk wwn dd if=/dev/zero of="$partdisk" bs=1M count=16 - lodev="$(losetup --show -f -P "$partdisk")" - sfdisk "${lodev:?}" <<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" "${lodev}p2" - losetup -d "$lodev" # Add 16 multipath devices, each backed by 4 paths for ndisk in {0..15}; do @@ -388,7 +369,7 @@ testcase_lvm_basic() { return 77 fi - local qemu_opts=("-device ahci,id=ahci0") + local qemu_opts=("-device virtio-scsi-pci,id=scsi0") local diskpath i # Attach 4 SATA disks to the VM (and set their model and serial fields @@ -397,7 +378,7 @@ testcase_lvm_basic() { diskpath="${TESTDIR:?}/lvmbasic${i}.img" dd if=/dev/zero of="$diskpath" bs=1M count=32 qemu_opts+=( - "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeeflvm$i" + "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeeflvm$i" "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i" ) done @@ -415,7 +396,7 @@ testcase_btrfs_basic() { return 77 fi - local qemu_opts=("-device ahci,id=ahci0") + local qemu_opts=("-device virtio-scsi-pci,id=scsi0") local diskpath i size for i in {0..3}; do @@ -425,7 +406,7 @@ testcase_btrfs_basic() { dd if=/dev/zero of="$diskpath" bs=1M count="$size" qemu_opts+=( - "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefbtrfs$i" + "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefbtrfs$i" "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i" ) done @@ -443,7 +424,7 @@ testcase_iscsi_lvm() { return 77 fi - local qemu_opts=("-device ahci,id=ahci0") + local qemu_opts=("-device virtio-scsi-pci,id=scsi0") local diskpath i size for i in {0..3}; do @@ -454,7 +435,7 @@ testcase_iscsi_lvm() { dd if=/dev/zero of="$diskpath" bs=1M count="$size" qemu_opts+=( - "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefiscsi$i" + "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefiscsi$i" "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i" ) done @@ -475,18 +456,6 @@ testcase_long_sysfs_path() { ) dd if=/dev/zero of="$testdisk" bs=1M count=64 - lodev="$(losetup --show -f -P "$testdisk")" - sfdisk "${lodev:?}" <<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" "${lodev}p1" - mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${lodev}p2" - losetup -d "$lodev" - # Create 25 additional PCI bridges, each one connected to the previous one # (basically a really long extension cable), and attach a virtio drive to # the last one. This should force udev into attempting to create a device @@ -510,7 +479,7 @@ testcase_mdadm_basic() { return 77 fi - local qemu_opts=("-device ahci,id=ahci0") + local qemu_opts=("-device virtio-scsi-pci,id=scsi0") local diskpath i size for i in {0..4}; do @@ -518,7 +487,7 @@ testcase_mdadm_basic() { dd if=/dev/zero of="$diskpath" bs=1M count=64 qemu_opts+=( - "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefmdadm$i" + "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefmdadm$i" "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i" ) done @@ -536,7 +505,7 @@ testcase_mdadm_lvm() { return 77 fi - local qemu_opts=("-device ahci,id=ahci0") + local qemu_opts=("-device virtio-scsi-pci,id=scsi0") local diskpath i size for i in {0..4}; do @@ -544,7 +513,7 @@ testcase_mdadm_lvm() { dd if=/dev/zero of="$diskpath" bs=1M count=64 qemu_opts+=( - "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefmdadmlvm$i" + "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefmdadmlvm$i" "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i" ) done diff --git a/test/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure b/test/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure new file mode 100755 index 0000000..ab8d530 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import sys + + +config = json.load(sys.stdin) + +for i in range(4): + config["QemuArgs"] += ['-device', f"virtio-scsi-pci,id=scsi{i}"] + +for i in range(128): + id = f"drive{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "1M", + "Options": "cache=unsafe", + } + ] + config["QemuArgs"] += [ + '-device', + f"scsi-hd,drive={id},bus=scsi{i // 32}.0,channel=0," + f"scsi-id={i % 32},lun=0", + ] + +json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure b/test/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure new file mode 100755 index 0000000..e850247 --- /dev/null +++ b/test/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import json +import os +import subprocess +import sys + + +config = json.load(sys.stdin) + +qemu = f"qemu-system-{os.environ["QEMU_ARCHITECTURE"]}" +result = subprocess.run([qemu, '-device', 'help'], check=True, text=True, stdout=subprocess.PIPE) +if 'name "virtio-scsi-pci"' not in result.stdout: + print("virtio-scsi-pci device driver is not available, skipping test...", file=sys.stderr) + exit(77) + +num_disk = 16 + +config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0,num_queues=4"] + +for i in range(0, num_disk): + id = f"drive{i}" + config["QemuDrives"] += [ + { + "Id": id, + "Size": "40M", + "Options": "cache=unsafe" + } + ] + config["QemuArgs"] += ["-device", f"scsi-hd,drive={id},bus=scsi0.0,channel=0,scsi-id=0,lun={i}"] + +json.dump(config, sys.stdout) diff --git a/test/TEST-65-ANALYZE/meson.build b/test/TEST-65-ANALYZE/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-65-ANALYZE/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-66-DEVICE-ISOLATION/meson.build b/test/TEST-66-DEVICE-ISOLATION/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-66-DEVICE-ISOLATION/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-67-INTEGRITY/meson.build b/test/TEST-67-INTEGRITY/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-67-INTEGRITY/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build b/test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service b/test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service new file mode 100644 index 0000000..1467f71 --- /dev/null +++ b/test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-69-SHUTDOWN +Wants=basic.target multi-user.target +After=basic.target +Before=getty-pre.target + +ConditionPathExists=/usr/bin/python3 + +[Service] +Type=oneshot +ExecStart=/usr/lib/systemd/tests/testdata/units/TEST-69-SHUTDOWN.py diff --git a/test/TEST-69-SHUTDOWN/meson.build b/test/TEST-69-SHUTDOWN/meson.build new file mode 100644 index 0000000..c1b1ab5 --- /dev/null +++ b/test/TEST-69-SHUTDOWN/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'unit' : files('TEST-69-SHUTDOWN.service'), + 'configuration' : integration_test_template['configuration'] + { + 'wants' : '@0@ systemd-user-sessions.service'.format(integration_test_template['configuration']['wants']), + 'after' : '@0@ systemd-user-sessions.service'.format(integration_test_template['configuration']['after']), + }, + }, +] diff --git a/test/TEST-69-SHUTDOWN/test.sh b/test/TEST-69-SHUTDOWN/test.sh index 0e12857..0229d89 100755 --- a/test/TEST-69-SHUTDOWN/test.sh +++ b/test/TEST-69-SHUTDOWN/test.sh @@ -17,7 +17,7 @@ SYSTEMD_NSPAWN="${STATEDIR:?}/run-nspawn" setup_nspawn_root_hook() { cat >"${STATEDIR:?}/run-nspawn" <<EOF #!/bin/bash -exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" "\$@" +exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" --background= "\$@" exit 1 EOF chmod 755 "${STATEDIR:?}"/run-nspawn diff --git a/test/TEST-70-TPM2/meson.build b/test/TEST-70-TPM2/meson.build new file mode 100644 index 0000000..d84c2b7 --- /dev/null +++ b/test/TEST-70-TPM2/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'configuration' : integration_test_template['configuration'] + { + 'wants' : '@0@ tpm2.target'.format(integration_test_template['configuration']['wants']), + 'after' : '@0@ tpm2.target'.format(integration_test_template['configuration']['after']), + }, + 'vm' : true, + }, +] diff --git a/test/TEST-71-HOSTNAME/meson.build b/test/TEST-71-HOSTNAME/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-71-HOSTNAME/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-72-SYSUPDATE/meson.build b/test/TEST-72-SYSUPDATE/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-72-SYSUPDATE/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-73-LOCALE/meson.build b/test/TEST-73-LOCALE/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-73-LOCALE/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-74-AUX-UTILS/meson.build b/test/TEST-74-AUX-UTILS/meson.build new file mode 100644 index 0000000..43a733e --- /dev/null +++ b/test/TEST-74-AUX-UTILS/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'storage': 'persistent', + }, +] diff --git a/test/TEST-74-AUX-UTILS/test.sh b/test/TEST-74-AUX-UTILS/test.sh index e3eb62f..2ee4a75 100755 --- a/test/TEST-74-AUX-UTILS/test.sh +++ b/test/TEST-74-AUX-UTILS/test.sh @@ -8,8 +8,9 @@ NSPAWN_ARGUMENTS="--private-network" # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" -# (Hopefully) a temporary workaround for https://github.com/systemd/systemd/issues/30573 -KERNEL_APPEND="${KERNEL_APPEND:-} SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST=100" +# Make sure vsock is available in the VM +CID=$((RANDOM + 3)) +QEMU_OPTIONS+=" -device vhost-vsock-pci,guest-cid=$CID" test_append_files() { local workspace="${1:?}" @@ -25,6 +26,16 @@ test_append_files() { install_mdadm generate_module_dependencies fi + + inst_binary socat + inst_binary ssh + inst_binary sshd + inst_binary ssh-keygen + inst_binary usermod + instmods vmw_vsock_virtio_transport + instmods vsock_loopback + instmods vmw_vsock_vmci_transport + generate_module_dependencies } do_test "$@" diff --git a/test/TEST-75-RESOLVED/meson.build b/test/TEST-75-RESOLVED/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-75-RESOLVED/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-75-RESOLVED/test.sh b/test/TEST-75-RESOLVED/test.sh index 55a9f1b..6272d5a 100755 --- a/test/TEST-75-RESOLVED/test.sh +++ b/test/TEST-75-RESOLVED/test.sh @@ -11,14 +11,7 @@ NSPAWN_ARGUMENTS="--private-network" test_require_bin knotd -# We need at least Knot 3.0 which support (among others) the ds-push directive -if ! knotc -c "${TEST_BASE_DIR:?}/knot-data/knot.conf" conf-check; then - echo "This test requires at least Knot 3.0. skipping..." - exit 0 -fi - test_append_files() { - local workspace="${1:?}" # Install knot image_install kzonecheck keymgr kjournalprint knotc knotd image_install "${ROOTLIBDIR:?}/system/knot.service" @@ -26,14 +19,6 @@ test_append_files() { image_install -o /etc/dbus-1/system.d/cz.nic.knotd.conf image_install -o /etc/default/knot - # Copy over our configuration - mkdir -p "${workspace:?}/var/lib/knot/zones/" "${workspace:?}/etc/knot/" - cp -rfv "${TEST_BASE_DIR:?}"/knot-data/zones/* "$workspace/var/lib/knot/zones/" - cp -fv "${TEST_BASE_DIR:?}/knot-data/knot.conf" "$workspace/etc/knot/knot.conf" - chgrp -R knot "$workspace/etc/knot/" "$workspace/var/lib/knot/" - chmod -R ug+rwX "$workspace/var/lib/knot/" - chmod -R g+r "$workspace/etc/knot/" - # Install DNS-related utilities (usually found in the bind-utils package) image_install delv dig host nslookup diff --git a/test/TEST-76-SYSCTL/meson.build b/test/TEST-76-SYSCTL/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-76-SYSCTL/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-77-OPENFILE/Makefile b/test/TEST-77-OPENFILE/Makefile deleted file mode 120000 index e9f93b1..0000000 --- a/test/TEST-77-OPENFILE/Makefile +++ /dev/null @@ -1 +0,0 @@ -../TEST-01-BASIC/Makefile
\ No newline at end of file diff --git a/test/TEST-77-OPENFILE/test.sh b/test/TEST-77-OPENFILE/test.sh deleted file mode 100755 index 92afa4c..0000000 --- a/test/TEST-77-OPENFILE/test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -e - -TEST_DESCRIPTION="Openfile tests" - -# shellcheck source=test/test-functions -. "${TEST_BASE_DIR:?}/test-functions" - -test_append_files() { - local workspace="${1:?}" - echo "Open" >"$workspace/test-77-open.dat" - echo "File" >"$workspace/test-77-file.dat" -} - -do_test "$@" diff --git a/test/TEST-78-SIGQUEUE/meson.build b/test/TEST-78-SIGQUEUE/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-78-SIGQUEUE/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/TEST-79-MEMPRESS/meson.build b/test/TEST-79-MEMPRESS/meson.build new file mode 100644 index 0000000..2b832a8 --- /dev/null +++ b/test/TEST-79-MEMPRESS/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'configuration' : integration_test_template['configuration'] + { + 'memory-accounting' : 'yes', + }, + }, +] diff --git a/test/testsuite-80.units/fdstore-nopin.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service index 29ffd23..f658853 100644 --- a/test/testsuite-80.units/fdstore-nopin.service +++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service @@ -3,4 +3,4 @@ Type=notify NotifyAccess=all FileDescriptorStoreMax=10 FileDescriptorStorePreserve=restart -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 0 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 0 diff --git a/test/testsuite-80.units/fdstore-pin.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service index 913daa2..393b5ac 100644 --- a/test/testsuite-80.units/fdstore-pin.service +++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service @@ -3,4 +3,4 @@ Type=notify NotifyAccess=all FileDescriptorStoreMax=10 FileDescriptorStorePreserve=yes -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 1 +ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 1 diff --git a/test/testsuite-80.units/fdstore-pin.sh b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh index 4cb041a..4cb041a 100755 --- a/test/testsuite-80.units/fdstore-pin.sh +++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh diff --git a/test/testsuite-80.units/fdstore-pin.target b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target index 319b7e1..319b7e1 100644 --- a/test/testsuite-80.units/fdstore-pin.target +++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service new file mode 100644 index 0000000..5693be6 --- /dev/null +++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service @@ -0,0 +1,4 @@ +[Service] +Type=notify +NotifyAccess=all +ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/test.sh diff --git a/test/testsuite-80.units/test.sh b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh index 565ed8d..565ed8d 100755 --- a/test/testsuite-80.units/test.sh +++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh diff --git a/test/TEST-80-NOTIFYACCESS/meson.build b/test/TEST-80-NOTIFYACCESS/meson.build new file mode 100644 index 0000000..f78c6fd --- /dev/null +++ b/test/TEST-80-NOTIFYACCESS/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-80-NOTIFYACCESS.units'] diff --git a/test/TEST-81-GENERATORS/meson.build b/test/TEST-81-GENERATORS/meson.build new file mode 100644 index 0000000..8dec5f3 --- /dev/null +++ b/test/TEST-81-GENERATORS/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + }, +] diff --git a/test/units/testsuite-82.service b/test/TEST-82-SOFTREBOOT/TEST-82-SOFTREBOOT.service index a8fc4f9..f3892a7 100644 --- a/test/units/testsuite-82.service +++ b/test/TEST-82-SOFTREBOOT/TEST-82-SOFTREBOOT.service @@ -2,7 +2,9 @@ [Unit] Description=TEST-82-SOFTREBOOT DefaultDependencies=no +Wants=basic.target multi-user.target After=basic.target +Before=getty-pre.target [Service] Type=oneshot diff --git a/test/TEST-82-SOFTREBOOT/meson.build b/test/TEST-82-SOFTREBOOT/meson.build new file mode 100644 index 0000000..a7c1bf0 --- /dev/null +++ b/test/TEST-82-SOFTREBOOT/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'unit' : files('TEST-82-SOFTREBOOT.service'), + 'cmdline' : ['systemd.set_credential=kernelcmdlinecred:uff'], + 'storage' : 'persistent', + 'vm' : true, + }, +] diff --git a/test/TEST-82-SOFTREBOOT/test.sh b/test/TEST-82-SOFTREBOOT/test.sh index 0494149..2109cc8 100755 --- a/test/TEST-82-SOFTREBOOT/test.sh +++ b/test/TEST-82-SOFTREBOOT/test.sh @@ -7,8 +7,22 @@ TEST_DESCRIPTION="Test Soft-Rebooting" IGNORE_MISSING_COVERAGE=yes # Prevent shutdown in test suite, the expect script does that manually. TEST_SKIP_SHUTDOWN=yes +IMAGE_NAME="softreboot" +TEST_NO_NSPAWN=1 +TEST_INSTALL_VERITY_MINIMAL=1 +KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff" # shellcheck source=test/test-functions . "$TEST_BASE_DIR/test-functions" +test_require_bin mksquashfs veritysetup sfdisk + +test_append_files() { + instmods squashfs =squashfs + instmods dm_verity =md + install_dmevent + generate_module_dependencies + install_verity_minimal +} + do_test "$@" diff --git a/test/TEST-83-BTRFS/meson.build b/test/TEST-83-BTRFS/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-83-BTRFS/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-84-STORAGETM/meson.build b/test/TEST-84-STORAGETM/meson.build new file mode 100644 index 0000000..77370ce --- /dev/null +++ b/test/TEST-84-STORAGETM/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/TEST-85-NETWORK/Makefile b/test/TEST-85-NETWORK/Makefile new file mode 100644 index 0000000..653f161 --- /dev/null +++ b/test/TEST-85-NETWORK/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +all setup run clean clean-again: + true + +.PHONY: all setup run clean clean-again diff --git a/test/TEST-85-NETWORK/meson.build b/test/TEST-85-NETWORK/meson.build new file mode 100644 index 0000000..47ec029 --- /dev/null +++ b/test/TEST-85-NETWORK/meson.build @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +name = fs.name(meson.current_source_dir()) +unit = configure_file( + input : files('../test.service.in'), + output : '@0@.service'.format(name), + configuration : integration_test_template['configuration'] + { + 'command' : '@0@ --no-journal'.format(testdata_dir / 'test-network/systemd-networkd-tests.py') + }, +) + +systemd_networkd_tests_py = files('../test-network/systemd-networkd-tests.py') +network_testcases = run_command('sed', + '-ne', + '/^class .*Tests/ { s/^class *//; s/(.*$//; p}', + systemd_networkd_tests_py, + check : true).stdout().split() + +foreach testcase : network_testcases + integration_tests += [ + integration_test_template + { + 'name' : '@0@-@1@'.format(name, testcase), + 'unit' : unit, + 'cmdline' : integration_test_template['cmdline'] + [ + 'systemd.setenv=TEST_MATCH_TESTCASE=@0@'.format(testcase) + ], + 'priority' : 10, + 'vm' : true, + }, + ] +endforeach diff --git a/test/fuzz/fuzz-catalog/systemd.pl.catalog b/test/fuzz/fuzz-catalog/systemd.pl.catalog index 6e99c04..99a62ce 100644 --- a/test/fuzz/fuzz-catalog/systemd.pl.catalog +++ b/test/fuzz/fuzz-catalog/systemd.pl.catalog @@ -4,7 +4,7 @@ # Polish translation # The catalog format is documented on -# https://www.freedesktop.org/wiki/Software/systemd/catalog +# https://systemd.io/CATALOG # For an explanation why we do all this, see https://xkcd.com/1024/ diff --git a/test/fuzz/fuzz-execute-serialize/initial b/test/fuzz/fuzz-execute-serialize/initial index 403cf08..4d9103c 100644 --- a/test/fuzz/fuzz-execute-serialize/initial +++ b/test/fuzz/fuzz-execute-serialize/initial @@ -31,7 +31,6 @@ exec-context-protect-home= exec-context-protect-system= exec-context-mount-api-vfs= exec-context-same-pgrp= -exec-context-cpu-sched-reset-on-fork= exec-context-non-blocking= exec-context-ignore-sigpipe= exec-context-memory-deny-write-execute= diff --git a/test/fuzz/fuzz-netdev-parser/bond.netdev b/test/fuzz/fuzz-netdev-parser/bond.netdev index 4e4885c..04e237d 100644 --- a/test/fuzz/fuzz-netdev-parser/bond.netdev +++ b/test/fuzz/fuzz-netdev-parser/bond.netdev @@ -14,5 +14,6 @@ MinLinks=1 AdActorSystemPriority=1218 AdUserPortKey=811 AdActorSystem=00:11:22:33:44:55 +ARPMissedMax=10 # feed the sanitizer AdActorSystem=00:11:22:33:44:55 diff --git a/test/fuzz/fuzz-network-parser/sysctl b/test/fuzz/fuzz-network-parser/sysctl index 2452fb7..01b45a2 100644 --- a/test/fuzz/fuzz-network-parser/sysctl +++ b/test/fuzz/fuzz-network-parser/sysctl @@ -7,4 +7,5 @@ IPv6PrivacyExtensions=true IPv6DuplicateAddressDetection=3 IPv6HopLimit=5 IPv4ProxyARP=true +IPv4ProxyARPPrivateVLAN=true IPv6ProxyNDP=true diff --git a/test/fuzz/fuzz-unit-file/binfmt_misc.automount b/test/fuzz/fuzz-unit-file/binfmt_misc.automount index 8d761b0..d9fd0f0 100644 --- a/test/fuzz/fuzz-unit-file/binfmt_misc.automount +++ b/test/fuzz/fuzz-unit-file/binfmt_misc.automount @@ -11,7 +11,7 @@ automount [Unit] Description=Arbitrary Executable File Formats File System Automount Point Documentation=https://docs.kernel.org/admin-guide/binfmt-misc.html -Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +Documentation=https://systemd.io/API_FILE_SYSTEMS DefaultDependencies=no Before=sysinit.target ConditionPathExists=/proc/sys/fs/binfmt_misc/ diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service index 4bdc48a..dbd56ec 100644 --- a/test/fuzz/fuzz-unit-file/directives-all.service +++ b/test/fuzz/fuzz-unit-file/directives-all.service @@ -185,6 +185,7 @@ PAMName= PIDFile= PartOf= PassCredentials= +PassFileDescriptorsToExec= PassSecurity= PassPacketInfo= PathChanged= @@ -276,6 +277,7 @@ User= WakeSystem= WantedBy= Wants= +WantsMountsFor= WatchdogSec= What= Where= @@ -471,6 +473,7 @@ IPForward= IPMasquerade= IPv4LLRoute= IPv4ProxyARP= +IPv4ProxyARPPrivateVLAN= IPv6AcceptRA= IPv6DuplicateAddressDetection= IPv6FlowLabel= @@ -494,6 +497,7 @@ KernelVersion= Key= Kind= L2MissNotification= +L3MasterDevice= L3MissNotification= LACPTransmitRate= LLDP= @@ -702,6 +706,7 @@ CPUAffinity= CapabilityBoundingSet= CrashChangeVT= CrashReboot= +CrashAction= CrashShell= CtrlAltDelBurstAction= DefaultBlockIOAccounting= @@ -862,6 +867,7 @@ MaxLevelKMsg= MaxLevelStore= MaxLevelSyslog= MaxLevelWall= +MaxLevelSocket= MaxRetentionSec= MaxUse= MemoryDenyWriteExecute= diff --git a/test/fuzz/fuzz-unit-file/syslog.socket b/test/fuzz/fuzz-unit-file/syslog.socket index 969ee42..bb046a5 100644 --- a/test/fuzz/fuzz-unit-file/syslog.socket +++ b/test/fuzz/fuzz-unit-file/syslog.socket @@ -11,7 +11,7 @@ socket [Unit] Description=Syslog Socket Documentation=man:systemd.special(7) -Documentation=https://www.freedesktop.org/wiki/Software/systemd/syslog +Documentation=https://systemd.io/SYSLOG DefaultDependencies=no Before=sockets.target @@ -45,7 +45,7 @@ ReceiveBuffer=8M # [Install] # Alias=syslog.service # -# See https://www.freedesktop.org/wiki/Software/systemd/syslog for details. +# See https://systemd.io/SYSLOG for details. [Socket] ListenStream=1.2.3.4:1234 diff --git a/test/fuzz/fuzz-unit-file/systemd-resolved.service b/test/fuzz/fuzz-unit-file/systemd-resolved.service index 49d272b..1b7cc15 100644 --- a/test/fuzz/fuzz-unit-file/systemd-resolved.service +++ b/test/fuzz/fuzz-unit-file/systemd-resolved.service @@ -12,8 +12,8 @@ service Description=Network Name Resolution Documentation=man:systemd-resolved.service(8) Documentation=man:org.freedesktop.resolve1(5) -Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers -Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients +Documentation=https://systemd.io/WRITING_NETWORK_CONFIGURATION_MANAGERS +Documentation=https://systemd.io/WRITING_RESOLVER_CLIENTS DefaultDependencies=no After=systemd-sysusers.service systemd-networkd.service Before=network.target nss-lookup.target shutdown.target diff --git a/test/hwdb-test.sh b/test/hwdb-test.sh index 432a49f..89a5c7e 100755 --- a/test/hwdb-test.sh +++ b/test/hwdb-test.sh @@ -10,6 +10,7 @@ set -e export SYSTEMD_LOG_LEVEL=info +export SYSTEMD_HWDB_UPDATE_BYPASS=0 ROOTDIR="$(dirname "$(dirname "$(readlink -f "$0")")")" SYSTEMD_HWDB="${1:?}" diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py new file mode 100755 index 0000000..5b098a3 --- /dev/null +++ b/test/integration-test-wrapper.py @@ -0,0 +1,196 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +'''Test wrapper command for driving integration tests. + +Note: This is deliberately rough and only intended to drive existing tests +with the expectation that as part of formally defining the API it will be tidy. + +''' + +import argparse +import json +import os +import shlex +import subprocess +import sys +import textwrap +from pathlib import Path + + +EMERGENCY_EXIT_DROPIN = """\ +[Unit] +Wants=emergency-exit.service +""" + + +EMERGENCY_EXIT_SERVICE = """\ +[Unit] +DefaultDependencies=no +Conflicts=shutdown.target +Conflicts=rescue.service +Before=shutdown.target +Before=rescue.service +FailureAction=exit + +[Service] +ExecStart=false +""" + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--mkosi', required=True) + parser.add_argument('--meson-source-dir', required=True, type=Path) + parser.add_argument('--meson-build-dir', required=True, type=Path) + parser.add_argument('--name', required=True) + parser.add_argument('--unit', required=True) + parser.add_argument('--storage', required=True) + parser.add_argument('--firmware', required=True) + parser.add_argument('--slow', action=argparse.BooleanOptionalAction) + parser.add_argument('--vm', action=argparse.BooleanOptionalAction) + parser.add_argument('--exit-code', required=True, type=int) + parser.add_argument('mkosi_args', nargs="*") + args = parser.parse_args() + + if not bool(int(os.getenv("SYSTEMD_INTEGRATION_TESTS", "0"))): + print(f"SYSTEMD_INTEGRATION_TESTS=1 not found in environment, skipping {args.name}", file=sys.stderr) + exit(77) + + if args.slow and not bool(int(os.getenv("SYSTEMD_SLOW_TESTS", "0"))): + print(f"SYSTEMD_SLOW_TESTS=1 not found in environment, skipping {args.name}", file=sys.stderr) + exit(77) + + name = args.name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "") + + dropin = textwrap.dedent( + """\ + [Unit] + SuccessAction=exit + SuccessActionExitStatus=123 + + [Service] + StandardOutput=journal+console + """ + ) + + if os.getenv("TEST_MATCH_SUBTEST"): + dropin += textwrap.dedent( + f""" + [Service] + Environment=TEST_MATCH_SUBTEST={os.environ["TEST_MATCH_SUBTEST"]} + """ + ) + + if os.getenv("TEST_MATCH_TESTCASE"): + dropin += textwrap.dedent( + f""" + [Service] + Environment=TEST_MATCH_TESTCASE={os.environ["TEST_MATCH_TESTCASE"]} + """ + ) + + if not sys.stderr.isatty(): + dropin += textwrap.dedent( + """ + [Unit] + FailureAction=exit + """ + ) + + journal_file = (args.meson_build_dir / (f"test/journal/{name}.journal")).absolute() + journal_file.unlink(missing_ok=True) + else: + journal_file = None + + cmd = [ + args.mkosi, + '--directory', os.fspath(args.meson_source_dir), + '--output-dir', os.fspath(args.meson_build_dir / 'mkosi.output'), + '--extra-search-path', os.fspath(args.meson_build_dir), + '--machine', name, + '--ephemeral', + *(['--forward-journal', journal_file] if journal_file else []), + *( + [ + '--credential', + f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)}", + '--credential', + f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}", + ] + if not sys.stderr.isatty() + else [] + ), + '--credential', + f"systemd.unit-dropin.{args.unit}={shlex.quote(dropin)}", + '--runtime-network=none', + '--runtime-scratch=no', + *args.mkosi_args, + '--append', + '--qemu-firmware', args.firmware, + '--kernel-command-line-extra', + ' '.join([ + 'systemd.hostname=H', + f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/{args.name}.units:/usr/lib/systemd/tests/testdata/units:", + f"systemd.unit={args.unit}", + 'systemd.mask=systemd-networkd-wait-online.service', + *( + [ + "systemd.mask=serial-getty@.service", + "systemd.show_status=no", + "systemd.crash_shell=0", + "systemd.crash_action=poweroff", + ] + if not sys.stderr.isatty() + else [] + ), + ]), + '--credential', f"journal.storage={'persistent' if sys.stderr.isatty() else args.storage}", + 'qemu' if args.vm or os.getuid() != 0 else 'boot', + ] + + result = subprocess.run(cmd) + + if result.returncode in (args.exit_code, 77): + # Do not keep journal files for tests that don't fail. + if journal_file: + journal_file.unlink(missing_ok=True) + + exit(0 if result.returncode == args.exit_code else 77) + + if journal_file: + ops = [] + + if os.getenv("GITHUB_ACTIONS"): + id = os.environ["GITHUB_RUN_ID"] + iteration = os.environ["GITHUB_RUN_ATTEMPT"] + j = json.loads( + subprocess.run( + [ + args.mkosi, + "--directory", os.fspath(args.meson_source_dir), + "--json", + "summary", + ], + stdout=subprocess.PIPE, + text=True, + ).stdout + ) + images = {image["Image"]: image for image in j["Images"]} + distribution = images["system"]["Distribution"] + release = images["system"]["Release"] + artifact = f"ci-mkosi-{id}-{iteration}-{distribution}-{release}-failed-test-journals" + ops += [f"gh run download {id} --name {artifact} -D ci/{artifact}"] + journal_file = Path(f"ci/{artifact}/test/journal/{name}.journal") + + ops += [f"journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info"] + + print("Test failed, relevant logs can be viewed with: \n\n" + f"{(' && '.join(ops))}\n", file=sys.stderr) + + # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed. + exit(result.returncode or 1) + + +if __name__ == '__main__': + main() diff --git a/test/knot-data/knot.conf b/test/knot-data/knot.conf index b925812..b8b9e79 100644 --- a/test/knot-data/knot.conf +++ b/test/knot-data/knot.conf @@ -19,19 +19,27 @@ acl: address: fd00:dead:beef:cafe::/64 action: update + - id: transfer_acl + address: 10.0.0.0/24 + address: fd00:dead:beef:cafe::/64 + action: transfer + remote: - id: parent_zone_server address: 10.0.0.1@53 address: fd00:dead:beef:cafe::1@53 + - id: forwarded + address: 10.99.0.1@53 + submission: - id: parent_zone_sbm check-interval: 2s parent: [parent_zone_server] -# Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS -# records to the parent zone policy: + # Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS + # records to the parent zone - id: auto_rollover algorithm: ECDSAP256SHA256 cds-cdnskey-publish: always @@ -43,8 +51,7 @@ policy: zone-max-ttl: 1s zsk-lifetime: 60d -# Same as auto_rollover, but with NSEC3 turned on -policy: + # Same as auto_rollover, but with NSEC3 turned on - id: auto_rollover_nsec3 algorithm: ECDSAP256SHA256 cds-cdnskey-publish: always @@ -58,17 +65,20 @@ policy: zone-max-ttl: 1s zsk-lifetime: 60d -policy: - id: untrusted cds-cdnskey-publish: none -# Manual ZSK/KSK management -policy: + # Manual ZSK/KSK management - id: manual manual: on -# Sign everything by default and propagate the respective DS records to the parent +mod-dnsproxy: + - id: forwarded + remote: forwarded + fallback: off + template: + # Sign everything by default and propagate the respective DS records to the parent - id: default acl: update_acl dnssec-policy: auto_rollover @@ -77,14 +87,18 @@ template: semantic-checks: on storage: "/var/lib/knot/zones" -# A template for unsigned zones (i.e. without DNSSEC) -template: + # A template for unsigned zones (i.e. without DNSSEC) - id: unsigned dnssec-signing: off file: "%s.zone" semantic-checks: on storage: "/var/lib/knot/zones" + - id: forwarded + dnssec-signing: off + module: mod-dnsproxy/forwarded + zonefile-load: none + zone: # Create our own DNSSEC-aware root zone, so we can test the whole chain of # trust. This needs a ZSK/KSK keypair to be generated before running knot + @@ -98,8 +112,9 @@ zone: - domain: test dnssec-policy: auto_rollover_nsec3 - # A fully (pre-)signed zone + # A fully (pre-)signed zone with allowed zone transfers (AXFR/IXFR) - domain: signed.test + acl: [update_acl, transfer_acl] # A fully (online)-signed zone # See: https://www.knot-dns.cz/docs/3.1/singlehtml/index.html#mod-onlinesign @@ -117,3 +132,7 @@ zone: # An unsigned zone - domain: unsigned.test template: unsigned + + # Forward all queries for this zone to our dummy test server + - domain: forwarded.test + template: forwarded diff --git a/test/knot-data/zones/signed.test.zone b/test/knot-data/zones/signed.test.zone index a2baac4..5d75aa0 100644 --- a/test/knot-data/zones/signed.test.zone +++ b/test/knot-data/zones/signed.test.zone @@ -53,6 +53,7 @@ follow14.final A 10.0.0.14 myservice A 10.0.0.20 myservice AAAA fd00:dead:beef:cafe::17 _mysvc._tcp SRV 10 5 1234 myservice +_mysvc._tcp TXT "This is TXT for myservice" _invalidsvc._udp SRV 5 5 1111 invalidservice diff --git a/test/knot-data/zones/test.zone b/test/knot-data/zones/test.zone index ba5fceb..065ff7e 100644 --- a/test/knot-data/zones/test.zone +++ b/test/knot-data/zones/test.zone @@ -19,3 +19,6 @@ ns1.unsigned AAAA fd00:dead:beef:cafe::1 onlinesign NS ns1.unsigned signed NS ns1.unsigned unsigned NS ns1.unsigned + +svcb SVCB 1 . alpn=dot ipv4hint=10.0.0.1 ipv6hint=fd00:dead:beef:cafe::1 +https HTTPS 1 . alpn="h2,h3" diff --git a/test/meson.build b/test/meson.build index b47fa61..173d90c 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,65 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -if install_tests - foreach subdir : [ - 'auxv', - 'journal-data', - '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() does not handle symlinks correctly which causes a very ugly warning when - # installing, so if rsync is available we use that instead. TODO: switch to the new option - # 'follow_symlinks' once we require Meson 1.3.0 or greater, see: - # https://github.com/mesonbuild/meson/commit/0af126fec798d6dbb0d1ad52168cc1f3f1758acd - if rsync.found() - rsync_r = rsync.full_path() + ' -rlpt --exclude .gitattributes -- "@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', - install_dir : testdata_dir) - endif - endforeach - - 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', @@ -330,3 +270,230 @@ if want_tests != 'false' and conf.get('ENABLE_KERNEL_INSTALL') == 1 depends : deps, suite : 'kernel-install') endif + +############################################################ + +integration_test_wrapper = find_program('integration-test-wrapper.py') +integration_tests = [] +integration_test_template = { + 'mkosi-args' : [], + 'timeout' : 1800, + 'storage' : 'volatile', + 'priority' : 0, + 'firmware' : 'linux', + 'enabled' : true, + 'configuration' : { + 'memory-accounting' : 'no', + 'command' : testdata_dir / 'units/%N.sh', + 'wants' : 'multi-user.target user@4711.service', + 'after' : 'user@4711.service', + }, + 'cmdline' : [], + 'credentials' : [], + 'qemu-args' : [], + 'exit-code' : 123, + 'vm' : 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', + 'TEST-02-UNITTESTS', + 'TEST-03-JOBS', + 'TEST-04-JOURNAL', + 'TEST-05-RLIMITS', + 'TEST-06-SELINUX', + 'TEST-07-PID1', + 'TEST-08-INITRD', + 'TEST-09-REBOOT', + 'TEST-13-NSPAWN', + 'TEST-15-DROPIN', + 'TEST-16-EXTEND-TIMEOUT', + 'TEST-17-UDEV', + 'TEST-18-FAILUREACTION', + 'TEST-19-CGROUP', + 'TEST-21-DFUZZER', + 'TEST-22-TMPFILES', + 'TEST-23-UNIT-FILE', + 'TEST-24-CRYPTSETUP', + 'TEST-25-IMPORT', + 'TEST-26-SYSTEMCTL', + 'TEST-29-PORTABLE', + 'TEST-30-ONCLOCKCHANGE', + 'TEST-31-DEVICE-ENUMERATION', + 'TEST-32-OOMPOLICY', + 'TEST-34-DYNAMICUSERMIGRATE', + 'TEST-35-LOGIN', + 'TEST-36-NUMAPOLICY', + 'TEST-38-FREEZER', + 'TEST-43-PRIVATEUSER-UNPRIV', + 'TEST-44-LOG-NAMESPACE', + 'TEST-45-TIMEDATE', + 'TEST-46-HOMED', + 'TEST-50-DISSECT', + 'TEST-52-HONORFIRSTSHUTDOWN', + 'TEST-53-ISSUE-16347', + 'TEST-54-CREDS', + 'TEST-55-OOMD', + 'TEST-58-REPART', + 'TEST-59-RELOADING-RESTART', + 'TEST-60-MOUNT-RATELIMIT', + 'TEST-62-RESTRICT-IFACES', + 'TEST-63-PATH', + 'TEST-64-UDEV-STORAGE', + 'TEST-65-ANALYZE', + 'TEST-66-DEVICE-ISOLATION', + 'TEST-67-INTEGRITY', + 'TEST-68-PROPAGATE-EXIT-STATUS', + 'TEST-69-SHUTDOWN', + 'TEST-70-TPM2', + 'TEST-71-HOSTNAME', + 'TEST-72-SYSUPDATE', + 'TEST-73-LOCALE', + 'TEST-74-AUX-UTILS', + 'TEST-75-RESOLVED', + 'TEST-76-SYSCTL', + 'TEST-78-SIGQUEUE', + 'TEST-79-MEMPRESS', + 'TEST-80-NOTIFYACCESS', + 'TEST-81-GENERATORS', + 'TEST-82-SOFTREBOOT', + 'TEST-83-BTRFS', + 'TEST-84-STORAGETM', + 'TEST-85-NETWORK', +] + subdir(dirname) +endforeach + +foreach integration_test : integration_tests + integration_test_args = [ + '--meson-source-dir', meson.project_source_root(), + '--meson-build-dir', meson.project_build_root(), + '--name', integration_test['name'], + '--storage', integration_test['storage'], + '--firmware', integration_test['firmware'], + '--exit-code', integration_test['exit-code'].to_string(), + ] + + if 'unit' in integration_test + integration_test_unit = integration_test['unit'] + else + integration_test_unit = configure_file( + input : 'test.service.in', + output : '@0@.service'.format(integration_test['name']), + configuration : integration_test['configuration'], + ) + endif + + integration_test_args += ['--unit', fs.name(integration_test_unit)] + if install_tests + install_data(integration_test_unit, install_dir : testdata_dir / 'units') + endif + + if integration_test['vm'] + integration_test_args += ['--vm'] + endif + + if not mkosi.found() + continue + endif + + integration_test_args += ['--mkosi', mkosi.full_path(), '--'] + + if integration_test['cmdline'].length() > 0 + integration_test_args += [ + '--kernel-command-line-extra=@0@'.format(' '.join(integration_test['cmdline'])) + ] + endif + + foreach credential : integration_test['credentials'] + integration_test_args += ['--credential', credential] + endforeach + + if integration_test['qemu-args'].length() > 0 + integration_test_args += ['--qemu-args=@0@'.format(' '.join(integration_test['qemu-args']))] + endif + + integration_test_args += integration_test['mkosi-args'] + + integration_test_env = {} + + if want_integration_tests + integration_test_env += {'SYSTEMD_INTEGRATION_TESTS': '1'} + endif + + if not integration_test['enabled'] + continue + endif + + # We don't explicitly depend on the "mkosi" target because that means the image is rebuilt on every + # "ninja -C build". Instead, the mkosi target has to be rebuilt manually before running the + # integration tests with mkosi. + test( + integration_test['name'], + integration_test_wrapper, + env : integration_test_env, + args : integration_test_args, + timeout : integration_test['timeout'], + priority : integration_test['priority'], + 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 diff --git a/test/networkd-test.py b/test/networkd-test.py index 512137c..47a3ad7 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -888,8 +888,10 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase): set -eu mkdir -p /run/systemd/network mkdir -p /run/systemd/netif +mkdir -p /var/lib/systemd/network mount -t tmpfs none /run/systemd/network mount -t tmpfs none /run/systemd/netif +mount -t tmpfs none /var/lib/systemd/network [ ! -e /run/dbus ] || mount -t tmpfs none /run/dbus # create router/client veth pair cat <<EOF >/run/systemd/network/50-test.netdev @@ -917,6 +919,10 @@ DNS=192.168.5.1 {dhopts} EOF +# For the networkd instance invoked below cannot support varlink connection. +# Hence, 'networkctl persistent-storage yes' cannot be used. +export SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY=1 + # run networkd as in systemd-networkd.service exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=//; s/^[@+-]//; s/^!*//; p}}') '''.format(ifr=self.if_router, @@ -930,6 +936,7 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/ '-p', 'InaccessibleDirectories=-/etc/systemd/network', '-p', 'InaccessibleDirectories=-/run/systemd/network', '-p', 'InaccessibleDirectories=-/run/systemd/netif', + '-p', 'InaccessibleDirectories=-/var/lib/systemd/network', '--service-type=notify', script]) # wait until devices got created @@ -1017,17 +1024,19 @@ DNS=127.0.0.1 self.start_unit('systemd-resolved') self.start_unit('systemd-networkd') - for timeout in range(50): + subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', 'dummy0', '--timeout=10']) + + out = subprocess.check_output(['networkctl', 'status', 'dummy0']) + self.assertIn(b'50-test.network.d/dns.conf', out) + + for _ in range(50): with open(RESOLV_CONF) as f: contents = f.read() - if ' 127.0.0.1' in contents and '192.168.42.1' in contents: + if 'nameserver 127.0.0.1\n' in contents and 'nameserver 192.168.42.1\n' in contents: break time.sleep(0.1) - self.assertIn('nameserver 192.168.42.1\n', contents) - self.assertIn('nameserver 127.0.0.1\n', contents) - - out = subprocess.check_output(['networkctl', 'status', 'dummy0']) - self.assertIn(b'50-test.network.d/dns.conf', out) + else: + self.fail(f'Expected DNS servers not found in resolv.conf: {contents}') def test_dhcp_timezone(self): '''networkd sets time zone from DHCP''' @@ -1046,13 +1055,16 @@ DNS=127.0.0.1 self.create_iface(dhcpserver_opts='EmitTimezone=yes\nTimezone=Pacific/Honolulu') self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=false\n[DHCP]\nUseTimezone=true', dhcp_mode='ipv4') - # should have applied the received timezone - try: - self.assertEqual(get_tz(), 'Pacific/Honolulu') - except AssertionError: + # Should have applied the received timezone. This is asynchronous, so we need to wait for a while: + for _ in range(20): + tz = get_tz() + if tz == 'Pacific/Honolulu': + break + time.sleep(0.5) + else: self.show_journal('systemd-networkd.service') - self.show_journal('systemd-hostnamed.service') - raise + self.show_journal('systemd-timedated.service') + self.fail(f'Timezone: {tz}, expected: Pacific/Honolulu') class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities): diff --git a/test/run-integration-tests.sh b/test/run-integration-tests.sh index 2a2b075..e1c0c0a 100755 --- a/test/run-integration-tests.sh +++ b/test/run-integration-tests.sh @@ -2,130 +2,156 @@ # SPDX-License-Identifier: LGPL-2.1-or-later set -e -if [ "$NO_BUILD" ]; then +is_valid_target() { + local target="${1:?}" + local t + + for t in all setup run clean clean-again; do + [[ "$target" == "$t" ]] && return 0 + done + + return 1 +} + +pass_deny_list() { + local test="${1:?}" + local marker + + for marker in $DENY_LIST_MARKERS $BLACKLIST_MARKERS; do + if [[ -f "$test/$marker" ]]; then + echo "========== DENY-LISTED: $test ($marker) ==========" + return 1 + fi + done + + return 0 +} + +test_run() { + local test_name="${1:?}" + shift + + if [[ $# -eq 0 ]]; then + echo >&2 "test_run: missing arguments" + exit 1 + fi + + # Note: let's be very explicit in reporting the return code of the test command here, i.e don't rely on + # `set -e` or the return code of the last statement in the function, since reporting false positive + # would be very bad in this case. + if [[ "${SPLIT_TEST_LOGS:-0}" -ne 0 && -n "${ARTIFACT_DIRECTORY:-}" ]]; then + (set -x; "$@") &>>"$ARTIFACT_DIRECTORY/$test_name.log" || return $? + else + (set -x; "$@") || return $? + fi +} + +ARGS=(setup run clean-again) +CLEAN=0 +CLEAN_AGAIN=0 +COUNT=0 +FAILURES=0 +declare -A RESULTS +declare -A TIMES + +if [[ "${NO_BUILD:-0}" =~ ^(1|yes|true)$ ]]; then BUILD_DIR="" elif BUILD_DIR="$("$(dirname "$0")/../tools/find-build-dir.sh")"; then ninja -C "$BUILD_DIR" else - echo "No build found, please set BUILD_DIR or NO_BUILD" >&2 + echo >&2 "No build found, please set BUILD_DIR or NO_BUILD" exit 1 fi -if [ $# -gt 0 ]; then - args="$*" -else - args="setup run clean-again" +if [[ $# -gt 0 ]]; then + ARGS=("$@") fi -VALID_TARGETS="all setup run clean clean-again" - -is_valid_target() { - for target in $VALID_TARGETS; do - [ "$1" = "$target" ] && return 0 - done - return 1 -} - -# reject invalid make targets in $args -for arg in $args; do +# Reject invalid make targets +for arg in "${ARGS[@]}"; do if ! is_valid_target "$arg"; then - echo "Invalid target: $arg" >&2 + echo >&2 "Invalid target: $arg" exit 1 fi done -CLEAN=0 -CLEANAGAIN=0 - -# separate 'clean' and 'clean-again' operations -[[ "$args" =~ "clean-again" ]] && CLEANAGAIN=1 -args=${args/clean-again} -[[ "$args" =~ "clean" ]] && CLEAN=1 -args=${args/clean} - -declare -A results -declare -A times - -COUNT=0 -FAILURES=0 +# Separate 'clean' and 'clean-again' operations +args_filtered=() +for arg in "${ARGS[@]}"; do + if [[ "$arg" == "clean-again" ]]; then + CLEAN_AGAIN=1 + elif [[ "$arg" == "clean" ]]; then + CLEAN=1 + else + args_filtered+=("$arg") + fi +done +ARGS=("${args_filtered[@]}") cd "$(dirname "$0")" -pass_deny_list() { - for marker in $DENY_LIST_MARKERS $BLACKLIST_MARKERS; do - if [ -f "$1/$marker" ]; then - echo "========== DENY-LISTED: $1 ($marker) ==========" - return 1 - fi - done - return 0 -} - SELECTED_TESTS="${SELECTED_TESTS:-TEST-??-*}" # Let's always do the cleaning operation first, because it destroys the image # cache. -if [ $CLEAN = 1 ]; then - for TEST in $SELECTED_TESTS; do - ( set -x ; make -C "$TEST" clean ) +if [[ $CLEAN -eq 1 ]]; then + for test in $SELECTED_TESTS; do + test_run "$test" make -C "$test" clean done fi # Run actual tests (if requested) -if [[ $args =~ [a-z] ]]; then - for TEST in $SELECTED_TESTS; do - COUNT=$((COUNT+1)) +if [[ ${#ARGS[@]} -ne 0 ]]; then + for test in $SELECTED_TESTS; do + COUNT=$((COUNT + 1)) - pass_deny_list "$TEST" || continue - start=$(date +%s) + pass_deny_list "$test" || continue + SECONDS=0 - echo -e "\n[$(date +%R:%S)] --x-- Running $TEST --x--" + echo -e "\n[$(date +%R:%S)] --x-- Running $test --x--" set +e - # shellcheck disable=SC2086 - ( set -x ; make -C "$TEST" $args ) - RESULT=$? + test_run "$test" make -C "$test" "${ARGS[@]}" + result=$? set -e - echo "[$(date +%R:%S)] --x-- Result of $TEST: $RESULT --x--" - - results["$TEST"]="$RESULT" - times["$TEST"]=$(( $(date +%s) - start )) + echo "[$(date +%R:%S)] --x-- Result of $test: $result --x--" - [ "$RESULT" -ne "0" ] && FAILURES=$((FAILURES+1)) - done -fi + RESULTS["$test"]="$result" + TIMES["$test"]="$SECONDS" -# Run clean-again, if requested, and if no tests failed -if [[ $FAILURES -eq 0 && $CLEANAGAIN -eq 1 ]]; then - for TEST in "${!results[@]}"; do - ( set -x ; make -C "$TEST" clean-again ) + # Run clean-again here to free up space, if requested, and if the test succeeded + if [[ "$result" -ne 0 ]]; then + FAILURES=$((FAILURES + 1)) + elif [[ $CLEAN_AGAIN -eq 1 ]]; then + test_run "$test" make -C "$test" clean-again + fi done fi echo "" -for TEST in "${!results[@]}"; do - RESULT="${results[$TEST]}" - time="${times[$TEST]}" - string=$([ "$RESULT" = "0" ] && echo "SUCCESS" || echo "FAIL") - printf "%-35s %-8s (%3s s)\n" "${TEST}:" "${string}" "$time" +for test in "${!RESULTS[@]}"; do + result="${RESULTS[$test]}" + time="${TIMES[$test]}" + [[ "$result" -eq 0 ]] && string="SUCCESS" || string="FAIL" + printf "%-35s %-8s (%3s s)\n" "$test:" "$string" "$time" done | sort -if [ "$FAILURES" -eq 0 ] ; then +if [[ "$FAILURES" -eq 0 ]]; then echo -e "\nALL $COUNT TESTS PASSED" else echo -e "\nTOTAL FAILURES: $FAILURES OF $COUNT" fi # If we have coverage files, merge them into a single report for upload -if [ -n "${ARTIFACT_DIRECTORY}" ]; then +if [[ -n "$ARTIFACT_DIRECTORY" ]]; then lcov_args=() while read -r info_file; do - lcov_args+=(--add-tracefile "${info_file}") - done < <(find "${ARTIFACT_DIRECTORY}" -maxdepth 1 -name "*.coverage-info") + lcov_args+=(--add-tracefile "$info_file") + done < <(find "$ARTIFACT_DIRECTORY" -maxdepth 1 -name "*.coverage-info") - if [ ${#lcov_args[@]} -gt 1 ]; then - lcov "${lcov_args[@]}" --output-file "${ARTIFACT_DIRECTORY}/merged.coverage-info" + if [[ ${#lcov_args[@]} -gt 1 ]]; then + lcov "${lcov_args[@]}" --output-file "$ARTIFACT_DIRECTORY/merged.coverage-info" fi fi diff --git a/test/run-unit-tests.py b/test/run-unit-tests.py index e6f26c2..de8ac5c 100755 --- a/test/run-unit-tests.py +++ b/test/run-unit-tests.py @@ -28,6 +28,9 @@ def argument_parser(): help='run "unsafe" tests too') p.add_argument('-A', '--artifact_directory', help='store output from failed tests in this dir') + p.add_argument('-s', '--skip', action='append', default=[], + help='skip the named test') + return p opts = argument_parser().parse_args() @@ -42,9 +45,14 @@ if not opts.artifact_directory and os.getenv('ARTIFACT_DIRECTORY'): opts.artifact_directory = os.getenv('ARTIFACT_DIRECTORY') total.total = len(tests) -for test in tests: +for test in sorted(tests): name = os.path.basename(test) + if name in opts.skip: + print(f'{YELLOW}SKIP: {name} (by user) {RESET_ALL}') + total.skip += 1 + continue + ex = subprocess.run(test, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if ex.returncode == 0: print(f'{GREEN}PASS: {name}{RESET_ALL}') diff --git a/test/test-exec-deserialization.py b/test/test-exec-deserialization.py deleted file mode 100755 index f8f3a6d..0000000 --- a/test/test-exec-deserialization.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: LGPL-2.1-or-later -# -# Copyright © 2017 Michal Sekletar <msekleta@redhat.com> - -# ATTENTION: This uses the *installed* systemd, not the one from the built -# source tree. - -import os -import subprocess -import sys -import time -import unittest -import uuid -from enum import Enum - -class InstallChange(Enum): - NO_CHANGE = 0 - LINES_SWAPPED = 1 - COMMAND_ADDED_BEFORE = 2 - COMMAND_ADDED_AFTER = 3 - COMMAND_INTERLEAVED = 4 - REMOVAL = 5 - -class ExecutionResumeTest(unittest.TestCase): - def setUp(self): - self.unit = 'test-issue-518.service' - self.unitfile_path = f'/run/systemd/system/{self.unit}' - self.output_file = f"/tmp/test-issue-518-{uuid.uuid4()}" - self.unit_files = {} - - unit_file_content = f''' - [Service] - Type=oneshot - ExecStart=/bin/sleep 3 - ExecStart=/bin/bash -c "echo foo >>{self.output_file}" - ''' - self.unit_files[InstallChange.NO_CHANGE] = unit_file_content - - unit_file_content = f''' - [Service] - Type=oneshot - ExecStart=/bin/bash -c "echo foo >>{self.output_file}" - ExecStart=/bin/sleep 3 - ''' - self.unit_files[InstallChange.LINES_SWAPPED] = unit_file_content - - unit_file_content = f''' - [Service] - Type=oneshot - ExecStart=/bin/bash -c "echo bar >>{self.output_file}" - ExecStart=/bin/sleep 3 - ExecStart=/bin/bash -c "echo foo >>{self.output_file}" - ''' - self.unit_files[InstallChange.COMMAND_ADDED_BEFORE] = unit_file_content - - unit_file_content = f''' - [Service] - Type=oneshot - ExecStart=/bin/sleep 3 - ExecStart=/bin/bash -c "echo foo >>{self.output_file}" - ExecStart=/bin/bash -c "echo bar >>{self.output_file}" - ''' - self.unit_files[InstallChange.COMMAND_ADDED_AFTER] = unit_file_content - - unit_file_content = f''' - [Service] - Type=oneshot - ExecStart=/bin/bash -c "echo baz >>{self.output_file}" - ExecStart=/bin/sleep 3 - ExecStart=/bin/bash -c "echo foo >>{self.output_file}" - ExecStart=/bin/bash -c "echo bar >>{self.output_file}" - ''' - self.unit_files[InstallChange.COMMAND_INTERLEAVED] = unit_file_content - - unit_file_content = f''' - [Service] - Type=oneshot - ExecStart=/bin/bash -c "echo bar >>{self.output_file}" - ExecStart=/bin/bash -c "echo baz >>{self.output_file}" - ''' - self.unit_files[InstallChange.REMOVAL] = unit_file_content - - def reload(self): - subprocess.check_call(['systemctl', 'daemon-reload']) - - def write_unit_file(self, unit_file_change): - if not isinstance(unit_file_change, InstallChange): - raise ValueError('Unknown unit file change') - - content = self.unit_files[unit_file_change] - - with open(self.unitfile_path, 'w', encoding='utf-8') as f: - f.write(content) - - self.reload() - - def check_output(self, expected_output): - for _ in range(15): - # Wait until the unit finishes so we don't check an incomplete log - if subprocess.call(['systemctl', '-q', 'is-active', self.unit]) == 0: - continue - - os.sync() - - try: - with open(self.output_file, 'r', encoding='utf-8') as log: - output = log.read() - self.assertEqual(output, expected_output) - return - except IOError: - pass - - time.sleep(1) - - self.fail(f'Timed out while waiting for the output file {self.output_file} to appear') - - def setup_unit(self): - self.write_unit_file(InstallChange.NO_CHANGE) - subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', self.unit]) - time.sleep(1) - - def test_no_change(self): - expected_output = 'foo\n' - - self.setup_unit() - self.reload() - - self.check_output(expected_output) - - def test_swapped(self): - self.setup_unit() - self.write_unit_file(InstallChange.LINES_SWAPPED) - self.reload() - - self.assertTrue(not os.path.exists(self.output_file)) - - def test_added_before(self): - expected_output = 'foo\n' - - self.setup_unit() - self.write_unit_file(InstallChange.COMMAND_ADDED_BEFORE) - self.reload() - - self.check_output(expected_output) - - def test_added_after(self): - expected_output = 'foo\nbar\n' - - self.setup_unit() - self.write_unit_file(InstallChange.COMMAND_ADDED_AFTER) - self.reload() - - self.check_output(expected_output) - - def test_interleaved(self): - expected_output = 'foo\nbar\n' - - self.setup_unit() - self.write_unit_file(InstallChange.COMMAND_INTERLEAVED) - self.reload() - - self.check_output(expected_output) - - def test_removal(self): - self.setup_unit() - self.write_unit_file(InstallChange.REMOVAL) - self.reload() - - self.assertTrue(not os.path.exists(self.output_file)) - - def test_issue_6533(self): - unit = "test-issue-6533.service" - unitfile_path = f"/run/systemd/system/{unit}" - - content = ''' - [Service] - ExecStart=/bin/sleep 5 - ''' - - with open(unitfile_path, 'w', encoding='utf-8') as f: - f.write(content) - - self.reload() - - subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', unit]) - time.sleep(2) - - content = ''' - [Service] - ExecStart=/bin/sleep 5 - ExecStart=/bin/true - ''' - - with open(unitfile_path, 'w', encoding='utf-8') as f: - f.write(content) - - self.reload() - time.sleep(5) - - self.assertNotEqual(subprocess.call("journalctl -b _PID=1 | grep -q 'Freezing execution'", shell=True), 0) - - def tearDown(self): - for f in [self.output_file, self.unitfile_path]: - try: - os.remove(f) - except OSError: - # ignore error if log file doesn't exist - pass - - self.reload() - -if __name__ == '__main__': - unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3)) diff --git a/test/test-execute/exec-ambientcapabilities-dynuser.service b/test/test-execute/exec-ambientcapabilities-dynuser.service index 560628e..ab815f3 100644 --- a/test/test-execute/exec-ambientcapabilities-dynuser.service +++ b/test/test-execute/exec-ambientcapabilities-dynuser.service @@ -3,8 +3,9 @@ Description=Test for AmbientCapabilities (dynamic user) [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002081"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002081"' Type=oneshot AmbientCapabilities=CAP_CHOWN CAP_SETUID CAP_NET_RAW DynamicUser=yes PrivateUsers=yes +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service b/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service index 4960da5..a170b3d 100644 --- a/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service +++ b/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service @@ -3,7 +3,7 @@ Description=Test for AmbientCapabilities [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' Type=oneshot User=nfsnobody AmbientCapabilities=CAP_CHOWN diff --git a/test/test-execute/exec-ambientcapabilities-merge-nobody.service b/test/test-execute/exec-ambientcapabilities-merge-nobody.service index 4c72b2e..2e21bbc 100644 --- a/test/test-execute/exec-ambientcapabilities-merge-nobody.service +++ b/test/test-execute/exec-ambientcapabilities-merge-nobody.service @@ -3,7 +3,7 @@ Description=Test for AmbientCapabilities [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' Type=oneshot User=nobody AmbientCapabilities=CAP_CHOWN diff --git a/test/test-execute/exec-ambientcapabilities-merge.service b/test/test-execute/exec-ambientcapabilities-merge.service index 13a5d45..c4bb21b 100644 --- a/test/test-execute/exec-ambientcapabilities-merge.service +++ b/test/test-execute/exec-ambientcapabilities-merge.service @@ -3,7 +3,7 @@ Description=Test for AmbientCapabilities (daemon) [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' Type=oneshot User=daemon AmbientCapabilities=CAP_CHOWN diff --git a/test/test-execute/exec-ambientcapabilities-nfsnobody.service b/test/test-execute/exec-ambientcapabilities-nfsnobody.service index 10cb440..0bf91cc 100644 --- a/test/test-execute/exec-ambientcapabilities-nfsnobody.service +++ b/test/test-execute/exec-ambientcapabilities-nfsnobody.service @@ -3,7 +3,7 @@ Description=Test for AmbientCapabilities [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' Type=oneshot User=nfsnobody AmbientCapabilities=CAP_CHOWN CAP_NET_RAW diff --git a/test/test-execute/exec-ambientcapabilities-nobody.service b/test/test-execute/exec-ambientcapabilities-nobody.service index 5400cac..8bd7ac4 100644 --- a/test/test-execute/exec-ambientcapabilities-nobody.service +++ b/test/test-execute/exec-ambientcapabilities-nobody.service @@ -3,7 +3,7 @@ Description=Test for AmbientCapabilities [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' Type=oneshot User=nobody AmbientCapabilities=CAP_CHOWN CAP_NET_RAW diff --git a/test/test-execute/exec-ambientcapabilities.service b/test/test-execute/exec-ambientcapabilities.service index 5336bec..1bbc727 100644 --- a/test/test-execute/exec-ambientcapabilities.service +++ b/test/test-execute/exec-ambientcapabilities.service @@ -3,7 +3,7 @@ Description=Test for AmbientCapabilities (daemon) [Service] -ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' +ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"' Type=oneshot User=daemon AmbientCapabilities=CAP_CHOWN CAP_NET_RAW diff --git a/test/test-execute/exec-bindpaths.service b/test/test-execute/exec-bindpaths.service index bf6968f..12e92e2 100644 --- a/test/test-execute/exec-bindpaths.service +++ b/test/test-execute/exec-bindpaths.service @@ -11,7 +11,7 @@ ExecStart=test -f /tmp/thisisasimpletest # Also, through /tmp/test-exec-bindreadonlypaths ExecStart=test -f /tmp/test-exec-bindreadonlypaths/thisisasimpletest # The file cannot modify through /tmp/test-exec-bindreadonlypaths -ExecStart=/bin/sh -x -c '! touch /tmp/test-exec-bindreadonlypaths/thisisasimpletest' +ExecStart=sh -x -c '! touch /tmp/test-exec-bindreadonlypaths/thisisasimpletest' # Cleanup ExecStart=rm /tmp/thisisasimpletest BindPaths=/tmp:/tmp/test-exec-bindpaths diff --git a/test/test-execute/exec-capabilityboundingset-invert.service b/test/test-execute/exec-capabilityboundingset-invert.service index 1b1217e..14f16c6 100644 --- a/test/test-execute/exec-capabilityboundingset-invert.service +++ b/test/test-execute/exec-capabilityboundingset-invert.service @@ -4,6 +4,6 @@ Description=Test for CapabilityBoundingSet [Service] # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"' +ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"' Type=oneshot CapabilityBoundingSet=~CAP_CHOWN diff --git a/test/test-execute/exec-capabilityboundingset-merge.service b/test/test-execute/exec-capabilityboundingset-merge.service index 1ed3ccb..d3a2370 100644 --- a/test/test-execute/exec-capabilityboundingset-merge.service +++ b/test/test-execute/exec-capabilityboundingset-merge.service @@ -3,7 +3,7 @@ Description=Test for CapabilityBoundingSet [Service] -ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_chown,cap_fowner,cap_kill"' +ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_chown,cap_fowner,cap_kill"' Type=oneshot CapabilityBoundingSet=CAP_FOWNER CapabilityBoundingSet=CAP_KILL CAP_CHOWN diff --git a/test/test-execute/exec-capabilityboundingset-reset.service b/test/test-execute/exec-capabilityboundingset-reset.service index 8eb142c..2443951 100644 --- a/test/test-execute/exec-capabilityboundingset-reset.service +++ b/test/test-execute/exec-capabilityboundingset-reset.service @@ -3,7 +3,7 @@ Description=Test for CapabilityBoundingSet [Service] -ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set ="' +ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set ="' Type=oneshot CapabilityBoundingSet=CAP_FOWNER CAP_KILL CapabilityBoundingSet= diff --git a/test/test-execute/exec-capabilityboundingset-simple.service b/test/test-execute/exec-capabilityboundingset-simple.service index be5a5e5..3df3e6d 100644 --- a/test/test-execute/exec-capabilityboundingset-simple.service +++ b/test/test-execute/exec-capabilityboundingset-simple.service @@ -3,6 +3,6 @@ Description=Test for CapabilityBoundingSet [Service] -ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_fowner,cap_kill"' +ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_fowner,cap_kill"' Type=oneshot CapabilityBoundingSet=CAP_FOWNER CAP_KILL diff --git a/test/test-execute/exec-condition-failed.service b/test/test-execute/exec-condition-failed.service index 342219c..eb136ff 100644 --- a/test/test-execute/exec-condition-failed.service +++ b/test/test-execute/exec-condition-failed.service @@ -9,4 +9,4 @@ Type=oneshot ExecCondition=/bin/sh -c 'exit 255' # This should not get run -ExecStart=/bin/sh -c 'true' +ExecStart=sh -c 'true' diff --git a/test/test-execute/exec-condition-skip.service b/test/test-execute/exec-condition-skip.service index b69e161..4ee58b9 100644 --- a/test/test-execute/exec-condition-skip.service +++ b/test/test-execute/exec-condition-skip.service @@ -13,4 +13,4 @@ ExecCondition=/bin/sh -c 'exit 254' ExecCondition=/bin/sh -c 'exit 255' # This should not get run -ExecStart=/bin/sh -c 'true' +ExecStart=sh -c 'true' diff --git a/test/test-execute/exec-cpuaffinity1.service b/test/test-execute/exec-cpuaffinity1.service index 2a8544a..c0941a5 100644 --- a/test/test-execute/exec-cpuaffinity1.service +++ b/test/test-execute/exec-cpuaffinity1.service @@ -3,5 +3,5 @@ Description=Test for CPUAffinity (simple) [Service] -ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1' +ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1' CPUAffinity=0 diff --git a/test/test-execute/exec-cpuaffinity2.service b/test/test-execute/exec-cpuaffinity2.service index bed48c8..d699ecc 100644 --- a/test/test-execute/exec-cpuaffinity2.service +++ b/test/test-execute/exec-cpuaffinity2.service @@ -3,7 +3,7 @@ Description=Test for CPUAffinity (reset) [Service] -ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1' +ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1' CPUAffinity=0-1 3 CPUAffinity= CPUAffinity=0 diff --git a/test/test-execute/exec-cpuaffinity3.service b/test/test-execute/exec-cpuaffinity3.service index 774cd64..8e8f782 100644 --- a/test/test-execute/exec-cpuaffinity3.service +++ b/test/test-execute/exec-cpuaffinity3.service @@ -3,6 +3,6 @@ Description=Test for CPUAffinity (merge) [Service] -ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 7' +ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 7' CPUAffinity=0,1 CPUAffinity=1-2 diff --git a/test/test-execute/exec-dynamicuser-fixeduser-adm.service b/test/test-execute/exec-dynamicuser-fixeduser-adm.service index daaed6c..1b7f232 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser-adm.service +++ b/test/test-execute/exec-dynamicuser-fixeduser-adm.service @@ -5,8 +5,8 @@ Description=Test DynamicUser with static User= whose uid and gid are different [Service] Type=oneshot -ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"' +ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"' # Multiple ExecStart= lines causes the issue #9702. -ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"' +ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"' DynamicUser=yes User=adm diff --git a/test/test-execute/exec-dynamicuser-fixeduser-games.service b/test/test-execute/exec-dynamicuser-fixeduser-games.service index db8b88e..b13c23a 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser-games.service +++ b/test/test-execute/exec-dynamicuser-fixeduser-games.service @@ -5,8 +5,8 @@ Description=Test DynamicUser with static User= whose uid and gid are different [Service] Type=oneshot -ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"' +ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"' # Multiple ExecStart= lines causes the issue #9702. -ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"' +ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"' DynamicUser=yes User=games diff --git a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service index bbb1af5..e494c33 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service +++ b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service @@ -3,8 +3,8 @@ Description=Test DynamicUser with User= and SupplementaryGroups= [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' Type=oneshot User=1 DynamicUser=yes diff --git a/test/test-execute/exec-dynamicuser-fixeduser.service b/test/test-execute/exec-dynamicuser-fixeduser.service index c5828c2..4ebfc20 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser.service +++ b/test/test-execute/exec-dynamicuser-fixeduser.service @@ -3,8 +3,8 @@ Description=Test DynamicUser with User= [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' Type=oneshot User=1 DynamicUser=yes diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory1.service b/test/test-execute/exec-dynamicuser-runtimedirectory1.service index 790279a..59d3bf0 100644 --- a/test/test-execute/exec-dynamicuser-runtimedirectory1.service +++ b/test/test-execute/exec-dynamicuser-runtimedirectory1.service @@ -3,10 +3,11 @@ Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes [Service] -ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve' -ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"' -ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test' +ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve' +ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"' +ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test' Type=oneshot RuntimeDirectory=test-exec_runtimedirectorypreserve RuntimeDirectoryPreserve=yes DynamicUser=yes +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory2.service b/test/test-execute/exec-dynamicuser-runtimedirectory2.service index 18df74e..6ff9d75 100644 --- a/test/test-execute/exec-dynamicuser-runtimedirectory2.service +++ b/test/test-execute/exec-dynamicuser-runtimedirectory2.service @@ -3,11 +3,12 @@ Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes 2nd trial [Service] -ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve' -ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"' -ExecStart=/bin/sh -x -c 'test -f $$RUNTIME_DIRECTORY/test' -ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test' +ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve' +ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"' +ExecStart=sh -x -c 'test -f $$RUNTIME_DIRECTORY/test' +ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test' Type=oneshot RuntimeDirectory=test-exec_runtimedirectorypreserve RuntimeDirectoryPreserve=yes DynamicUser=yes +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory3.service b/test/test-execute/exec-dynamicuser-runtimedirectory3.service index 831a808..cebb819 100644 --- a/test/test-execute/exec-dynamicuser-runtimedirectory3.service +++ b/test/test-execute/exec-dynamicuser-runtimedirectory3.service @@ -3,10 +3,11 @@ Description=Test for RuntimeDirectory with DynamicUser=yes migrated from RuntimeDirectoryPreserve=yes [Service] -ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve' -ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"' -ExecStart=/bin/sh -x -c 'test -f $$RUNTIME_DIRECTORY/test' -ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test' +ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve' +ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"' +ExecStart=sh -x -c 'test -f $$RUNTIME_DIRECTORY/test' +ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test' Type=oneshot RuntimeDirectory=test-exec_runtimedirectorypreserve DynamicUser=yes +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service index 2a5a1e1..12375af 100644 --- a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service +++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service @@ -11,7 +11,7 @@ ExecStart=test -d %S/test-dynamicuser-migrate ExecStart=test -d %S/test-dynamicuser-migrate2/hoge ExecStart=touch %S/test-dynamicuser-migrate/yay ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay -ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"' +ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"' Type=oneshot DynamicUser=no diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service index e89f0c5..7261f4a 100644 --- a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service +++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service @@ -19,8 +19,9 @@ ExecStart=touch %S/test-dynamicuser-migrate/yay ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay ExecStart=touch %S/private/test-dynamicuser-migrate/yay ExecStart=touch %S/private/test-dynamicuser-migrate2/hoge/yayyay -ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"' +ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"' Type=oneshot DynamicUser=yes StateDirectory=test-dynamicuser-migrate test-dynamicuser-migrate2/hoge +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-dynamicuser-statedir.service b/test/test-execute/exec-dynamicuser-statedir.service index 734fa20..636a702 100644 --- a/test/test-execute/exec-dynamicuser-statedir.service +++ b/test/test-execute/exec-dynamicuser-statedir.service @@ -83,3 +83,4 @@ ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/aaa:%S/aaa/bbb:%S/aaa/ccc:%S/ Type=oneshot DynamicUser=yes StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333 abc:d\:ef +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-dynamicuser-supplementarygroups.service b/test/test-execute/exec-dynamicuser-supplementarygroups.service index d601af2..be1b8f7 100644 --- a/test/test-execute/exec-dynamicuser-supplementarygroups.service +++ b/test/test-execute/exec-dynamicuser-supplementarygroups.service @@ -3,8 +3,9 @@ Description=Test DynamicUser with SupplementaryGroups= [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' Type=oneshot DynamicUser=yes SupplementaryGroups=1 2 +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env diff --git a/test/test-execute/exec-environment-empty.service b/test/test-execute/exec-environment-empty.service index 6c31186..e5af6ff 100644 --- a/test/test-execute/exec-environment-empty.service +++ b/test/test-execute/exec-environment-empty.service @@ -3,7 +3,7 @@ Description=Test for Environment [Service] -ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset"' +ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset"' Type=oneshot Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" Environment= diff --git a/test/test-execute/exec-environment-multiple.service b/test/test-execute/exec-environment-multiple.service index d9b8d22..4199a46 100644 --- a/test/test-execute/exec-environment-multiple.service +++ b/test/test-execute/exec-environment-multiple.service @@ -3,7 +3,7 @@ Description=Test for Environment [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = foobar' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = foobar' Type=oneshot Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" Environment="VAR3=foobar" diff --git a/test/test-execute/exec-environment-no-substitute.service b/test/test-execute/exec-environment-no-substitute.service index b5cb2a4..7396576 100644 --- a/test/test-execute/exec-environment-no-substitute.service +++ b/test/test-execute/exec-environment-no-substitute.service @@ -3,7 +3,7 @@ Description=Test for No Environment Variable Substitution [Service] -ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\'' +ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\'' ExecStart=:/bin/sh -x -c 'test "$${VAR1-unset}" != "unset" && test "$${VAR2}" != "word3" && test "$${VAR3-unset}" != \'$word 5 6\'' Type=oneshot Environment="VAR2=word3" "VAR3=$word 5 6" diff --git a/test/test-execute/exec-environment.service b/test/test-execute/exec-environment.service index 5655be0..7e3cb0e 100644 --- a/test/test-execute/exec-environment.service +++ b/test/test-execute/exec-environment.service @@ -3,6 +3,6 @@ Description=Test for Environment [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6"' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6"' Type=oneshot Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" diff --git a/test/test-execute/exec-environmentfile.service b/test/test-execute/exec-environmentfile.service index 4ad5a9b..3f739fa 100644 --- a/test/test-execute/exec-environmentfile.service +++ b/test/test-execute/exec-environmentfile.service @@ -3,6 +3,6 @@ Description=Test for EnvironmentFile [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes' Type=oneshot EnvironmentFile=/tmp/test-exec_environmentfile.conf diff --git a/test/test-execute/exec-execsearchpath-environment-path-set.service b/test/test-execute/exec-execsearchpath-environment-path-set.service index 5969cc6..424c4ac 100644 --- a/test/test-execute/exec-execsearchpath-environment-path-set.service +++ b/test/test-execute/exec-execsearchpath-environment-path-set.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"' +ExecStart=sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"' Type=oneshot ExecSearchPath=/tmp:/bin Environment="PATH=/usr" VAR1=word3 "VAR2=$word 5 6" diff --git a/test/test-execute/exec-execsearchpath-environment.service b/test/test-execute/exec-execsearchpath-environment.service index b0fa6a3..5c39d9c 100644 --- a/test/test-execute/exec-execsearchpath-environment.service +++ b/test/test-execute/exec-execsearchpath-environment.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"' Type=oneshot ExecSearchPath=/tmp:/bin Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" diff --git a/test/test-execute/exec-execsearchpath-environmentfile-set.service b/test/test-execute/exec-execsearchpath-environmentfile-set.service index 5f55a4b..8741582 100644 --- a/test/test-execute/exec-execsearchpath-environmentfile-set.service +++ b/test/test-execute/exec-execsearchpath-environmentfile-set.service @@ -3,7 +3,7 @@ Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile sets PATH [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = /usr' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = /usr' Type=oneshot EnvironmentFile=/tmp/test-exec_execsearchpath_environmentfile-set.conf ExecSearchPath=/tmp:/bin diff --git a/test/test-execute/exec-execsearchpath-environmentfile.service b/test/test-execute/exec-execsearchpath-environmentfile.service index b8335bc..53cede8 100644 --- a/test/test-execute/exec-execsearchpath-environmentfile.service +++ b/test/test-execute/exec-execsearchpath-environmentfile.service @@ -3,7 +3,7 @@ Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile does not set PATH [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"' Type=oneshot ExecSearchPath=/tmp:/bin EnvironmentFile=/tmp/test-exec_execsearchpath_environmentfile.conf diff --git a/test/test-execute/exec-execsearchpath-passenvironment-set.service b/test/test-execute/exec-execsearchpath-passenvironment-set.service index a151161..2d4e75a 100644 --- a/test/test-execute/exec-execsearchpath-passenvironment-set.service +++ b/test/test-execute/exec-execsearchpath-passenvironment-set.service @@ -3,7 +3,7 @@ Description=Test for PassEnvironment with ExecSearchPath with PATH set by user [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/usr"' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/usr"' Type=oneshot PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 PATH ExecSearchPath=/tmp:/bin diff --git a/test/test-execute/exec-execsearchpath-passenvironment.service b/test/test-execute/exec-execsearchpath-passenvironment.service index d8a41c1..5bdab47 100644 --- a/test/test-execute/exec-execsearchpath-passenvironment.service +++ b/test/test-execute/exec-execsearchpath-passenvironment.service @@ -3,7 +3,7 @@ Description=Test for PassEnvironment with ExecSearchPath with PATH not set by user [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"' Type=oneshot PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 ExecSearchPath=/tmp:/bin diff --git a/test/test-execute/exec-execsearchpath-unit-specifier.service b/test/test-execute/exec-execsearchpath-unit-specifier.service index 30d6b32..a2037e9 100644 --- a/test/test-execute/exec-execsearchpath-unit-specifier.service +++ b/test/test-execute/exec-execsearchpath-unit-specifier.service @@ -5,4 +5,4 @@ Description=Test for specifiers with exec search path [Service] Type=oneshot ExecSearchPath=/tmp:/bin:/usr/bin:%V -ExecStart=/bin/sh -x -c 'test %V = /var/tmp && test "$$PATH" = "/tmp:/bin:/usr/bin:/var/tmp"' +ExecStart=sh -x -c 'test %V = /var/tmp && test "$$PATH" = "/tmp:/bin:/usr/bin:/var/tmp"' diff --git a/test/test-execute/exec-group-nfsnobody.service b/test/test-execute/exec-group-nfsnobody.service index a1e59c5..aebb198 100644 --- a/test/test-execute/exec-group-nfsnobody.service +++ b/test/test-execute/exec-group-nfsnobody.service @@ -3,6 +3,6 @@ Description=Test for Group [Service] -ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nfsnobody"' +ExecStart=sh -x -c 'test "$$(id -n -g)" = "nfsnobody"' Type=oneshot Group=nfsnobody diff --git a/test/test-execute/exec-group-nobody.service b/test/test-execute/exec-group-nobody.service index 58dce1e..cf283cb 100644 --- a/test/test-execute/exec-group-nobody.service +++ b/test/test-execute/exec-group-nobody.service @@ -3,6 +3,6 @@ Description=Test for Group [Service] -ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nobody"' +ExecStart=sh -x -c 'test "$$(id -n -g)" = "nobody"' Type=oneshot Group=nobody diff --git a/test/test-execute/exec-group-nogroup.service b/test/test-execute/exec-group-nogroup.service index 7f16729..46c3dd3 100644 --- a/test/test-execute/exec-group-nogroup.service +++ b/test/test-execute/exec-group-nogroup.service @@ -3,6 +3,6 @@ Description=Test for Group [Service] -ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nogroup"' +ExecStart=sh -x -c 'test "$$(id -n -g)" = "nogroup"' Type=oneshot Group=nogroup diff --git a/test/test-execute/exec-group.service b/test/test-execute/exec-group.service index 9f21557..bd5ac2d 100644 --- a/test/test-execute/exec-group.service +++ b/test/test-execute/exec-group.service @@ -3,6 +3,6 @@ Description=Test for Group (daemon) [Service] -ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "daemon"' +ExecStart=sh -x -c 'test "$$(id -n -g)" = "daemon"' Type=oneshot Group=daemon diff --git a/test/test-execute/exec-ignoresigpipe-no.service b/test/test-execute/exec-ignoresigpipe-no.service index e972481..ce8b258 100644 --- a/test/test-execute/exec-ignoresigpipe-no.service +++ b/test/test-execute/exec-ignoresigpipe-no.service @@ -3,6 +3,6 @@ Description=Test for IgnoreSIGPIPE=no [Service] -ExecStart=/bin/sh -x -c 'kill -PIPE 0' +ExecStart=sh -x -c 'kill -PIPE 0' Type=oneshot IgnoreSIGPIPE=no diff --git a/test/test-execute/exec-ignoresigpipe-yes.service b/test/test-execute/exec-ignoresigpipe-yes.service index ee3aa9a..a26f53c 100644 --- a/test/test-execute/exec-ignoresigpipe-yes.service +++ b/test/test-execute/exec-ignoresigpipe-yes.service @@ -3,6 +3,6 @@ Description=Test for IgnoreSIGPIPE=yes [Service] -ExecStart=/bin/sh -x -c 'kill -PIPE 0' +ExecStart=sh -x -c 'kill -PIPE 0' Type=oneshot IgnoreSIGPIPE=yes diff --git a/test/test-execute/exec-inaccessiblepaths-mount-propagation.service b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service index 520bc53..8580f52 100644 --- a/test/test-execute/exec-inaccessiblepaths-mount-propagation.service +++ b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service @@ -4,5 +4,5 @@ Description=Test to make sure that InaccessiblePaths= disconnect mount propagati [Service] InaccessiblePaths=-/i-dont-exist -ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' +ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' Type=oneshot diff --git a/test/test-execute/exec-inaccessiblepaths-sys.service b/test/test-execute/exec-inaccessiblepaths-sys.service index 0d64aa1..64a570c 100644 --- a/test/test-execute/exec-inaccessiblepaths-sys.service +++ b/test/test-execute/exec-inaccessiblepaths-sys.service @@ -4,5 +4,5 @@ Description=Test to make sure that mount namespace setup works properly with the [Service] InaccessiblePaths=/sys -ExecStart=/bin/sh -x -c 'test "$$(stat -c %%a /sys)" = "0"' +ExecStart=sh -x -c 'test "$$(stat -c %%a /sys)" = "0"' Type=oneshot diff --git a/test/test-execute/exec-ioschedulingclass-best-effort.service b/test/test-execute/exec-ioschedulingclass-best-effort.service index 3b946b7..569183f 100644 --- a/test/test-execute/exec-ioschedulingclass-best-effort.service +++ b/test/test-execute/exec-ioschedulingclass-best-effort.service @@ -3,6 +3,6 @@ Description=Test for IOSchedulingClass=best-effort [Service] -ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "best-effort"' +ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "best-effort"' Type=oneshot IOSchedulingClass=best-effort diff --git a/test/test-execute/exec-ioschedulingclass-idle.service b/test/test-execute/exec-ioschedulingclass-idle.service index b1e64bb..93377ea 100644 --- a/test/test-execute/exec-ioschedulingclass-idle.service +++ b/test/test-execute/exec-ioschedulingclass-idle.service @@ -3,6 +3,6 @@ Description=Test for IOSchedulingClass=idle [Service] -ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "idle"' +ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "idle"' Type=oneshot IOSchedulingClass=idle diff --git a/test/test-execute/exec-ioschedulingclass-none.service b/test/test-execute/exec-ioschedulingclass-none.service index 0494d45..b8198d1 100644 --- a/test/test-execute/exec-ioschedulingclass-none.service +++ b/test/test-execute/exec-ioschedulingclass-none.service @@ -4,6 +4,6 @@ Description=Test for IOSchedulingClass=none [Service] # Old kernels might report "none" here, new kernels "best-effort". -ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"' +ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"' Type=oneshot IOSchedulingClass=none diff --git a/test/test-execute/exec-ioschedulingclass-realtime.service b/test/test-execute/exec-ioschedulingclass-realtime.service index ef8e2eb..a7edb6d 100644 --- a/test/test-execute/exec-ioschedulingclass-realtime.service +++ b/test/test-execute/exec-ioschedulingclass-realtime.service @@ -3,6 +3,6 @@ Description=Test for IOSchedulingClass=realtime [Service] -ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "realtime"' +ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "realtime"' Type=oneshot IOSchedulingClass=realtime diff --git a/test/test-execute/exec-load-credential.service b/test/test-execute/exec-load-credential.service index 3a29b6d..9da19e6 100644 --- a/test/test-execute/exec-load-credential.service +++ b/test/test-execute/exec-load-credential.service @@ -3,9 +3,9 @@ Description=Test for LoadCredential= [Service] -ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' -ExecStartPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' -ExecStop=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' -ExecStopPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' +ExecStart=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' +ExecStartPost=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' +ExecStop=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' +ExecStopPost=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"' Type=oneshot LoadCredential=test-execute.load-credential diff --git a/test/test-execute/exec-networknamespacepath-privatemounts-no.service b/test/test-execute/exec-networknamespacepath-privatemounts-no.service index 49277e3..07c0525 100644 --- a/test/test-execute/exec-networknamespacepath-privatemounts-no.service +++ b/test/test-execute/exec-networknamespacepath-privatemounts-no.service @@ -3,14 +3,14 @@ Description=Test for NetworkNamespacePath= without mount namespacing [Service] -ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec' -ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' +ExecStart=sh -x -c '! ip link show dummy-test-exec' +ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' # Without mount namespacing, we can access the dummy-test-exec interface through sysfs. -ExecStart=/bin/sh -x -c 'test -e /sys/class/net/dummy-test-exec' -ExecStart=/bin/sh -x -c 'ip link show dummy-test-ns' -ExecStart=/bin/sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns' +ExecStart=sh -x -c 'test -e /sys/class/net/dummy-test-exec' +ExecStart=sh -x -c 'ip link show dummy-test-ns' +ExecStart=sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns' # Without mount namespacing, we cannot access the dummy-test-ns interface through sysfs. -ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-ns' +ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-ns' Type=oneshot NetworkNamespacePath=/run/netns/test-execute-netns PrivateMounts=no diff --git a/test/test-execute/exec-networknamespacepath-privatemounts-yes.service b/test/test-execute/exec-networknamespacepath-privatemounts-yes.service index 078fba8..10bc192 100644 --- a/test/test-execute/exec-networknamespacepath-privatemounts-yes.service +++ b/test/test-execute/exec-networknamespacepath-privatemounts-yes.service @@ -3,14 +3,14 @@ Description=Test for NetworkNamespacePath= with mount namespacing [Service] -ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec' -ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' +ExecStart=sh -x -c '! ip link show dummy-test-exec' +ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' # With mount namespacing, we cannot access the dummy-test-exec interface through sysfs. -ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-exec' -ExecStart=/bin/sh -x -c 'ip link show dummy-test-ns' -ExecStart=/bin/sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns' +ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-exec' +ExecStart=sh -x -c 'ip link show dummy-test-ns' +ExecStart=sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns' # With mount namespacing, we can access the dummy-test-ns interface through sysfs. -ExecStart=/bin/sh -x -c 'test -e /sys/class/net/dummy-test-ns' +ExecStart=sh -x -c 'test -e /sys/class/net/dummy-test-ns' Type=oneshot NetworkNamespacePath=/run/netns/test-execute-netns # NetworkNamespacePath= implies PrivateMounts=yes diff --git a/test/test-execute/exec-noexecpaths-simple.service b/test/test-execute/exec-noexecpaths-simple.service index 5d954da..503be5a 100644 --- a/test/test-execute/exec-noexecpaths-simple.service +++ b/test/test-execute/exec-noexecpaths-simple.service @@ -7,5 +7,5 @@ Type=oneshot # This should work, as we explicitly disable the effect of NoExecPaths= ExecStart=+/bin/sh -c '/bin/cat /dev/null' # This should also work, as we do not disable the effect of NoExecPaths= but invert the exit code -ExecStart=/bin/sh -x -c '! /bin/cat /dev/null' +ExecStart=sh -x -c '! /bin/cat /dev/null' NoExecPaths=/bin/cat diff --git a/test/test-execute/exec-oomscoreadjust-negative.service b/test/test-execute/exec-oomscoreadjust-negative.service index 25b5f1f..5656030 100644 --- a/test/test-execute/exec-oomscoreadjust-negative.service +++ b/test/test-execute/exec-oomscoreadjust-negative.service @@ -3,6 +3,6 @@ Description=Test for OOMScoreAdjust [Service] -ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq -100' +ExecStart=sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq -100' Type=oneshot OOMScoreAdjust=-100 diff --git a/test/test-execute/exec-oomscoreadjust-positive.service b/test/test-execute/exec-oomscoreadjust-positive.service index ea6c23f..a2079b8 100644 --- a/test/test-execute/exec-oomscoreadjust-positive.service +++ b/test/test-execute/exec-oomscoreadjust-positive.service @@ -3,6 +3,6 @@ Description=Test for OOMScoreAdjust [Service] -ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq 100' +ExecStart=sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq 100' Type=oneshot OOMScoreAdjust=100 diff --git a/test/test-execute/exec-passenvironment-absent.service b/test/test-execute/exec-passenvironment-absent.service index 6b19a12..b2e5c20 100644 --- a/test/test-execute/exec-passenvironment-absent.service +++ b/test/test-execute/exec-passenvironment-absent.service @@ -3,6 +3,6 @@ Description=Test for PassEnvironment with variables absent from the execution environment [Service] -ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"' +ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"' Type=oneshot PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 diff --git a/test/test-execute/exec-passenvironment-empty.service b/test/test-execute/exec-passenvironment-empty.service index 6ffc5e7..a5fd092 100644 --- a/test/test-execute/exec-passenvironment-empty.service +++ b/test/test-execute/exec-passenvironment-empty.service @@ -3,7 +3,7 @@ Description=Test for PassEnvironment and erasing the variable list [Service] -ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"' +ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"' Type=oneshot PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 PassEnvironment= diff --git a/test/test-execute/exec-passenvironment-repeated.service b/test/test-execute/exec-passenvironment-repeated.service index b8e904f..f3b886c 100644 --- a/test/test-execute/exec-passenvironment-repeated.service +++ b/test/test-execute/exec-passenvironment-repeated.service @@ -3,7 +3,7 @@ Description=Test for PassEnvironment with a variable name repeated [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes' Type=oneshot PassEnvironment=VAR1 VAR2 PassEnvironment=VAR1 VAR3 diff --git a/test/test-execute/exec-passenvironment.service b/test/test-execute/exec-passenvironment.service index b69592a..1dcbcf9 100644 --- a/test/test-execute/exec-passenvironment.service +++ b/test/test-execute/exec-passenvironment.service @@ -3,6 +3,6 @@ Description=Test for PassEnvironment [Service] -ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes' +ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes' Type=oneshot PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 diff --git a/test/test-execute/exec-personality-aarch64.service b/test/test-execute/exec-personality-aarch64.service index 0783a87..e4ea294 100644 --- a/test/test-execute/exec-personality-aarch64.service +++ b/test/test-execute/exec-personality-aarch64.service @@ -3,6 +3,6 @@ Description=Test for Personality=aarch64 [Service] -ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")' +ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")' Type=oneshot Personality=aarch64 diff --git a/test/test-execute/exec-personality-loongarch64.service b/test/test-execute/exec-personality-loongarch64.service index 0531ad1..31c6b25 100644 --- a/test/test-execute/exec-personality-loongarch64.service +++ b/test/test-execute/exec-personality-loongarch64.service @@ -2,6 +2,6 @@ Description=Test for Personality=loongarch64 [Service] -ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "loongarch64")' +ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "loongarch64")' Type=oneshot Personality=loongarch64 diff --git a/test/test-execute/exec-personality-ppc64.service b/test/test-execute/exec-personality-ppc64.service index 72f063a..dd83bf6 100644 --- a/test/test-execute/exec-personality-ppc64.service +++ b/test/test-execute/exec-personality-ppc64.service @@ -3,6 +3,6 @@ Description=Test for Personality=ppc64 [Service] -ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")' +ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")' Type=oneshot Personality=ppc64 diff --git a/test/test-execute/exec-personality-ppc64le.service b/test/test-execute/exec-personality-ppc64le.service index 5e38029..3f19d82 100644 --- a/test/test-execute/exec-personality-ppc64le.service +++ b/test/test-execute/exec-personality-ppc64le.service @@ -3,6 +3,6 @@ Description=Test for Personality=ppc64le [Service] -ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")' +ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")' Type=oneshot Personality=ppc64le diff --git a/test/test-execute/exec-personality-s390.service b/test/test-execute/exec-personality-s390.service index 439dc5f..7d120cd 100644 --- a/test/test-execute/exec-personality-s390.service +++ b/test/test-execute/exec-personality-s390.service @@ -3,6 +3,6 @@ Description=Test for Personality=s390 [Service] -ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "s390"' +ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390"' Type=oneshot Personality=s390 diff --git a/test/test-execute/exec-personality-s390x.service b/test/test-execute/exec-personality-s390x.service new file mode 100644 index 0000000..4545dee --- /dev/null +++ b/test/test-execute/exec-personality-s390x.service @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Test for Personality=s390x + +[Service] +ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390x"' +Type=oneshot +Personality=s390x diff --git a/test/test-execute/exec-personality-x86-64.service b/test/test-execute/exec-personality-x86-64.service index c6a0a40..e7b945c 100644 --- a/test/test-execute/exec-personality-x86-64.service +++ b/test/test-execute/exec-personality-x86-64.service @@ -3,6 +3,6 @@ Description=Test for Personality=x86-64 [Service] -ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "x86_64"' +ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "x86_64"' Type=oneshot Personality=x86-64 diff --git a/test/test-execute/exec-personality-x86.service b/test/test-execute/exec-personality-x86.service index 8b820b3..95ec353 100644 --- a/test/test-execute/exec-personality-x86.service +++ b/test/test-execute/exec-personality-x86.service @@ -3,6 +3,6 @@ Description=Test for Personality=x86 [Service] -ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "i686" -o "$$c" = "x86_64"' +ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "i686" -o "$$c" = "x86_64"' Type=oneshot Personality=x86 diff --git a/test/test-execute/exec-privatedevices-bind.service b/test/test-execute/exec-privatedevices-bind.service index dbbbb4e..c2229a4 100644 --- a/test/test-execute/exec-privatedevices-bind.service +++ b/test/test-execute/exec-privatedevices-bind.service @@ -3,8 +3,8 @@ Description=Test for PrivateDevices=yes with a bind mounted device [Service] -ExecStart=/bin/sh -c 'test -c /dev/kmsg' -ExecStart=/bin/sh -c 'test ! -w /dev/' +ExecStart=sh -c 'test -c /dev/kmsg' +ExecStart=sh -c 'test ! -w /dev/' Type=oneshot PrivateDevices=yes BindPaths=/dev/kmsg diff --git a/test/test-execute/exec-privatedevices-disabled-by-prefix.service b/test/test-execute/exec-privatedevices-disabled-by-prefix.service index 021cadf..8f09c4a 100644 --- a/test/test-execute/exec-privatedevices-disabled-by-prefix.service +++ b/test/test-execute/exec-privatedevices-disabled-by-prefix.service @@ -3,7 +3,7 @@ Description=Test for PrivateDevices=yes with prefix [Service] -ExecStart=/bin/sh -x -c '! test -c /dev/kmsg' +ExecStart=sh -x -c '! test -c /dev/kmsg' ExecStart=+/bin/sh -x -c 'test -c /dev/kmsg' Type=oneshot PrivateDevices=yes diff --git a/test/test-execute/exec-privatedevices-no-capability-mknod.service b/test/test-execute/exec-privatedevices-no-capability-mknod.service index a07e822..811f4ad 100644 --- a/test/test-execute/exec-privatedevices-no-capability-mknod.service +++ b/test/test-execute/exec-privatedevices-no-capability-mknod.service @@ -5,5 +5,5 @@ Description=Test CAP_MKNOD capability for PrivateDevices=no [Service] PrivateDevices=no # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod' +ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod' Type=oneshot diff --git a/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service b/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service index b0ce2d4..47be622 100644 --- a/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service +++ b/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service @@ -5,5 +5,5 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=no [Service] PrivateDevices=no # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio' +ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio' Type=oneshot diff --git a/test/test-execute/exec-privatedevices-no.service b/test/test-execute/exec-privatedevices-no.service index 31a5e3c..5b8a051 100644 --- a/test/test-execute/exec-privatedevices-no.service +++ b/test/test-execute/exec-privatedevices-no.service @@ -3,6 +3,6 @@ Description=Test for PrivateDevices=no [Service] -ExecStart=/bin/sh -x -c 'test -c /dev/kmsg' +ExecStart=sh -x -c 'test -c /dev/kmsg' Type=oneshot PrivateDevices=no diff --git a/test/test-execute/exec-privatedevices-yes-capability-mknod.service b/test/test-execute/exec-privatedevices-yes-capability-mknod.service index f798f31..3d29a9c 100644 --- a/test/test-execute/exec-privatedevices-yes-capability-mknod.service +++ b/test/test-execute/exec-privatedevices-yes-capability-mknod.service @@ -5,5 +5,5 @@ Description=Test CAP_MKNOD capability for PrivateDevices=yes [Service] PrivateDevices=yes # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod' +ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod' Type=oneshot diff --git a/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service b/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service index d902c23..b1c0617 100644 --- a/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service +++ b/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service @@ -5,5 +5,5 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=yes [Service] PrivateDevices=yes # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio' +ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio' Type=oneshot diff --git a/test/test-execute/exec-privatedevices-yes-with-group.service b/test/test-execute/exec-privatedevices-yes-with-group.service index a39ae0f..094ac22 100644 --- a/test/test-execute/exec-privatedevices-yes-with-group.service +++ b/test/test-execute/exec-privatedevices-yes-with-group.service @@ -8,10 +8,10 @@ Group=daemon Type=oneshot # Check the group applied -ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "daemon"' +ExecStart=sh -x -c 'test "$$(id -n -g)" = "daemon"' # Check that the namespace applied -ExecStart=/bin/sh -c 'test ! -c /dev/kmsg' +ExecStart=sh -c 'test ! -c /dev/kmsg' # Check that the owning group of a node is not daemon (should be the host root) -ExecStart=/bin/sh -x -c 'test ! "$$(stat -c %%G /dev/stderr)" = "daemon"' +ExecStart=sh -x -c 'test ! "$$(stat -c %%G /dev/stderr)" = "daemon"' diff --git a/test/test-execute/exec-privatedevices-yes.service b/test/test-execute/exec-privatedevices-yes.service index 564e958..2d32753 100644 --- a/test/test-execute/exec-privatedevices-yes.service +++ b/test/test-execute/exec-privatedevices-yes.service @@ -3,6 +3,6 @@ Description=Test for PrivateDevices=yes [Service] -ExecStart=/bin/sh -c 'test ! -c /dev/kmsg' +ExecStart=sh -c 'test ! -c /dev/kmsg' Type=oneshot PrivateDevices=yes diff --git a/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service b/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service index 83708df..c16102d 100644 --- a/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service +++ b/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service @@ -3,10 +3,10 @@ Description=Test for PrivateNetwork= without mount namespacing [Service] -ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec' -ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' +ExecStart=sh -x -c '! ip link show dummy-test-exec' +ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' # Without mount namespacing, we can access the dummy-test-exec interface through sysfs -ExecStart=/bin/sh -x -c 'test -d /sys/class/net/dummy-test-exec' +ExecStart=sh -x -c 'test -d /sys/class/net/dummy-test-exec' Type=oneshot PrivateNetwork=yes PrivateMounts=no diff --git a/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service b/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service index 874f100..eb48d6e 100644 --- a/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service +++ b/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service @@ -3,10 +3,10 @@ Description=Test for PrivateNetwork= with mount namespacing [Service] -ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec' -ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' +ExecStart=sh -x -c '! ip link show dummy-test-exec' +ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec' # With mount namespacing, we cannot access the dummy-test-exec interface through sysfs. -ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-exec' +ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-exec' Type=oneshot PrivateNetwork=yes # PrivateNetwork=yes implies PrivateMounts=yes diff --git a/test/test-execute/exec-privatetmp-disabled-by-prefix.service b/test/test-execute/exec-privatetmp-disabled-by-prefix.service index f67afee..9dfcecc 100644 --- a/test/test-execute/exec-privatetmp-disabled-by-prefix.service +++ b/test/test-execute/exec-privatetmp-disabled-by-prefix.service @@ -3,7 +3,7 @@ Description=Test for PrivateTmp=yes with prefix [Service] -ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp' +ExecStart=sh -x -c 'test ! -f /tmp/test-exec_privatetmp' ExecStart=+/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp' Type=oneshot PrivateTmp=yes diff --git a/test/test-execute/exec-privatetmp-no.service b/test/test-execute/exec-privatetmp-no.service index 6a8a3fc..599203a 100644 --- a/test/test-execute/exec-privatetmp-no.service +++ b/test/test-execute/exec-privatetmp-no.service @@ -3,6 +3,6 @@ Description=Test for PrivateTmp=no [Service] -ExecStart=/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp' +ExecStart=sh -x -c 'test -f /tmp/test-exec_privatetmp' Type=oneshot PrivateTmp=no diff --git a/test/test-execute/exec-privatetmp-yes.service b/test/test-execute/exec-privatetmp-yes.service index 6395be0..5ea5263 100644 --- a/test/test-execute/exec-privatetmp-yes.service +++ b/test/test-execute/exec-privatetmp-yes.service @@ -3,6 +3,6 @@ Description=Test for PrivateTmp=yes [Service] -ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp' +ExecStart=sh -x -c 'test ! -f /tmp/test-exec_privatetmp' Type=oneshot PrivateTmp=yes diff --git a/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service b/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service index f84e6b6..c51cacf 100644 --- a/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service +++ b/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service @@ -7,4 +7,4 @@ Description=Test ProtectHome=tmpfs vs ProtectSystem=strict ProtectHome=tmpfs ProtectSystem=strict Type=oneshot -ExecStart=/bin/sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"' +ExecStart=sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"' diff --git a/test/test-execute/exec-protectkernellogs-no-capabilities.service b/test/test-execute/exec-protectkernellogs-no-capabilities.service index 5478962..be64c58 100644 --- a/test/test-execute/exec-protectkernellogs-no-capabilities.service +++ b/test/test-execute/exec-protectkernellogs-no-capabilities.service @@ -5,5 +5,5 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=no [Service] ProtectKernelLogs=no # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog' +ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog' Type=oneshot diff --git a/test/test-execute/exec-protectkernellogs-yes-capabilities.service b/test/test-execute/exec-protectkernellogs-yes-capabilities.service index 6fe1241..646ff75 100644 --- a/test/test-execute/exec-protectkernellogs-yes-capabilities.service +++ b/test/test-execute/exec-protectkernellogs-yes-capabilities.service @@ -5,5 +5,5 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=yes [Service] ProtectKernelLogs=yes # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog' +ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog' Type=oneshot diff --git a/test/test-execute/exec-protectkernelmodules-no-capabilities.service b/test/test-execute/exec-protectkernelmodules-no-capabilities.service index 7236af2..cefdb60 100644 --- a/test/test-execute/exec-protectkernelmodules-no-capabilities.service +++ b/test/test-execute/exec-protectkernelmodules-no-capabilities.service @@ -5,5 +5,5 @@ Description=Test CAP_SYS_MODULE ProtectKernelModules=no [Service] ProtectKernelModules=no # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module' +ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module' Type=oneshot diff --git a/test/test-execute/exec-protectkernelmodules-yes-capabilities.service b/test/test-execute/exec-protectkernelmodules-yes-capabilities.service index e40160d..1f327a2 100644 --- a/test/test-execute/exec-protectkernelmodules-yes-capabilities.service +++ b/test/test-execute/exec-protectkernelmodules-yes-capabilities.service @@ -5,5 +5,5 @@ Description=Test CAP_SYS_MODULE for ProtectKernelModules=yes [Service] ProtectKernelModules=yes # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output -ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module' +ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module' Type=oneshot diff --git a/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service b/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service index 0ecf1a2..16399bd 100644 --- a/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service +++ b/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service @@ -4,5 +4,5 @@ Description=Test to make sure that passing ProtectKernelModules=yes disconnect m [Service] ProtectKernelModules=yes -ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' +ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' Type=oneshot diff --git a/test/test-execute/exec-readonlypaths-mount-propagation.service b/test/test-execute/exec-readonlypaths-mount-propagation.service index abc180b..e896bac 100644 --- a/test/test-execute/exec-readonlypaths-mount-propagation.service +++ b/test/test-execute/exec-readonlypaths-mount-propagation.service @@ -4,5 +4,5 @@ Description=Test to make sure that passing ReadOnlyPaths= disconnect mount propa [Service] ReadOnlyPaths=-/i-dont-exist -ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' +ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' Type=oneshot diff --git a/test/test-execute/exec-readonlypaths-simple.service b/test/test-execute/exec-readonlypaths-simple.service index 5587e8d..80e6c83 100644 --- a/test/test-execute/exec-readonlypaths-simple.service +++ b/test/test-execute/exec-readonlypaths-simple.service @@ -7,6 +7,6 @@ Type=oneshot # This should work, as we explicitly disable the effect of ReadOnlyPaths= ExecStart=+/bin/sh -c 'touch /tmp/thisisasimpletest' # This should also work, as we do not disable the effect of ReadOnlyPaths= but invert the exit code -ExecStart=/bin/sh -x -c '! touch /tmp/thisisasimpletest' +ExecStart=sh -x -c '! touch /tmp/thisisasimpletest' ExecStart=+/bin/sh -c 'rm /tmp/thisisasimpletest' ReadOnlyPaths=/tmp diff --git a/test/test-execute/exec-readonlypaths-with-bindpaths.service b/test/test-execute/exec-readonlypaths-with-bindpaths.service index 71c7e7b..7a18367 100644 --- a/test/test-execute/exec-readonlypaths-with-bindpaths.service +++ b/test/test-execute/exec-readonlypaths-with-bindpaths.service @@ -5,5 +5,5 @@ Description=Test for ReadOnlyPaths= [Service] ReadOnlyPaths=/etc -/i-dont-exist /usr BindPaths=/etc:/tmp/etc2 -ExecStart=/bin/sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var' +ExecStart=sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var' Type=oneshot diff --git a/test/test-execute/exec-readonlypaths.service b/test/test-execute/exec-readonlypaths.service index 21814c2..a0eff8b 100644 --- a/test/test-execute/exec-readonlypaths.service +++ b/test/test-execute/exec-readonlypaths.service @@ -5,6 +5,6 @@ Description=Test for ReadOnlyPaths= [Service] ReadOnlyPaths=/usr /etc /sys /dev -/i-dont-exist PrivateDevices=yes -ExecStart=/bin/sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup' -ExecStart=/bin/sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var' +ExecStart=sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup' +ExecStart=sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var' Type=oneshot diff --git a/test/test-execute/exec-readwritepaths-mount-propagation.service b/test/test-execute/exec-readwritepaths-mount-propagation.service index 35e736f..9b844cf 100644 --- a/test/test-execute/exec-readwritepaths-mount-propagation.service +++ b/test/test-execute/exec-readwritepaths-mount-propagation.service @@ -4,5 +4,5 @@ Description=Test to make sure that passing ReadWritePaths= disconnect mount prop [Service] ReadWritePaths=-/i-dont-exist -ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' +ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo' Type=oneshot diff --git a/test/test-execute/exec-runtimedirectory-mode.service b/test/test-execute/exec-runtimedirectory-mode.service index 580bac9..e75e0d2 100644 --- a/test/test-execute/exec-runtimedirectory-mode.service +++ b/test/test-execute/exec-runtimedirectory-mode.service @@ -3,8 +3,8 @@ Description=Test for RuntimeDirectoryMode [Service] -ExecStart=/bin/sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"' -ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"' +ExecStart=sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"' +ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"' Type=oneshot RuntimeDirectory=test-exec_runtimedirectory-mode RuntimeDirectoryMode=0750 diff --git a/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service b/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service index 79bebc4..4bc3361 100644 --- a/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service +++ b/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service @@ -3,7 +3,7 @@ Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set) [Service] -ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"' +ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"' Type=oneshot Group=nfsnobody User=root diff --git a/test/test-execute/exec-runtimedirectory-owner-nobody.service b/test/test-execute/exec-runtimedirectory-owner-nobody.service index 3b42a9f..5f94bf9 100644 --- a/test/test-execute/exec-runtimedirectory-owner-nobody.service +++ b/test/test-execute/exec-runtimedirectory-owner-nobody.service @@ -3,7 +3,7 @@ Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set) [Service] -ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nobody"' +ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nobody"' Type=oneshot Group=nobody User=root diff --git a/test/test-execute/exec-runtimedirectory-owner-nogroup.service b/test/test-execute/exec-runtimedirectory-owner-nogroup.service index 804048e..6d50895 100644 --- a/test/test-execute/exec-runtimedirectory-owner-nogroup.service +++ b/test/test-execute/exec-runtimedirectory-owner-nogroup.service @@ -3,7 +3,7 @@ Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set) [Service] -ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nogroup"' +ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nogroup"' Type=oneshot Group=nogroup User=root diff --git a/test/test-execute/exec-runtimedirectory-owner.service b/test/test-execute/exec-runtimedirectory-owner.service index e2c0890..64d66b3 100644 --- a/test/test-execute/exec-runtimedirectory-owner.service +++ b/test/test-execute/exec-runtimedirectory-owner.service @@ -3,7 +3,7 @@ Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set) [Service] -ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner-daemon); test "$$group" = "daemon"' +ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner-daemon); test "$$group" = "daemon"' Type=oneshot Group=daemon User=root diff --git a/test/test-execute/exec-runtimedirectory.service b/test/test-execute/exec-runtimedirectory.service index 1928c57..f60110a 100644 --- a/test/test-execute/exec-runtimedirectory.service +++ b/test/test-execute/exec-runtimedirectory.service @@ -3,9 +3,9 @@ Description=Test for RuntimeDirectory [Service] -ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory' -ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge' -ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"' +ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectory' +ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge' +ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"' Type=oneshot RuntimeDirectory=test-exec_runtimedirectory RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/. diff --git a/test/test-execute/exec-set-credential.service b/test/test-execute/exec-set-credential.service index 9db6c5f..2263436 100644 --- a/test/test-execute/exec-set-credential.service +++ b/test/test-execute/exec-set-credential.service @@ -3,9 +3,9 @@ Description=Test for SetCredential= [Service] -ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' -ExecStartPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' -ExecStop=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' -ExecStopPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' +ExecStart=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' +ExecStartPost=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' +ExecStop=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' +ExecStopPost=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"' Type=oneshot SetCredential=test-execute.set-credential:hoge diff --git a/test/test-execute/exec-specifier-interpolation.service b/test/test-execute/exec-specifier-interpolation.service index 2e8882c..aa0ecdf 100644 --- a/test/test-execute/exec-specifier-interpolation.service +++ b/test/test-execute/exec-specifier-interpolation.service @@ -4,4 +4,4 @@ Description=https://github.com/systemd/systemd/issues/2637 [Service] Type=oneshot -ExecStart=/bin/bash -x -c "[[ %%U == ?U ]]" +ExecStart=bash -x -c "[[ %%U == ?U ]]" diff --git a/test/test-execute/exec-standardinput-data.service b/test/test-execute/exec-standardinput-data.service index 838fea7..fd56f7e 100644 --- a/test/test-execute/exec-standardinput-data.service +++ b/test/test-execute/exec-standardinput-data.service @@ -3,7 +3,7 @@ Description=Test for StandardInputText= and StandardInputData= [Service] -ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" >$d/text ; cmp $d/text ; rm -rf $d' +ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" >$d/text ; cmp $d/text ; rm -rf $d' Type=oneshot StandardInput=data StandardInputText=this is a test diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service b/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service index 0ecc344..3c90124 100644 --- a/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service +++ b/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service @@ -3,9 +3,9 @@ Description=Test for Supplementary Group with multiple groups without Group and User [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' +ExecStart=sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"' Type=oneshot SupplementaryGroups=1 2 diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service b/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service index cd1021b..0fd1c62 100644 --- a/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service +++ b/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service @@ -3,9 +3,9 @@ Description=Test for Supplementary Group with multiple groups and Group=1 [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' +ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"' Type=oneshot Group=1 SupplementaryGroups=1 2 diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service b/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service index 7913a2c..c430e54 100644 --- a/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service +++ b/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service @@ -3,8 +3,8 @@ Description=Test for Supplementary Group with multiple groups and Uid=1 [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1' Type=oneshot User=1 SupplementaryGroups=1 2 diff --git a/test/test-execute/exec-supplementarygroups-single-group-user.service b/test/test-execute/exec-supplementarygroups-single-group-user.service index ee4017e..20a3561 100644 --- a/test/test-execute/exec-supplementarygroups-single-group-user.service +++ b/test/test-execute/exec-supplementarygroups-single-group-user.service @@ -3,8 +3,8 @@ Description=Test for Supplementary Group with only one group and uid 1 [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' Type=oneshot User=1 Group=1 diff --git a/test/test-execute/exec-supplementarygroups-single-group.service b/test/test-execute/exec-supplementarygroups-single-group.service index 6227520..8c81257 100644 --- a/test/test-execute/exec-supplementarygroups-single-group.service +++ b/test/test-execute/exec-supplementarygroups-single-group.service @@ -3,8 +3,8 @@ Description=Test for Supplementary Group with only one group [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"' Type=oneshot Group=1 SupplementaryGroups=1 diff --git a/test/test-execute/exec-supplementarygroups.service b/test/test-execute/exec-supplementarygroups.service index 03406c3..0a3d370 100644 --- a/test/test-execute/exec-supplementarygroups.service +++ b/test/test-execute/exec-supplementarygroups.service @@ -3,7 +3,7 @@ Description=Test for Supplementary Group [Service] -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1' -ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1' +ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1' Type=oneshot SupplementaryGroups=1 diff --git a/test/test-execute/exec-systemcallerrornumber-name.service b/test/test-execute/exec-systemcallerrornumber-name.service index f2be600..00a4508 100644 --- a/test/test-execute/exec-systemcallerrornumber-name.service +++ b/test/test-execute/exec-systemcallerrornumber-name.service @@ -3,7 +3,7 @@ Description=Test for SystemCallErrorNumber [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname SystemCallErrorNumber=EACCES diff --git a/test/test-execute/exec-systemcallerrornumber-number.service b/test/test-execute/exec-systemcallerrornumber-number.service index 5d99a97..3b5fb6e 100644 --- a/test/test-execute/exec-systemcallerrornumber-number.service +++ b/test/test-execute/exec-systemcallerrornumber-number.service @@ -3,7 +3,7 @@ Description=Test for SystemCallErrorNumber [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname SystemCallErrorNumber=255 diff --git a/test/test-execute/exec-systemcallfilter-failing.service b/test/test-execute/exec-systemcallfilter-failing.service index 3aad372..7437d30 100644 --- a/test/test-execute/exec-systemcallfilter-failing.service +++ b/test/test-execute/exec-systemcallfilter-failing.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter [Service] -ExecStart=/bin/sh -c '/bin/echo "This should not be seen"' +ExecStart=sh -c '/bin/echo "This should not be seen"' Type=oneshot LimitCORE=0 SystemCallFilter=ioperm diff --git a/test/test-execute/exec-systemcallfilter-failing2.service b/test/test-execute/exec-systemcallfilter-failing2.service index 8cdb8de..92672d1 100644 --- a/test/test-execute/exec-systemcallfilter-failing2.service +++ b/test/test-execute/exec-systemcallfilter-failing2.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter [Service] -ExecStart=/bin/sh -c '/bin/echo "This should not be seen"' +ExecStart=sh -c '/bin/echo "This should not be seen"' Type=oneshot LimitCORE=0 SystemCallFilter=~write open execve fexecve execveat exit_group close mmap munmap fstat DONOTEXIST diff --git a/test/test-execute/exec-systemcallfilter-failing3.service b/test/test-execute/exec-systemcallfilter-failing3.service index 98c88fd..4e7b812 100644 --- a/test/test-execute/exec-systemcallfilter-failing3.service +++ b/test/test-execute/exec-systemcallfilter-failing3.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter [Service] -ExecStart=/bin/sh -c '/bin/echo "This should not be seen"' +ExecStart=sh -c '/bin/echo "This should not be seen"' Type=oneshot LimitCORE=0 SystemCallArchitectures=native diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service index 8f8192c..eaa75df 100644 --- a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service +++ b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service @@ -3,7 +3,7 @@ Description=Test bounding set is right with SystemCallFilter and non-root user [Service] -ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_net_bind_service"' +ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_net_bind_service"' Type=oneshot User=1 SystemCallFilter=@system-service diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service index d78c323..fd0e3a2 100644 --- a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service +++ b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service @@ -3,7 +3,7 @@ Description=Test bounding set is right with SystemCallFilter and non-root user [Service] -ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_setpcap,cap_net_bind_service,cap_sys_admin"' +ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_setpcap,cap_net_bind_service,cap_sys_admin"' Type=oneshot User=1 SystemCallFilter=@system-service diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service b/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service index f33a2a0..76b028c 100644 --- a/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service +++ b/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service @@ -3,7 +3,7 @@ Description=Test no_new_privs is unset for ProtectClock and non-root user [Service] -ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"' +ExecStart=sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"' Type=oneshot User=1 ProtectClock=yes diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges.service b/test/test-execute/exec-systemcallfilter-nonewprivileges.service index 8bfd0a7..2091b71 100644 --- a/test/test-execute/exec-systemcallfilter-nonewprivileges.service +++ b/test/test-execute/exec-systemcallfilter-nonewprivileges.service @@ -3,7 +3,7 @@ Description=Test no_new_privs is unset for SystemCallFilter and non-root user [Service] -ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"' +ExecStart=sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"' Type=oneshot User=1 SystemCallFilter=@system-service diff --git a/test/test-execute/exec-systemcallfilter-not-failing.service b/test/test-execute/exec-systemcallfilter-not-failing.service index c7eddea..bb2ea55 100644 --- a/test/test-execute/exec-systemcallfilter-not-failing.service +++ b/test/test-execute/exec-systemcallfilter-not-failing.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter [Service] -ExecStart=/bin/sh -c 'echo "Foo bar"' +ExecStart=sh -c 'echo "Foo bar"' Type=oneshot SystemCallFilter=~read write open execve ioperm SystemCallFilter=ioctl diff --git a/test/test-execute/exec-systemcallfilter-not-failing2.service b/test/test-execute/exec-systemcallfilter-not-failing2.service index 96eaf16..d9f0a37 100644 --- a/test/test-execute/exec-systemcallfilter-not-failing2.service +++ b/test/test-execute/exec-systemcallfilter-not-failing2.service @@ -3,6 +3,6 @@ Description=Test for SystemCallFilter [Service] -ExecStart=/bin/sh -c 'echo "Foo bar"' +ExecStart=sh -c 'echo "Foo bar"' Type=oneshot SystemCallFilter= diff --git a/test/test-execute/exec-systemcallfilter-not-failing3.service b/test/test-execute/exec-systemcallfilter-not-failing3.service index f8f4092..df4e662 100644 --- a/test/test-execute/exec-systemcallfilter-not-failing3.service +++ b/test/test-execute/exec-systemcallfilter-not-failing3.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter [Service] -ExecStart=/bin/sh -c 'echo "Foo bar"' +ExecStart=sh -c 'echo "Foo bar"' Type=oneshot SystemCallArchitectures=native SystemCallFilter= diff --git a/test/test-execute/exec-systemcallfilter-override-error-action.service b/test/test-execute/exec-systemcallfilter-override-error-action.service index de2c6ad..6107d11 100644 --- a/test/test-execute/exec-systemcallfilter-override-error-action.service +++ b/test/test-execute/exec-systemcallfilter-override-error-action.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter with specific kill action overriding default errno action [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname:kill SystemCallErrorNumber=EILSEQ diff --git a/test/test-execute/exec-systemcallfilter-override-error-action2.service b/test/test-execute/exec-systemcallfilter-override-error-action2.service index ffa35e6..e049275 100644 --- a/test/test-execute/exec-systemcallfilter-override-error-action2.service +++ b/test/test-execute/exec-systemcallfilter-override-error-action2.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter with specific errno action overriding default kill action [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname:EILSEQ SystemCallErrorNumber=kill diff --git a/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service b/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service index deba154..1912286 100644 --- a/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service +++ b/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter in system mode with User set [Service] -ExecStart=/bin/sh -c 'echo "Foo bar"' +ExecStart=sh -c 'echo "Foo bar"' Type=oneshot User=nfsnobody SystemCallFilter=~read write open execve ioperm diff --git a/test/test-execute/exec-systemcallfilter-system-user-nobody.service b/test/test-execute/exec-systemcallfilter-system-user-nobody.service index 43fb9c3..0c2ebdd 100644 --- a/test/test-execute/exec-systemcallfilter-system-user-nobody.service +++ b/test/test-execute/exec-systemcallfilter-system-user-nobody.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter in system mode with User set [Service] -ExecStart=/bin/sh -c 'echo "Foo bar"' +ExecStart=sh -c 'echo "Foo bar"' Type=oneshot User=nobody SystemCallFilter=~read write open execve ioperm diff --git a/test/test-execute/exec-systemcallfilter-system-user.service b/test/test-execute/exec-systemcallfilter-system-user.service index 005c4ac..6de3964 100644 --- a/test/test-execute/exec-systemcallfilter-system-user.service +++ b/test/test-execute/exec-systemcallfilter-system-user.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter in system mode with User set (daemon) [Service] -ExecStart=/bin/sh -c 'echo "Foo bar"' +ExecStart=sh -c 'echo "Foo bar"' Type=oneshot User=daemon SystemCallFilter=~read write open execve ioperm diff --git a/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service b/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service index c7a4c4a..a8dc10f 100644 --- a/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service +++ b/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter with errno name (for issue #18916) [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=@system-service SystemCallFilter=~uname:EILSEQ diff --git a/test/test-execute/exec-systemcallfilter-with-errno-multi.service b/test/test-execute/exec-systemcallfilter-with-errno-multi.service index 2678323..224df01 100644 --- a/test/test-execute/exec-systemcallfilter-with-errno-multi.service +++ b/test/test-execute/exec-systemcallfilter-with-errno-multi.service @@ -4,7 +4,7 @@ Description=Test for SystemCallFilter updating errno # test for issue #9939 which is fixed by a5404992cc7724ebf7572a0aa89d9fdb26ce0b62 (#9942) [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname:ENOENT uname:EILSEQ SystemCallErrorNumber=EACCES diff --git a/test/test-execute/exec-systemcallfilter-with-errno-name.service b/test/test-execute/exec-systemcallfilter-with-errno-name.service index a902331..bed7961 100644 --- a/test/test-execute/exec-systemcallfilter-with-errno-name.service +++ b/test/test-execute/exec-systemcallfilter-with-errno-name.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter with errno name [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname:EILSEQ SystemCallErrorNumber=EACCES diff --git a/test/test-execute/exec-systemcallfilter-with-errno-number.service b/test/test-execute/exec-systemcallfilter-with-errno-number.service index ffbc84a..8db2281 100644 --- a/test/test-execute/exec-systemcallfilter-with-errno-number.service +++ b/test/test-execute/exec-systemcallfilter-with-errno-number.service @@ -3,7 +3,7 @@ Description=Test for SystemCallFilter with errno number [Service] -ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname:255 SystemCallErrorNumber=EACCES diff --git a/test/test-execute/exec-temporaryfilesystem-options.service b/test/test-execute/exec-temporaryfilesystem-options.service index 1610c63..b000301 100644 --- a/test/test-execute/exec-temporaryfilesystem-options.service +++ b/test/test-execute/exec-temporaryfilesystem-options.service @@ -10,8 +10,8 @@ Type=oneshot TemporaryFileSystem=/var:ro,mode=0700,nostrictatime # Check /proc/self/mountinfo -ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' +ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' -ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' -ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)nodev(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' -ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 ~ /(^|,)strictatime(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' +ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' +ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)nodev(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' +ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 ~ /(^|,)strictatime(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""' diff --git a/test/test-execute/exec-temporaryfilesystem-ro.service b/test/test-execute/exec-temporaryfilesystem-ro.service index 2ee5c26..0a4b0f2 100644 --- a/test/test-execute/exec-temporaryfilesystem-ro.service +++ b/test/test-execute/exec-temporaryfilesystem-ro.service @@ -6,31 +6,31 @@ Description=Test for TemporaryFileSystem with read-only mode Type=oneshot # Check directories exist -ExecStart=/bin/sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /var/test-exec-temporaryfilesystem/ro' +ExecStart=sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /var/test-exec-temporaryfilesystem/ro' # Check TemporaryFileSystem= are empty -ExecStart=/bin/sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done' +ExecStart=sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done' # Check default mode ExecStart=sh -x -c 'test "$$(stat -c %%a /var)" = "755"' # Cannot create a file in /var -ExecStart=/bin/sh -c '! touch /var/hoge' +ExecStart=sh -c '! touch /var/hoge' # Create a file in /var/test-exec-temporaryfilesystem/rw -ExecStart=/bin/sh -c 'touch /var/test-exec-temporaryfilesystem/rw/thisisasimpletest-temporaryfilesystem' +ExecStart=sh -c 'touch /var/test-exec-temporaryfilesystem/rw/thisisasimpletest-temporaryfilesystem' # Then, the file can be access through /tmp -ExecStart=/bin/sh -c 'test -f /tmp/thisisasimpletest-temporaryfilesystem' +ExecStart=sh -c 'test -f /tmp/thisisasimpletest-temporaryfilesystem' # Also, through /var/test-exec-temporaryfilesystem/ro -ExecStart=/bin/sh -c 'test -f /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem' +ExecStart=sh -c 'test -f /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem' # The file cannot modify through /var/test-exec-temporaryfilesystem/ro -ExecStart=/bin/sh -c '! touch /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem' +ExecStart=sh -c '! touch /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem' # Cleanup -ExecStart=/bin/sh -c 'rm /tmp/thisisasimpletest-temporaryfilesystem' +ExecStart=sh -c 'rm /tmp/thisisasimpletest-temporaryfilesystem' TemporaryFileSystem=/var:ro BindPaths=/tmp:/var/test-exec-temporaryfilesystem/rw diff --git a/test/test-execute/exec-temporaryfilesystem-usr.service b/test/test-execute/exec-temporaryfilesystem-usr.service index f62ce1a..455344e 100644 --- a/test/test-execute/exec-temporaryfilesystem-usr.service +++ b/test/test-execute/exec-temporaryfilesystem-usr.service @@ -6,11 +6,11 @@ Description=Test for TemporaryFileSystem on /usr Type=oneshot # Check TemporaryFileSystem= are empty -ExecStart=/bin/sh -c 'for i in $$(ls -A /usr); do test $$i = lib -o $$i = lib64 -o $$i = bin -o $$i = sbin || false; done' +ExecStart=sh -c 'for i in $$(ls -A /usr); do test $$i = lib -o $$i = lib64 -o $$i = bin -o $$i = sbin || false; done' # Cannot create files under /usr -ExecStart=/bin/sh -c '! touch /usr/hoge' -ExecStart=/bin/sh -c '! touch /usr/bin/hoge' +ExecStart=sh -c '! touch /usr/hoge' +ExecStart=sh -c '! touch /usr/bin/hoge' TemporaryFileSystem=/usr:ro BindReadOnlyPaths=-/usr/lib -/usr/lib64 /usr/bin /usr/sbin diff --git a/test/test-execute/exec-umask-0177.service b/test/test-execute/exec-umask-0177.service index 380cb82..de9ac5a 100644 --- a/test/test-execute/exec-umask-0177.service +++ b/test/test-execute/exec-umask-0177.service @@ -3,7 +3,7 @@ Description=Test for UMask [Service] -ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"' +ExecStart=sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"' Type=oneshot UMask=0177 PrivateTmp=yes diff --git a/test/test-execute/exec-umask-default.service b/test/test-execute/exec-umask-default.service index b28023d..6d13c0b 100644 --- a/test/test-execute/exec-umask-default.service +++ b/test/test-execute/exec-umask-default.service @@ -3,6 +3,6 @@ Description=Test for UMask default [Service] -ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"' +ExecStart=sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"' Type=oneshot PrivateTmp=yes diff --git a/test/test-execute/exec-umask-namespace.service b/test/test-execute/exec-umask-namespace.service index 8419c86..aac1dad 100644 --- a/test/test-execute/exec-umask-namespace.service +++ b/test/test-execute/exec-umask-namespace.service @@ -3,7 +3,7 @@ Description=Test for UMask= + namespacing [Service] -ExecStart=/bin/ls -lahd /tmp/subdir +ExecStart=ls -lahd /tmp/subdir Type=oneshot User=65534 Group=65534 diff --git a/test/test-execute/exec-unsetenvironment.service b/test/test-execute/exec-unsetenvironment.service index b79e3d4..9c5e277 100644 --- a/test/test-execute/exec-unsetenvironment.service +++ b/test/test-execute/exec-unsetenvironment.service @@ -3,7 +3,7 @@ Description=Test for UnsetEnvironment [Service] -ExecStart=/bin/sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"' +ExecStart=sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"' Type=oneshot Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U diff --git a/test/test-execute/exec-user-nfsnobody.service b/test/test-execute/exec-user-nfsnobody.service index 8f0943c..1ce5f08 100644 --- a/test/test-execute/exec-user-nfsnobody.service +++ b/test/test-execute/exec-user-nfsnobody.service @@ -3,6 +3,6 @@ Description=Test for User [Service] -ExecStart=/bin/sh -x -c 'test "$$USER" = "nfsnobody"' +ExecStart=sh -x -c 'test "$$USER" = "nfsnobody"' Type=oneshot User=nfsnobody diff --git a/test/test-execute/exec-user-nobody.service b/test/test-execute/exec-user-nobody.service index 834d11a..003b873 100644 --- a/test/test-execute/exec-user-nobody.service +++ b/test/test-execute/exec-user-nobody.service @@ -3,6 +3,6 @@ Description=Test for User [Service] -ExecStart=/bin/sh -x -c 'test "$$USER" = "nobody"' +ExecStart=sh -x -c 'test "$$USER" = "nobody"' Type=oneshot User=nobody diff --git a/test/test-execute/exec-user.service b/test/test-execute/exec-user.service index b9863d2..696c7e5 100644 --- a/test/test-execute/exec-user.service +++ b/test/test-execute/exec-user.service @@ -3,6 +3,6 @@ Description=Test for User (daemon) [Service] -ExecStart=/bin/sh -x -c 'test "$$USER" = "daemon"' +ExecStart=sh -x -c 'test "$$USER" = "daemon"' Type=oneshot User=daemon diff --git a/test/test-execute/exec-workingdirectory-trailing-dot.service b/test/test-execute/exec-workingdirectory-trailing-dot.service index 130d9d5..3c4869d 100644 --- a/test/test-execute/exec-workingdirectory-trailing-dot.service +++ b/test/test-execute/exec-workingdirectory-trailing-dot.service @@ -3,6 +3,6 @@ Description=Test for WorkingDirectory with trailing dot [Service] -ExecStart=/bin/sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"' +ExecStart=sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"' Type=oneshot WorkingDirectory=/tmp///./test-exec_workingdirectory/. diff --git a/test/test-execute/exec-workingdirectory.service b/test/test-execute/exec-workingdirectory.service index b53bf60..4c40faf 100644 --- a/test/test-execute/exec-workingdirectory.service +++ b/test/test-execute/exec-workingdirectory.service @@ -3,6 +3,6 @@ Description=Test for WorkingDirectory [Service] -ExecStart=/bin/sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"' +ExecStart=sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"' Type=oneshot WorkingDirectory=/tmp/test-exec_workingdirectory diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount new file mode 120000 index 0000000..14fd395 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount @@ -0,0 +1 @@ +../mnt-wantedby-automount.automount
\ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount index 5edc4dd..96fd672 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount @@ -3,7 +3,9 @@ [Unit] Documentation=man:fstab(5) man:systemd-fstab-generator(8) SourcePath=/etc/fstab -Before=local-fs.target +DefaultDependencies=no +Conflicts=umount.target +Before=umount.target After=blockdev@dev-sdx8.target [Mount] diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount new file mode 100644 index 0000000..b9f2d28 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount @@ -0,0 +1,8 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +SourcePath=/etc/fstab +Documentation=man:fstab(5) man:systemd-fstab-generator(8) + +[Automount] +Where=/mnt/wantedby-automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount new file mode 100644 index 0000000..d909476 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount @@ -0,0 +1,14 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +Documentation=man:fstab(5) man:systemd-fstab-generator(8) +SourcePath=/etc/fstab +DefaultDependencies=no +Conflicts=umount.target +Before=umount.target +After=blockdev@dev-sdx17.target + +[Mount] +What=/dev/sdx17 +Where=/mnt/wantedby +Options=x-systemd.wanted-by=foo.service,x-systemd.automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount index e12df82..ff76a6f 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount @@ -3,7 +3,9 @@ [Unit] Documentation=man:fstab(5) man:systemd-fstab-generator(8) SourcePath=/etc/fstab -Before=local-fs.target +DefaultDependencies=no +Conflicts=umount.target +Before=umount.target After=blockdev@dev-sdx7.target [Mount] diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount new file mode 120000 index 0000000..14fd395 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount @@ -0,0 +1 @@ +../mnt-wantedby-automount.automount
\ No newline at end of file diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount index 5edc4dd..96fd672 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount @@ -3,7 +3,9 @@ [Unit] Documentation=man:fstab(5) man:systemd-fstab-generator(8) SourcePath=/etc/fstab -Before=local-fs.target +DefaultDependencies=no +Conflicts=umount.target +Before=umount.target After=blockdev@dev-sdx8.target [Mount] diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount new file mode 100644 index 0000000..b9f2d28 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount @@ -0,0 +1,8 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +SourcePath=/etc/fstab +Documentation=man:fstab(5) man:systemd-fstab-generator(8) + +[Automount] +Where=/mnt/wantedby-automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount new file mode 100644 index 0000000..d909476 --- /dev/null +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount @@ -0,0 +1,14 @@ +# Automatically generated by systemd-fstab-generator + +[Unit] +Documentation=man:fstab(5) man:systemd-fstab-generator(8) +SourcePath=/etc/fstab +DefaultDependencies=no +Conflicts=umount.target +Before=umount.target +After=blockdev@dev-sdx17.target + +[Mount] +What=/dev/sdx17 +Where=/mnt/wantedby +Options=x-systemd.wanted-by=foo.service,x-systemd.automount diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount index e12df82..ff76a6f 100644 --- a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount +++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount @@ -3,7 +3,9 @@ [Unit] Documentation=man:fstab(5) man:systemd-fstab-generator(8) SourcePath=/etc/fstab -Before=local-fs.target +DefaultDependencies=no +Conflicts=umount.target +Before=umount.target After=blockdev@dev-sdx7.target [Mount] diff --git a/test/test-fstab-generator/test-18-options.fstab.input b/test/test-fstab-generator/test-18-options.fstab.input index 98ef0b9..2112d68 100644 --- a/test/test-fstab-generator/test-18-options.fstab.input +++ b/test/test-fstab-generator/test-18-options.fstab.input @@ -1,16 +1,17 @@ -/dev/sdx1 /sysroot auto defaults 0 1 -/dev/sdx2 /mnt/timeout auto x-systemd.mount-timeout=10m 0 0 -/dev/sdx3 /mnt/after auto x-systemd.after=foo.service 0 0 -/dev/sdx4 /mnt/before auto x-systemd.before=foo.service 0 0 -/dev/sdx5 /mnt/requires auto x-systemd.requires=foo.service 0 0 -/dev/sdx6 /mnt/reqmounts auto x-systemd.requires-mounts-for=/hoge 0 0 -/dev/sdx7 /mnt/wantedby auto x-systemd.wanted-by=foo.service 0 0 -/dev/sdx8 /mnt/requiredby auto x-systemd.required-by=foo.service 0 0 -/dev/sdx9 /mnt/automount1 auto x-systemd.automount,x-systemd.idle-timeout=30m 0 0 -/dev/sdx10 /mnt/automount2 auto x-systemd.automount,nofail 0 0 -/dev/sdx11 /mnt/rwonly auto x-systemd.rw-only 0 0 -/dev/sdx12 /mnt/mkfs ext4 x-systemd.makefs 0 0 -/dev/sdx13 /mnt/growfs auto x-systemd.growfs 0 0 -/dev/sdx14 /mnt/pcrfs auto x-systemd.pcrfs 0 0 -/dev/sdx15 /mnt/noauto auto noauto 0 0 -/dev/sdx16 /mnt/nofail auto nofail 0 0 +/dev/sdx1 /sysroot auto defaults 0 1 +/dev/sdx2 /mnt/timeout auto x-systemd.mount-timeout=10m 0 0 +/dev/sdx3 /mnt/after auto x-systemd.after=foo.service 0 0 +/dev/sdx4 /mnt/before auto x-systemd.before=foo.service 0 0 +/dev/sdx5 /mnt/requires auto x-systemd.requires=foo.service 0 0 +/dev/sdx6 /mnt/reqmounts auto x-systemd.requires-mounts-for=/hoge 0 0 +/dev/sdx7 /mnt/wantedby auto x-systemd.wanted-by=foo.service 0 0 +/dev/sdx8 /mnt/requiredby auto x-systemd.required-by=foo.service 0 0 +/dev/sdx9 /mnt/automount1 auto x-systemd.automount,x-systemd.idle-timeout=30m 0 0 +/dev/sdx10 /mnt/automount2 auto x-systemd.automount,nofail 0 0 +/dev/sdx11 /mnt/rwonly auto x-systemd.rw-only 0 0 +/dev/sdx12 /mnt/mkfs ext4 x-systemd.makefs 0 0 +/dev/sdx13 /mnt/growfs auto x-systemd.growfs 0 0 +/dev/sdx14 /mnt/pcrfs auto x-systemd.pcrfs 0 0 +/dev/sdx15 /mnt/noauto auto noauto 0 0 +/dev/sdx16 /mnt/nofail auto nofail 0 0 +/dev/sdx17 /mnt/wantedby-automount auto x-systemd.wanted-by=foo.service,x-systemd.automount 0 0 diff --git a/test/test-functions b/test/test-functions index f7376bf..be6eb1d 100644 --- a/test/test-functions +++ b/test/test-functions @@ -39,6 +39,8 @@ os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os # shellcheck source=/dev/null source "$os_release" [[ "$ID" == "debian" || " $ID_LIKE " == *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN=no +# shellcheck disable=SC2034 +[[ "$ID" == "ubuntu" ]] && LOOKS_LIKE_UBUNTU=yes || LOOKS_LIKE_UBUNTU=no [[ "$ID" == "arch" || " $ID_LIKE " == *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH=no [[ "$ID" == "fedora" ]] && LOOKS_LIKE_FEDORA=yes || LOOKS_LIKE_FEDORA=no [[ " $ID_LIKE " == *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE=no @@ -120,6 +122,7 @@ SYSTEMD="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR SYSTEMD_NSPAWN="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}" JOURNALCTL="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}" SYSTEMCTL="${SYSTEMCTL:-$(command -v "$BUILD_DIR/systemctl" || command -v systemctl)}" +SYSTEMD_ID128="${SYSTEMD_ID128:-$(command -v "$BUILD_DIR/systemd-id128" || command -v systemd-id128)}" TESTFILE="${BASH_SOURCE[1]}" if [ -z "$TESTFILE" ]; then @@ -143,12 +146,6 @@ if ! [[ "$TESTNAME" =~ ^TEST\-([0-9]+)\-.+$ ]]; then 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 - exit 1 -fi BASICTOOLS=( awk @@ -206,6 +203,7 @@ BASICTOOLS=( mkfifo mknod mktemp + modinfo modprobe mount mountpoint @@ -221,7 +219,6 @@ BASICTOOLS=( rm rmdir rmmod - route script sed seq @@ -474,8 +471,7 @@ run_qemu() { local CONSOLE=ttyS0 - # Reset the boot counter, if present - rm -f "${initdir:?}/var/tmp/.systemd_reboot_count" + find "${initdir:?}/var/tmp" -mindepth 1 -delete rm -f "$initdir"/{testok,failed,skipped} # make sure the initdir is not mounted to avoid concurrent access cleanup_initdir @@ -565,9 +561,9 @@ run_qemu() { "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"} @@ -649,8 +645,7 @@ run_qemu() { # success), or 1 if nspawn is not available. run_nspawn() { [[ -d /run/systemd/system ]] || return 1 - # Reset the boot counter, if present - rm -f "${initdir:?}/var/tmp/.systemd_reboot_count" + find "${initdir:?}/var/tmp" -mindepth 1 -delete rm -f "${initdir:?}"/{testok,failed,skipped} local nspawn_cmd=() @@ -658,13 +653,13 @@ run_nspawn() { "--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"} ) @@ -733,6 +728,7 @@ install_verity_minimal() { BASICTOOLS=( bash cat + echo grep mount sleep @@ -780,110 +776,6 @@ EOF mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \ grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash" - - # Rolling distros like Arch do not set VERSION_ID - local version_id="" - if grep -q "^VERSION_ID=" "$os_release"; then - version_id="$(grep "^VERSION_ID=" "$os_release")" - fi - - export initdir="$TESTDIR/app0" - mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" - grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0" - echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0" - ( echo "${version_id}" - echo "SYSEXT_IMAGE_ID=app" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app0" - cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/opt/script0.sh -TemporaryFileSystem=/var/lib -StateDirectory=app0 -RuntimeDirectory=app0 -EOF - cat >"$initdir/opt/script0.sh" <<EOF -#!/bin/bash -set -e -test -e /usr/lib/os-release -echo bar >\${STATE_DIRECTORY}/foo -cat /usr/lib/extension-release.d/extension-release.app0 -EOF - chmod +x "$initdir/opt/script0.sh" - echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" - mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend - - export initdir="$TESTDIR/conf0" - mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" "$initdir/opt" - grep "^ID=" "$os_release" >"$initdir/etc/extension-release.d/extension-release.conf0" - echo "${version_id}" >>"$initdir/etc/extension-release.d/extension-release.conf0" - ( echo "${version_id}" - echo "CONFEXT_IMAGE_ID=app" ) >>"$initdir/etc/extension-release.d/extension-release.conf0" - echo MARKER_1 >"$initdir/etc/systemd/system/some_file" - mksquashfs "$initdir" "$oldinitdir/usr/share/conf0.raw" -noappend - - export initdir="$TESTDIR/app1" - mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" - grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2" - ( echo "${version_id}" - echo "SYSEXT_SCOPE=portable" - echo "SYSEXT_IMAGE_ID=app" - echo "SYSEXT_IMAGE_VERSION=1" - echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2" - setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2" - cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/opt/script1.sh -StateDirectory=app1 -RuntimeDirectory=app1 -EOF - cat >"$initdir/opt/script1.sh" <<EOF -#!/bin/bash -set -e -test -e /usr/lib/os-release -echo baz >\${STATE_DIRECTORY}/foo -cat /usr/lib/extension-release.d/extension-release.app2 -EOF - chmod +x "$initdir/opt/script1.sh" - echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file" - mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend - - export initdir="$TESTDIR/app-nodistro" - mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" - ( echo "ID=_any" - echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" - echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" - mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend - - export initdir="$TESTDIR/service-scoped-test" - mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" - ( echo "ID=_any" - echo "ARCHITECTURE=_any" ) >"$initdir/etc/extension-release.d/extension-release.service-scoped-test" - echo MARKER_CONFEXT_123 >"$initdir/etc/systemd/system/some_file" - mksquashfs "$initdir" "$oldinitdir/etc/service-scoped-test.raw" -noappend - - # We need to create a dedicated sysext image to test the reload mechanism. If we share an image to install the - # 'foo.service' it will be loaded from another test run, which will impact the targeted test. - export initdir="$TESTDIR/app-reload" - mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" - ( echo "ID=_any" - echo "ARCHITECTURE=_any" - echo "EXTENSION_RELOAD_MANAGER=1" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-reload" - mkdir -p "$initdir/usr/lib/systemd/system/multi-user.target.d" - cat >"${initdir}/usr/lib/systemd/system/foo.service" <<EOF -[Service] -Type=oneshot -RemainAfterExit=yes -SyslogIdentifier=sysext-foo -ExecStart=echo foo - -[Install] -WantedBy=multi-user.target -EOF - { echo "[Unit]"; echo "Upholds=foo.service"; } > "$initdir/usr/lib/systemd/system/multi-user.target.d/10-foo-service.conf" - mksquashfs "$initdir" "$oldinitdir/usr/share/app-reload.raw" -noappend ) } @@ -997,7 +889,7 @@ create_asan_wrapper() { [[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be" - default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}" + default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:disable_coredump=0:use_madv_dontdump=1}" default_ubsan_options="${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}" if [[ "$ASAN_COMPILER" == "clang" ]]; then @@ -1115,6 +1007,9 @@ install_fs_tools() { # we use mkfs in system-repart tests image_install /sbin/mkfs.ext4 image_install /sbin/mkfs.vfat + + # we use mkswap in udev-storage tests + image_install /sbin/mkswap } install_modules() { @@ -1153,9 +1048,9 @@ install_multipath() { image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket} if get_bool "$LOOKS_LIKE_DEBIAN"; then - # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 90-kpartx.rules (as seen on + # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 95-kpartx.rules (as seen on # Ubuntu Jammy with 0.8.8-1ubuntu1.22.04.4) - inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 90-kpartx.rules + inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules else inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules fi @@ -1226,7 +1121,8 @@ install_iscsi() { image_install iscsi-iname iscsiadm iscsid iscsistart image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket} - image_install "${ROOTLIBDIR:?}"/system/iscsi.service + image_install -o "${ROOTLIBDIR:?}"/system/iscsi.service + image_install -o /usr/lib/open-iscsi/startup-checks.sh mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static} mkdir -p "${initdir:?}/etc/iscsi" echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf" @@ -1256,7 +1152,11 @@ install_iscsi() { if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg image_install -o /etc/sysconfig/tgtd - image_install "${ROOTLIBDIR:?}"/system/tgtd.service + if get_bool "$LOOKS_LIKE_DEBIAN"; then + image_install "${ROOTLIBDIR:?}"/system/tgt.service + else + image_install "${ROOTLIBDIR:?}"/system/tgtd.service + fi mkdir -p "${initdir:?}/etc/tgt" touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf # Install perl modules required by tgt-admin @@ -1495,7 +1395,7 @@ install_systemd() { # units using DynamicUser=yes. Do this only for services with test- prefix and a couple of # known-to-use DynamicUser=yes services, as setting this system-wide has many undesirable # side-effects, as it creates its own namespace. - for service in test- systemd-journal-{gatewayd,upload}; do + for service in capsule@ test- systemd-journal-{gatewayd,upload}; do mkdir -p "$initdir/etc/systemd/system/$service.service.d/" echo -ne "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/$service.service.d/99-gcov-rwpaths-override.conf" done @@ -1556,7 +1456,7 @@ install_missing_libraries() { local lib path # A number of dependencies is now optional via dlopen, so the install # script will not pick them up, since it looks at linkage. - for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1; do + for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do ddebug "Searching for $lib via pkg-config" if pkg-config --exists "$lib"; then path="$(pkg-config --variable=libdir "$lib")" @@ -1659,7 +1559,7 @@ create_empty_image() { sfdisk "$LOOPDEV" <<EOF label: gpt type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=${esp_size}M -type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=${root_size}M bootable +type=$("${SYSTEMD_ID128:?}" show root -Pu) name=root size=${root_size}M bootable type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot size=${boot_size}M type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data EOF @@ -1789,7 +1689,7 @@ check_coverage_reports() { # Create a coverage report that will later be uploaded. Remove info about system # libraries/headers and generated files, as we don't really care about them. - lcov --directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}.new" + lcov --directory "${root}/${BUILD_DIR:?}" --capture --exclude "*.gperf" --output-file "${dest}.new" if [[ -f "$dest" ]]; then # If the destination report file already exists, don't overwrite it, but # merge it with the already present one - this usually happens when @@ -1845,10 +1745,11 @@ save_journal() { 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" + PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0 + _SYSTEMD_UNIT="${TESTNAME:?}.service" + SYSLOG_IDENTIFIER="$TESTNAME.sh" + \ + PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0 if get_bool "$save"; then # If we don't have systemd-journal-remote copy all journals from /var/log/journal/ @@ -2398,7 +2299,7 @@ install_zoneinfo() { inst_any /usr/share/zoneinfo/Australia/Sydney inst_any /usr/share/zoneinfo/Europe/Berlin inst_any /usr/share/zoneinfo/Europe/Dublin - inst_any /usr/share/zoneinfo/Europe/Kiev + inst_any /usr/share/zoneinfo/Europe/Kyiv inst_any /usr/share/zoneinfo/Pacific/Auckland inst_any /usr/share/zoneinfo/Pacific/Honolulu inst_any /usr/share/zoneinfo/CET @@ -3338,6 +3239,13 @@ test_cleanup_again() { [ -n "$TESTDIR" ] || return rm -rf "$TESTDIR/unprivileged-nspawn-root" [[ -n "$initdir" ]] && _umount_dir "$initdir" + # Test specific images are not reused, so delete them or we run out of disk space + if [[ -n "$IMAGE_PUBLIC" ]] && [ "$(basename "$IMAGE_PUBLIC")" != "default.img" ]; then + rm -vf "$IMAGE_PUBLIC" + fi + if [[ -n "$IMAGE_PRIVATE" ]] && [ "$(basename "$IMAGE_PRIVATE")" != "default.img" ]; then + rm -vf "$IMAGE_PRIVATE" + fi } test_create_image() { @@ -3405,7 +3313,7 @@ test_setup() { if get_bool "$IS_BUILT_WITH_COVERAGE"; then # Do an initial coverage capture, to make sure the final report includes # files that the tests didn't touch at all - lcov --initial --capture --directory "${initdir}/${BUILD_DIR:?}" --output-file "${TESTDIR:?}/coverage-base" + lcov --initial --capture --directory "${initdir}/${BUILD_DIR:?}" --exclude "*.gperf" --output-file "${TESTDIR:?}/coverage-base" fi if get_bool "$hook_defined"; then @@ -3417,11 +3325,11 @@ test_setup() { } 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" @@ -3429,7 +3337,7 @@ test_run() { 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" @@ -3437,7 +3345,7 @@ test_run() { 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" @@ -3493,7 +3401,7 @@ do_test() { 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]" @@ -3525,7 +3433,7 @@ do_test() { 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 diff --git a/test/test-network-generator-conversion.sh b/test/test-network-generator-conversion.sh index 9a4732c..12254ac 100755 --- a/test/test-network-generator-conversion.sh +++ b/test/test-network-generator-conversion.sh @@ -49,15 +49,15 @@ run_network_generator() { rm -rf "${WORK_DIR:?}"/* stderr="$WORK_DIR/stderr" - if ! "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then + if ! SYSTEMD_LOG_LEVEL="info" "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then echo >&2 "Generator failed when parsing $SYSTEMD_PROC_CMDLINE" - cat "$stderr" + cat >&2 "$stderr" return 1 fi if [[ -s "$stderr" ]]; then echo >&2 "Generator generated unexpected messages on stderr" - cat "$stderr" + cat >&2 "$stderr" return 1 fi @@ -226,7 +226,7 @@ for f in "$TEST_DATA"/test-*.input; do "$GENERATOR_BIN" --root "$out" -- $(cat "$f") if ! diff -u "$out/run/systemd/network" "${f%.input}.expected"; then - echo "**** Unexpected output for $f" + echo >&2 "**** Unexpected output for $f" exit 1 fi diff --git a/test/test-network/conf/11-test-unit-file.link b/test/test-network/conf/11-test-unit-file.link new file mode 100644 index 0000000..429ac31 --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.link @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: MIT-0 +# +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the MIT No Attribution license). +# +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/99-default.link.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-udevd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. + +[Match] +OriginalName=* + +[Link] +NamePolicy=keep kernel database onboard slot path +AlternativeNamesPolicy=database onboard slot path +MACAddressPolicy=persistent diff --git a/test/test-network/conf/11-test-unit-file.link.d/dropin.conf b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf new file mode 100644 index 0000000..dfddc0a --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Link] diff --git a/test/test-network/conf/11-test-unit-file.netdev b/test/test-network/conf/11-test-unit-file.netdev new file mode 100644 index 0000000..86af17f --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.netdev @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=test1 +Kind=dummy diff --git a/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf new file mode 100644 index 0000000..6fde598 --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] diff --git a/test/test-network/conf/11-test-unit-file.network b/test/test-network/conf/11-test-unit-file.network new file mode 100644 index 0000000..0a4511b --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/11-test-unit-file.network.d/dropin.conf b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf new file mode 100644 index 0000000..f2f6099 --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Network] diff --git a/test/test-network/conf/21-macvlan.netdev b/test/test-network/conf/21-macvlan.netdev index fdc81ea..d742f64 100644 --- a/test/test-network/conf/21-macvlan.netdev +++ b/test/test-network/conf/21-macvlan.netdev @@ -3,3 +3,7 @@ Name=macvlan99 Kind=macvlan MTUBytes=2000 + +[MACVLAN] +BroadcastMulticastQueueLength=1234 +BroadcastQueueThreshold=2147483647 diff --git a/test/test-network/conf/24-rps-cpu-disable.link b/test/test-network/conf/24-rps-cpu-disable.link new file mode 100644 index 0000000..fb3451a --- /dev/null +++ b/test/test-network/conf/24-rps-cpu-disable.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=disable diff --git a/test/test-network/conf/24-rps-cpu-empty.link b/test/test-network/conf/24-rps-cpu-empty.link new file mode 100644 index 0000000..fc1342b --- /dev/null +++ b/test/test-network/conf/24-rps-cpu-empty.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask= diff --git a/test/test-network/conf/24-rps-cpu-invalid.link b/test/test-network/conf/24-rps-cpu-invalid.link new file mode 100644 index 0000000..76d6713 --- /dev/null +++ b/test/test-network/conf/24-rps-cpu-invalid.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=0 3 8-invalid diff --git a/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf b/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf new file mode 100644 index 0000000..a38b07c --- /dev/null +++ b/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +[Network] +# An empty string clears previously configured addresses. +Address= +Address=10.4.0.1/16 diff --git a/test/test-network/conf/25-agent-bridge-port.network b/test/test-network/conf/25-agent-bridge-port.network new file mode 100644 index 0000000..709a783 --- /dev/null +++ b/test/test-network/conf/25-agent-bridge-port.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=client-peer + +[Network] +Bridge=bridge-relay +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-agent-bridge.netdev b/test/test-network/conf/25-agent-bridge.netdev new file mode 100644 index 0000000..a611337 --- /dev/null +++ b/test/test-network/conf/25-agent-bridge.netdev @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=bridge-relay +Kind=bridge diff --git a/test/test-network/conf/25-agent-bridge.network b/test/test-network/conf/25-agent-bridge.network new file mode 100644 index 0000000..8383790 --- /dev/null +++ b/test/test-network/conf/25-agent-bridge.network @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=bridge-relay + +[Network] +Address=192.168.2.1/24 +DHCPServer=yes +IPv6AcceptRA=no + +[DHCPServer] +RelayTarget=192.168.1.1 +RelayAgentRemoteId=string:aabbccdd diff --git a/test/test-network/conf/25-agent-client-peer.network b/test/test-network/conf/25-agent-client-peer.network index e31108b..4d7d758 100644 --- a/test/test-network/conf/25-agent-client-peer.network +++ b/test/test-network/conf/25-agent-client-peer.network @@ -5,7 +5,7 @@ Name=client-peer [Network] Address=192.168.6.2/24 DHCPServer=yes -IPForward=ipv4 +IPv4Forwarding=yes IPv6AcceptRA=no [DHCPServer] diff --git a/test/test-network/conf/25-agent-client.network b/test/test-network/conf/25-agent-client.network index cfa7e5a..219d40a 100644 --- a/test/test-network/conf/25-agent-client.network +++ b/test/test-network/conf/25-agent-client.network @@ -4,5 +4,5 @@ Name=client [Network] DHCP=yes -IPForward=ipv4 +IPv4Forwarding=yes IPv6AcceptRA=no diff --git a/test/test-network/conf/25-agent-server-peer.network b/test/test-network/conf/25-agent-server-peer.network index 1f6fa4b..5e005c7 100644 --- a/test/test-network/conf/25-agent-server-peer.network +++ b/test/test-network/conf/25-agent-server-peer.network @@ -4,5 +4,5 @@ Name=server-peer [Network] Address=192.168.5.2/24 -IPForward=ipv4 +IPv4Forwarding=yes IPv6AcceptRA=no diff --git a/test/test-network/conf/25-agent-server.network b/test/test-network/conf/25-agent-server.network index 905508f..0108039 100644 --- a/test/test-network/conf/25-agent-server.network +++ b/test/test-network/conf/25-agent-server.network @@ -4,7 +4,7 @@ Name=server [Network] Address=192.168.5.1/24 -IPForward=ipv4 +IPv4Forwarding=yes DHCPServer=yes IPv6AcceptRA=no diff --git a/test/test-network/conf/25-bond-property.netdev b/test/test-network/conf/25-bond-property.netdev new file mode 100644 index 0000000..3954689 --- /dev/null +++ b/test/test-network/conf/25-bond-property.netdev @@ -0,0 +1,10 @@ +[NetDev] +Name=bond97 +Kind=bond + +[Bond] +Mode=active-backup +PrimaryReselectPolicy=always +MIIMonitorSec=1s +ARPMissedMax=10 +PeerNotifyDelaySec=300s diff --git a/test/test-network/conf/25-dhcp-server-veth-peer.network b/test/test-network/conf/25-dhcp-server-veth-peer.network index d5cc6d3..abe3fa2 100644 --- a/test/test-network/conf/25-dhcp-server-veth-peer.network +++ b/test/test-network/conf/25-dhcp-server-veth-peer.network @@ -6,3 +6,5 @@ Name=veth-peer IPv6AcceptRA=no Address=2600::1/0 Address=192.168.5.1/24 +# To make the kernel send NA with IsRouter flag. +IPv6Forwarding=yes diff --git a/test/test-network/conf/25-dummy.netdev b/test/test-network/conf/25-dummy.netdev new file mode 100644 index 0000000..d7cf7b4 --- /dev/null +++ b/test/test-network/conf/25-dummy.netdev @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=test25 +Kind=dummy diff --git a/test/test-network/conf/25-dummy.network b/test/test-network/conf/25-dummy.network new file mode 100644 index 0000000..a6e93fd --- /dev/null +++ b/test/test-network/conf/25-dummy.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-fibrule-l3mdev.network b/test/test-network/conf/25-fibrule-l3mdev.network new file mode 100644 index 0000000..a1afcd2 --- /dev/null +++ b/test/test-network/conf/25-fibrule-l3mdev.network @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no + +[RoutingPolicyRule] +Priority=1500 +L3MasterDevice=true + +[RoutingPolicyRule] +Priority=2000 +L3MasterDevice=true +Type=unreachable diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network new file mode 100644 index 0000000..04c7c49 --- /dev/null +++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no +IPv6RetransmissionTimeSec=0 diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network new file mode 100644 index 0000000..b4dbd06 --- /dev/null +++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no +IPv6RetransmissionTimeSec=3 diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network new file mode 100644 index 0000000..cbdf4f3 --- /dev/null +++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no +IPv6RetransmissionTimeSec=4 diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network new file mode 100644 index 0000000..085cb30 --- /dev/null +++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no +IPv6RetransmissionTimeSec=infinity diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network new file mode 100644 index 0000000..8a0bf83 --- /dev/null +++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no +IPv6RetransmissionTimeSec=-2 diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network new file mode 100644 index 0000000..0976bae --- /dev/null +++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test25 + +[Network] +IPv6AcceptRA=no +IPv6RetransmissionTimeSec=999999999999999999 diff --git a/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network b/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network index ac50700..11502fd 100644 --- a/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network +++ b/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network @@ -6,8 +6,6 @@ Name=veth99 IPv6AcceptRA=true [IPv6AcceptRA] -Token=prefixstable:2002:da8:1:: -Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a # invalid tokens Token=prefixstable:2002:da8:1::,00000000000000000000000000000000 Token=prefixstable:2002:da8:1::, @@ -17,3 +15,10 @@ Token=prefixstable@ Token=static Token=static: Token=static::: +# valid token +Token=prefixstable:2002:da8:1:: +Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a +# reset token +Token= +# set token again +Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a diff --git a/test/test-network/conf/25-ipv6-prefix.network b/test/test-network/conf/25-ipv6-prefix.network index 9cf8f25..e801581 100644 --- a/test/test-network/conf/25-ipv6-prefix.network +++ b/test/test-network/conf/25-ipv6-prefix.network @@ -10,6 +10,8 @@ IPv6SendRA=yes DNS=_link_local 2002:da8:1:0::1 DNSLifetimeSec=1min Domains=hogehoge.test +ReachableTimeSec=42 +RetransmitSec=500ms [IPv6Prefix] Prefix=2002:da8:1:0::/64 diff --git a/test/test-network/conf/25-ipv6-proxy-ndp.network b/test/test-network/conf/25-ipv6-proxy-ndp.network index 81302ab..d01a633 100644 --- a/test/test-network/conf/25-ipv6-proxy-ndp.network +++ b/test/test-network/conf/25-ipv6-proxy-ndp.network @@ -9,7 +9,6 @@ IPv6ProxyNDPAddress=2607:5300:203:5215:3::1 IPv6ProxyNDPAddress=2607:5300:203:5215:2::1 IPv6ProxyNDPAddress=2607:5300:203:5215:1::1 IPv6AcceptRA=no -IPForward=yes Address=66.70.129.136/32 Address=66.70.129.142/32 Address=66.70.129.143/32 diff --git a/test/test-network/conf/25-neighbor-ip-dummy.network b/test/test-network/conf/25-neighbor-dummy.network index f1b1151..f1b1151 100644 --- a/test/test-network/conf/25-neighbor-ip-dummy.network +++ b/test/test-network/conf/25-neighbor-dummy.network diff --git a/test/test-network/conf/25-neighbor-section.network b/test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf index 59e21eb..727089e 100644 --- a/test/test-network/conf/25-neighbor-section.network +++ b/test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf @@ -1,10 +1,4 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -[Match] -Name=dummy98 - -[Network] -IPv6AcceptRA=no - [Neighbor] Address=192.168.10.1 LinkLayerAddress=00:00:5e:00:02:65 diff --git a/test/test-network/conf/25-neighbor-section.network.d/override.conf b/test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf index 01027e3..e28e3fc 100644 --- a/test/test-network/conf/25-neighbor-section.network.d/override.conf +++ b/test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf @@ -1,5 +1,4 @@ # SPDX-License-Identifier: LGPL-2.1-or-later - [Neighbor] Address=192.168.10.1 LinkLayerAddress=00:00:5e:00:03:65 diff --git a/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf b/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf new file mode 100644 index 0000000..9262b74 --- /dev/null +++ b/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Neighbor] +Address=192.168.10.1 +LinkLayerAddress=00:00:5e:00:03:66 diff --git a/test/test-network/conf/25-netdevsim.link b/test/test-network/conf/25-netdevsim.link new file mode 100644 index 0000000..f8beb55 --- /dev/null +++ b/test/test-network/conf/25-netdevsim.link @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Driver=netdevsim + +[Link] +NamePolicy=keep kernel database onboard slot path +AlternativeNamesPolicy=database onboard slot path mac +# Also set a fixed name. Workaround for bug in kernel 6.9: +# https://github.com/torvalds/linux/commit/8debcf5832c3e8a6baaea27c75ad8a6ba5077beb +AlternativeName=sim99 +MACAddressPolicy=persistent diff --git a/test/test-network/conf/25-nexthop.network b/test/test-network/conf/25-nexthop-1.network index f53a58b..a5a8d81 100644 --- a/test/test-network/conf/25-nexthop.network +++ b/test/test-network/conf/25-nexthop-1.network @@ -6,7 +6,6 @@ Name=veth99 IPv6AcceptRA=no Address=2001:1234:5:8f63::1/120 Address=192.168.5.10/24 -Gateway=192.168.5.1 [NextHop] Id=1 diff --git a/test/test-network/conf/25-nexthop-2.network b/test/test-network/conf/25-nexthop-2.network new file mode 100644 index 0000000..63062f3 --- /dev/null +++ b/test/test-network/conf/25-nexthop-2.network @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=veth99 + +[Network] +IPv6AcceptRA=no +Address=2001:1234:5:8f63::1/120 +Address=192.168.5.10/24 + +# Commented out lines are specified in 25-nexthop.network + +[NextHop] +#Id=1 +Id=6 +Gateway=192.168.5.1 + +[NextHop] +#Id=2 +Id=7 +Gateway=2001:1234:5:8f63::2 + +[NextHop] +#Id=3 +Id=4 +Family=ipv6 + +[NextHop] +#Id=4 +Id=3 +Family=ipv4 + +[NextHop] +Id=5 +#Gateway=192.168.10.1 +#OnLink=yes +Gateway=192.168.5.3 +OnLink=no + +[NextHop] +#Id=6 +Id=1 +Family=ipv4 +Blackhole=yes + +[NextHop] +#Id=7 +Id=2 +Family=ipv6 +Blackhole=yes + +[NextHop] +Id=8 +Gateway=fe80::222:4dff:ff:ff:ff:ff + +[NextHop] +Gateway=192.168.5.2 + +[NextHop] +Family=ipv4 +Blackhole=yes + +[NextHop] +Family=ipv6 +Blackhole=yes + +[Route] +#NextHop=1 +NextHop=6 +Destination=10.10.10.10 + +[Route] +#NextHop=2 +NextHop=7 +Destination=10.10.10.11 + +[Route] +#NextHop=2 +NextHop=7 +Destination=2001:1234:5:8f62::1 + +[Route] +NextHop=5 +Destination=10.10.10.12 + +[Route] +#NextHop=6 +NextHop=1 +Destination=10.10.10.13 + +[Route] +#NextHop=7 +NextHop=2 +Destination=2001:1234:5:8f62::2 + +[Route] +#NextHop=21 +NextHop=20 +Destination=10.10.10.14 diff --git a/test/test-network/conf/25-nexthop-dummy.network b/test/test-network/conf/25-nexthop-dummy-1.network index a7bdaa9..a7bdaa9 100644 --- a/test/test-network/conf/25-nexthop-dummy.network +++ b/test/test-network/conf/25-nexthop-dummy-1.network diff --git a/test/test-network/conf/25-nexthop-dummy-2.network b/test/test-network/conf/25-nexthop-dummy-2.network new file mode 100644 index 0000000..2556b1f --- /dev/null +++ b/test/test-network/conf/25-nexthop-dummy-2.network @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=dummy98 + +[Network] +Address=192.168.20.20/24 +IPv6AcceptRA=no + +# Commented out lines are specified in 25-nexthop-dummy.network + +[NextHop] +#Id=20 +Id=21 +Gateway=192.168.20.1 + +[NextHop] +#Id=21 +#Group=1:3 20:1 +Id=20 +Group=5:3 21:1 diff --git a/test/test-network/conf/25-nexthop-test1.network b/test/test-network/conf/25-nexthop-test1.network new file mode 100644 index 0000000..5a4c596 --- /dev/null +++ b/test/test-network/conf/25-nexthop-test1.network @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test1 + +[Network] +Address=192.168.20.21/24 +IPv6AcceptRA=no + +[Route] +Destination=10.10.11.10 +# Nexthop 21 is configured as a group nexthop of 1 and 20 +NextHop=21 diff --git a/test/test-network/conf/25-route-static.network b/test/test-network/conf/25-route-static.network index 7ef211d..5ddd7de 100644 --- a/test/test-network/conf/25-route-static.network +++ b/test/test-network/conf/25-route-static.network @@ -96,7 +96,7 @@ MultiPathRoute=149.10.124.59 10 MultiPathRoute=149.10.124.60 5 [Route] -Destination=2001:1234:5:7fff:ff:ff:ff:ff/128 +Destination=2001:1234:5:bfff:ff:ff:ff:ff/128 MultiPathRoute=2001:1234:5:6fff:ff:ff:ff:ff@test1 20 MultiPathRoute=2001:1234:5:7fff:ff:ff:ff:ff@test1 30 MultiPathRoute=2001:1234:5:8fff:ff:ff:ff:ff@dummy98 10 diff --git a/test/test-network/conf/25-rps-cpu-0-1.link b/test/test-network/conf/25-rps-cpu-0-1.link new file mode 100644 index 0000000..d026248 --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-0-1.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=0 1 diff --git a/test/test-network/conf/25-rps-cpu-0-empty.link b/test/test-network/conf/25-rps-cpu-0-empty.link new file mode 100644 index 0000000..b25b417 --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-0-empty.link @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=0 +ReceivePacketSteeringCPUMask= diff --git a/test/test-network/conf/25-rps-cpu-0-invalid.link b/test/test-network/conf/25-rps-cpu-0-invalid.link new file mode 100644 index 0000000..26147a0 --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-0-invalid.link @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=0 +ReceivePacketSteeringCPUMask=invalid diff --git a/test/test-network/conf/25-rps-cpu-0.link b/test/test-network/conf/25-rps-cpu-0.link new file mode 100644 index 0000000..b1f4bc2 --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-0.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=0 diff --git a/test/test-network/conf/25-rps-cpu-1.link b/test/test-network/conf/25-rps-cpu-1.link new file mode 100644 index 0000000..d24d713 --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-1.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=1 diff --git a/test/test-network/conf/25-rps-cpu-all.link b/test/test-network/conf/25-rps-cpu-all.link new file mode 100644 index 0000000..b7a8eda --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-all.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=all diff --git a/test/test-network/conf/25-rps-cpu-multi.link b/test/test-network/conf/25-rps-cpu-multi.link new file mode 100644 index 0000000..d7d4d04 --- /dev/null +++ b/test/test-network/conf/25-rps-cpu-multi.link @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=dummy98 + +[Link] +ReceivePacketSteeringCPUMask=0 1 +ReceivePacketSteeringCPUMask=2,3 diff --git a/test/test-network/conf/25-sriov-udev.network b/test/test-network/conf/25-sriov-udev.network index e914131..308f5a0 100644 --- a/test/test-network/conf/25-sriov-udev.network +++ b/test/test-network/conf/25-sriov-udev.network @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Match] -Name=eni99np1 +Name=sim99 [Network] Address=192.168.100.100/24 diff --git a/test/test-network/conf/25-sriov.link b/test/test-network/conf/25-sriov.link index cc19561..8f6e377 100644 --- a/test/test-network/conf/25-sriov.link +++ b/test/test-network/conf/25-sriov.link @@ -5,6 +5,9 @@ Driver=netdevsim [Link] NamePolicy=keep kernel database onboard slot path AlternativeNamesPolicy=database onboard slot path mac +# Also set a fixed name. Workaround for bug in kernel 6.9: +# https://github.com/torvalds/linux/commit/8debcf5832c3e8a6baaea27c75ad8a6ba5077beb +AlternativeName=sim99 MACAddressPolicy=persistent [SR-IOV] diff --git a/test/test-network/conf/25-sriov.network b/test/test-network/conf/25-sriov.network index d87615e..46573d9 100644 --- a/test/test-network/conf/25-sriov.network +++ b/test/test-network/conf/25-sriov.network @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Match] -Name=eni99np1 +Name=sim99 [Network] Address=192.168.100.100/24 diff --git a/test/test-network/conf/25-sysctl.network b/test/test-network/conf/25-sysctl.network index a71ffb2..759fe65 100644 --- a/test/test-network/conf/25-sysctl.network +++ b/test/test-network/conf/25-sysctl.network @@ -3,10 +3,12 @@ Name=dummy98 [Network] -IPForward=yes +IPv4Forwarding=yes +IPv6Forwarding=yes IPv6DuplicateAddressDetection=3 IPv6HopLimit=5 IPv4ProxyARP=yes +IPv4ProxyARPPrivateVLAN=yes IPv6ProxyNDP=yes IPv6AcceptRA=no IPv4AcceptLocal=yes diff --git a/test/test-network/conf/25-veth-bridge.network b/test/test-network/conf/25-veth-bridge.network index b2f1634..a8be06d 100644 --- a/test/test-network/conf/25-veth-bridge.network +++ b/test/test-network/conf/25-veth-bridge.network @@ -1,10 +1,10 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Match] Name=client-p +Name=router-p Name=router-high-p Name=router-low-p [Network] Bridge=bridge99 IPv6AcceptRA=no -IPv6SendRA=yes diff --git a/test/test-network/conf/25-veth-peer-no-address.network b/test/test-network/conf/25-veth-peer-no-address.network new file mode 100644 index 0000000..dbea3bd --- /dev/null +++ b/test/test-network/conf/25-veth-peer-no-address.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=veth-peer + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-veth-router-high2.network b/test/test-network/conf/25-veth-router-high2.network new file mode 100644 index 0000000..47e8cd7 --- /dev/null +++ b/test/test-network/conf/25-veth-router-high2.network @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=router-low + +[Network] +IPv6AcceptRA=no +IPv6SendRA=yes + +[IPv6SendRA] +# changed from low to high +RouterPreference=high +EmitDNS=no +EmitDomains=no + +[IPv6Prefix] +Prefix=2002:da8:1:98::/64 +PreferredLifetimeSec=1000s +ValidLifetimeSec=2100s diff --git a/test/test-network/conf/25-veth-router-hop-limit.network b/test/test-network/conf/25-veth-router-hop-limit.network new file mode 100644 index 0000000..643e362 --- /dev/null +++ b/test/test-network/conf/25-veth-router-hop-limit.network @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=router + +[Network] +IPv6AcceptRA=no +IPv6SendRA=yes + +[IPv6SendRA] +RouterPreference=high +EmitDNS=no +EmitDomains=no +HopLimit=42 + +[IPv6Prefix] +Prefix=2002:da8:1:99::/64 +PreferredLifetimeSec=1000s +ValidLifetimeSec=2100s diff --git a/test/test-network/conf/25-veth-router-low2.network b/test/test-network/conf/25-veth-router-low2.network new file mode 100644 index 0000000..f318938 --- /dev/null +++ b/test/test-network/conf/25-veth-router-low2.network @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=router-high + +[Network] +IPv6AcceptRA=no +IPv6SendRA=yes + +[IPv6SendRA] +# changed from high to low +RouterPreference=low +EmitDNS=no +EmitDomains=no + +[IPv6Prefix] +Prefix=2002:da8:1:99::/64 +PreferredLifetimeSec=1000s +ValidLifetimeSec=2100s diff --git a/test/test-network/conf/25-veth-router.netdev b/test/test-network/conf/25-veth-router.netdev new file mode 100644 index 0000000..d462313 --- /dev/null +++ b/test/test-network/conf/25-veth-router.netdev @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=router +Kind=veth +MACAddress=12:34:56:78:9a:99 + +[Peer] +Name=router-p +MACAddress=12:34:56:78:9b:99 diff --git a/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt b/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt new file mode 100644 index 0000000..b4251c3 --- /dev/null +++ b/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt @@ -0,0 +1 @@ +192.168.27.3:51820 diff --git a/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt b/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt new file mode 100644 index 0000000..8011c64 --- /dev/null +++ b/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt @@ -0,0 +1 @@ +EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong= diff --git a/test/test-network/conf/25-wireguard-no-peer.netdev b/test/test-network/conf/25-wireguard-no-peer.netdev index ce3b31a..8c90735 100644 --- a/test/test-network/conf/25-wireguard-no-peer.netdev +++ b/test/test-network/conf/25-wireguard-no-peer.netdev @@ -4,6 +4,6 @@ Name=wg97 Kind=wireguard [WireGuard] -PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong= +#PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong= ListenPort=51821 FwMark=1235 diff --git a/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt b/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt new file mode 100644 index 0000000..5e79c19 --- /dev/null +++ b/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt @@ -0,0 +1 @@ +6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I= diff --git a/test/test-network/conf/25-wireguard.netdev b/test/test-network/conf/25-wireguard.netdev index 4fed38e..6a2bb88 100644 --- a/test/test-network/conf/25-wireguard.netdev +++ b/test/test-network/conf/25-wireguard.netdev @@ -13,8 +13,8 @@ RouteMetric=456 [WireGuardPeer] PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= AllowedIPs=fd31:bf08:57cb::/48,192.168.26.3/24 -#Endpoint=wireguard.example.com:51820 -Endpoint=192.168.27.3:51820 +#Endpoint=192.168.27.3:51820 +Endpoint=@network.wireguard.peer0.endpoint PresharedKey=IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M= PersistentKeepalive=20 RouteTable=1234 diff --git a/test/test-network/conf/25-wireguard.netdev.d/peer2.conf b/test/test-network/conf/25-wireguard.netdev.d/peer2.conf index bf99a5a..f3440df 100644 --- a/test/test-network/conf/25-wireguard.netdev.d/peer2.conf +++ b/test/test-network/conf/25-wireguard.netdev.d/peer2.conf @@ -1,5 +1,5 @@ [WireGuardPeer] PublicKey=9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= -PresharedKey=6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I= +PresharedKey=@network.wireguard.peer2.psk AllowedIPs=192.168.124.3 diff --git a/test/test-network/conf/26-bridge-mac-master.network b/test/test-network/conf/26-bridge-mac-master.network new file mode 100644 index 0000000..d08970b --- /dev/null +++ b/test/test-network/conf/26-bridge-mac-master.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=bridge99 + +[Network] +IPv6AcceptRA=false diff --git a/test/test-network/conf/25-neighbor-next.network b/test/test-network/conf/26-bridge-mac-slave.network index 6911f48..81a0b46 100644 --- a/test/test-network/conf/25-neighbor-next.network +++ b/test/test-network/conf/26-bridge-mac-slave.network @@ -4,7 +4,4 @@ Name=dummy98 [Network] IPv6AcceptRA=no - -[Neighbor] -Address=192.168.10.1 -LinkLayerAddress=00:00:5e:00:02:66 +Bridge=bridge99 diff --git a/test/test-network/conf/26-bridge-mac.link b/test/test-network/conf/26-bridge-mac.link new file mode 100644 index 0000000..82ed937 --- /dev/null +++ b/test/test-network/conf/26-bridge-mac.link @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +OriginalName=bridge99 + +[Link] +MACAddressPolicy=none diff --git a/test/test-network/conf/26-bridge-mac.netdev b/test/test-network/conf/26-bridge-mac.netdev new file mode 100644 index 0000000..2d26a03 --- /dev/null +++ b/test/test-network/conf/26-bridge-mac.netdev @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=bridge99 +Kind=bridge +MACAddress=none diff --git a/test/test-network/conf/26-bridge-vlan-master-issue-20373.network b/test/test-network/conf/26-bridge-vlan-master-issue-20373.network index 7a69757..67011ac 100644 --- a/test/test-network/conf/26-bridge-vlan-master-issue-20373.network +++ b/test/test-network/conf/26-bridge-vlan-master-issue-20373.network @@ -4,7 +4,6 @@ Name=bridge99 [Network] VLAN=vlan99 -IPForward=yes ConfigureWithoutCarrier=yes LLDP=yes IPv6AcceptRA=false diff --git a/test/test-network/conf/26-bridge-vlan-master.network b/test/test-network/conf/26-bridge-vlan-master.network index 4bbbc56..90ae0b2 100644 --- a/test/test-network/conf/26-bridge-vlan-master.network +++ b/test/test-network/conf/26-bridge-vlan-master.network @@ -6,4 +6,8 @@ Name=bridge99 IPv6AcceptRA=false [BridgeVLAN] -VLAN=4060-4094 +PVID=1020 +VLAN=1018-1023 +VLAN=1200-1210 +EgressUntagged=1022-1025 +EgressUntagged=1203-1208 diff --git a/test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf b/test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf new file mode 100644 index 0000000..25d213f --- /dev/null +++ b/test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[BridgeVLAN] +PVID= +VLAN= +EgressUntagged= + +PVID=2020 +VLAN=2018-2023 +VLAN=2200-2210 +EgressUntagged=2022-2025 +EgressUntagged=2203-2208 diff --git a/test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf b/test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf new file mode 100644 index 0000000..0dcaee0 --- /dev/null +++ b/test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[BridgeVLAN] +PVID= +VLAN= +EgressUntagged= + +PVID=2020 +VLAN=2018-2023 +EgressUntagged=2022-2025 diff --git a/test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf b/test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf new file mode 100644 index 0000000..409296f --- /dev/null +++ b/test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[BridgeVLAN] +PVID=no +VLAN= +EgressUntagged= diff --git a/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network b/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network index 876219f..bc7c3b0 100644 --- a/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network +++ b/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network @@ -4,7 +4,6 @@ Name=test1 [Network] IPv6AcceptRA=no -IPForward=yes Bridge=bridge99 LinkLocalAddressing=no EmitLLDP=nearest-bridge diff --git a/test/test-network/conf/26-bridge-vlan-slave.network b/test/test-network/conf/26-bridge-vlan-slave.network index 9ac8510..d23d0fc 100644 --- a/test/test-network/conf/26-bridge-vlan-slave.network +++ b/test/test-network/conf/26-bridge-vlan-slave.network @@ -7,4 +7,8 @@ IPv6AcceptRA=no Bridge=bridge99 [BridgeVLAN] -VLAN=4064-4094 +PVID=1010 +VLAN=1008-1013 +VLAN=1100-1110 +EgressUntagged=1012-1015 +EgressUntagged=1103-1108 diff --git a/test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf b/test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf new file mode 100644 index 0000000..0511b64 --- /dev/null +++ b/test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[BridgeVLAN] +PVID= +VLAN= +EgressUntagged= + +PVID=2010 +VLAN=2008-2013 +VLAN=2100-2110 +EgressUntagged=2012-2015 +EgressUntagged=2103-2108 diff --git a/test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf b/test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf new file mode 100644 index 0000000..58df686 --- /dev/null +++ b/test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[BridgeVLAN] +PVID= +VLAN= +EgressUntagged= + +PVID=2010 +VLAN=2008-2013 +EgressUntagged=2012-2015 diff --git a/test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf b/test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf new file mode 100644 index 0000000..409296f --- /dev/null +++ b/test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[BridgeVLAN] +PVID=no +VLAN= +EgressUntagged= diff --git a/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf new file mode 100644 index 0000000..9bf5d7e --- /dev/null +++ b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Network] +ManageForeignNextHops=no diff --git a/test/test-network/conf/persist-leases-no.conf b/test/test-network/conf/persist-leases-no.conf new file mode 100644 index 0000000..5025265 --- /dev/null +++ b/test/test-network/conf/persist-leases-no.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[DHCPServer] +PersistLeases=no diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index a871d19..92cb07f 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4,6 +4,16 @@ # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM, # simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py. +# +# To run an individual test, specify it as a command line argument in the form +# of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test +# function called test_ipv6_mtu(). To run just that test use: +# +# sudo ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu +# +# Similarly, other individual tests can be run, eg.: +# +# sudo ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time import argparse import datetime @@ -12,6 +22,7 @@ import itertools import json import os import pathlib +import random import re import shutil import signal @@ -27,6 +38,7 @@ network_unit_dir = '/run/systemd/network' networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d' networkd_ci_temp_dir = '/run/networkd-ci' udev_rules_dir = '/run/udev/rules.d' +credstore_dir = '/run/credstore' dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid' dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log' @@ -39,18 +51,18 @@ radvd_pid_file = '/run/networkd-ci/test-radvd.pid' systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd'] which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':')) -systemd_source_dir = None networkd_bin = shutil.which('systemd-networkd', path=which_paths) resolved_bin = shutil.which('systemd-resolved', path=which_paths) timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths) -udevd_bin = shutil.which('systemd-udevd', path=which_paths) wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths) networkctl_bin = shutil.which('networkctl', path=which_paths) resolvectl_bin = shutil.which('resolvectl', path=which_paths) timedatectl_bin = shutil.which('timedatectl', path=which_paths) udevadm_bin = shutil.which('udevadm', path=which_paths) -systemd_udev_rules_build_dir = None +test_ndisc_send = None +build_dir = None +source_dir = None use_valgrind = False valgrind_cmd = '' @@ -61,6 +73,7 @@ asan_options = None lsan_options = None ubsan_options = None with_coverage = False +show_journal = True # When true, show journal on stopping networkd. active_units = [] protected_links = { @@ -93,7 +106,7 @@ def cp(src, dst): shutil.copy(src, dst) def cp_r(src, dst): - shutil.copytree(src, dst, copy_function=shutil.copy) + shutil.copytree(src, dst, copy_function=shutil.copy, dirs_exist_ok=True) def mkdir_p(path): os.makedirs(path, exist_ok=True) @@ -180,8 +193,10 @@ def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable(): def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable(): def f(func): - rc = call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7') - call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7') + # IP protocol name is parsed by getprotobyname(), and it requires /etc/protocols. + # Hence. here we use explicit number: 6 == tcp. + rc = call_quiet('ip rule add not from 192.168.100.19 ipproto 6 table 7') + call_quiet('ip rule del not from 192.168.100.19 ipproto 6 table 7') return func if rc == 0 else unittest.expectedFailure(func) return f @@ -197,6 +212,14 @@ def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable(): return f +def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable(): + def f(func): + rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev') + call_quiet('ip rule del not from 192.168.100.19 l3mdev') + return func if rc == 0 else unittest.expectedFailure(func) + + return f + def expectedFailureIfNexthopIsNotAvailable(): def f(func): rc = call_quiet('ip nexthop list') @@ -246,6 +269,21 @@ def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable(): return f +def expectedFailureIfKernelReturnsInvalidFlags(): + ''' + This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9), + fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3). + ''' + def f(func): + call_quiet('ip link add dummy98 type dummy') + call_quiet('ip link set up dev dummy98') + call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute') + output = check_output('ip address show dev dummy98') + remove_link('dummy98') + return func if 'noprefixroute' in output else unittest.expectedFailure(func) + + return f + # pylint: disable=C0415 def compare_kernel_version(min_kernel_version): try: @@ -258,12 +296,11 @@ def compare_kernel_version(min_kernel_version): # Get only the actual kernel version without any build/distro/arch stuff # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5' kver = platform.release().split('-')[0] + # Get also rid of '+' + kver = kver.split('+')[0] return version.parse(kver) >= version.parse(min_kernel_version) -def udev_reload(): - check_output(*udevadm_cmd, 'control', '--reload') - def copy_network_unit(*units, copy_dropins=True): """ Copy networkd unit files into the testbed. @@ -294,7 +331,12 @@ def copy_network_unit(*units, copy_dropins=True): has_link = True if has_link: - udev_reload() + udevadm_reload() + +def copy_credential(src, target): + mkdir_p(credstore_dir) + cp(os.path.join(networkd_ci_temp_dir, src), + os.path.join(credstore_dir, target)) def remove_network_unit(*units): """ @@ -311,7 +353,7 @@ def remove_network_unit(*units): has_link = True if has_link: - udev_reload() + udevadm_reload() def clear_network_units(): has_link = False @@ -324,7 +366,7 @@ def clear_network_units(): rm_rf(network_unit_dir) if has_link: - udev_reload() + udevadm_reload() def copy_networkd_conf_dropin(*dropins): """Copy networkd.conf dropin files into the testbed.""" @@ -341,12 +383,16 @@ def clear_networkd_conf_dropins(): rm_rf(networkd_conf_dropin_dir) def setup_systemd_udev_rules(): - if not systemd_udev_rules_build_dir: + if not build_dir and not source_dir: return mkdir_p(udev_rules_dir) - for path in [systemd_udev_rules_build_dir, os.path.join(systemd_source_dir, "rules.d")]: + for path in [build_dir, source_dir]: + if not path: + continue + + path = os.path.join(path, "rules.d") print(f"Copying udev rules from {path} to {udev_rules_dir}") for rule in os.listdir(path): @@ -354,6 +400,9 @@ def setup_systemd_udev_rules(): continue cp(os.path.join(path, rule), udev_rules_dir) +def clear_networkd_state_files(): + rm_rf('/var/lib/systemd/network/') + def copy_udev_rule(*rules): """Copy udev rules""" mkdir_p(udev_rules_dir) @@ -388,11 +437,12 @@ def create_unit_dropin(unit, contents): f.write('\n'.join(contents)) def create_service_dropin(service, command, additional_settings=None): - drop_in = [ - '[Service]', - 'ExecStart=', - f'ExecStart=!!{valgrind_cmd}{command}', - ] + drop_in = ['[Service]'] + if command: + drop_in += [ + 'ExecStart=', + f'ExecStart=!!{valgrind_cmd}{command}', + ] if enable_debug: drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug'] if asan_options: @@ -420,11 +470,99 @@ def create_service_dropin(service, command, additional_settings=None): create_unit_dropin(f'{service}.service', drop_in) -def link_exists(link): - return call_quiet(f'ip link show {link}') == 0 +def setup_system_units(): + if build_dir or source_dir: + mkdir_p('/run/systemd/system/') + + for unit in [ + 'systemd-networkd.service', + 'systemd-networkd.socket', + 'systemd-networkd-persistent-storage.service', + 'systemd-resolved.service', + 'systemd-timesyncd.service', + 'systemd-udevd.service', + ]: + for path in [build_dir, source_dir]: + if not path: + continue + + fullpath = os.path.join(os.path.join(path, "units"), unit) + if os.path.exists(fullpath): + print(f"Copying unit file from {fullpath} to /run/systemd/system/") + cp(fullpath, '/run/systemd/system/') + break + + create_service_dropin('systemd-networkd', networkd_bin, + ['[Service]', + 'Restart=no', + 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes', + '[Unit]', + 'StartLimitIntervalSec=0']) + create_service_dropin('systemd-resolved', resolved_bin) + create_service_dropin('systemd-timesyncd', timesyncd_bin) + + # TODO: also run udevd with sanitizers, valgrind, or coverage + create_unit_dropin( + 'systemd-udevd.service', + [ + '[Service]', + 'ExecStart=', + f'ExecStart=!!@{udevadm_bin} systemd-udevd', + ] + ) + create_unit_dropin( + 'systemd-networkd.socket', + [ + '[Unit]', + 'StartLimitIntervalSec=0', + ] + ) + create_unit_dropin( + 'systemd-networkd-persistent-storage.service', + [ + '[Unit]', + 'StartLimitIntervalSec=0', + '[Service]', + 'ExecStart=', + f'ExecStart={networkctl_bin} persistent-storage yes', + 'ExecStop=', + f'ExecStop={networkctl_bin} persistent-storage no', + 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug else '', + ] + ) + + check_output('systemctl daemon-reload') + print(check_output('systemctl cat systemd-networkd.service')) + print(check_output('systemctl cat systemd-networkd-persistent-storage.service')) + print(check_output('systemctl cat systemd-resolved.service')) + print(check_output('systemctl cat systemd-timesyncd.service')) + print(check_output('systemctl cat systemd-udevd.service')) + check_output('systemctl restart systemd-resolved.service') + check_output('systemctl restart systemd-timesyncd.service') + check_output('systemctl restart systemd-udevd.service') + +def clear_system_units(): + def rm_unit(name): + rm_f(f'/run/systemd/system/{name}') + rm_rf(f'/run/systemd/system/{name}.d') + + rm_unit('systemd-networkd.service') + rm_unit('systemd-networkd.socket') + rm_unit('systemd-networkd-persistent-storage.service') + rm_unit('systemd-resolved.service') + rm_unit('systemd-timesyncd.service') + rm_unit('systemd-udevd.service') + check_output('systemctl daemon-reload') + check_output('systemctl restart systemd-udevd.service') + +def link_exists(*links): + for link in links: + if call_quiet(f'ip link show {link}') != 0: + return False + return True def link_resolve(link): - return check_output(f'ip link show {link}').split(':')[1].strip() + return check_output(f'ip link show {link}').split(':')[1].strip().split('@')[0] def remove_link(*links, protect=False): for link in links: @@ -442,6 +580,15 @@ def save_existing_links(): print('### The following links will be protected:') print(', '.join(sorted(list(protected_links)))) +def unmanage_existing_links(): + mkdir_p(network_unit_dir) + + with open(os.path.join(network_unit_dir, '00-unmanaged.network'), mode='w', encoding='utf-8') as f: + f.write('[Match]\n') + for link in protected_links: + f.write(f'Name={link}\n') + f.write('\n[Link]\nUnmanaged=yes\n') + def flush_links(): links = os.listdir('/sys/class/net') remove_link(*links, protect=True) @@ -565,9 +712,16 @@ def read_ip_sysctl_attr(link, attribute, ipv): with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f: return f.readline().strip() +def read_ip_neigh_sysctl_attr(link, attribute, ipv): + with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f: + return f.readline().strip() + def read_ipv6_sysctl_attr(link, attribute): return read_ip_sysctl_attr(link, attribute, 'ipv6') +def read_ipv6_neigh_sysctl_attr(link, attribute): + return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6') + def read_ipv4_sysctl_attr(link, attribute): return read_ip_sysctl_attr(link, attribute, 'ipv4') @@ -700,11 +854,16 @@ def radvd_check_config(config_file): def networkd_invocation_id(): return check_output('systemctl show --value -p InvocationID systemd-networkd.service') +def networkd_pid(): + return check_output('systemctl show --value -p MainPID systemd-networkd.service') + def read_networkd_log(invocation_id=None, since=None): if not invocation_id: invocation_id = networkd_invocation_id() command = [ 'journalctl', + '--no-hostname', + '--output=short-monotonic', f'_SYSTEMD_INVOCATION_ID={invocation_id}', ] if since: @@ -716,23 +875,36 @@ def networkd_is_failed(): return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1 def stop_networkd(show_logs=True): + global show_journal + show_logs = show_logs and show_journal if show_logs: invocation_id = networkd_invocation_id() check_output('systemctl stop systemd-networkd.socket') check_output('systemctl stop systemd-networkd.service') if show_logs: print(read_networkd_log(invocation_id)) + # Check if networkd exits cleanly. + assert not networkd_is_failed() def start_networkd(): check_output('systemctl start systemd-networkd') + invocation_id = networkd_invocation_id() + pid = networkd_pid() + print(f'Started systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}') def restart_networkd(show_logs=True): + global show_journal + show_logs = show_logs and show_journal if show_logs: invocation_id = networkd_invocation_id() check_output('systemctl restart systemd-networkd.service') if show_logs: print(read_networkd_log(invocation_id)) + invocation_id = networkd_invocation_id() + pid = networkd_pid() + print(f'Restarted systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}') + def networkd_pid(): return int(check_output('systemctl show --value -p MainPID systemd-networkd.service')) @@ -751,12 +923,8 @@ def networkctl_json(*args): def networkctl_reconfigure(*links): networkctl('reconfigure', *links) -def networkctl_reload(sleep_time=1): +def networkctl_reload(): networkctl('reload') - # 'networkctl reload' asynchronously reconfigure links. - # Hence, we need to wait for a short time for link to be in configuring state. - if sleep_time > 0: - time.sleep(sleep_time) def resolvectl(*args): return check_output(*(resolvectl_cmd + list(args)), env=env) @@ -764,8 +932,24 @@ def resolvectl(*args): def timedatectl(*args): return check_output(*(timedatectl_cmd + list(args)), env=env) +def udevadm(*args): + return check_output(*(udevadm_cmd + list(args))) + +def udevadm_reload(): + udevadm('control', '--reload') + +def udevadm_trigger(*args, action='add'): + udevadm('trigger', '--settle', f'--action={action}', *args) + def setup_common(): + # Protect existing links + unmanage_existing_links() + + # We usually show something in each test. So, let's break line to make the title of a test and output + # from the test mixed. Then, flush stream buffer and journals. print() + sys.stdout.flush() + check_output('journalctl --sync') def tear_down_common(): # 1. stop DHCP/RA servers @@ -790,6 +974,7 @@ def tear_down_common(): # 6. remove configs clear_network_units() clear_networkd_conf_dropins() + clear_networkd_state_files() # 7. flush settings flush_fou_ports() @@ -797,12 +982,17 @@ def tear_down_common(): flush_routing_policy_rules() flush_routes() + # 8. flush stream buffer and journals to make not any output from the test with the next one + sys.stdout.flush() + check_output('journalctl --sync') + def setUpModule(): rm_rf(networkd_ci_temp_dir) cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir) clear_network_units() clear_networkd_conf_dropins() + clear_networkd_state_files() clear_udev_rules() setup_systemd_udev_rules() @@ -815,68 +1005,32 @@ def setUpModule(): save_routing_policy_rules() save_timezone() - create_service_dropin('systemd-networkd', networkd_bin, - ['[Service]', - 'Restart=no', - 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes', - '[Unit]', - 'StartLimitIntervalSec=0']) - create_service_dropin('systemd-resolved', resolved_bin) - create_service_dropin('systemd-timesyncd', timesyncd_bin) - - # TODO: also run udevd with sanitizers, valgrind, or coverage - #create_service_dropin('systemd-udevd', udevd_bin, - # f'{udevadm_bin} control --reload --timeout 0') - create_unit_dropin( - 'systemd-udevd.service', - [ - '[Service]', - 'ExecStart=', - f'ExecStart=!!@{udevd_bin} systemd-udevd', - ] - ) - create_unit_dropin( - 'systemd-networkd.socket', - [ - '[Unit]', - 'StartLimitIntervalSec=0', - ] - ) - - check_output('systemctl daemon-reload') - print(check_output('systemctl cat systemd-networkd.service')) - print(check_output('systemctl cat systemd-resolved.service')) - print(check_output('systemctl cat systemd-timesyncd.service')) - print(check_output('systemctl cat systemd-udevd.service')) - check_output('systemctl restart systemd-resolved.service') - check_output('systemctl restart systemd-timesyncd.service') - check_output('systemctl restart systemd-udevd.service') + setup_system_units() def tearDownModule(): rm_rf(networkd_ci_temp_dir) clear_udev_rules() clear_network_units() clear_networkd_conf_dropins() + clear_networkd_state_files() restore_timezone() - rm_rf('/run/systemd/system/systemd-networkd.service.d') - rm_rf('/run/systemd/system/systemd-networkd.socket.d') - rm_rf('/run/systemd/system/systemd-resolved.service.d') - rm_rf('/run/systemd/system/systemd-timesyncd.service.d') - rm_rf('/run/systemd/system/systemd-udevd.service.d') - check_output('systemctl daemon-reload') - check_output('systemctl restart systemd-udevd.service') + clear_system_units() restore_active_units() + # Flush stream buffer and journals before showing the test summary. + sys.stdout.flush() + check_output('journalctl --sync') + class Utilities(): # pylint: disable=no-member - def check_link_exists(self, link, expected=True): + def check_link_exists(self, *link, expected=True): if expected: - self.assertTrue(link_exists(link)) + self.assertTrue(link_exists(*link)) else: - self.assertFalse(link_exists(link)) + self.assertFalse(link_exists(*link)) def check_link_attr(self, *args): self.assertEqual(read_link_attr(*args[:-1]), args[-1]) @@ -894,38 +1048,29 @@ class Utilities(): def check_ipv6_sysctl_attr(self, link, attribute, expected): self.assertEqual(read_ipv6_sysctl_attr(link, attribute), expected) - def wait_links(self, *links, timeout=20, fail_assert=True): - def links_exist(*links): - for link in links: - if not link_exists(link): - return False - return True - - for iteration in range(timeout + 1): - if iteration > 0: - time.sleep(1) + def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected): + self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected) - if links_exist(*links): - return True - if fail_assert: + def wait_links(self, *links, trial=40): + for _ in range(trial): + if link_exists(*links): + break + time.sleep(0.5) + else: self.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links))) - return False - def wait_activated(self, link, state='down', timeout=20, fail_assert=True): + def wait_activated(self, link, state='down', trial=40): # wait for the interface is activated. needle = f'{link}: Bringing link {state}' flag = state.upper() - for iteration in range(timeout + 1): - if iteration != 0: - time.sleep(1) - if not link_exists(link): - continue - output = read_networkd_log() - if needle in output and flag in check_output(f'ip link show {link}'): - return True - if fail_assert: + self.wait_links(link, trial=trial) + self.check_networkd_log(needle, trial=trial) + for _ in range(trial): + if flag in check_output(f'ip link show {link}'): + break + time.sleep(0.5) + else: self.fail(f'Timed out waiting for {link} activated.') - return False def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True): """Wait for the link to reach the specified operstate and/or setup state. @@ -946,20 +1091,20 @@ class Utilities(): if not setup_state: setup_state = r'\S+' - for secs in range(setup_timeout + 1): - if secs != 0: - time.sleep(1) + for _ in range(setup_timeout * 2): if not link_exists(link): + time.sleep(0.5) continue output = networkctl_status(link) if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output): return True + time.sleep(0.5) if fail_assert: self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}') return False - def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5): + def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5): """Wait for the links to reach the specified operstate and/or setup state. This is similar to wait_operstate() but can be used for multiple links, @@ -1000,42 +1145,50 @@ class Utilities(): for link in links_with_operstate: name = link.split(':')[0] if link_exists(name): - networkctl_status(name) + print(networkctl_status(name)) + else: + print(f'Interface {name} not found.') raise if not bool_any and setup_state: for link in links_with_operstate: self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout) def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100): - for i in range(timeout_sec): - if i > 0: - time.sleep(1) + for _ in range(timeout_sec * 2): output = check_output(f'ip {ipv} address show dev {link} scope {scope}') if re.search(address_regex, output) and 'tentative' not in output: break + time.sleep(0.5) self.assertRegex(output, address_regex) def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100): - for i in range(timeout_sec): - if i > 0: - time.sleep(1) + for _ in range(timeout_sec * 2): output = check_output(f'ip {ipv} address show dev {link} scope {scope}') if not re.search(address_regex, output): break + time.sleep(0.5) self.assertNotRegex(output, address_regex) def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100): - for i in range(timeout_sec): - if i > 0: - time.sleep(1) + for _ in range(timeout_sec * 2): output = check_output(f'ip {ipv} route show dev {link} table {table}') if re.search(route_regex, output): break + time.sleep(0.5) self.assertRegex(output, route_regex) + def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100): + for _ in range(timeout_sec * 2): + output = check_output(f'ip {ipv} route show dev {link} table {table}') + if not re.search(route_regex, output): + break + time.sleep(0.5) + + self.assertNotRegex(output, route_regex) + def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'): if not shutil.which('selinuxenabled'): print('## Checking NetLabel skipped: selinuxenabled command not found.') @@ -1079,6 +1232,14 @@ class Utilities(): print(output) self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*') + def check_networkd_log(self, contents, since=None, trial=20): + for _ in range(trial): + if contents in read_networkd_log(since=since): + break + time.sleep(0.5) + else: + self.fail(f'"{contents}" not found in journal.') + class NetworkctlTests(unittest.TestCase, Utilities): def setUp(self): @@ -1091,7 +1252,7 @@ class NetworkctlTests(unittest.TestCase, Utilities): def test_altname(self): copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link') start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') output = networkctl_status('dummy98') self.assertRegex(output, 'hogehogehogehogehogehoge') @@ -1101,16 +1262,16 @@ class NetworkctlTests(unittest.TestCase, Utilities): copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy-rename-to-altname.link') start_networkd() - self.wait_online(['dummyalt:degraded']) + self.wait_online('dummyalt:degraded') output = networkctl_status('dummyalt') self.assertIn('hogehogehogehogehogehoge', output) self.assertNotIn('dummy98', output) def test_reconfigure(self): - copy_network_unit('25-address-static.network', '12-dummy.netdev') + copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False) start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dev dummy98') print(output) @@ -1123,7 +1284,7 @@ class NetworkctlTests(unittest.TestCase, Utilities): check_output('ip address del 10.2.2.4/16 dev dummy98') networkctl_reconfigure('dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dev dummy98') print(output) @@ -1142,9 +1303,9 @@ class NetworkctlTests(unittest.TestCase, Utilities): self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output) self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output) - copy_network_unit('25-address-static.network') + copy_network_unit('25-address-static.network', copy_dropins=False) networkctl_reload() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dev dummy98') print(output) @@ -1154,10 +1315,10 @@ class NetworkctlTests(unittest.TestCase, Utilities): def test_renew(self): def check(): - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = networkctl_status('veth99') print(output) - self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)') + self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)') self.assertIn('Gateway: 192.168.5.3', output) self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10') self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11') @@ -1174,18 +1335,18 @@ class NetworkctlTests(unittest.TestCase, Utilities): check() def test_up_down(self): - copy_network_unit('25-address-static.network', '12-dummy.netdev') + copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False) start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') networkctl('down', 'dummy98') - self.wait_online(['dummy98:off']) + self.wait_online('dummy98:off') networkctl('up', 'dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') networkctl('down', 'dummy98', 'dummy98', 'dummy98') - self.wait_online(['dummy98:off']) + self.wait_online('dummy98:off') networkctl('up', 'dummy98', 'dummy98', 'dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') def test_reload(self): start_networkd() @@ -1196,7 +1357,7 @@ class NetworkctlTests(unittest.TestCase, Utilities): copy_network_unit('11-dummy.network') networkctl_reload() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') remove_network_unit('11-dummy.network') networkctl_reload() @@ -1214,7 +1375,7 @@ class NetworkctlTests(unittest.TestCase, Utilities): copy_network_unit('11-dummy.netdev', '11-dummy.network') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = networkctl('list') self.assertRegex(output, '1 lo ') @@ -1240,7 +1401,7 @@ class NetworkctlTests(unittest.TestCase, Utilities): copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = networkctl_status('test1') self.assertRegex(output, 'MTU: 1600') @@ -1248,7 +1409,7 @@ class NetworkctlTests(unittest.TestCase, Utilities): def test_type(self): copy_network_unit('11-dummy.netdev', '11-dummy.network') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = networkctl_status('test1') print(output) @@ -1258,36 +1419,35 @@ class NetworkctlTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'Type: loopback') - def test_udev_link_file(self): - copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link') + def test_unit_file(self): + copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = networkctl_status('test1') print(output) - self.assertRegex(output, r'Link File: /run/systemd/network/25-default.link') - self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network') + self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output) + self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output) + self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output) + self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output) + + self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).') # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249). # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property. # Let's reprocess the interface and drop the property. - check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo') + udevadm_trigger('/sys/class/net/lo') output = networkctl_status('lo') print(output) - self.assertRegex(output, r'Link File: n/a') - self.assertRegex(output, r'Network File: n/a') + self.assertIn('Link File: n/a', output) + self.assertIn('Network File: n/a', output) def test_delete_links(self): - copy_network_unit('11-dummy.netdev', '11-dummy.network', - '25-veth.netdev', '26-netdev-link-local-addressing-yes.network') + copy_network_unit('11-dummy.netdev', '25-veth.netdev') start_networkd() - - self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded']) - + self.wait_links('test1', 'veth99', 'veth-peer') networkctl('delete', 'test1', 'veth99') - self.check_link_exists('test1', expected=False) - self.check_link_exists('veth99', expected=False) - self.check_link_exists('veth-peer', expected=False) + self.check_link_exists('test1', 'veth99', 'veth-peer', expected=False) def test_label(self): networkctl('label') @@ -1310,7 +1470,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): '12-dummy-altname.link') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = networkctl_status('dummy98') self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output) output = check_output('ip -4 address show dev dummy98') @@ -1320,7 +1480,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): check_output('ip link set dev dummy98 address 12:34:56:78:9a:02') self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10) - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = networkctl_status('dummy98') self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output) @@ -1328,23 +1488,23 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): check_output('ip link set dev dummy98 name dummy98-1') self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10) - self.wait_online(['dummy98-1:routable']) + self.wait_online('dummy98-1:routable') output = networkctl_status('dummy98-1') self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output) check_output('ip link set dev dummy98-1 down') check_output('ip link set dev dummy98-1 name dummy98-2') - check_output(*udevadm_cmd, 'trigger', '--action=add', '/sys/class/net/dummy98-2') + udevadm_trigger('/sys/class/net/dummy98-2') self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10) - self.wait_online(['dummy98-2:routable']) + self.wait_online('dummy98-2:routable') output = networkctl_status('dummy98-2') self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output) def test_match_udev_property(self): copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = networkctl_status('dummy98') print(output) @@ -1362,7 +1522,7 @@ class WaitOnlineTests(unittest.TestCase, Utilities): copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network') start_networkd() - self.wait_online(['bridge99', 'test1:degraded'], bool_any=True) + self.wait_online('bridge99', 'test1:degraded', bool_any=True) self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring') self.wait_operstate('test1', 'degraded') @@ -1379,7 +1539,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev') start_networkd() - self.wait_online(['dropin-test:off'], setup_state='unmanaged') + self.wait_online('dropin-test:off', setup_state='unmanaged') output = check_output('ip link show dropin-test') print(output) @@ -1390,7 +1550,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['bareudp99:degraded']) + self.wait_online('bareudp99:degraded') output = check_output('ip -d link show bareudp99') print(output) @@ -1402,7 +1562,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['batadv99:degraded']) + self.wait_online('batadv99:degraded') output = check_output('ip -d link show batadv99') print(output) @@ -1412,7 +1572,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network') start_networkd() - self.wait_online(['bridge99:no-carrier']) + self.wait_online('bridge99:no-carrier') tick = os.sysconf('SC_CLK_TCK') self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick)) @@ -1438,10 +1598,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('vlan_default_pvid 9 ', output) def test_bond(self): - copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev') + copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev') start_networkd() - self.wait_online(['bond99:off', 'bond98:off'], setup_state='unmanaged') + self.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state='unmanaged') self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4') self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1') @@ -1469,12 +1629,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertIn('Mode: balance-tlb', output) + output = networkctl_status('bond97') + print(output) + + self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10') + self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000') + def test_vlan(self): copy_network_unit('21-vlan.netdev', '11-dummy.netdev', '21-vlan.network', '21-vlan-test1.network') start_networkd() - self.wait_online(['test1:degraded', 'vlan99:routable']) + self.wait_online('test1:degraded', 'vlan99:routable') output = check_output('ip -d link show test1') print(output) @@ -1507,23 +1673,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network', '21-vlan-on-bond.netdev', '21-vlan-on-bond.network') start_networkd() - self.wait_online(['bond99:off']) + self.wait_online('bond99:off') self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10) - # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed - # that the issue is fixed by the commit, let's allow to match both string. - log_re = re.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re.MULTILINE) - for i in range(20): - if i > 0: - time.sleep(0.5) - if log_re.search(read_networkd_log()): - break - else: - self.fail() + self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down') copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network') networkctl_reload() - self.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable']) + self.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable') def test_macvtap(self): first = True @@ -1541,13 +1698,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): f.write('[MACVTAP]\nMode=' + mode) start_networkd() - self.wait_online(['macvtap99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded']) + self.wait_online('macvtap99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded') output = check_output('ip -d link show macvtap99') print(output) self.assertRegex(output, 'macvtap mode ' + mode + ' ') + @expectedFailureIfModuleIsNotAvailable('macvlan') def test_macvlan(self): first = True for mode in ['private', 'vepa', 'bridge', 'passthru']: @@ -1564,33 +1722,36 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): f.write('[MACVLAN]\nMode=' + mode) start_networkd() - self.wait_online(['macvlan99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded']) + self.wait_online('macvlan99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded') output = check_output('ip -d link show test1') print(output) - self.assertRegex(output, ' mtu 2000 ') + self.assertIn(' mtu 2000 ', output) output = check_output('ip -d link show macvlan99') print(output) - self.assertRegex(output, ' mtu 2000 ') - self.assertRegex(output, 'macvlan mode ' + mode + ' ') + self.assertIn(' mtu 2000 ', output) + self.assertIn(f' macvlan mode {mode} ', output) remove_link('test1') time.sleep(1) check_output("ip link add test1 type dummy") - self.wait_online(['macvlan99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded']) + self.wait_online('macvlan99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded') output = check_output('ip -d link show test1') print(output) - self.assertRegex(output, ' mtu 2000 ') + self.assertIn(' mtu 2000 ', output) output = check_output('ip -d link show macvlan99') print(output) - self.assertRegex(output, ' mtu 2000 ') - self.assertRegex(output, 'macvlan mode ' + mode + ' ') + self.assertIn(' mtu 2000 ', output) + self.assertIn(f' macvlan mode {mode} ', output) + self.assertIn(' bcqueuelen 1234 ', output) + if ' bclim ' in output: # This is new in kernel and iproute2 v6.4 + self.assertIn(' bclim 2147483647 ', output) @expectedFailureIfModuleIsNotAvailable('ipvlan') def test_ipvlan(self): @@ -1609,7 +1770,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag) start_networkd() - self.wait_online(['ipvlan99:degraded', 'test1:degraded']) + self.wait_online('ipvlan99:degraded', 'test1:degraded') output = check_output('ip -d link show ipvlan99') print(output) @@ -1632,7 +1793,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag) start_networkd() - self.wait_online(['ipvtap99:degraded', 'test1:degraded']) + self.wait_online('ipvtap99:degraded', 'test1:degraded') output = check_output('ip -d link show ipvtap99') print(output) @@ -1643,7 +1804,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-veth-mtu.netdev') start_networkd() - self.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded']) + self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded') output = check_output('ip -d link show veth99') print(output) @@ -1661,91 +1822,82 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf') self.assertRegex(output, 'mtu 1800') - def test_tuntap(self): - copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network') - start_networkd() - - self.wait_online(['testtun99:degraded', 'testtap99:degraded']) - + def check_tuntap(self, attached): pid = networkd_pid() name = psutil.Process(pid).name()[:15] - output = check_output('ip -d tuntap show') + output = check_output('ip -d -oneline tuntap show') print(output) - self.assertRegex(output, fr'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$') - self.assertRegex(output, fr'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$') + self.assertRegex(output, r'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:') + self.assertRegex(output, r'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:') - output = check_output('ip -d link show testtun99') - print(output) - # Old ip command does not support IFF_ flags - self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') - self.assertIn('UP,LOWER_UP', output) + if attached: + self.assertRegex(output, fr'testtap99: .*{name}\({pid}\)') + self.assertRegex(output, fr'testtun99: .*{name}\({pid}\)') + self.assertRegex(output, r'testtap99: .*systemd\(1\)') + self.assertRegex(output, r'testtun99: .*systemd\(1\)') - output = check_output('ip -d link show testtap99') - print(output) - self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') - self.assertIn('UP,LOWER_UP', output) + output = check_output('ip -d link show testtun99') + print(output) + # Old ip command does not support IFF_ flags + self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') + self.assertIn('UP,LOWER_UP', output) - remove_network_unit('26-netdev-link-local-addressing-yes.network') + output = check_output('ip -d link show testtap99') + print(output) + self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') + self.assertIn('UP,LOWER_UP', output) - restart_networkd() - self.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state='unmanaged') + else: + self.assertNotIn(f'{name}({pid})', output) + self.assertNotIn('systemd(1)', output) - pid = networkd_pid() - name = psutil.Process(pid).name()[:15] + for _ in range(20): + output = check_output('ip -d link show testtun99') + print(output) + self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') + if 'NO-CARRIER' in output: + break + time.sleep(0.5) + else: + self.fail() - output = check_output('ip -d tuntap show') - print(output) - self.assertRegex(output, fr'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$') - self.assertRegex(output, fr'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$') + for _ in range(20): + output = check_output('ip -d link show testtap99') + print(output) + self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') + if 'NO-CARRIER' in output: + break + time.sleep(0.5) + else: + self.fail() - output = check_output('ip -d link show testtun99') - print(output) - self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') - self.assertIn('UP,LOWER_UP', output) + def test_tuntap(self): + copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network') + start_networkd() + self.wait_online('testtun99:degraded', 'testtap99:degraded') - output = check_output('ip -d link show testtap99') - print(output) - self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') - self.assertIn('UP,LOWER_UP', output) + self.check_tuntap(True) - clear_network_units() + remove_network_unit('26-netdev-link-local-addressing-yes.network') restart_networkd() - self.wait_online(['testtun99:off', 'testtap99:off'], setup_state='unmanaged') + self.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state='unmanaged') - output = check_output('ip -d tuntap show') - print(output) - self.assertRegex(output, r'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$') - self.assertRegex(output, r'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$') + self.check_tuntap(True) - for i in range(10): - if i != 0: - time.sleep(1) - output = check_output('ip -d link show testtun99') - print(output) - self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') - if 'NO-CARRIER' in output: - break - else: - self.fail() + clear_network_units() + unmanage_existing_links() + restart_networkd() + self.wait_online('testtun99:off', 'testtap99:off', setup_state='unmanaged') - for i in range(10): - if i != 0: - time.sleep(1) - output = check_output('ip -d link show testtap99') - print(output) - self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') - if 'NO-CARRIER' in output: - break - else: - self.fail() + self.check_tuntap(False) @expectedFailureIfModuleIsNotAvailable('vrf') def test_vrf(self): copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['vrf99:carrier']) + self.wait_online('vrf99:carrier') @expectedFailureIfModuleIsNotAvailable('vcan') def test_vcan(self): @@ -1753,7 +1905,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vcan98.netdev', '25-vcan98.network') start_networkd() - self.wait_online(['vcan99:carrier', 'vcan98:carrier']) + self.wait_online('vcan99:carrier', 'vcan98:carrier') + # For can devices, 'carrier' is the default required operational state. + self.wait_online('vcan99', 'vcan98') # https://github.com/systemd/systemd/issues/30140 output = check_output('ip -d link show vcan99') @@ -1769,16 +1923,22 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier']) + self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier') + # For can devices, 'carrier' is the default required operational state. + self.wait_online('vxcan99', 'vxcan-peer') @expectedFailureIfModuleIsNotAvailable('wireguard') def test_wireguard(self): + copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint') + copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk') + copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer') + copy_network_unit('25-wireguard.netdev', '25-wireguard.network', '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt', '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network') start_networkd() - self.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier']) + self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier') output = check_output('ip -4 address show dev wg99') print(output) @@ -1802,7 +1962,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -4 route show dev wg99 table 1234') print(output) - self.assertIn('192.168.26.0/24 proto static metric 123', output) + self.assertIn('192.168.26.0/24 proto static scope link metric 123', output) output = check_output('ip -6 route show dev wg99 table 1234') print(output) @@ -1899,7 +2059,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['geneve99:degraded']) + self.wait_online('geneve99:degraded') output = check_output('ip -d link show geneve99') print(output) @@ -1915,7 +2075,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network') start_networkd() - self.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded']) + self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded') output = check_output('ip -d link show ipiptun99') print(output) @@ -1937,7 +2097,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network') start_networkd() - self.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded']) + self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded') output = check_output('ip -d link show gretun99') print(output) @@ -1998,7 +2158,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-gretap-tunnel.netdev', '25-tunnel.network', '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network') start_networkd() - self.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded']) + self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded') output = check_output('ip -d link show gretap99') print(output) @@ -2022,7 +2182,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6gretap-tunnel.netdev', '25-tunnel.network', '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network') start_networkd() - self.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded']) + self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded') output = check_output('ip -d link show ip6gretap99') print(output) @@ -2038,7 +2198,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network') start_networkd() - self.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded']) + self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded') output = check_output('ip -d link show vtitun99') print(output) @@ -2059,7 +2219,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network', '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') start_networkd() - self.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded']) + self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded') output = check_output('ip -d link show vti6tun99') print(output) @@ -2080,9 +2240,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network', '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', - 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded', - 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded']) + self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', + 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded', + 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded') output = check_output('ip -d link show ip6tnl99') print(output) @@ -2116,7 +2276,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network') start_networkd() - self.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded']) + self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded') output = check_output('ip -d link show sittun99') print(output) @@ -2135,7 +2295,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('12-dummy.netdev', '25-isatap.network', '25-isatap-tunnel.netdev', '25-tunnel.network') start_networkd() - self.wait_online(['isataptun99:routable', 'dummy98:degraded']) + self.wait_online('isataptun99:routable', 'dummy98:degraded') output = check_output('ip -d link show isataptun99') print(output) @@ -2145,7 +2305,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('12-dummy.netdev', '25-6rd.network', '25-6rd-tunnel.netdev', '25-tunnel.network') start_networkd() - self.wait_online(['sittun99:routable', 'dummy98:degraded']) + self.wait_online('sittun99:routable', 'dummy98:degraded') output = check_output('ip -d link show sittun99') print(output) @@ -2157,7 +2317,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-erspan0-tunnel.netdev', '25-tunnel.network', '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network') start_networkd() - self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded']) + self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded') output = check_output('ip -d link show erspan99') print(output) @@ -2185,7 +2345,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-erspan1-tunnel.netdev', '25-tunnel.network', '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network') start_networkd() - self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded']) + self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded') output = check_output('ip -d link show erspan99') print(output) @@ -2216,7 +2376,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-erspan2-tunnel.netdev', '25-tunnel.network', '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network') start_networkd() - self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded']) + self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded') output = check_output('ip -d link show erspan99') print(output) @@ -2245,13 +2405,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['ipiptun99:carrier']) + self.wait_online('ipiptun99:carrier') def test_tunnel_independent_loopback(self): copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['ipiptun99:carrier']) + self.wait_online('ipiptun99:carrier') @expectedFailureIfModuleIsNotAvailable('xfrm_interface') def test_xfrm(self): @@ -2260,7 +2420,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded']) + self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded') output = check_output('ip -d link show dev xfrm98') print(output) @@ -2283,7 +2443,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-fou-gre.netdev', '25-fou-gretap.netdev') start_networkd() - self.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state='unmanaged') + self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged') output = check_output('ip fou show') print(output) @@ -2312,8 +2472,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network') start_networkd() - self.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded', - 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded']) + self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded', + 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded') output = check_output('ip -d -d link show vxlan99') print(output) @@ -2364,7 +2524,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '26-macsec.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:degraded', 'macsec99:routable']) + self.wait_online('dummy98:degraded', 'macsec99:routable') output = check_output('ip -d link show macsec99') print(output) @@ -2391,14 +2551,119 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['nlmon99:carrier']) + self.wait_online('nlmon99:carrier') @expectedFailureIfModuleIsNotAvailable('ifb') def test_ifb(self): copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['ifb99:degraded']) + self.wait_online('ifb99:degraded') + + @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test") + def test_rps_cpu_1(self): + copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link') + start_networkd() + + self.wait_online('dummy98:carrier') + + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 2) + + @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test") + def test_rps_cpu_0_1(self): + copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link') + start_networkd() + + self.wait_online('dummy98:carrier') + + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 3) + + @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test") + def test_rps_cpu_multi(self): + copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link') + start_networkd() + + self.wait_online('dummy98:carrier') + + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 15) + + def test_rps_cpu(self): + cpu_count = os.cpu_count() + + copy_network_unit('12-dummy.netdev', '12-dummy.network') + start_networkd() + + self.wait_online('dummy98:carrier') + + # 0 + copy_network_unit('25-rps-cpu-0.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 1) + remove_network_unit('25-rps-cpu-0.link') + + # all + copy_network_unit('25-rps-cpu-all.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + remove_network_unit('25-rps-cpu-all.link') + + # disable + copy_network_unit('24-rps-cpu-disable.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 0) + remove_network_unit('24-rps-cpu-disable.link') + + # set all again + copy_network_unit('25-rps-cpu-all.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + remove_network_unit('25-rps-cpu-all.link') + + # empty -> unchanged + copy_network_unit('24-rps-cpu-empty.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + remove_network_unit('24-rps-cpu-empty.link') + + # 0, then empty -> unchanged + copy_network_unit('25-rps-cpu-0-empty.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + remove_network_unit('25-rps-cpu-0-empty.link') + + # 0, then invalid -> 0 + copy_network_unit('25-rps-cpu-0-invalid.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 1) + remove_network_unit('25-rps-cpu-0-invalid.link') + + # invalid -> unchanged + copy_network_unit('24-rps-cpu-invalid.link') + udevadm_trigger('/sys/class/net/dummy98') + output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') + print(output) + self.assertEqual(int(output.replace(',', ''), base=16), 1) + remove_network_unit('24-rps-cpu-invalid.link') class NetworkdL2TPTests(unittest.TestCase, Utilities): @@ -2414,7 +2679,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): '25-l2tp-udp.netdev', '25-l2tp.network') start_networkd() - self.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded']) + self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded') output = check_output('ip l2tp show tunnel tunnel_id 10') print(output) @@ -2442,7 +2707,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): '25-l2tp-ip.netdev', '25-l2tp.network') start_networkd() - self.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded']) + self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded') output = check_output('ip l2tp show tunnel tunnel_id 10') print(output) @@ -2581,6 +2846,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_json(networkctl_json()) + @expectedFailureIfKernelReturnsInvalidFlags() def test_address_static(self): copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False) self.setup_nftset('addr4', 'ipv4_addr') @@ -2588,7 +2854,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.setup_nftset('ifindex', 'iface_index') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') ip4_null_16 = None ip4_null_24 = None @@ -2650,7 +2916,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-address-static.network.d/10-override.conf') networkctl_reload() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') self.verify_address_static( label1='new-label1', label2='dummy98', @@ -2682,7 +2948,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): ) networkctl_reconfigure('dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') self.verify_address_static( label1='new-label1', label2='dummy98', @@ -2724,7 +2990,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # 2. reconfigure the interface, and check the deprecated flag is set again networkctl_reconfigure('dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') self.verify_address_static( label1='new-label1', label2='dummy98', @@ -2758,18 +3024,27 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # test for ENOBUFS issue #17012 (with reload) copy_network_unit('25-address-static.network.d/10-many-address.conf') networkctl_reload() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dev dummy98') for i in range(1, 254): self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output) # (with reconfigure) networkctl_reconfigure('dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dev dummy98') for i in range(1, 254): self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output) + # test for an empty string assignment for Address= in [Network] + copy_network_unit('25-address-static.network.d/20-clear-addresses.conf') + networkctl_reload() + self.wait_online('dummy98:routable') + output = check_output('ip -4 address show dev dummy98') + for i in range(1, 254): + self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output) + self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output) + def test_address_ipv4acd(self): check_output('ip netns add ns99') check_output('ip link add veth99 type veth peer veth-peer') @@ -2780,7 +3055,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins=False) start_networkd() - self.wait_online(['veth99:routable']) + self.wait_online('veth99:routable') output = check_output('ip -4 address show dev veth99') print(output) @@ -2806,7 +3081,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): else: restart_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dev dummy98') self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output) @@ -2822,7 +3097,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): else: restart_networkd() - self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier']) + self.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier') output = check_output('ip route show table 42 dev dummy98') print('### ip route show table 42 dev dummy98') @@ -2886,7 +3161,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins=False) restart_networkd() - self.wait_online(['test1:no-carrier']) + self.wait_online('test1:no-carrier') carrier_map = {'on': '1', 'off': '0'} routable_map = {'on': 'routable', 'off': 'no-carrier'} @@ -2894,7 +3169,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): with self.subTest(carrier=carrier): if carrier_map[carrier] != read_link_attr('test1', 'carrier'): check_output(f'ip link set dev test1 carrier {carrier}') - self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}']) + self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}') output = networkctl_status('test1') print(output) @@ -2910,7 +3185,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-test1.network') restart_networkd() - self.wait_online(['test1:no-carrier']) + self.wait_online('test1:no-carrier') carrier_map = {'on': '1', 'off': '0'} routable_map = {'on': 'routable', 'off': 'no-carrier'} @@ -2918,7 +3193,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): with self.subTest(carrier=carrier, have_config=have_config): if carrier_map[carrier] != read_link_attr('test1', 'carrier'): check_output(f'ip link set dev test1 carrier {carrier}') - self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}']) + self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}') output = networkctl_status('test1') print(output) @@ -2933,7 +3208,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_routing_policy_rule(self): copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule list iif test1 priority 111') print(output) @@ -2973,7 +3248,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): for trial in range(3): restart_networkd(show_logs=(trial > 0)) - self.wait_online(['test1:degraded', 'dummy98:degraded']) + self.wait_online('test1:degraded', 'dummy98:degraded') output = check_output('ip rule list table 7') print(output) @@ -2986,7 +3261,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_routing_policy_rule_reconfigure(self): copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule list table 1011') print(output) @@ -3001,7 +3276,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev') networkctl_reload() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule list table 1011') print(output) @@ -3030,7 +3305,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertEqual(output, '') networkctl_reconfigure('test1') - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule list table 1011') print(output) @@ -3047,42 +3322,54 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_routing_policy_rule_port_range(self): copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule') print(output) - self.assertRegex(output, '111') - self.assertRegex(output, 'from 192.168.100.18') - self.assertRegex(output, '1123-1150') - self.assertRegex(output, '3224-3290') - self.assertRegex(output, 'tcp') - self.assertRegex(output, 'lookup 7') + self.assertIn('111:', output) + self.assertIn('from 192.168.100.18 ', output) + self.assertIn('sport 1123-1150 ', output) + self.assertIn('dport 3224-3290 ', output) + self.assertRegex(output, 'ipproto (tcp|ipproto-6) ') + self.assertIn('lookup 7 ', output) @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable() def test_routing_policy_rule_invert(self): copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule') print(output) - self.assertRegex(output, '111') - self.assertRegex(output, 'not.*?from.*?192.168.100.18') - self.assertRegex(output, 'tcp') - self.assertRegex(output, 'lookup 7') + self.assertIn('111:', output) + self.assertIn('not ', output) + self.assertIn('from 192.168.100.18 ', output) + self.assertRegex(output, 'ipproto (tcp|ipproto-6) ') + self.assertIn('lookup 7 ', output) + + @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable() + def test_routing_policy_rule_l3mdev(self): + copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev') + start_networkd() + self.wait_online('test1:degraded') + + output = check_output('ip rule') + print(output) + self.assertIn('1500: from all lookup [l3mdev-table]', output) + self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output) @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable() def test_routing_policy_rule_uidrange(self): copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev') start_networkd() - self.wait_online(['test1:degraded']) + self.wait_online('test1:degraded') output = check_output('ip rule') print(output) - self.assertRegex(output, '111') - self.assertRegex(output, 'from 192.168.100.18') - self.assertRegex(output, 'lookup 7') - self.assertRegex(output, 'uidrange 100-200') + self.assertIn('111:', output) + self.assertIn('from 192.168.100.18 ', output) + self.assertIn('lookup 7 ', output) + self.assertIn('uidrange 100-200 ', output) def _test_route_static(self, manage_foreign_routes): if not manage_foreign_routes: @@ -3091,7 +3378,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-route-static.network', '12-dummy.netdev', '25-route-static-test1.network', '11-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = networkctl_status('dummy98') print(output) @@ -3186,11 +3473,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertIn('dev dummy98 weight 10', output) self.assertIn('dev dummy98 weight 5', output) - print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff') - output = check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff') + print('### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff') + output = check_output('ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff') print(output) # old ip command does not show 'nexthop' keyword and weight... - self.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output) + self.assertIn('2001:1234:5:bfff:ff:ff:ff:ff', output) self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output) self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output) self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output) @@ -3198,9 +3485,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-address-static.network') + copy_network_unit('25-address-static.network', copy_dropins=False) networkctl_reload() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') # check all routes managed by Manager are removed print('### ip -4 route show type blackhole') @@ -3235,7 +3522,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): remove_network_unit('25-address-static.network') networkctl_reload() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') # check all routes managed by Manager are reconfigured print('### ip -4 route show type blackhole') @@ -3302,8 +3589,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual(output, '') - self.tearDown() - def test_route_static(self): first = True for manage_foreign_routes in [True, False]: @@ -3320,7 +3605,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_route_via_ipv6(self): copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = networkctl_status('dummy98') print(output) @@ -3341,7 +3626,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_route_congctl(self): copy_network_unit('25-route-congctl.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff') output = check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff') @@ -3361,7 +3646,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-route-vrf.network', '12-dummy.netdev', '25-vrf.netdev', '25-vrf.network') start_networkd() - self.wait_online(['dummy98:routable', 'vrf99:carrier']) + self.wait_online('dummy98:routable', 'vrf99:carrier') output = check_output('ip route show vrf vrf99') print(output) @@ -3374,7 +3659,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_gateway_reconfigure(self): copy_network_unit('25-gateway-static.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') print('### ip -4 route show dev dummy98 default') output = check_output('ip -4 route show dev dummy98 default') print(output) @@ -3384,7 +3669,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): remove_network_unit('25-gateway-static.network') copy_network_unit('25-gateway-next-static.network') networkctl_reload() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') print('### ip -4 route show dev dummy98 default') output = check_output('ip -4 route show dev dummy98 default') print(output) @@ -3397,7 +3682,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # tentative state, and do our test on that copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:enslaved', 'bond199:routable']) + self.wait_online('dummy98:enslaved', 'bond199:routable') output = check_output('ip -6 route list dev bond199') print(output) @@ -3412,7 +3697,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): if i != 0: networkctl_reconfigure('dummy98') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -6 route list dev dummy98') print(output) @@ -3421,7 +3706,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_ip_link_mac_address(self): copy_network_unit('25-address-link-section.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') output = check_output('ip link show dummy98') print(output) @@ -3436,7 +3721,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_ipv6_address_label(self): copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') output = check_output('ip addrlabel list') print(output) @@ -3446,17 +3731,82 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip neighbor show proxy dev dummy98') print(output) for i in range(1, 5): self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy') - def test_neighbor_section(self): - copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins=False) + def test_ipv6_neigh_retrans_time(self): + link='test25' + copy_network_unit('25-dummy.netdev', '25-dummy.network') + start_networkd() + + self.wait_online(f'{link}:degraded') + remove_network_unit('25-dummy.network') + + # expect retrans_time_ms updated + copy_network_unit('25-ipv6-neigh-retrans-time-3s.network') + networkctl_reload() + self.wait_online(f'{link}:degraded') + self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000') + remove_network_unit('25-ipv6-neigh-retrans-time-3s.network') + + # expect retrans_time_ms unchanged + copy_network_unit('25-ipv6-neigh-retrans-time-0s.network') + networkctl_reload() + self.wait_online(f'{link}:degraded') + self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000') + remove_network_unit('25-ipv6-neigh-retrans-time-0s.network') + + # expect retrans_time_ms unchanged + copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network') + networkctl_reload() + self.wait_online(f'{link}:degraded') + self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000') + remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network') + + # expect retrans_time_ms unchanged + copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network') + networkctl_reload() + self.wait_online(f'{link}:degraded') + self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000') + remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network') + + # expect retrans_time_ms unchanged + copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network') + networkctl_reload() + self.wait_online(f'{link}:degraded') + self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000') + remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network') + + # expect retrans_time_ms updated + copy_network_unit('25-ipv6-neigh-retrans-time-4s.network') + networkctl_reload() + self.wait_online(f'{link}:degraded') + self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '4000') + remove_network_unit('25-ipv6-neigh-retrans-time-4s.network') + + def test_neighbor(self): + copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf', + '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network', + '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network', + copy_dropins=False) start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable') + + print('### ip neigh list dev gretun97') + output = check_output('ip neigh list dev gretun97') + print(output) + self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output) + self.assertNotIn('10.0.0.23', output) + + print('### ip neigh list dev ip6gretun97') + output = check_output('ip neigh list dev ip6gretun97') + print(output) + self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT') + self.assertNotIn('2001:db8:0:f102::18', output) print('### ip neigh list dev dummy98') output = check_output('ip neigh list dev dummy98') @@ -3469,64 +3819,43 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-neighbor-section.network.d/override.conf') + # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides + # the valid configurations in 10-step1.conf. + copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf') networkctl_reload() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') - print('### ip neigh list dev dummy98 (after reloading)') + print('### ip neigh list dev dummy98') output = check_output('ip neigh list dev dummy98') print(output) self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output) self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output) self.assertNotIn('2004:da8:1:0::2', output) self.assertNotIn('192.168.10.2', output) - self.assertNotIn('00:00:5e:00:02', output) - - def test_neighbor_reconfigure(self): - copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins=False) - start_networkd() - self.wait_online(['dummy98:degraded']) + self.assertNotIn('00:00:5e:00:02:67', output) - print('### ip neigh list dev dummy98') - output = check_output('ip neigh list dev dummy98') - print(output) - self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output) - self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output) + check_json(networkctl_json()) - remove_network_unit('25-neighbor-section.network') - copy_network_unit('25-neighbor-next.network') + remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf', + '25-neighbor-dummy.network.d/10-step2.conf') + copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf') networkctl_reload() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') + print('### ip neigh list dev dummy98') output = check_output('ip neigh list dev dummy98') print(output) + self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output) self.assertNotIn('00:00:5e:00:02:65', output) - self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output) + self.assertNotIn('00:00:5e:00:02:66', output) + self.assertNotIn('00:00:5e:00:03:65', output) self.assertNotIn('2004:da8:1::1', output) - def test_neighbor_gre(self): - copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network', - '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev') - start_networkd() - self.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout='40s') - - output = check_output('ip neigh list dev gretun97') - print(output) - self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output) - self.assertNotIn('10.0.0.23', output) - - output = check_output('ip neigh list dev ip6gretun97') - print(output) - self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT') - self.assertNotIn('2001:db8:0:f102::18', output) - - check_json(networkctl_json()) - def test_link_local_addressing(self): copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev', '25-link-local-addressing-no.network', '12-dummy.netdev') start_networkd() - self.wait_online(['test1:degraded', 'dummy98:carrier']) + self.wait_online('test1:degraded', 'dummy98:carrier') output = check_output('ip address show dev test1') print(output) @@ -3556,7 +3885,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_link_local_addressing_ipv6ll(self): copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') # An IPv6LL address exists by default. output = check_output('ip address show dev dummy98') @@ -3565,7 +3894,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('25-link-local-addressing-no.network') networkctl_reload() - self.wait_online(['dummy98:carrier']) + self.wait_online('dummy98:carrier') # Check if the IPv6LL address is removed. output = check_output('ip address show dev dummy98') @@ -3574,19 +3903,18 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): remove_network_unit('25-link-local-addressing-no.network') networkctl_reload() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') # Check if a new IPv6LL address is assigned. output = check_output('ip address show dev dummy98') print(output) self.assertRegex(output, 'inet6 .* scope link') - @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved") def test_sysctl(self): copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf') copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False) start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') self.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1') self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1') @@ -3595,12 +3923,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1') self.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1') self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1') + self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1') self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1') self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0') copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf') networkctl_reload() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2') @@ -3612,7 +3941,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_output('sysctl net.ipv6.conf.default.disable_ipv6=1') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dummy98') print(output) @@ -3636,7 +3965,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_output('sysctl net.ipv6.conf.default.disable_ipv6=0') restart_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip -4 address show dummy98') print(output) @@ -3667,7 +3996,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # add one bound interface. The interface will be up. check_output('ip link add dummy98 type dummy') check_output('ip link set dummy98 up') - self.wait_online(['test1:routable']) + self.wait_online('test1:routable') output = check_output('ip address show test1') print(output) self.assertIn('UP,LOWER_UP', output) @@ -3701,7 +4030,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # bring up the bound interface. The interface will be up. check_output('ip link set dummy99 up') - self.wait_online(['test1:routable']) + self.wait_online('test1:routable') output = check_output('ip address show test1') print(output) self.assertIn('UP,LOWER_UP', output) @@ -3720,7 +4049,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # re-add one bound interface. The interface will be up. check_output('ip link add dummy98 type dummy') check_output('ip link set dummy98 up') - self.wait_online(['test1:routable']) + self.wait_online('test1:routable') output = check_output('ip address show test1') print(output) self.assertIn('UP,LOWER_UP', output) @@ -3794,7 +4123,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): if policy.endswith('down') or policy == 'manual': self.wait_operstate('test1', 'off', setup_state='configuring') else: - self.wait_online(['test1']) + self.wait_online('test1') if policy == 'always-down': # if always-down, required for online is forced to no @@ -3832,7 +4161,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_domain(self): copy_network_unit('12-dummy.netdev', '24-search-domain.network') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = networkctl_status('dummy98') print(output) @@ -3853,85 +4182,146 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): copy_network_unit('24-keep-configuration-static.network') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('ip address show dummy98') print(output) self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98') self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98') - @expectedFailureIfNexthopIsNotAvailable() - def test_nexthop(self): - def check_nexthop(self): - self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) + def check_nexthop(self, manage_foreign_nexthops, first): + self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable') - output = check_output('ip nexthop list dev veth99') - print(output) + output = check_output('ip nexthop list dev veth99') + print(output) + if first: self.assertIn('id 1 via 192.168.5.1 dev veth99', output) self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output) - self.assertIn('id 3 dev veth99', output) - self.assertIn('id 4 dev veth99', output) + else: + self.assertIn('id 6 via 192.168.5.1 dev veth99', output) + self.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output) + self.assertIn('id 3 dev veth99', output) + self.assertIn('id 4 dev veth99', output) + if first: self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink') - self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output) + else: + self.assertIn('id 5 via 192.168.5.3 dev veth99', output) + self.assertNotRegex(output, 'id 5 via 192.168.5.3 dev veth99 .*onlink') + self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output) + if manage_foreign_nexthops: self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99') - output = check_output('ip nexthop list dev dummy98') - print(output) + output = check_output('ip nexthop list dev dummy98') + print(output) + if first: self.assertIn('id 20 via 192.168.20.1 dev dummy98', output) + else: + self.assertIn('id 21 via 192.168.20.1 dev dummy98', output) + if manage_foreign_nexthops: + self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output) + else: + self.assertIn('id 42 via 192.168.20.2 dev dummy98', output) - # kernel manages blackhole nexthops on lo - output = check_output('ip nexthop list dev lo') - print(output) + # kernel manages blackhole nexthops on lo + output = check_output('ip nexthop list dev lo') + print(output) + if first: self.assertIn('id 6 blackhole', output) self.assertIn('id 7 blackhole', output) + else: + self.assertIn('id 1 blackhole', output) + self.assertIn('id 2 blackhole', output) - # group nexthops are shown with -0 option + # group nexthops are shown with -0 option + if first: output = check_output('ip -0 nexthop list id 21') print(output) self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)') - - output = check_output('ip route show dev veth99 10.10.10.10') + else: + output = check_output('ip -0 nexthop list id 20') print(output) + self.assertRegex(output, r'id 20 group (5,3/21|21/5,3)') + + output = check_output('ip route show dev veth99 10.10.10.10') + print(output) + if first: self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output) + else: + self.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output) - output = check_output('ip route show dev veth99 10.10.10.11') - print(output) + output = check_output('ip route show dev veth99 10.10.10.11') + print(output) + if first: self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output) + else: + self.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output) - output = check_output('ip route show dev veth99 10.10.10.12') - print(output) + output = check_output('ip route show dev veth99 10.10.10.12') + print(output) + if first: self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output) + else: + self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output) - output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1') - print(output) + output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1') + print(output) + if first: self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) + else: + self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) - output = check_output('ip route show 10.10.10.13') - print(output) + output = check_output('ip route show 10.10.10.13') + print(output) + if first: self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output) + else: + self.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output) - output = check_output('ip -6 route show 2001:1234:5:8f62::2') - print(output) + output = check_output('ip -6 route show 2001:1234:5:8f62::2') + print(output) + if first: self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output) + else: + self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output) - output = check_output('ip route show 10.10.10.14') - print(output) + output = check_output('ip route show 10.10.10.14') + print(output) + if first: self.assertIn('10.10.10.14 nhid 21 proto static', output) - self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output) self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output) + else: + self.assertIn('10.10.10.14 nhid 20 proto static', output) + self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output) + self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output) - check_json(networkctl_json()) + output = networkctl_json() + check_json(output) + self.assertNotIn('"Destination":[10.10.10.14]', output) + + def _test_nexthop(self, manage_foreign_nexthops): + if not manage_foreign_nexthops: + copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf') + + check_output('ip link add dummy98 type dummy') + check_output('ip link set dummy98 up') + check_output('ip address add 192.168.20.20/24 dev dummy98') + check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98') - copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network', - '12-dummy.netdev', '25-nexthop-dummy.network') + copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network', + '12-dummy.netdev', '25-nexthop-dummy-1.network') start_networkd() - check_nexthop(self) + self.check_nexthop(manage_foreign_nexthops, first=True) - remove_network_unit('25-nexthop.network') + remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network') + copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network') + networkctl_reload() + self.check_nexthop(manage_foreign_nexthops, first=False) + + remove_network_unit('25-nexthop-2.network') copy_network_unit('25-nexthop-nothing.network') networkctl_reload() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = check_output('ip nexthop list dev veth99') print(output) @@ -3940,12 +4330,47 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual(output, '') - remove_network_unit('25-nexthop-nothing.network') - copy_network_unit('25-nexthop.network') - networkctl_reconfigure('dummy98') + remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network') + copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network') + # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional + # here to test reconfiguring with different .network files does not trigger race. + # See also comments in link_drop_requests(). + networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network + networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network + + self.check_nexthop(manage_foreign_nexthops, first=True) + + # Remove nexthop with ID 20 + check_output('ip nexthop del id 20') + copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network') networkctl_reload() - check_nexthop(self) + # 25-nexthop-test1.network requests a route with nexthop ID 21, + # which is silently removed by the kernel when nexthop with ID 20 is removed in the above, + # hence test1 should be stuck in the configuring state. + self.wait_operstate('test1', operstate='routable', setup_state='configuring') + + # Wait for a while, and check if the interface is still in the configuring state. + time.sleep(1) + output = networkctl_status('test1') + self.assertIn('State: routable (configuring)', output) + + # Check if the route which needs nexthop 20 and 21 are forgotten. + output = networkctl_json() + check_json(output) + self.assertNotIn('"Destination":[10.10.10.14]', output) + + # Reconfigure the interface that has nexthop with ID 20 and 21, + # then the route requested by test1 can be configured. + networkctl_reconfigure('dummy98') + self.wait_online('test1:routable') + + # Check if the requested route actually configured. + output = check_output('ip route show 10.10.11.10') + print(output) + self.assertIn('10.10.11.10 nhid 21 proto static', output) + self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output) + self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output) remove_link('veth99') time.sleep(2) @@ -3954,6 +4379,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual(output, '') + @expectedFailureIfNexthopIsNotAvailable() + def test_nexthop(self): + first = True + for manage_foreign_nexthops in [True, False]: + if first: + first = False + else: + self.tearDown() + + print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})') + with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops): + self._test_nexthop(manage_foreign_nexthops) + class NetworkdTCTests(unittest.TestCase, Utilities): def setUp(self): @@ -3966,7 +4404,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_cake(self): copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -3990,7 +4428,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_codel(self): copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4001,7 +4439,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_drr(self): copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4014,7 +4452,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_ets(self): copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4028,7 +4466,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_fq(self): copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4042,7 +4480,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_fq_codel(self): copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4053,7 +4491,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_fq_pie(self): copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4065,7 +4503,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_gred(self): copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4076,7 +4514,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_hhf(self): copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4087,7 +4525,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_htb_fifo(self): copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4124,7 +4562,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev', '25-qdisc-ingress.network', '11-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable', 'test1:routable']) + self.wait_online('dummy98:routable', 'test1:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4139,7 +4577,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev', '25-qdisc-netem-compat.network', '11-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable', 'test1:routable']) + self.wait_online('dummy98:routable', 'test1:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4155,7 +4593,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_pie(self): copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4166,7 +4604,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_qfq(self): copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4180,7 +4618,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_sfb(self): copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4191,7 +4629,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_sfq(self): copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4202,7 +4640,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_tbf(self): copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4217,7 +4655,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): start_networkd() self.wait_links('dummy98') check_output('modprobe sch_teql max_equalizers=2') - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') output = check_output('tc qdisc show dev dummy98') print(output) @@ -4227,7 +4665,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): def test_qdisc_drop(self): copy_network_unit('12-dummy.netdev', '12-dummy.network') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') # Test case for issue #32247 and #32254. for _ in range(20): @@ -4251,7 +4689,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): def test_state_file(self): copy_network_unit('12-dummy.netdev', '25-state-file-tests.network') start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') # make link state file updated resolvectl('revert', 'dummy98') @@ -4328,7 +4766,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network') start_networkd() - self.wait_online(['dummy98:degraded']) + self.wait_online('dummy98:degraded') output = read_link_state_file('dummy98') self.assertIn('IPV4_ADDRESS_STATE=off', output) @@ -4336,8 +4774,8 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): # with a routable IPv4 address check_output('ip address add 10.1.2.3/16 dev dummy98') - self.wait_online(['dummy98:routable'], ipv4=True) - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable', ipv4=True) + self.wait_online('dummy98:routable') output = read_link_state_file('dummy98') self.assertIn('IPV4_ADDRESS_STATE=routable', output) @@ -4347,8 +4785,8 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): # with a routable IPv6 address check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98') - self.wait_online(['dummy98:routable'], ipv6=True) - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable', ipv6=True) + self.wait_online('dummy98:routable') output = read_link_state_file('dummy98') self.assertIn('IPV4_ADDRESS_STATE=off', output) @@ -4369,7 +4807,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities): copy_network_unit('23-keep-master.network') start_networkd() - self.wait_online(['dummy98:enslaved']) + self.wait_online('dummy98:enslaved') output = check_output('ip -d link show bond199') print(output) @@ -4382,7 +4820,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities): def test_bond_active_slave(self): copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:enslaved', 'bond199:degraded']) + self.wait_online('dummy98:enslaved', 'bond199:degraded') output = check_output('ip -d link show bond199') print(output) @@ -4391,13 +4829,13 @@ class NetworkdBondTests(unittest.TestCase, Utilities): # test case for issue #31165. since = datetime.datetime.now() networkctl_reconfigure('dummy98') - self.wait_online(['dummy98:enslaved', 'bond199:degraded']) + self.wait_online('dummy98:enslaved', 'bond199:degraded') self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since)) def test_bond_primary_slave(self): copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') start_networkd() - self.wait_online(['dummy98:enslaved', 'bond199:degraded']) + self.wait_online('dummy98:enslaved', 'bond199:degraded') output = check_output('ip -d link show bond199') print(output) @@ -4410,7 +4848,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities): f.write(f'[Link]\nMACAddress={mac}\n') networkctl_reload() - self.wait_online(['dummy98:enslaved', 'bond199:degraded']) + self.wait_online('dummy98:enslaved', 'bond199:degraded') output = check_output('ip -d link show bond199') print(output) @@ -4420,7 +4858,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities): copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev', '25-bond99.network', '25-bond-slave.network') start_networkd() - self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable']) + self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable') output = check_output('ip -d link show dummy98') print(output) @@ -4469,32 +4907,139 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): def tearDown(self): tear_down_common() + def test_bridge_mac_none(self): + copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network', + '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link') + start_networkd() + self.wait_online('dummy98:enslaved', 'bridge99:degraded') + + output = check_output('ip link show dev dummy98') + print(output) + self.assertIn('link/ether 12:34:56:78:9a:01', output) + + output = check_output('ip link show dev bridge99') + print(output) + self.assertIn('link/ether 12:34:56:78:9a:01', output) + def test_bridge_vlan(self): copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network', - '26-bridge.netdev', '26-bridge-vlan-master.network') + '26-bridge.netdev', '26-bridge-vlan-master.network', + copy_dropins=False) start_networkd() - self.wait_online(['test1:enslaved', 'bridge99:degraded']) + self.wait_online('test1:enslaved', 'bridge99:degraded') + + output = check_output('bridge vlan show dev test1') + print(output) + # check if the default VID is removed + self.assertNotIn('1 Egress Untagged', output) + for i in range(1000, 3000): + if i == 1010: + self.assertIn(f'{i} PVID', output) + elif i in range(1012, 1016) or i in range(1103, 1109): + self.assertIn(f'{i} Egress Untagged', output) + elif i in range(1008, 1014) or i in range(1100, 1111): + self.assertIn(f'{i}', output) + else: + self.assertNotIn(f'{i}', output) + + output = check_output('bridge vlan show dev bridge99') + print(output) + # check if the default VID is removed + self.assertNotIn('1 Egress Untagged', output) + for i in range(1000, 3000): + if i == 1020: + self.assertIn(f'{i} PVID', output) + elif i in range(1022, 1026) or i in range(1203, 1209): + self.assertIn(f'{i} Egress Untagged', output) + elif i in range(1018, 1024) or i in range(1200, 1211): + self.assertIn(f'{i}', output) + else: + self.assertNotIn(f'{i}', output) + + # Change vlan IDs + copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf', + '26-bridge-vlan-master.network.d/10-override.conf') + networkctl_reload() + self.wait_online('test1:enslaved', 'bridge99:degraded') + + output = check_output('bridge vlan show dev test1') + print(output) + for i in range(1000, 3000): + if i == 2010: + self.assertIn(f'{i} PVID', output) + elif i in range(2012, 2016) or i in range(2103, 2109): + self.assertIn(f'{i} Egress Untagged', output) + elif i in range(2008, 2014) or i in range(2100, 2111): + self.assertIn(f'{i}', output) + else: + self.assertNotIn(f'{i}', output) + + output = check_output('bridge vlan show dev bridge99') + print(output) + for i in range(1000, 3000): + if i == 2020: + self.assertIn(f'{i} PVID', output) + elif i in range(2022, 2026) or i in range(2203, 2209): + self.assertIn(f'{i} Egress Untagged', output) + elif i in range(2018, 2024) or i in range(2200, 2211): + self.assertIn(f'{i}', output) + else: + self.assertNotIn(f'{i}', output) + + # Remove several vlan IDs + copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf', + '26-bridge-vlan-master.network.d/20-override.conf') + networkctl_reload() + self.wait_online('test1:enslaved', 'bridge99:degraded') + + output = check_output('bridge vlan show dev test1') + print(output) + for i in range(1000, 3000): + if i == 2010: + self.assertIn(f'{i} PVID', output) + elif i in range(2012, 2016): + self.assertIn(f'{i} Egress Untagged', output) + elif i in range(2008, 2014): + self.assertIn(f'{i}', output) + else: + self.assertNotIn(f'{i}', output) + + output = check_output('bridge vlan show dev bridge99') + print(output) + for i in range(1000, 3000): + if i == 2020: + self.assertIn(f'{i} PVID', output) + elif i in range(2022, 2026): + self.assertIn(f'{i} Egress Untagged', output) + elif i in range(2018, 2024): + self.assertIn(f'{i}', output) + else: + self.assertNotIn(f'{i}', output) + + # Remove all vlan IDs + copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf', + '26-bridge-vlan-master.network.d/30-override.conf') + networkctl_reload() + self.wait_online('test1:enslaved', 'bridge99:degraded') output = check_output('bridge vlan show dev test1') print(output) - self.assertNotRegex(output, '4063') - for i in range(4064, 4095): - self.assertRegex(output, f'{i}') - self.assertNotRegex(output, '4095') + self.assertNotIn('PVID', output) + for i in range(1000, 3000): + self.assertNotIn(f'{i}', output) output = check_output('bridge vlan show dev bridge99') print(output) - self.assertNotRegex(output, '4059') - for i in range(4060, 4095): - self.assertRegex(output, f'{i}') - self.assertNotRegex(output, '4095') + self.assertNotIn('PVID', output) + for i in range(1000, 3000): + self.assertNotIn(f'{i}', output) def test_bridge_vlan_issue_20373(self): copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network', '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network', '21-vlan.netdev', '21-vlan.network') start_networkd() - self.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable']) + self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable') output = check_output('bridge vlan show dev test1') print(output) @@ -4512,7 +5057,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network', '26-bridge.netdev', '26-bridge-mdb-master.network') start_networkd() - self.wait_online(['test1:enslaved', 'bridge99:degraded']) + self.wait_online('test1:enslaved', 'bridge99:degraded') output = check_output('bridge mdb show dev bridge99') print(output) @@ -4532,7 +5077,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): copy_network_unit('23-keep-master.network') start_networkd() - self.wait_online(['dummy98:enslaved']) + self.wait_online('dummy98:enslaved') output = check_output('ip -d link show dummy98') print(output) @@ -4559,7 +5104,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', '25-bridge99.network') start_networkd() - self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable']) + self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable') output = check_output('ip -d link show bridge99') print(output) @@ -4644,7 +5189,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium') check_output('ip link add dummy98 type dummy') - self.wait_online(['dummy98:enslaved', 'bridge99:routable']) + self.wait_online('dummy98:enslaved', 'bridge99:routable') output = check_output('ip -d link show bridge99') print(output) @@ -4682,22 +5227,22 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): elif test == 'slave-up': # bring up slave, which will have carrier; bridge gains carrier check_output('ip link set dev test1 up') - self.wait_online(['bridge99:routable']) + self.wait_online('bridge99:routable') self.check_link_attr('bridge99', 'carrier', '1') elif test == 'slave-no-carrier': # drop slave carrier; bridge loses carrier check_output('ip link set dev test1 carrier off') - self.wait_online(['bridge99:no-carrier:no-carrier']) + self.wait_online('bridge99:no-carrier:no-carrier') self.check_link_attr('bridge99', 'carrier', '0') elif test == 'slave-carrier': # restore slave carrier; bridge gains carrier check_output('ip link set dev test1 carrier on') - self.wait_online(['bridge99:routable']) + self.wait_online('bridge99:routable') self.check_link_attr('bridge99', 'carrier', '1') elif test == 'slave-down': # bring down slave; bridge loses carrier check_output('ip link set dev test1 down') - self.wait_online(['bridge99:no-carrier:no-carrier']) + self.wait_online('bridge99:no-carrier:no-carrier') self.check_link_attr('bridge99', 'carrier', '0') output = networkctl_status('bridge99') @@ -4709,7 +5254,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', '25-bridge99-ignore-carrier-loss.network') start_networkd() - self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable']) + self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable') check_output('ip address add 192.168.0.16/24 dev bridge99') remove_link('test1', 'dummy98') @@ -4725,7 +5270,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network', '25-bridge99-ignore-carrier-loss.network') start_networkd() - self.wait_online(['bridge99:no-carrier']) + self.wait_online('bridge99:no-carrier') for trial in range(4): check_output('ip link add dummy98 type dummy') @@ -4733,7 +5278,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): if trial < 3: remove_link('dummy98') - self.wait_online(['bridge99:routable', 'dummy98:enslaved']) + self.wait_online('bridge99:routable', 'dummy98:enslaved') output = check_output('ip address show bridge99') print(output) @@ -4751,22 +5296,28 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): def tearDown(self): tear_down_common() - @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable() - def test_sriov(self): - copy_network_unit('25-default.link', '25-sriov.network') - + def setup_netdevsim(self, id=99, num_ports=1, num_vfs=0): call('modprobe netdevsim') + # Create netdevsim device. with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f: - f.write('99 1') + f.write(f'{id} {num_ports}') + + # Create VF. + if num_vfs > 0: + with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f: + f.write(f'{num_vfs}') + + @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable() + def test_sriov(self): + copy_network_unit('25-netdevsim.link', '25-sriov.network') - with open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode='w', encoding='utf-8') as f: - f.write('3') + self.setup_netdevsim(num_vfs=3) start_networkd() - self.wait_online(['eni99np1:routable']) + self.wait_online('sim99:routable') - output = check_output('ip link show dev eni99np1') + output = check_output('ip link show dev sim99') print(output) self.assertRegex(output, 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' @@ -4778,18 +5329,15 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): def test_sriov_udev(self): copy_network_unit('25-sriov.link', '25-sriov-udev.network') - call('modprobe netdevsim') - - with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f: - f.write('99 1') + self.setup_netdevsim() start_networkd() - self.wait_online(['eni99np1:routable']) + self.wait_online('sim99:routable') - # the name eni99np1 may be an alternative name. - ifname = link_resolve('eni99np1') + # The name sim99 is an alternative name, and cannot be used by udevadm below. + ifname = link_resolve('sim99') - output = check_output('ip link show dev eni99np1') + output = check_output('ip link show dev sim99') print(output) self.assertRegex(output, 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' @@ -4802,10 +5350,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f: f.write('[Link]\nSR-IOVVirtualFunctions=4\n') - udev_reload() - check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}') + udevadm_reload() + udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}') - output = check_output('ip link show dev eni99np1') + output = check_output('ip link show dev sim99') print(output) self.assertRegex(output, 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' @@ -4818,10 +5366,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f: f.write('[Link]\nSR-IOVVirtualFunctions=\n') - udev_reload() - check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}') + udevadm_reload() + udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}') - output = check_output('ip link show dev eni99np1') + output = check_output('ip link show dev sim99') print(output) self.assertRegex(output, 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' @@ -4834,10 +5382,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f: f.write('[Link]\nSR-IOVVirtualFunctions=2\n') - udev_reload() - check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}') + udevadm_reload() + udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}') - output = check_output('ip link show dev eni99np1') + output = check_output('ip link show dev sim99') print(output) self.assertRegex(output, 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' @@ -4850,10 +5398,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f: f.write('[Link]\nSR-IOVVirtualFunctions=\n') - udev_reload() - check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}') + udevadm_reload() + udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}') - output = check_output('ip link show dev eni99np1') + output = check_output('ip link show dev sim99') print(output) self.assertRegex(output, 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' @@ -4874,19 +5422,105 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): def test_lldp(self): copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev') start_networkd() - self.wait_online(['veth99:degraded', 'veth-peer:degraded']) + self.wait_online('veth99:degraded', 'veth-peer:degraded') - for trial in range(10): - if trial > 0: - time.sleep(1) + for _ in range(20): + output = networkctl('lldp') + print(output) + if re.search(r'veth99 .* veth-peer .* .......a...', output): + break + time.sleep(0.5) + else: + self.fail() + + # With interface name + output = networkctl('lldp', 'veth99'); + print(output) + self.assertRegex(output, r'veth99 .* veth-peer .* .......a...') + + # With interface name pattern + output = networkctl('lldp', 've*9'); + print(output) + self.assertRegex(output, r'veth99 .* veth-peer .* .......a...') + # json format + output = networkctl('--json=short', 'lldp') + print(output) + self.assertIn('"InterfaceName":"veth99"', output) + self.assertIn('"PortID":"veth-peer"', output) + self.assertIn('"EnabledCapabilities":128', output) + + # json format with interface name + output = networkctl('--json=short', 'lldp', 'veth99') + print(output) + self.assertIn('"InterfaceName":"veth99"', output) + self.assertIn('"PortID":"veth-peer"', output) + self.assertIn('"EnabledCapabilities":128', output) + + # json format with interface name pattern + output = networkctl('--json=short', 'lldp', 've*9') + print(output) + self.assertIn('"InterfaceName":"veth99"', output) + self.assertIn('"PortID":"veth-peer"', output) + self.assertIn('"EnabledCapabilities":128', output) + + # LLDP neighbors in status + output = networkctl_status('veth99') + print(output) + self.assertRegex(output, r'Connected To: .* on port veth-peer') + + # enable forwarding, to enable the Router flag + with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f: + f.write('[Network]\nIPv4Forwarding=yes\n') + + networkctl_reload() + self.wait_online('veth-peer:degraded') + + for _ in range(20): output = networkctl('lldp') print(output) - if re.search(r'veth99 .* veth-peer', output): + if re.search(r'veth99 .* veth-peer .* ....r......', output): break + time.sleep(0.5) else: self.fail() + # With interface name + output = networkctl('lldp', 'veth99'); + print(output) + self.assertRegex(output, r'veth99 .* veth-peer .* ....r......') + + # With interface name pattern + output = networkctl('lldp', 've*9'); + print(output) + self.assertRegex(output, r'veth99 .* veth-peer .* ....r......') + + # json format + output = networkctl('--json=short', 'lldp') + print(output) + self.assertIn('"InterfaceName":"veth99"', output) + self.assertIn('"PortID":"veth-peer"', output) + self.assertIn('"EnabledCapabilities":16', output) + + # json format with interface name + output = networkctl('--json=short', 'lldp', 'veth99') + print(output) + self.assertIn('"InterfaceName":"veth99"', output) + self.assertIn('"PortID":"veth-peer"', output) + self.assertIn('"EnabledCapabilities":16', output) + + # json format with interface name pattern + output = networkctl('--json=short', 'lldp', 've*9') + print(output) + self.assertIn('"InterfaceName":"veth99"', output) + self.assertIn('"PortID":"veth-peer"', output) + self.assertIn('"EnabledCapabilities":16', output) + + # LLDP neighbors in status + output = networkctl_status('veth99') + print(output) + self.assertRegex(output, r'Connected To: .* on port veth-peer') + class NetworkdRATests(unittest.TestCase, Utilities): def setUp(self): @@ -4901,7 +5535,10 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.setup_nftset('network6', 'ipv6_addr', 'flags interval;') self.setup_nftset('ifindex', 'iface_index') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:degraded']) + self.wait_online('veth99:routable', 'veth-peer:degraded') + + # IPv6SendRA=yes implies IPv6Forwarding. + self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1') output = resolvectl('dns', 'veth99') print(output) @@ -4916,6 +5553,9 @@ class NetworkdRATests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '2002:da8:1:0') + self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000') + self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500') + self.check_netlabel('veth99', '2002:da8:1::/64') self.check_netlabel('veth99', '2002:da8:2::/64') @@ -4927,10 +5567,8 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.teardown_nftset('addr6', 'network6', 'ifindex') - def test_ipv6_token_static(self): - copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network') - start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:degraded']) + def check_ipv6_token_static(self): + self.wait_online('veth99:routable', 'veth-peer:degraded') output = networkctl_status('veth99') print(output) @@ -4939,26 +5577,178 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d') self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe') + def test_ipv6_token_static(self): + copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network') + start_networkd() + + self.check_ipv6_token_static() + + for _ in range(20): + check_output('ip link set veth99 down') + check_output('ip link set veth99 up') + + self.check_ipv6_token_static() + + for _ in range(20): + check_output('ip link set veth99 down') + time.sleep(random.uniform(0, 0.1)) + check_output('ip link set veth99 up') + time.sleep(random.uniform(0, 0.1)) + + self.check_ipv6_token_static() + + def test_ndisc_redirect(self): + if not os.path.exists(test_ndisc_send): + self.skipTest(f"{test_ndisc_send} does not exist.") + + copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network') + start_networkd() + + self.check_ipv6_token_static() + + # Introduce three redirect routes. + check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d') + check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d') + check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:3:1a:2b:3c:4d --redirect-destination 2002:da8:1:3:1a:2b:3c:4d') + self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) + self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) + self.wait_route('veth99', '2002:da8:1:3:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) + + # Change the target address of the redirects. + check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d') + check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d') + self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) + self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) + self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', ipv='-6', timeout_sec=10) + self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', ipv='-6', timeout_sec=10) + + # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore. + # Then, verify that all redirect routes and the default route are dropped. + output = check_output('ip -6 address show dev veth-peer scope link') + veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group() + print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}') + check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no') + self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10) + self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10) + + # Check if sd-radv refuses RS from the same interface. + # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306 + since = datetime.datetime.now() + check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}') + self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since) + + def check_ndisc_mtu(self, mtu): + for _ in range(20): + output = read_ipv6_sysctl_attr('veth99', 'mtu') + if output == f'{mtu}': + break + time.sleep(0.5) + else: + self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}') + + def test_ndisc_mtu(self): + if not os.path.exists(test_ndisc_send): + self.skipTest(f"{test_ndisc_send} does not exist.") + + copy_network_unit('25-veth.netdev', + '25-veth-peer-no-address.network', + '25-ipv6-prefix-veth-token-static.network') + start_networkd() + self.wait_online('veth-peer:degraded') + + self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client') + + check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400') + self.check_ndisc_mtu(1400) + + check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410') + self.check_ndisc_mtu(1410) + + check_output('ip link set dev veth99 mtu 1600') + self.check_ndisc_mtu(1410) + + check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700') + self.check_ndisc_mtu(1600) + + check_output('ip link set dev veth99 mtu 1800') + self.check_ndisc_mtu(1700) + def test_ipv6_token_prefixstable(self): copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:degraded']) + self.wait_online('veth99:routable', 'veth-peer:degraded') - output = networkctl_status('veth99') + output = check_output('ip -6 address show dev veth99') print(output) - self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output) - self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64 + self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable + self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64 + + with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f: + f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n') + + networkctl_reload() + self.wait_online('veth99:routable') + + output = check_output('ip -6 address show dev veth99') + print(output) + self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable + self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64 + + check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99') + check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad') + + networkctl_reconfigure('veth99') + self.wait_online('veth99:routable') + self.wait_address('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable + self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable + + check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99') + check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad') + + networkctl_reconfigure('veth99') + self.wait_online('veth99:routable') + self.wait_address('veth99', '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', ipv='-6', timeout_sec=10) # the 3rd prefixstable + self.wait_address_dropped('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable + self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable def test_ipv6_token_prefixstable_without_address(self): copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:degraded']) + self.wait_online('veth99:routable', 'veth-peer:degraded') output = networkctl_status('veth99') print(output) self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output) self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output) + def test_router_hop_limit(self): + copy_network_unit('25-veth-client.netdev', + '25-veth-router.netdev', + '26-bridge.netdev', + '25-veth-bridge.network', + '25-veth-client.network', + '25-veth-router-hop-limit.network', + '25-bridge99.network') + start_networkd() + self.wait_online('client:routable', 'client-p:enslaved', + 'router:degraded', 'router-p:enslaved', + 'bridge99:routable') + + self.check_ipv6_sysctl_attr('client', 'hop_limit', '42') + + with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f: + f.write('\n[IPv6SendRA]\nHopLimit=43\n') + + networkctl_reload() + + for _ in range(20): + output = read_ipv6_sysctl_attr('client', 'hop_limit') + if output == '43': + break + time.sleep(0.5) + + self.check_ipv6_sysctl_attr('client', 'hop_limit', '43') + def test_router_preference(self): copy_network_unit('25-veth-client.netdev', '25-veth-router-high.netdev', @@ -4970,13 +5760,13 @@ class NetworkdRATests(unittest.TestCase, Utilities): '25-veth-router-low.network', '25-bridge99.network') start_networkd() - self.wait_online(['client-p:enslaved', - 'router-high:degraded', 'router-high-p:enslaved', - 'router-low:degraded', 'router-low-p:enslaved', - 'bridge99:routable']) + self.wait_online('client-p:enslaved', + 'router-high:degraded', 'router-high-p:enslaved', + 'router-low:degraded', 'router-low-p:enslaved', + 'bridge99:routable') networkctl_reconfigure('client') - self.wait_online(['client:routable']) + self.wait_online('client:routable') self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10) self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10) @@ -4985,16 +5775,18 @@ class NetworkdRATests(unittest.TestCase, Utilities): output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99') print(output) + self.assertIn('metric 512', output) self.assertIn('pref high', output) output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98') print(output) + self.assertIn('metric 2048', output) self.assertIn('pref low', output) with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f: f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n') networkctl_reload() - self.wait_online(['client:routable']) + self.wait_online('client:routable') self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10) self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10) @@ -5003,11 +5795,35 @@ class NetworkdRATests(unittest.TestCase, Utilities): output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99') print(output) + self.assertIn('metric 100', output) + self.assertNotIn('metric 512', output) self.assertIn('pref high', output) output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98') print(output) + self.assertIn('metric 300', output) + self.assertNotIn('metric 2048', output) self.assertIn('pref low', output) + # swap the preference (for issue #28439) + remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network') + copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network') + networkctl_reload() + self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv='-6', timeout_sec=10) + self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv='-6', timeout_sec=10) + + output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99') + print(output) + self.assertIn('metric 300', output) + self.assertNotIn('metric 100', output) + self.assertIn('pref low', output) + self.assertNotIn('pref high', output) + output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98') + print(output) + self.assertIn('metric 100', output) + self.assertNotIn('metric 300', output) + self.assertIn('pref high', output) + self.assertNotIn('pref low', output) + @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals") def test_captive_portal(self): copy_network_unit('25-veth-client.netdev', @@ -5018,12 +5834,12 @@ class NetworkdRATests(unittest.TestCase, Utilities): '25-veth-bridge-captive.network', '25-bridge99.network') start_networkd() - self.wait_online(['bridge99:routable', 'client-p:enslaved', - 'router-captive:degraded', 'router-captivep:enslaved']) + self.wait_online('bridge99:routable', 'client-p:enslaved', + 'router-captive:degraded', 'router-captivep:enslaved') start_radvd(config_file='captive-portal.conf') networkctl_reconfigure('client') - self.wait_online(['client:routable']) + self.wait_online('client:routable') self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10) output = networkctl_status('client') @@ -5050,8 +5866,8 @@ class NetworkdRATests(unittest.TestCase, Utilities): '25-veth-bridge-captive.network', '25-bridge99.network') start_networkd() - self.wait_online(['bridge99:routable', 'client-p:enslaved', - 'router-captive:degraded', 'router-captivep:enslaved']) + self.wait_online('bridge99:routable', 'client-p:enslaved', + 'router-captive:degraded', 'router-captivep:enslaved') for uri in captive_portal_uris: print(f"Captive portal: {uri}") @@ -5059,7 +5875,7 @@ class NetworkdRATests(unittest.TestCase, Utilities): stop_radvd() start_radvd(config_file='bogus-captive-portal.conf') networkctl_reconfigure('client') - self.wait_online(['client:routable']) + self.wait_online('client:routable') self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10) output = networkctl_status('client') @@ -5074,25 +5890,62 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): def tearDown(self): tear_down_common() - def test_dhcp_server(self): - copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network') - start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) - + def check_dhcp_server(self, persist_leases=True): output = networkctl_status('veth99') print(output) - self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)') + self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)') self.assertIn('Gateway: 192.168.5.3', output) self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10') self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11') output = networkctl_status('veth-peer') + print(output) self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*") + if persist_leases: + with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f: + check_json(f.read()) + else: + self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer')) + + def test_dhcp_server(self): + copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network') + start_networkd() + self.wait_online('veth99:routable', 'veth-peer:routable') + + self.check_dhcp_server() + + networkctl_reconfigure('veth-peer') + self.wait_online('veth-peer:routable') + + for _ in range(10): + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env) + if 'Offered DHCP leases: 192.168.5.' in output: + break + time.sleep(.2) + else: + self.fail() + + def test_dhcp_server_persist_leases_no(self): + copy_networkd_conf_dropin('persist-leases-no.conf') + copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network') + start_networkd() + self.wait_online('veth99:routable', 'veth-peer:routable') + + self.check_dhcp_server(persist_leases=False) + + remove_networkd_conf_dropin('persist-leases-no.conf') + with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f: + f.write('[DHCPServer]\nPersistLeases=no') + restart_networkd() + self.wait_online('veth99:routable', 'veth-peer:routable') + + self.check_dhcp_server(persist_leases=False) + def test_dhcp_server_null_server_address(self): copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = check_output('ip --json address show dev veth-peer') server_address = json.loads(output)[0]['addr_info'][0]['local'] @@ -5104,7 +5957,29 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): output = networkctl_status('veth99') print(output) - self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)') + self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)') + self.assertIn(f'Gateway: {server_address}', output) + self.assertIn(f'DNS: {server_address}', output) + self.assertIn(f'NTP: {server_address}', output) + + output = networkctl_status('veth-peer') + self.assertIn(f'Offered DHCP leases: {client_address}', output) + + # Check if the same addresses are used even if the service is restarted. + restart_networkd() + self.wait_online('veth99:routable', 'veth-peer:routable') + + output = check_output('ip -4 address show dev veth-peer') + print(output) + self.assertIn(f'{server_address}', output) + + output = check_output('ip -4 address show dev veth99') + print(output) + self.assertIn(f'{client_address}', output) + + output = networkctl_status('veth99') + print(output) + self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)') self.assertIn(f'Gateway: {server_address}', output) self.assertIn(f'DNS: {server_address}', output) self.assertIn(f'NTP: {server_address}', output) @@ -5116,11 +5991,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network', '12-dummy.netdev', '25-dhcp-server-uplink.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = networkctl_status('veth99') print(output) - self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)') + self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)') self.assertIn('Gateway: 192.168.5.3', output) self.assertIn('DNS: 192.168.5.1', output) self.assertIn('NTP: 192.168.5.1', output) @@ -5128,33 +6003,33 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): def test_emit_router_timezone(self): copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = networkctl_status('veth99') print(output) - self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)') + self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)') self.assertIn('Gateway: 192.168.5.1', output) self.assertIn('Time Zone: Europe/Berlin', output) def test_dhcp_server_static_lease(self): copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = networkctl_status('veth99') print(output) - self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output) - self.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output) + self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output) + self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output) def test_dhcp_server_static_lease_default_client_id(self): copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = networkctl_status('veth99') print(output) - self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output) - self.assertRegex(output, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID') + self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output) + self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID') class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): @@ -5173,11 +6048,23 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): '25-agent-server-peer.network') start_networkd() - self.wait_online(['client:routable']) + self.wait_online('client:routable') output = networkctl_status('client') print(output) - self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)') + self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)') + + def test_relay_agent_on_bridge(self): + copy_network_unit('25-agent-bridge.netdev', + '25-agent-veth-client.netdev', + '25-agent-bridge.network', + '25-agent-bridge-port.network', + '25-agent-client.network') + start_networkd() + self.wait_online('bridge-relay:routable', 'client-peer:enslaved') + + # For issue #30763. + self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED') class NetworkdDHCPClientTests(unittest.TestCase, Utilities): @@ -5191,7 +6078,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') # information request mode # The name ipv6-only option may not be supported by older dnsmasq @@ -5200,7 +6087,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option6:dns-server,[2600::ee]', '--dhcp-option=option6:ntp-server,[2600::ff]', ra_mode='ra-stateless') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state. # Let's wait for the expected DNS server being listed in the state file. @@ -5242,7 +6129,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option6:dns-server,[2600::ee]', '--dhcp-option=option6:ntp-server,[2600::ff]') networkctl_reconfigure('veth99') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') # checking address output = check_output('ip address show dev veth99 scope global') @@ -5300,7 +6187,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option6:ntp-server,[2600::ff]') networkctl_reload() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') # checking address output = check_output('ip address show dev veth99 scope global') @@ -5346,7 +6233,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_ipv6_dbus_status(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') # Note that at this point the DHCPv6 client has not been started because no RA (with managed # bit set) has yet been received and the configuration does not include WithoutRA=true @@ -5359,7 +6246,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertEqual(state, 'selecting') start_dnsmasq('--dhcp-option=108,00:00:02:00') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') state = get_dhcp6_client_state('veth99') print(f"DHCPv6 client state = {state}") @@ -5404,9 +6291,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') # checking address output = check_output('ip address show dev veth99 scope global') @@ -5423,6 +6310,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertIn('DHCPREPLY(veth-peer)', output) self.assertIn('sent size: 0 option: 14 rapid-commit', output) + @expectedFailureIfKernelReturnsInvalidFlags() def test_dhcp_client_ipv4_only(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network') @@ -5431,13 +6319,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.setup_nftset('ifindex', 'iface_index') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22', '--dhcp-option=option:domain-search,example.com', '--dhcp-alternate-port=67,5555', ipv4_range='192.168.5.110,192.168.5.119') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4') print('## ip address show dev veth99 scope global') @@ -5531,7 +6419,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120) self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') print('## ip address show dev veth99 scope global') output = check_output('ip address show dev veth99 scope global') @@ -5624,7 +6512,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_ipv4_dbus_status(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') state = get_dhcp4_client_state('veth99') print(f"State = {state}") @@ -5634,7 +6522,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option:domain-search,example.com', '--dhcp-alternate-port=67,5555', ipv4_range='192.168.5.110,192.168.5.119') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4') state = get_dhcp4_client_state('veth99') @@ -5645,50 +6533,32 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False) start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') since = datetime.datetime.now() start_dnsmasq() - expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.' - for _ in range(20): - if expect in read_networkd_log(since=since): - break - time.sleep(0.5) - else: - self.fail() + self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since) copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf') since = datetime.datetime.now() networkctl_reload() - expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.' - for _ in range(20): - if expect in read_networkd_log(since=since): - break - time.sleep(0.5) - else: - self.fail() + self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since) copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf') since = datetime.datetime.now() networkctl_reload() - expect = 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.' - for _ in range(20): - if expect in read_networkd_log(since=since): - break - time.sleep(0.5) - else: - self.fail() + self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since) @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support") def test_dhcp_client_rapid_commit(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq('--dhcp-rapid-commit') - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4') state = get_dhcp4_client_state('veth99') @@ -5706,7 +6576,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '25-dhcp-server-ipv6-only-mode.network', '25-dhcp-client-ipv6-only-mode.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable'], timeout='40s') + self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s') self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4') state = get_dhcp4_client_state('veth99') @@ -5734,7 +6604,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit(*testunits, copy_dropins=False) start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') additional_options = [ '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8', '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9', @@ -5745,7 +6615,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option:classless-static-route,0.0.0.0/0,192.168.5.4,8.0.0.0/8,192.168.5.5,192.168.5.64/26,192.168.5.5' ] start_dnsmasq(*additional_options) - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = check_output('ip -4 route show dev veth99') print(output) @@ -5810,9 +6680,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_settings_anonymize(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') print('## dnsmasq log') output = read_dnsmasq_log_file() @@ -5826,9 +6696,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '25-dhcp-server-veth-peer.network', '25-dhcp-client-keep-configuration-dhcp.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = check_output('ip address show dev veth99 scope global') print(output) @@ -5860,7 +6730,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): f.write('[Network]\nDHCP=no\n') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') # Still the lease address should be kept after networkd restarted output = check_output('ip address show dev veth99 scope global') @@ -5873,9 +6743,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '25-dhcp-server-veth-peer.network', '25-dhcp-client-keep-configuration-dhcp-on-stop.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = check_output('ip address show dev veth99 scope global') print(output) @@ -5889,7 +6759,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') start_networkd() - self.wait_online(['veth-peer:routable']) + self.wait_online('veth-peer:routable') output = check_output('ip address show dev veth99 scope global') print(output) @@ -5898,9 +6768,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_reuse_address_as_static(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') # link become 'routable' when at least one protocol provide an valid address. self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') @@ -5918,7 +6788,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): f.write(static_network) restart_networkd() - self.wait_online(['veth99:routable']) + self.wait_online('veth99:routable') output = check_output('ip -4 address show dev veth99 scope global') print(output) @@ -5935,9 +6805,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network', '25-vrf.netdev', '25-vrf.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier']) + self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier') # link become 'routable' when at least one protocol provide an valid address. self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') @@ -5978,9 +6848,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-gateway-onlink-implicit.network') start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable') output = networkctl_status('veth99') print(output) @@ -5999,7 +6869,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): start_networkd() # we need to increase timeout above default, as this will need to wait for # systemd-networkd to get the dhcpv4 transient failure event - self.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout='60s') + self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s') output = check_output('ip -4 address show dev veth99') print(output) @@ -6010,7 +6880,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped') self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4') self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4') - self.wait_online(['veth99:routable']) + self.wait_online('veth99:routable') output = check_output('ip -4 address show dev veth99') print(output) @@ -6039,7 +6909,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): f.write('\n[IPv6AcceptRA]\nUseDNS=no') networkctl_reload() - self.wait_online(['veth99:routable']) + self.wait_online('veth99:routable') # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') @@ -6064,7 +6934,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1', '--dhcp-option=option6:dns-server,[2600::1]') @@ -6073,6 +6943,54 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check(self, False, True) check(self, False, False) + def test_dhcp_client_default_use_domains(self): + def check(self, common, ipv4, ipv6): + mkdir_p(networkd_conf_dropin_dir) + with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f: + f.write('[Network]\nUseDomains=') + f.write('yes\n' if common else 'no\n') + f.write('[DHCPv4]\nUseDomains=') + f.write('yes\n' if ipv4 else 'no\n') + f.write('[DHCPv6]\nUseDomains=') + f.write('yes\n' if ipv6 else 'no\n') + + restart_networkd() + self.wait_online('veth-peer:carrier') + start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1', + '--dhcp-option=option6:dns-server,[2600::1]', + '--dhcp-option=option:domain-search,example.com', + '--dhcp-option=option6:domain-search,example.com') + + self.wait_online('veth99:routable') + + # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. + self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') + self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + + for _ in range(20): + output = resolvectl('domain', 'veth99') + if common or ipv4 or ipv6: + if 'example.com' in output: + break + else: + if 'example.com' not in output: + break + time.sleep(0.5) + else: + print(output) + print(read_link_state_file('veth99')) + self.fail('unexpected domain setting in resolved...') + + stop_dnsmasq() + remove_networkd_conf_dropin('default_use_domains.conf') + + copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + check(self, True, False, False) + check(self, False, True, True) + check(self, False, True, False) + check(self, False, False, True) + check(self, False, False, False) + def test_dhcp_client_use_captive_portal(self): def check(self, ipv4, ipv6): os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True) @@ -6084,7 +7002,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no') networkctl_reload() - self.wait_online(['veth99:routable']) + self.wait_online('veth99:routable') # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') @@ -6102,7 +7020,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') start_dnsmasq('--dhcp-option=114,http://systemd.io', '--dhcp-option=option6:103,http://systemd.io') @@ -6122,7 +7040,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no') networkctl_reload() - self.wait_online(['veth99:routable']) + self.wait_online('veth99:routable') # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') @@ -6138,7 +7056,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) start_networkd() - self.wait_online(['veth-peer:carrier']) + self.wait_online('veth-peer:carrier') masq = lambda bs: ':'.join(f'{b:02x}' for b in bs) start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'), '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url')) @@ -6181,9 +7099,9 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network') start_networkd() - self.wait_online(['veth-peer:routable']) + self.wait_online('veth-peer:routable') start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6') - self.wait_online(['veth99:degraded']) + self.wait_online('veth99:degraded') print('### ip -6 address show dev veth99 scope global') output = check_output('ip -6 address show dev veth99 scope global') @@ -6198,9 +7116,9 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network') start_networkd() - self.wait_online(['veth-peer:routable']) + self.wait_online('veth-peer:routable') start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6') - self.wait_online(['veth99:degraded']) + self.wait_online('veth99:degraded') print('### ip -6 address show dev veth99 scope global') output = check_output('ip -6 address show dev veth99 scope global') @@ -6219,10 +7137,10 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network') start_networkd() - self.wait_online(['veth-peer:routable']) + self.wait_online('veth-peer:routable') start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6') - self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', - 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) + self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', + 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable') self.setup_nftset('addr6', 'ipv6_addr') self.setup_nftset('network6', 'ipv6_addr', 'flags interval;') @@ -6367,7 +7285,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): # Test case for a downstream which appears later check_output('ip link add dummy97 type dummy') - self.wait_online(['dummy97:routable']) + self.wait_online('dummy97:routable') print('### ip -6 address show dev dummy97 scope global') output = check_output('ip -6 address show dev dummy97 scope global') @@ -6384,7 +7302,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): # Test case for reconfigure networkctl_reconfigure('dummy98', 'dummy99') - self.wait_online(['dummy98:routable', 'dummy99:degraded']) + self.wait_online('dummy98:routable', 'dummy99:degraded') print('### ip -6 address show dev dummy98 scope global') output = check_output('ip -6 address show dev dummy98 scope global') @@ -6623,7 +7541,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): '80-6rd-tunnel.network') start_networkd() - self.wait_online(['veth-peer:routable']) + self.wait_online('veth-peer:routable') # ipv4masklen: 8 # 6rd-prefix: 2001:db8::/32 @@ -6632,8 +7550,8 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): start_dnsmasq('--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01', ipv4_range='10.100.100.100,10.100.100.200', ipv4_router='10.0.0.1') - self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', - 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) + self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', + 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable') # Check the DBus interface for assigned prefix information prefixInfo = get_dhcp_6rd_prefix('veth99') @@ -6645,7 +7563,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): # Test case for a downstream which appears later check_output('ip link add dummy97 type dummy') - self.wait_online(['dummy97:routable']) + self.wait_online('dummy97:routable') # Find tunnel name tunnel_name = None @@ -6654,21 +7572,21 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): tunnel_name = name break - self.wait_online([f'{tunnel_name}:routable']) + self.wait_online(f'{tunnel_name}:routable') self.verify_dhcp4_6rd(tunnel_name) # Test case for reconfigure networkctl_reconfigure('dummy98', 'dummy99') - self.wait_online(['dummy98:routable', 'dummy99:degraded']) + self.wait_online('dummy98:routable', 'dummy99:degraded') self.verify_dhcp4_6rd(tunnel_name) print('Wait for the DHCP lease to be renewed/rebind') time.sleep(120) - self.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded', - 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) + self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded', + 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable') self.verify_dhcp4_6rd(tunnel_name) @@ -6685,7 +7603,7 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): '12-dummy.netdev', '25-ipv6ra-uplink.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable') output = check_output('ip address show dev veth-peer') print(output) @@ -6736,7 +7654,7 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): '12-dummy.netdev', '25-ipv6ra-uplink.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) + self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable') output = check_output('ip address show dev veth-peer') print(output) @@ -6777,13 +7695,13 @@ class NetworkdMTUTests(unittest.TestCase, Utilities): # test normal start start_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') self.check_link_attr('dummy98', 'mtu', mtu) self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu) # test normal restart restart_networkd() - self.wait_online(['dummy98:routable']) + self.wait_online('dummy98:routable') self.check_link_attr('dummy98', 'mtu', mtu) self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu) @@ -6846,63 +7764,33 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir') parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir') - parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin') - parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin') - parser.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest='timesyncd_bin') - parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin') - parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin') - parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin') - parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin') - parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin') - parser.add_argument('--udevadm', help='Path to udevadm', dest='udevadm_bin') parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind) parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug) parser.add_argument('--asan-options', help='ASAN options', dest='asan_options') parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options') parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options') parser.add_argument('--with-coverage', help='Loosen certain sandbox restrictions to make gcov happy', dest='with_coverage', type=bool, nargs='?', const=True, default=with_coverage) + parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false') ns, unknown_args = parser.parse_known_args(namespace=unittest) if ns.build_dir: - if ns.networkd_bin or ns.resolved_bin or ns.timesyncd_bin or ns.udevd_bin or \ - ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin or ns.udevadm_bin: - print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.') networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd') resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved') timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd') - udevd_bin = os.path.join(ns.build_dir, 'udevadm') wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online') networkctl_bin = os.path.join(ns.build_dir, 'networkctl') resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl') timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl') udevadm_bin = os.path.join(ns.build_dir, 'udevadm') - systemd_udev_rules_build_dir = os.path.join(ns.build_dir, 'rules.d') - else: - if ns.networkd_bin: - networkd_bin = ns.networkd_bin - if ns.resolved_bin: - resolved_bin = ns.resolved_bin - if ns.timesyncd_bin: - timesyncd_bin = ns.timesyncd_bin - if ns.udevd_bin: - udevd_bin = ns.udevd_bin - if ns.wait_online_bin: - wait_online_bin = ns.wait_online_bin - if ns.networkctl_bin: - networkctl_bin = ns.networkctl_bin - if ns.resolvectl_bin: - resolvectl_bin = ns.resolvectl_bin - if ns.timedatectl_bin: - timedatectl_bin = ns.timedatectl_bin - if ns.udevadm_bin: - udevadm_bin = ns.udevadm_bin + build_dir = ns.build_dir if ns.source_dir: - systemd_source_dir = ns.source_dir + source_dir = ns.source_dir + assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree." + elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../meson_options.txt"))): + source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) else: - systemd_source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) - if not os.path.exists(os.path.join(systemd_source_dir, "meson_options.txt")): - raise RuntimeError(f"{systemd_source_dir} doesn't appear to be a systemd source tree") + source_dir = None use_valgrind = ns.use_valgrind enable_debug = ns.enable_debug @@ -6910,6 +7798,7 @@ if __name__ == '__main__': lsan_options = ns.lsan_options ubsan_options = ns.ubsan_options with_coverage = ns.with_coverage + show_journal = ns.show_journal if use_valgrind: # Do not forget the trailing space. @@ -6921,6 +7810,11 @@ if __name__ == '__main__': udevadm_cmd = valgrind_cmd.split() + [udevadm_bin] wait_online_cmd = valgrind_cmd.split() + [wait_online_bin] + if build_dir: + test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send')) + else: + test_ndisc_send = '/usr/lib/tests/test-ndisc-send' + if asan_options: env.update({'ASAN_OPTIONS': asan_options}) if lsan_options: @@ -6934,5 +7828,11 @@ if __name__ == '__main__': if enable_debug: wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'}) - sys.argv[1:] = unknown_args - unittest.main(verbosity=3) + unittest.main( + verbosity=3, + argv=[ + sys.argv[0], + *unknown_args, + *(["-k", match] if (match := os.getenv("TEST_MATCH_TESTCASE")) else []) + ], + ) diff --git a/test/test-path/basic.target b/test/test-path/basic.target index 45f71aa..c4b04c4 100644 --- a/test/test-path/basic.target +++ b/test/test-path/basic.target @@ -12,4 +12,4 @@ After=sysinit.target sockets.target paths.target slices.target tmp.mount # require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as # we support that unit being masked, and this should not be considered an error. RequiresMountsFor=/var /var/tmp -Wants=tmp.mount +WantsMountsFor=/tmp diff --git a/test/test-rpm-macros.sh b/test/test-rpm-macros.sh index c7107de..c9a45dc 100755 --- a/test/test-rpm-macros.sh +++ b/test/test-rpm-macros.sh @@ -137,7 +137,7 @@ for i in sysusers tmpfiles; do PKG_DATA_FILE="$(mktemp "$WORK_DIR/pkg-data-XXX")" EXP_OUT="$(mktemp "$WORK_DIR/exp-out-XXX.log")" - CONF_DIR="$(pkg-config --variable="${i}dir" systemd)" + CONF_DIR="$(PKG_CONFIG_PATH="${BUILD_DIR}/src/core" pkg-config --variable="${i}dir" systemd)" EXTRA_ARGS=() if [[ "$i" == tmpfiles ]]; then diff --git a/test/test-systemctl-enable.sh b/test/test-systemctl-enable.sh index e22a3ef..5615c90 100644 --- a/test/test-systemctl-enable.sh +++ b/test/test-systemctl-enable.sh @@ -20,10 +20,10 @@ islink() { test "$(readlink "$1")" = "$2" || return 2 } -: '------enable nonexistent------------------------------------' +: '-------enable nonexistent--------------------------------------' ( ! "$systemctl" --root="$root" enable test1.service ) -: '------basic enablement--------------------------------------' +: '-------basic enablement----------------------------------------' mkdir -p "$root/etc/systemd/system" cat >"$root/etc/systemd/system/test1.service" <<EOF [Install] @@ -43,7 +43,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service" test ! -h "$root/etc/systemd/system/default.target.wants/test1.service" test ! -h "$root/etc/systemd/system/special.target.requires/test1.service" -: '------enable when link already exists-----------------------' +: '-------enable when link already exists-------------------------' # We don't read the symlink target, so it's OK for the symlink to point # to something else. We should just silently accept this. @@ -64,7 +64,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service" test ! -h "$root/etc/systemd/system/default.target.wants/test1.service" test ! -h "$root/etc/systemd/system/special.target.requires/test1.service" -: '------suffix guessing---------------------------------------' +: '-------suffix guessing-----------------------------------------' "$systemctl" --root="$root" enable test1 test -h "$root/etc/systemd/system/default.target.wants/test1.service" test -h "$root/etc/systemd/system/special.target.requires/test1.service" @@ -77,7 +77,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service" test ! -e "$root/etc/systemd/system/default.target.wants/test1.service" test ! -e "$root/etc/systemd/system/special.target.requires/test1.service" -: '-------aliases----------------------------------------------' +: '-------aliases-------------------------------------------------' cat >>"$root/etc/systemd/system/test1.service" <<EOF Alias=test1-goodalias.service Alias=test1@badalias.service @@ -97,7 +97,7 @@ test ! -e "$root/etc/systemd/system/test1-badalias.target" test ! -e "$root/etc/systemd/system/test1-badalias.socket" test -h "$root/etc/systemd/system/test1-goodalias2.service" -: '-------aliases in reeanble----------------------------------' +: '-------aliases in reenable-------------------------------------' ( ! "$systemctl" --root="$root" reenable test1 ) test -h "$root/etc/systemd/system/default.target.wants/test1.service" test ! -e "$root/etc/systemd/system/test1-goodalias.service" @@ -112,7 +112,7 @@ test ! -e "$root/etc/systemd/system/default.target.wants/test1.service" test ! -e "$root/etc/systemd/system/special.target.requires/test1.service" test ! -e "$root/etc/systemd/system/test1-goodalias.service" -: '-------aliases when link already exists---------------------' +: '-------aliases when link already exists------------------------' cat >"$root/etc/systemd/system/test1a.service" <<EOF [Install] Alias=test1a-alias.service @@ -126,7 +126,7 @@ test -h "$root/etc/systemd/system/test1a-alias.service" "$systemctl" --root="$root" disable test1a.service test ! -h "$root/etc/systemd/system/test1a-alias.service" -: '-------also units-------------------------------------------' +: '-------also units----------------------------------------------' cat >"$root/etc/systemd/system/test2.socket" <<EOF [Install] WantedBy=sockets.target @@ -152,7 +152,7 @@ test ! -e "$root/etc/systemd/system/default.target.wants/test2.service" test ! -e "$root/etc/systemd/system/sockets.target.wants/test2.socket" -: '-------link-------------------------------------------------' +: '-------link----------------------------------------------------' # File doesn't exist yet test ! -e "$root/link1.path" ( ! "$systemctl" --root="$root" link '/link1.path' ) @@ -166,65 +166,65 @@ EOF "$systemctl" --root="$root" link '/link1.path' islink "$root/etc/systemd/system/link1.path" "/link1.path" -: '-------link already linked same path------------------------' +: '-------link already linked same path---------------------------' SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" link '/link1.path' # this passes islink "$root/etc/systemd/system/link1.path" "/link1.path" -: '-------link already linked different path-------------------' +: '-------link already linked different path----------------------' mkdir "$root/subdir" cp "$root/link1.path" "$root/subdir/" ( ! "$systemctl" --root="$root" link '/subdir/link1.path' ) islink "$root/etc/systemd/system/link1.path" "/link1.path" -: '-------link bad suffix--------------------------------------' +: '-------link bad suffix-----------------------------------------' cp "$root/link1.path" "$root/subdir/link1.suffix" ( ! "$systemctl" --root="$root" link '/subdir/link1.suffix' ) test ! -e "$root/etc/systemd/system/link1.suffix" -: '-------unlink by unit name----------------------------------' +: '-------unlink by unit name-------------------------------------' "$systemctl" --root="$root" disable 'link1.path' test ! -e "$root/etc/systemd/system/link1.path" -: '-------unlink by path---------------------------------------' +: '-------unlink by path------------------------------------------' "$systemctl" --root="$root" link '/link1.path' test -h "$root/etc/systemd/system/link1.path" "$systemctl" --root="$root" disable '/link1.path' test ! -e "$root/etc/systemd/system/link1.path" -: '-------unlink by wrong path---------------------------------' +: '-------unlink by wrong path------------------------------------' "$systemctl" --root="$root" link '/link1.path' test -h "$root/etc/systemd/system/link1.path" "$systemctl" --root="$root" disable '/subdir/link1.path' # we only care about the name test ! -e "$root/etc/systemd/system/link1.path" -: '-------link and enable--------------------------------------' +: '-------link and enable-----------------------------------------' "$systemctl" --root="$root" enable '/link1.path' islink "$root/etc/systemd/system/link1.path" "/link1.path" islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path" -: '-------enable already linked same path----------------------' +: '-------enable already linked same path-------------------------' "$systemctl" --root="$root" enable '/link1.path' islink "$root/etc/systemd/system/link1.path" "/link1.path" islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path" -: '-------enable already linked different path-----------------' +: '-------enable already linked different path--------------------' ( ! "$systemctl" --root="$root" enable '/subdir/link1.path' ) islink "$root/etc/systemd/system/link1.path" "/link1.path" islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path" -: '-------enable bad suffix------------------------------------' +: '-------enable bad suffix---------------------------------------' cp "$root/link1.path" "$root/subdir/link1.suffix" ( ! "$systemctl" --root="$root" enable '/subdir/link1.suffix' ) test ! -e "$root/etc/systemd/system/link1.suffix" test ! -e "$root/etc/systemd/system/paths.target.wants/link1.suffix" -: '-------disable by unit name---------------------------------' +: '-------disable by unit name------------------------------------' "$systemctl" --root="$root" disable 'link1.path' test ! -e "$root/etc/systemd/system/link1.path" test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path" -: '-------disable by path--------------------------------------' +: '-------disable by path-----------------------------------------' "$systemctl" --root="$root" enable '/link1.path' test -h "$root/etc/systemd/system/link1.path" test -h "$root/etc/systemd/system/paths.target.wants/link1.path" @@ -233,7 +233,7 @@ test ! -e "$root/etc/systemd/system/link1.path" test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path" -: '-------link and enable-------------------------------------' +: '-------link and enable-----------------------------------------' "$systemctl" --root="$root" link '/link1.path' islink "$root/etc/systemd/system/link1.path" "/link1.path" test ! -h "$root/etc/systemd/system/paths.target.wants/link1.path" @@ -246,7 +246,30 @@ islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path" islink "$root/etc/systemd/system/link1.path" "/link1.path" islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path" -: '-------manual link------------------------------------------' +: '-------link instance and enable--------------------------------' +cat >"$root/link-instance@.service" <<EOF +[Service] +ExecStart=true +[Install] +WantedBy=services.target +EOF + +"$systemctl" --root="$root" link '/link-instance@.service' +islink "$root/etc/systemd/system/link-instance@.service" "/link-instance@.service" + +"$systemctl" --root="$root" enable 'link-instance@first.service' +islink "$root/etc/systemd/system/link-instance@first.service" "/link-instance@.service" +islink "$root/etc/systemd/system/services.target.wants/link-instance@first.service" "/link-instance@.service" + +SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" reenable 'link-instance@first.service' +islink "$root/etc/systemd/system/link-instance@first.service" "/link-instance@.service" +islink "$root/etc/systemd/system/services.target.wants/link-instance@first.service" "/link-instance@.service" + +"$systemctl" --root="$root" disable 'link-instance@first.service' +test ! -h "$root/etc/systemd/system/link-instance@first.service" +test ! -h "$root/etc/systemd/system/services.target.wants/link-instance@first.service" + +: '-------manual link---------------------------------------------' cat >"$root/link3.suffix" <<EOF [Install] WantedBy=services.target @@ -263,18 +286,18 @@ SYSTEMD_LOG_LEVEL=debug SYSTEMD_LOG_LOCATION=1 "$systemctl" --root="$root" disab test ! -h "$root/etc/systemd/system/link3.service" test ! -h "$root/etc/systemd/system/services.target.wants/link3.service" -: '-------enable on masked-------------------------------------' +: '-------enable on masked----------------------------------------' ln -s "/dev/null" "$root/etc/systemd/system/masked.service" ( ! "$systemctl" --root="$root" enable 'masked.service' ) ( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked.service' ) -: '-------enable on masked alias-------------------------------' +: '-------enable on masked alias----------------------------------' test -h "$root/etc/systemd/system/masked.service" ln -s "masked.service" "$root/etc/systemd/system/masked-alias.service" ( ! "$systemctl" --root="$root" enable 'masked-alias.service' ) ( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked-alias.service' ) -: '-------issue 22000: link in subdirectory--------------------' +: '-------issue 22000: link in subdirectory-----------------------' mkdir -p "$root/etc/systemd/system/myown.d" cat >"$root/etc/systemd/system/link5-also.service" <<EOF [Install] @@ -295,7 +318,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/link5-also.service" test ! -h "$root/etc/systemd/system/services.target.wants/link5.service" islink "$root/etc/systemd/system/services.target.wants/link5-also.service" "/etc/systemd/system/link5-also.service" -: '-------template enablement----------------------------------' +: '-------template enablement-------------------------------------' cat >"$root/etc/systemd/system/templ1@.service" <<EOF [Install] WantedBy=services.target @@ -314,6 +337,11 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service" islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "/etc/systemd/system/templ1@.service" islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "/etc/systemd/system/templ1@.service" +"$systemctl" --root="$root" reenable 'templ1@two.service' +test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service" +islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "/etc/systemd/system/templ1@.service" +islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "/etc/systemd/system/templ1@.service" + "$systemctl" --root="$root" disable 'templ1@one.service' test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service" test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service" @@ -324,7 +352,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service" test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service" test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service" -: '-------template enablement w/ default instance--------------' +: '-------template enablement w/ default instance-----------------' cat >"$root/etc/systemd/system/templ1@.service" <<EOF [Install] # check enablement with @@ -373,7 +401,7 @@ test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.serv test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service" test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@two.service" -: '-------removal of relative enablement symlinks--------------' +: '-------removal of relative enablement symlinks-----------------' test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service" ln -s '../templ1@one.service' "$root/etc/systemd/system/services.target.wants/templ1@one.service" ln -s 'templ1@two.service' "$root/etc/systemd/system/services.target.wants/templ1@two.service" @@ -393,7 +421,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@five.service" test ! -h "$root/etc/systemd/system/services.target.wants/templ1@six.service" test ! -h "$root/etc/systemd/system/services.target.wants/templ1@seven.service" -: '-------template enablement for another template-------------' +: '-------template enablement for another template----------------' cat >"$root/etc/systemd/system/templ2@.service" <<EOF [Install] RequiredBy=another-template@.target @@ -418,7 +446,7 @@ test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@.service" test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service" -: '-------aliases w/ and w/o instance--------------------------' +: '-------aliases w/ and w/o instance-----------------------------' test ! -e "$root/etc/systemd/system/link4.service" cat >"$root/etc/systemd/system/link4.service" <<EOF [Install] @@ -443,7 +471,7 @@ test ! -h "$root/etc/systemd/system/link4@inst.service" test ! -h "$root/etc/systemd/system/link4alias.service" test ! -h "$root/etc/systemd/system/link4alias2.service" -: '-------systemctl enable on path to unit file----------------' +: '-------systemctl enable on path to unit file-------------------' cat >"$root/etc/systemd/system/link4.service" <<EOF [Install] Alias=link4alias.service @@ -461,7 +489,7 @@ test ! -h "$root/etc/systemd/system/link4.service" test ! -h "$root/etc/systemd/system/link4alias.service" test ! -h "$root/etc/systemd/system/link4alias2.service" -: '-------issue 661: enable on unit file--------------' +: '-------issue 661: enable on unit file--------------------------' test ! -e "$root/etc/systemd/system/link5.service" cat >"$root/etc/systemd/system/link5.service" <<EOF [Install] @@ -479,7 +507,7 @@ islink "$root/etc/systemd/system/link5alias2.service" "/etc/systemd/system/link5 test ! -h "$root/etc/systemd/system/link5alias.service" test ! -h "$root/etc/systemd/system/link5alias2.service" -: '-------issue 661: link and enable on unit file--------------' +: '-------issue 661: link and enable on unit file-----------------' test ! -e "$root/etc/systemd/system/link5copy.service" cat >"$root/link5copy.service" <<EOF [Install] @@ -506,15 +534,15 @@ test ! -h "$root/etc/systemd/system/link5alias2.service" "$systemctl" --root="$root" enable '/link5copy.service' islink "$root/etc/systemd/system/link5copy.service" '/link5copy.service' -islink "$root/etc/systemd/system/link5alias.service" '/link5copy.service' -islink "$root/etc/systemd/system/link5alias2.service" '/link5copy.service' +islink "$root/etc/systemd/system/link5alias.service" '/etc/systemd/system/link5copy.service' +islink "$root/etc/systemd/system/link5alias2.service" '/etc/systemd/system/link5copy.service' "$systemctl" --root="$root" disable 'link5copy.service' test ! -h "$root/etc/systemd/system/link5copy.service" test ! -h "$root/etc/systemd/system/link5alias.service" test ! -h "$root/etc/systemd/system/link5alias2.service" -: '----issue 19437: plain templates in .wants/ or .requires/---' +: '-------issue 19437: plain templates in .wants/ or .requires/---' test ! -e "$root/etc/systemd/system/link5@.path" cat >"$root/etc/systemd/system/link5@.path" <<EOF [Install] @@ -538,7 +566,7 @@ test ! -h "$root/etc/systemd/system/target5@.target.requires/link5@.path" test ! -h "$root/etc/systemd/system/target5@inst.target.wants/link5@.path" test ! -h "$root/etc/systemd/system/target5@inst.target.requires/link5@.path" -: '-------removal of symlinks not listed in [Install]----------' +: '-------removal of symlinks not listed in [Install]-------------' # c.f. 66a19d85a533b15ed32f4066ec880b5a8c06babd test ! -e "$root/etc/systemd/system/multilink.mount" cat >"$root/etc/systemd/system/multilink.mount" <<EOF @@ -557,7 +585,7 @@ test ! -h "$root/etc/systemd/system/default.target.wants/" test ! -h "$root/etc/systemd/system/multilink-alias.mount" test ! -h "$root/etc/systemd/system/multilink-badalias.service" -: '-------merge 20017: specifiers in the unit file-------------' +: '-------merge 20017: specifiers in the unit file----------------' test ! -e "$root/etc/systemd/system/some-some-link6@.socket" # c.f. de61a04b188f81a85cdb5c64ddb4987dcd9d30d3 @@ -661,7 +689,7 @@ uname -r | grep -q '[^a-zA-Z0-9_.\\-]' || \ # %z is not defined ( ! check_alias z 'z' ) -: '-------specifiers in WantedBy-------------------------------' +: '-------specifiers in WantedBy----------------------------------' # We don't need to repeat all the tests. Let's do a basic check that specifier # expansion is performed. @@ -687,7 +715,7 @@ test ! -h "$root/etc/systemd/system/another-target2@.target.requires/some-some-l # TODO: repeat the tests above for presets -: '-------SYSTEMD_OS_RELEASE relative to root-------------------' +: '-------SYSTEMD_OS_RELEASE relative to root---------------------' # check that os-release overwriting works as expected with root test -e "$root/etc/os-release" diff --git a/test/test-sysusers.sh.in b/test/test-sysusers.sh.in index 11e3940..9af253f 100755 --- a/test/test-sysusers.sh.in +++ b/test/test-sysusers.sh.in @@ -36,12 +36,12 @@ preprocess() { compare() { if ! diff -u "$TESTDIR/etc/passwd" <(preprocess "$1.expected-passwd" "$3"); then - echo "**** Unexpected output for $f $2" + echo >&2 "**** Unexpected output for $f $2" exit 1 fi if ! diff -u "$TESTDIR/etc/group" <(preprocess "$1.expected-group" "$3"); then - echo "**** Unexpected output for $f $2" + echo >&2 "**** Unexpected output for $f $2" exit 1 fi } @@ -168,8 +168,8 @@ for f in $(find "$SOURCE"/unhappy-*.input | sort -V); do cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf" SYSTEMD_LOG_LEVEL=info $SYSUSERS --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err" if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err"; then - echo "**** Unexpected error output for $f" - cat "$TESTDIR/err" + echo >&2 "**** Unexpected error output for $f" + cat >&2 "$TESTDIR/err" exit 1 fi done diff --git a/test/test.service.in b/test/test.service.in new file mode 100644 index 0000000..790c513 --- /dev/null +++ b/test/test.service.in @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=%N +Wants=basic.target network.target @wants@ +After=basic.target network.target @after@ +Before=getty-pre.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=@command@ +Type=oneshot +MemoryAccounting=@memory-accounting@ diff --git a/test/testsuite-04.units/logs-filtering.service b/test/testsuite-04.units/logs-filtering.service deleted file mode 100644 index 6e2af9a..0000000 --- a/test/testsuite-04.units/logs-filtering.service +++ /dev/null @@ -1,6 +0,0 @@ -[Unit] -Description=Log filtering unit - -[Service] -ExecStart=sh -c 'while true; do echo "Logging from the service, and ~more~ foo bar"; sleep .25; done' -SyslogLevel=notice diff --git a/test/testsuite-04.units/silent-success.service b/test/testsuite-04.units/silent-success.service deleted file mode 100644 index 3d83f87..0000000 --- a/test/testsuite-04.units/silent-success.service +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=Silent successful service - -[Service] -LogLevelMax=notice -ExecStart=/bin/true diff --git a/test/testsuite-23.units/testsuite-23-binds-to.service b/test/testsuite-23.units/testsuite-23-binds-to.service deleted file mode 100644 index 637fea4..0000000 --- a/test/testsuite-23.units/testsuite-23-binds-to.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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=/bin/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 diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service deleted file mode 100644 index 5e823a1..0000000 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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=/bin/bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity' diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service deleted file mode 100644 index bbbfd7c..0000000 --- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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=/bin/bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity' diff --git a/test/testsuite-23.units/testsuite-23-non-namespaced.service b/test/testsuite-23.units/testsuite-23-non-namespaced.service deleted file mode 100644 index 6f93c3b..0000000 --- a/test/testsuite-23.units/testsuite-23-non-namespaced.service +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Service] -RuntimeMaxSec=5 -Type=notify -RemainAfterExit=yes -ExecStart=/bin/sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0' diff --git a/test/testsuite-23.units/testsuite-23-prop-stop-one.service b/test/testsuite-23.units/testsuite-23-prop-stop-one.service deleted file mode 100644 index f068daf..0000000 --- a/test/testsuite-23.units/testsuite-23-prop-stop-one.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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=/bin/sleep infinity -ExecStopPost=systemctl kill --kill-whom=main -sUSR2 testsuite-23.service diff --git a/test/testsuite-80.units/notify.service b/test/testsuite-80.units/notify.service deleted file mode 100644 index 196b076..0000000 --- a/test/testsuite-80.units/notify.service +++ /dev/null @@ -1,4 +0,0 @@ -[Service] -Type=notify -NotifyAccess=all -ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/test.sh diff --git a/test/units/testsuite-01.sh b/test/units/TEST-01-BASIC.sh index 870b62d..bb3ff2f 100755 --- a/test/units/testsuite-01.sh +++ b/test/units/TEST-01-BASIC.sh @@ -48,14 +48,16 @@ systemd-analyze blame # Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'. # See issue #27163. # shellcheck disable=SC2034 -for _ in {0..10}; do - systemctl daemon-reexec & - pid_reexec=$! - # shellcheck disable=SC2034 +if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then for _ in {0..10}; do - SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel + 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 - wait "$pid_reexec" -done +fi touch /testok diff --git a/test/units/TEST-02-UNITTESTS.sh b/test/units/TEST-02-UNITTESTS.sh new file mode 100755 index 0000000..6392425 --- /dev/null +++ b/test/units/TEST-02-UNITTESTS.sh @@ -0,0 +1,102 @@ +#!/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##*/}" + local environment= + + echo "Executing test $name as unit $name.service" + + case "$name" in + test-journal-flush) + environment="SYSTEMD_LOG_LEVEL=info" + ;; + test-journal-verify) + environment="SYSTEMD_LOG_LEVEL=crit" + ;; + esac + + systemd-run \ + --quiet \ + --property Delegate=1 \ + --property EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \ + --property "Environment=$environment" \ + --unit="$name" \ + --wait "$test" && ret=0 || ret=$? + + exec {LOCK_FD}> /lock + flock --exclusive ${LOCK_FD} + + if [[ $ret -eq 77 ]] || [[ $ret -eq 127 ]]; then + echo "$name skipped" + echo "$name" >>/skipped-tests + { + echo "--- $name begin ---" + journalctl --unit="$name" --no-hostname -o short-monotonic + echo "--- $name end ---" + } >>/skipped + elif [[ $ret -ne 0 ]]; 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 + 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 diff --git a/test/units/testsuite-03.sh b/test/units/TEST-03-JOBS.sh index e3567c2..115b941 100755 --- a/test/units/testsuite-03.sh +++ b/test/units/TEST-03-JOBS.sh @@ -80,13 +80,13 @@ cat <<EOF >/run/systemd/system/wait2.service [Unit] Description=Wait for 2 seconds [Service] -ExecStart=/bin/sh -ec 'sleep 2' +ExecStart=sh -ec 'sleep 2' EOF cat <<EOF >/run/systemd/system/wait5fail.service [Unit] Description=Wait for 5 seconds and fail [Service] -ExecStart=/bin/sh -ec 'sleep 5; false' +ExecStart=sh -ec 'sleep 5; false' EOF # wait2 succeeds diff --git a/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh b/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh new file mode 100755 index 0000000..ec99b86 --- /dev/null +++ b/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +if ! cgroupfs_supports_user_xattrs; then + echo "CGroup does not support user xattrs, skipping LogFilterPatterns= tests." + exit 0 +fi + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +NEEDS_RELOAD= + +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" + NEEDS_RELOAD=1 +} + +run_service_and_fetch_logs() { + local unit="${1:?}" + local start + + if [[ -n "$NEEDS_RELOAD" ]]; then + systemctl daemon-reload + NEEDS_RELOAD= + fi + + journalctl --sync + start="$(date '+%Y-%m-%d %T.%6N')" + systemctl start "$unit" + journalctl -q -u "$unit" -S "$start" -p notice +} + +at_exit() { + rm -rf /run/systemd/system/{logs-filtering,delegated-cgroup-filtering}.service.d + systemctl daemon-reload +} + +trap at_exit EXIT + +# 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") ]] diff --git a/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh index 96d096d..6da9f5e 100755 --- a/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh +++ b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh @@ -7,6 +7,12 @@ set -o pipefail mkdir /run/systemd/system/systemd-journald.service.d MACHINE_ID="$(</etc/machine-id)" +mkdir -p /run/systemd/journald.conf.d +cat <<EOF >/run/systemd/journald.conf.d/compress.conf +[Journal] +Compress=yes +EOF + # Reset the start-limit counters, as we're going to restart journald a couple of times systemctl reset-failed systemd-journald.service @@ -35,6 +41,7 @@ EOF fi done +rm /run/systemd/journald.conf.d/compress.conf rm /run/systemd/system/systemd-journald.service.d/compress.conf systemctl daemon-reload systemctl restart systemd-journald.service diff --git a/test/units/testsuite-04.bsod.sh b/test/units/TEST-04-JOURNAL.bsod.sh index 30f0cb0..83feb89 100755 --- a/test/units/testsuite-04.bsod.sh +++ b/test/units/TEST-04-JOURNAL.bsod.sh @@ -4,7 +4,12 @@ set -eux set -o pipefail if systemd-detect-virt -cq; then - echo "This test requires a VM, skipping the test" + 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 @@ -17,8 +22,22 @@ at_exit() { 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 @@ -88,6 +107,7 @@ 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 diff --git a/test/units/TEST-04-JOURNAL.cat.sh b/test/units/TEST-04-JOURNAL.cat.sh new file mode 100755 index 0000000..bef3e18 --- /dev/null +++ b/test/units/TEST-04-JOURNAL.cat.sh @@ -0,0 +1,15 @@ +#!/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 diff --git a/test/units/testsuite-04.corrupted-journals.sh b/test/units/TEST-04-JOURNAL.corrupted-journals.sh index 2123b10..479011e 100755 --- a/test/units/testsuite-04.corrupted-journals.sh +++ b/test/units/TEST-04-JOURNAL.corrupted-journals.sh @@ -6,11 +6,11 @@ set -o pipefail JOURNAL_DIR="$(mktemp -d)" REMOTE_OUT="$(mktemp -d)" # tar on C8S doesn't support the --zstd option -unzstd --stdout "/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/" +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 /test-journals/corrupted/ -name "*.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 diff --git a/test/units/testsuite-04.fss.sh b/test/units/TEST-04-JOURNAL.fss.sh index 03351b8..03351b8 100755 --- a/test/units/testsuite-04.fss.sh +++ b/test/units/TEST-04-JOURNAL.fss.sh diff --git a/test/units/testsuite-04.journal-append.sh b/test/units/TEST-04-JOURNAL.journal-append.sh index 35f9433..35f9433 100755 --- a/test/units/testsuite-04.journal-append.sh +++ b/test/units/TEST-04-JOURNAL.journal-append.sh diff --git a/test/units/testsuite-04.journal-corrupt.sh b/test/units/TEST-04-JOURNAL.journal-corrupt.sh index 051d0ab..c25cf54 100755 --- a/test/units/testsuite-04.journal-corrupt.sh +++ b/test/units/TEST-04-JOURNAL.journal-corrupt.sh @@ -10,6 +10,7 @@ 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 @@ -20,6 +21,7 @@ for _ in {0..9}; do done systemctl stop --user -M testuser@ user-sleep.service +loginctl disable-linger testuser journalctl --sync journalctl --rotate --vacuum-files=1 diff --git a/test/units/TEST-04-JOURNAL.journal-gatewayd.sh b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh new file mode 100755 index 0000000..9c7a3d0 --- /dev/null +++ b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh @@ -0,0 +1,223 @@ +#!/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" diff --git a/test/units/testsuite-04.journal-remote.sh b/test/units/TEST-04-JOURNAL.journal-remote.sh index c7b99b1..c7b99b1 100755 --- a/test/units/testsuite-04.journal-remote.sh +++ b/test/units/TEST-04-JOURNAL.journal-remote.sh diff --git a/test/units/testsuite-04.journal.sh b/test/units/TEST-04-JOURNAL.journal.sh index 3b72aa4..bd9f8a5 100755 --- a/test/units/testsuite-04.journal.sh +++ b/test/units/TEST-04-JOURNAL.journal.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2317 set -eux set -o pipefail @@ -105,9 +104,16 @@ diff /tmp/expected /tmp/output # 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 +[[ -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 @@ -124,6 +130,8 @@ journalctl -b -n 1 -r --user-unit "*" # 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 @@ -135,6 +143,8 @@ 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 @@ -235,7 +245,7 @@ JOURNAL_DIR="$(mktemp -d)" while read -r file; do filename="${file##*/}" unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}" -done < <(find /test-journals/no-rtc -name "*.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' @@ -243,9 +253,6 @@ diff -u /tmp/lb1 - <<'EOF' EOF rm -rf "$JOURNAL_DIR" /tmp/lb1 -# v255-only: skip the following test case, as it suffers from systemd/systemd#30886 -exit 0 - # 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 @@ -253,8 +260,8 @@ 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 -xec "echo hello; echo world" -journalctl --sync +systemd-run --unit="$UNIT_NAME" --wait --service-type=exec -p LogLevelMax=info \ + bash -ec "set -x; echo hello; echo world; set +x; 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 @@ -267,6 +274,7 @@ diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UN hello + echo world world ++ set +x EOF rm -f "$CURSOR_FILE" diff --git a/test/units/testsuite-04.sh b/test/units/TEST-04-JOURNAL.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-04.sh +++ b/test/units/TEST-04-JOURNAL.sh diff --git a/test/units/TEST-05-RLIMITS.effective-limit.sh b/test/units/TEST-05-RLIMITS.effective-limit.sh new file mode 100755 index 0000000..f6639f1 --- /dev/null +++ b/test/units/TEST-05-RLIMITS.effective-limit.sh @@ -0,0 +1,68 @@ +#!/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=400M +MemoryHigh=400M +TasksMax=400 +EOF + +cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF +[Slice] +MemoryMax=100M +MemoryHigh=100M +TasksMax=100 +EOF + +cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF +[Slice] +MemoryMax=200M +MemoryHigh=200M +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=50M \ + -p MemoryHigh=50M \ + -p TasksMax=50 \ + sleep inf + +# Compare with inequality because test can run in a constrained container +assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "52428800" +assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "52428800" +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")" "104857600" +assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "104857600" +assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100" + +systemctl set-property "$slc0" \ + MemoryMax=50M \ + MemoryHigh=50M \ + TasksMax=50 + +assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "52428800" +assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "52428800" +assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50" + +systemctl stop "$srv" + +rm -f /run/systemd/system/"$pre"* || : diff --git a/test/units/testsuite-05.sh b/test/units/TEST-05-RLIMITS.rlimit.sh index 870845d..6b425d4 100755 --- a/test/units/testsuite-05.sh +++ b/test/units/TEST-05-RLIMITS.rlimit.sh @@ -16,12 +16,10 @@ 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" ]] +[[ "$(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" ]]' - -touch /testok diff --git a/test/units/testsuite-13.sh b/test/units/TEST-05-RLIMITS.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-13.sh +++ b/test/units/TEST-05-RLIMITS.sh diff --git a/test/units/testsuite-06.sh b/test/units/TEST-06-SELINUX.sh index 7fc3c37..937a040 100755 --- a/test/units/testsuite-06.sh +++ b/test/units/TEST-06-SELINUX.sh @@ -3,6 +3,12 @@ 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) diff --git a/test/units/TEST-07-PID1.aux-scope.sh b/test/units/TEST-07-PID1.aux-scope.sh new file mode 100755 index 0000000..8e2a99c --- /dev/null +++ b/test/units/TEST-07-PID1.aux-scope.sh @@ -0,0 +1,34 @@ +#!/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 diff --git a/test/units/testsuite-07.exec-context.sh b/test/units/TEST-07-PID1.exec-context.sh index b44658f..a3379ef 100755 --- a/test/units/testsuite-07.exec-context.sh +++ b/test/units/TEST-07-PID1.exec-context.sh @@ -32,10 +32,19 @@ proc_supports_option() { # 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; test ! -w /boot; test -w /etc; test -w /var" + 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; test ! -w /boot; test ! -w /etc; test -w /var" + 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 \ @@ -149,13 +158,13 @@ if ! systemd-detect-virt -cq; then -p DevicePolicy=closed -p DevicePolicy=strict -p DeviceAllow="char-mem rm" # Allow read & mknod for /dev/{null,zero,...} - -p DeviceAllow="/dev/loop0 rw" - -p DeviceAllow="/dev/loop0 w" # Allow write for /dev/loop0 + -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 /dev/loop0; test -w /dev/loop0; test ! -r /dev/tty; test ! -w /dev/tty' + 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*= diff --git a/test/units/TEST-07-PID1.exec-deserialization.sh b/test/units/TEST-07-PID1.exec-deserialization.sh new file mode 100755 index 0000000..04f17c8 --- /dev/null +++ b/test/units/TEST-07-PID1.exec-deserialization.sh @@ -0,0 +1,221 @@ +#!/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 diff --git a/test/units/TEST-07-PID1.exec-timestamps.sh b/test/units/TEST-07-PID1.exec-timestamps.sh new file mode 100755 index 0000000..0211166 --- /dev/null +++ b/test/units/TEST-07-PID1.exec-timestamps.sh @@ -0,0 +1,17 @@ +#!/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 ]] diff --git a/test/units/testsuite-07.issue-14566.sh b/test/units/TEST-07-PID1.issue-14566.sh index d4be5b5..d4be5b5 100755 --- a/test/units/testsuite-07.issue-14566.sh +++ b/test/units/TEST-07-PID1.issue-14566.sh diff --git a/test/units/testsuite-07.issue-16115.sh b/test/units/TEST-07-PID1.issue-16115.sh index 8f63826..8f63826 100755 --- a/test/units/testsuite-07.issue-16115.sh +++ b/test/units/TEST-07-PID1.issue-16115.sh diff --git a/test/units/testsuite-07.issue-1981.sh b/test/units/TEST-07-PID1.issue-1981.sh index 6eb802c..dcfa9b1 100755 --- a/test/units/testsuite-07.issue-1981.sh +++ b/test/units/TEST-07-PID1.issue-1981.sh @@ -24,7 +24,7 @@ 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=/bin/echo Timer runs me +ExecStart=echo Timer runs me EOF cat >/run/systemd/system/my.timer <<EOF diff --git a/test/units/testsuite-07.issue-2467.sh b/test/units/TEST-07-PID1.issue-2467.sh index de0577b..de0577b 100755 --- a/test/units/testsuite-07.issue-2467.sh +++ b/test/units/TEST-07-PID1.issue-2467.sh diff --git a/test/units/testsuite-07.issue-27953.sh b/test/units/TEST-07-PID1.issue-27953.sh index 8659970..8659970 100755 --- a/test/units/testsuite-07.issue-27953.sh +++ b/test/units/TEST-07-PID1.issue-27953.sh diff --git a/test/units/testsuite-07.issue-30412.sh b/test/units/TEST-07-PID1.issue-30412.sh index c1cb00e..c1cb00e 100755 --- a/test/units/testsuite-07.issue-30412.sh +++ b/test/units/TEST-07-PID1.issue-30412.sh diff --git a/test/units/testsuite-07.issue-3166.sh b/test/units/TEST-07-PID1.issue-3166.sh index 6677901..6677901 100755 --- a/test/units/testsuite-07.issue-3166.sh +++ b/test/units/TEST-07-PID1.issue-3166.sh diff --git a/test/units/testsuite-07.issue-3171.sh b/test/units/TEST-07-PID1.issue-3171.sh index db17c25..374df54 100755 --- a/test/units/testsuite-07.issue-3171.sh +++ b/test/units/TEST-07-PID1.issue-3171.sh @@ -24,7 +24,7 @@ cat >/run/systemd/system/issue-3171@.service <<EOF Description=Test service [Service] StandardInput=socket -ExecStart=/bin/sh -x -c cat +ExecStart=sh -x -c cat EOF systemctl start issue-3171.socket diff --git a/test/units/testsuite-07.main-PID-change.sh b/test/units/TEST-07-PID1.main-PID-change.sh index bd1144c..16f3510 100755 --- a/test/units/testsuite-07.main-PID-change.sh +++ b/test/units/TEST-07-PID1.main-PID-change.sh @@ -10,7 +10,7 @@ set -o pipefail # The main service PID should be the parent bash process MAINPID="${PPID:?}" -test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID" +test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID" # Start a test process inside of our own cgroup sleep infinity & @@ -23,41 +23,41 @@ 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" +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 testsuite-07.service)" -eq "$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 testsuite-07.service)" -eq "$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 testsuite-07.service)" -eq "$MAINPID" +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 testsuite-07.service)" -eq "$MAINPID" +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 testsuite-07.service)" -eq "$MAINPID" +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 testsuite-07.service)" -eq "$MAINPID" +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 testsuite-07.service)" -eq "$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 testsuite-07.service)" -eq "$MAINPID" +test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID" cat >/tmp/test-mainpid.sh <<\EOF #!/usr/bin/env bash diff --git a/test/units/testsuite-07.mount-invalid-chars.sh b/test/units/TEST-07-PID1.mount-invalid-chars.sh index a879334..a879334 100755 --- a/test/units/testsuite-07.mount-invalid-chars.sh +++ b/test/units/TEST-07-PID1.mount-invalid-chars.sh diff --git a/test/units/testsuite-07.poll-limit.sh b/test/units/TEST-07-PID1.poll-limit.sh index 480d7ee..ca988b2 100755 --- a/test/units/testsuite-07.poll-limit.sh +++ b/test/units/TEST-07-PID1.poll-limit.sh @@ -5,12 +5,12 @@ set -o pipefail systemd-analyze log-level debug -cat > /run/systemd/system/floodme@.service <<EOF +cat >/run/systemd/system/floodme@.service <<EOF [Service] -ExecStart=/bin/true +ExecStart=true EOF -cat > /run/systemd/system/floodme.socket <<EOF +cat >/run/systemd/system/floodme.socket <<EOF [Socket] ListenStream=/tmp/floodme PollLimitIntervalSec=10s @@ -24,7 +24,7 @@ systemctl start floodme.socket START=$(date +%s%N) # Trigger this 100 times in a flood -for (( i=0 ; i < 100; i++ )) ; do +for _ in {1..100}; do logger -u /tmp/floodme foo & done diff --git a/test/units/TEST-07-PID1.pr-31351.sh b/test/units/TEST-07-PID1.pr-31351.sh new file mode 100755 index 0000000..f6911f2 --- /dev/null +++ b/test/units/TEST-07-PID1.pr-31351.sh @@ -0,0 +1,22 @@ +#!/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 diff --git a/test/units/testsuite-07.private-network.sh b/test/units/TEST-07-PID1.private-network.sh index 37658f7..37658f7 100755 --- a/test/units/testsuite-07.private-network.sh +++ b/test/units/TEST-07-PID1.private-network.sh diff --git a/test/units/testsuite-07.sh b/test/units/TEST-07-PID1.sh index 2ee1457..873d638 100755 --- a/test/units/testsuite-07.sh +++ b/test/units/TEST-07-PID1.sh @@ -13,3 +13,4 @@ mountpoint /issue2730 run_subtests touch /testok +systemctl --no-block exit 123 diff --git a/test/units/TEST-07-PID1.socket-pass-fds.sh b/test/units/TEST-07-PID1.socket-pass-fds.sh new file mode 100755 index 0000000..a61b1c0 --- /dev/null +++ b/test/units/TEST-07-PID1.socket-pass-fds.sh @@ -0,0 +1,11 @@ +#!/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 diff --git a/test/units/TEST-07-PID1.type-exec-parallel.sh b/test/units/TEST-07-PID1.type-exec-parallel.sh new file mode 100755 index 0000000..30f80c5 --- /dev/null +++ b/test/units/TEST-07-PID1.type-exec-parallel.sh @@ -0,0 +1,9 @@ +#!/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 diff --git a/test/units/testsuite-08.sh b/test/units/TEST-08-INITRD.sh index 5c6b4ce..4804527 100755 --- a/test/units/testsuite-08.sh +++ b/test/units/TEST-08-INITRD.sh @@ -13,7 +13,7 @@ fi if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then echo "systemd didn't run in the initrd, skipping the test" touch /skipped - exit 0 + exit 77 fi # We should've created a mount under /run in initrd (see the other half of the test) @@ -22,9 +22,9 @@ 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 +# Copy the prepared exitrd to its intended location. Check the respective # test.sh file for details mkdir -p /run/initramfs -cp -r /shutdown-initrd/* /run/initramfs/ +cp -r /exitrd/* /run/initramfs/ touch /testok diff --git a/test/units/testsuite-09.journal.sh b/test/units/TEST-09-REBOOT.journal.sh index 2ef192c..726e800 100755 --- a/test/units/testsuite-09.journal.sh +++ b/test/units/TEST-09-REBOOT.journal.sh @@ -22,6 +22,22 @@ get_last_timestamp() { journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP' } +# There may be huge amount of pending messages in sockets. Processing them may cause journal rotation. +# If the journal is rotated in the loop below, some journal file may not be loaded and an unexpected +# result may be provided. To mitigate such, flush (if not yet) and sync before reading journals. +# Workaround for #32890. +journalctl --flush +journalctl --sync +# Sometimes, loading partially written .journal file, and journalctl handled that as 'truncated': +# === +# May 21 02:25:55 TEST-09-REBOOT.sh[433]: + journalctl --list-boots -o json +# May 21 02:25:55 journalctl[433]: Journal file /var/log/journal/173da2fad3064e3e9211a7ed7d59360b/system.journal is truncated, ignoring file. +# === +# If that happens, the entries stored in the journal file are ignored, and the results of --list-boots +# and subsequent call of journalctl may become inconsistent. To prevent such issue, let's also rotate +# the journal. Then, all journal entries we are interested in are stored in the archived journal files. +journalctl --rotate + # Issue: #29275, second part # Now let's check if the boot entries are in the correct/expected order index=0 @@ -70,3 +86,47 @@ journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, 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 diff --git a/test/units/testsuite-09.sh b/test/units/TEST-09-REBOOT.sh index cd95660..85630b6 100755 --- a/test/units/testsuite-09.sh +++ b/test/units/TEST-09-REBOOT.sh @@ -3,7 +3,7 @@ set -eux set -o pipefail -NUM_REBOOT=4 +export NUM_REBOOT=4 # shellcheck source=test/units/test-control.sh . "$(dirname "$0")"/test-control.sh diff --git a/test/units/TEST-13-NSPAWN.importctl.sh b/test/units/TEST-13-NSPAWN.importctl.sh new file mode 100755 index 0000000..a13e3fd --- /dev/null +++ b/test/units/TEST-13-NSPAWN.importctl.sh @@ -0,0 +1,66 @@ +#!/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 diff --git a/test/units/testsuite-13.machinectl.sh b/test/units/TEST-13-NSPAWN.machinectl.sh index b5f90f6..462cc6a 100755 --- a/test/units/testsuite-13.machinectl.sh +++ b/test/units/TEST-13-NSPAWN.machinectl.sh @@ -20,9 +20,12 @@ at_exit() { trap at_exit EXIT -# Mount tmpfs over /var/lib/machines to not pollute the image +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 -t tmpfs tmpfs /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 @@ -32,7 +35,7 @@ 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 +#!/usr/bin/bash PID=0 @@ -129,6 +132,9 @@ machinectl show-image clone1 machinectl rename clone1 clone2 (! machinectl show-image clone1) machinectl show-image clone2 +# `machinectl read-only` uses chattr (ioctl(FS_IOC_SETFLAGS)) when the container is backed by a directory, +# and this operation might not be implemented on certain filesystems (i.e. tmpfs on older kernels), so check +# if we have chattr support before running following tests if lsattr -d /var/lib/machines >/dev/null; then [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]] machinectl read-only clone2 yes @@ -151,37 +157,37 @@ test ! -d /var/lib/machines/.hidden1 # Prepare a simple raw container mkdir -p /tmp/mnt -dd if=/dev/zero of=/tmp/container.raw bs=1M count=64 -mkfs.ext4 /tmp/container.raw -mount -o loop /tmp/container.raw /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 /tmp/container.raw container-raw +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 /tmp/container-export.raw -machinectl import-raw /tmp/container-export.raw container-raw-reimport +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 /tmp/container{,-export}.raw +rm -f /var/tmp/container{,-export}.raw # Prepare a simple tar.gz container -tar -pczf /tmp/container.tar.gz -C /var/lib/machines/container1 . +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 /tmp/container.tar.gz container-tar -[[ "$(machinectl show-image --property=Type --value container-tar)" == "directory" ]] +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 /tmp/container-export.tar.gz -machinectl import-tar /tmp/container-export.tar.gz container-tar-reimport -[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" == "directory" ]] -rm -f /tmp/container{,-export}.tar.gz +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 /tmp/container.dir -machinectl import-fs /tmp/container.dir container-dir -[[ "$(machinectl show-image --property=Type --value container-dir)" == "directory" ]] +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 /tmp/container.dir +rm -fr /var/tmp/container.dir timeout 10 bash -c "until machinectl clean --all; do sleep .5; done" diff --git a/test/units/testsuite-13.nspawn-oci.sh b/test/units/TEST-13-NSPAWN.nspawn-oci.sh index 8fa0bc4..65dcc96 100755 --- a/test/units/testsuite-13.nspawn-oci.sh +++ b/test/units/TEST-13-NSPAWN.nspawn-oci.sh @@ -23,9 +23,9 @@ at_exit() { trap at_exit EXIT -# Mount tmpfs over /var/lib/machines to not pollute the image +# Mount temporary directory over /var/lib/machines to not pollute the image mkdir -p /var/lib/machines -mount -t tmpfs tmpfs /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)" @@ -34,7 +34,7 @@ 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)" +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" diff --git a/test/units/testsuite-13.nspawn.sh b/test/units/TEST-13-NSPAWN.nspawn.sh index 01f6eb6..7901e98 100755 --- a/test/units/testsuite-13.nspawn.sh +++ b/test/units/TEST-13-NSPAWN.nspawn.sh @@ -71,9 +71,9 @@ if unshare -U bash -c :; then IS_USERNS_SUPPORTED=yes fi -# Mount tmpfs over /var/lib/machines to not pollute the image +# Mount temporary directory over /var/lib/machines to not pollute the image mkdir -p /var/lib/machines -mount -t tmpfs tmpfs /var/lib/machines +mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines testcase_sanity() { local template root image uuid tmpdir @@ -82,8 +82,8 @@ testcase_sanity() { 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=64 + 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 @@ -94,7 +94,7 @@ testcase_sanity() { systemd-nspawn --version # --template= - root="$(mktemp -u -d /var/lib/machines/testsuite-13.sanity.XXX)" + 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 @@ -271,6 +271,13 @@ EOF --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 @@ -324,7 +331,7 @@ EOF } nspawn_settings_cleanup() { - for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do + 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 @@ -332,19 +339,25 @@ nspawn_settings_cleanup() { } testcase_nspawn_settings() { - local root container dev private_users + local root container dev private_users wlan_names mkdir -p /run/systemd/nspawn - root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)" + 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} - for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do + # 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 @@ -394,7 +407,7 @@ Private=yes VirtualEthernet=yes VirtualEthernetExtra=my-fancy-veth1 VirtualEthernetExtra=fancy-veth2:my-fancy-veth2 -Interface=sd-shared1 sd-shared2:sd-shared2 +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 @@ -440,12 +453,22 @@ 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-shared2 +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 @@ -482,7 +505,7 @@ bind_user_cleanup() { testcase_bind_user() { local root - root="$(mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX)" + 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 @@ -531,7 +554,7 @@ 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)" + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-tmp-path.XXX)" create_dummy_container "$root" : >/tmp/bind systemd-nspawn --register=no \ @@ -546,7 +569,7 @@ testcase_norbind() { # https://github.com/systemd/systemd/issues/13170 local root - root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)" + 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 @@ -573,7 +596,7 @@ testcase_rootidmap() { local root cmd permissions local owner=1000 - root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)" + 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 @@ -606,12 +629,82 @@ testcase_rootidmap() { 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/testsuite-13.check_notification_socket.XXX)" + 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" @@ -623,7 +716,7 @@ testcase_notification_socket() { testcase_os_release() { local root entrypoint os_release_source - root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)" + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.os-release.XXX)" create_dummy_container "$root" entrypoint="$root/entrypoint.sh" cat >"$entrypoint" <<\EOF @@ -665,7 +758,7 @@ 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)" + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.machinectl-bind.XXX)" create_dummy_container "$root" container_name="$(basename "$root")" @@ -700,7 +793,7 @@ testcase_selinux() { local root - root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)" + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.selinux.XXX)" create_dummy_container "$root" chcon -R -t container_t "$root" @@ -717,7 +810,7 @@ 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)" + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.ephemeral-config.XXX)" create_dummy_container "$root" container_name="$(basename "$root")" @@ -760,7 +853,7 @@ matrix_run_one() { return 0 fi - root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")" + 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" \ @@ -848,8 +941,8 @@ 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)" + 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=( diff --git a/test/units/testsuite-13.nss-mymachines.sh b/test/units/TEST-13-NSPAWN.nss-mymachines.sh index b566c73..817431b 100755 --- a/test/units/testsuite-13.nss-mymachines.sh +++ b/test/units/TEST-13-NSPAWN.nss-mymachines.sh @@ -17,8 +17,9 @@ at_exit() { trap at_exit EXIT +# Mount temporary directory over /var/lib/machines to not pollute the image mkdir -p /var/lib/machines -mount -t tmpfs tmpfs /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 @@ -56,7 +57,7 @@ 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 +ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad ip addr show dev ve-manyips touch /initialized sleep infinity @@ -90,7 +91,7 @@ done # 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 +ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad getent hosts -s mymachines getent ahosts -s mymachines diff --git a/test/units/TEST-13-NSPAWN.sh b/test/units/TEST-13-NSPAWN.sh new file mode 100755 index 0000000..dd7f274 --- /dev/null +++ b/test/units/TEST-13-NSPAWN.sh @@ -0,0 +1,18 @@ +#!/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 diff --git a/test/units/testsuite-15.sh b/test/units/TEST-15-DROPIN.sh index e790b37..5e4df52 100755 --- a/test/units/testsuite-15.sh +++ b/test/units/TEST-15-DROPIN.sh @@ -5,6 +5,8 @@ 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:?}" @@ -256,6 +258,8 @@ EOF 'MemoryMax' t 1000000002 \ 0 + timeout 1m bash -c 'until systemctl is-active a-b-c.slice; do sleep 1s; done' + # The override takes precedence for MemoryMax check_ok a-b-c.slice MemoryMax "1000000000" # The transient setting replaces the default @@ -273,6 +277,8 @@ EOF StopUnit 'ss' \ 'a-b-c.slice' 'replace' + timeout 1m bash -c 'while systemctl is-active a-b-c.slice; do sleep 1s; done' + rm -f "/run/systemd/system/$dropin/override.conf" done @@ -692,15 +698,15 @@ testcase_symlink_dropin_directory() { 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 + 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/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 + 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 diff --git a/test/units/testsuite-16.sh b/test/units/TEST-16-EXTEND-TIMEOUT.sh index c60995a..c60995a 100755 --- a/test/units/testsuite-16.sh +++ b/test/units/TEST-16-EXTEND-TIMEOUT.sh diff --git a/test/units/testsuite-17.00.sh b/test/units/TEST-17-UDEV.00.sh index d2aec60..d2aec60 100755 --- a/test/units/testsuite-17.00.sh +++ b/test/units/TEST-17-UDEV.00.sh diff --git a/test/units/testsuite-17.01.sh b/test/units/TEST-17-UDEV.01.sh index 44f36f5..44f36f5 100755 --- a/test/units/testsuite-17.01.sh +++ b/test/units/TEST-17-UDEV.01.sh diff --git a/test/units/testsuite-17.02.sh b/test/units/TEST-17-UDEV.02.sh index b232fca..96430e6 100755 --- a/test/units/testsuite-17.02.sh +++ b/test/units/TEST-17-UDEV.02.sh @@ -145,6 +145,7 @@ EOF # make sure that 'udevadm monitor' actually monitor uevents sleep 1 + journalctl --sync since="$(date '+%H:%M:%S')" # add another interface which will conflict with an existing interface @@ -172,7 +173,10 @@ EOF 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" + journalctl --sync + set +o pipefail + timeout -v 30 journalctl _PID=1 _COMM=systemd --since "$since" -n all --follow | grep -m 1 -q -F 'foobar: systemd-udevd failed to process the device, ignoring: File exists' + set -o pipefail # 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" } diff --git a/test/units/TEST-17-UDEV.03.sh b/test/units/TEST-17-UDEV.03.sh new file mode 100755 index 0000000..d6b3162 --- /dev/null +++ b/test/units/TEST-17-UDEV.03.sh @@ -0,0 +1,114 @@ +#!/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 diff --git a/test/units/testsuite-17.04.sh b/test/units/TEST-17-UDEV.04.sh index d1c3c85..d1c3c85 100755 --- a/test/units/testsuite-17.04.sh +++ b/test/units/TEST-17-UDEV.04.sh diff --git a/test/units/testsuite-17.05.sh b/test/units/TEST-17-UDEV.05.sh index 60be31a..60be31a 100755 --- a/test/units/testsuite-17.05.sh +++ b/test/units/TEST-17-UDEV.05.sh diff --git a/test/units/testsuite-17.06.sh b/test/units/TEST-17-UDEV.06.sh index 6d83645..6d83645 100755 --- a/test/units/testsuite-17.06.sh +++ b/test/units/TEST-17-UDEV.06.sh diff --git a/test/units/testsuite-17.07.sh b/test/units/TEST-17-UDEV.07.sh index 629393a..629393a 100755 --- a/test/units/testsuite-17.07.sh +++ b/test/units/TEST-17-UDEV.07.sh diff --git a/test/units/testsuite-17.08.sh b/test/units/TEST-17-UDEV.08.sh index e570c69..e570c69 100755 --- a/test/units/testsuite-17.08.sh +++ b/test/units/TEST-17-UDEV.08.sh diff --git a/test/units/testsuite-17.09.sh b/test/units/TEST-17-UDEV.09.sh index 9993196..9993196 100755 --- a/test/units/testsuite-17.09.sh +++ b/test/units/TEST-17-UDEV.09.sh diff --git a/test/units/testsuite-17.10.sh b/test/units/TEST-17-UDEV.10.sh index f229dcf..a2b8b14 100755 --- a/test/units/testsuite-17.10.sh +++ b/test/units/TEST-17-UDEV.10.sh @@ -45,8 +45,8 @@ udevadm control -S udevadm control -R udevadm control -p HELLO=world udevadm control -m 42 -udevadm control --ping -udevadm control -t 5 +udevadm control --ping -t 5 +udevadm control --load-credentials udevadm control -h udevadm info /dev/null diff --git a/test/units/testsuite-17.11.sh b/test/units/TEST-17-UDEV.11.sh index 42b925f..42b925f 100755 --- a/test/units/testsuite-17.11.sh +++ b/test/units/TEST-17-UDEV.11.sh diff --git a/test/units/testsuite-17.12.sh b/test/units/TEST-17-UDEV.12.sh index ccc91bf..ccc91bf 100755 --- a/test/units/testsuite-17.12.sh +++ b/test/units/TEST-17-UDEV.12.sh diff --git a/test/units/testsuite-17.13.sh b/test/units/TEST-17-UDEV.13.sh index d9dfdd7..d9dfdd7 100755 --- a/test/units/testsuite-17.13.sh +++ b/test/units/TEST-17-UDEV.13.sh diff --git a/test/units/TEST-17-UDEV.credentials.sh b/test/units/TEST-17-UDEV.credentials.sh new file mode 100755 index 0000000..42d3883 --- /dev/null +++ b/test/units/TEST-17-UDEV.credentials.sh @@ -0,0 +1,38 @@ +#!/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 diff --git a/test/units/TEST-17-UDEV.link-property.sh b/test/units/TEST-17-UDEV.link-property.sh new file mode 100755 index 0000000..517cc3f --- /dev/null +++ b/test/units/TEST-17-UDEV.link-property.sh @@ -0,0 +1,203 @@ +#!/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 diff --git a/test/units/testsuite-17.sh b/test/units/TEST-17-UDEV.sh index 14ceeba..14ceeba 100755 --- a/test/units/testsuite-17.sh +++ b/test/units/TEST-17-UDEV.sh diff --git a/test/units/testsuite-18.sh b/test/units/TEST-18-FAILUREACTION.sh index 44b792f..364c20d 100755 --- a/test/units/testsuite-18.sh +++ b/test/units/TEST-18-FAILUREACTION.sh @@ -11,7 +11,7 @@ if ! test -f /firstphase ; then systemd-run --wait -p SuccessAction=reboot true else echo OK >/testok - systemd-run --wait -p FailureAction=poweroff false + systemd-run --wait -p FailureAction=exit -p FailureActionExitStatus=123 false fi sleep infinity diff --git a/test/units/testsuite-19.ExitType-cgroup.sh b/test/units/TEST-19-CGROUP.ExitType-cgroup.sh index cd221d7..65c2606 100755 --- a/test/units/testsuite-19.ExitType-cgroup.sh +++ b/test/units/TEST-19-CGROUP.ExitType-cgroup.sh @@ -27,8 +27,9 @@ disown systemd-notify --ready -# Run the stop/kill command -\$1 & +# Run the stop/kill command, but sleep a bit to make the sleep infinity +# below actually started before stopping/killing the service. +(sleep 1; \$1) & # process tree: systemd -> bash -> sleep sleep infinity diff --git a/test/units/testsuite-19.cleanup-slice.sh b/test/units/TEST-19-CGROUP.cleanup-slice.sh index 5d63160..5d63160 100755 --- a/test/units/testsuite-19.cleanup-slice.sh +++ b/test/units/TEST-19-CGROUP.cleanup-slice.sh diff --git a/test/units/testsuite-19.delegate.sh b/test/units/TEST-19-CGROUP.delegate.sh index 74d36c4..022515f 100755 --- a/test/units/testsuite-19.delegate.sh +++ b/test/units/TEST-19-CGROUP.delegate.sh @@ -32,6 +32,7 @@ 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="MemoryAccounting=1" \ --property="DynamicUser=1" \ --property="Delegate=" \ test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ diff --git a/test/units/testsuite-19.sh b/test/units/TEST-19-CGROUP.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-19.sh +++ b/test/units/TEST-19-CGROUP.sh diff --git a/test/units/testsuite-21.sh b/test/units/TEST-21-DFUZZER.sh index 02673ab..08ebfd9 100755 --- a/test/units/testsuite-21.sh +++ b/test/units/TEST-21-DFUZZER.sh @@ -3,6 +3,12 @@ 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 @@ -21,14 +27,32 @@ at_exit() { 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 -sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:Reexecute FIXME' /etc/dfuzzer.conf -sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:SoftReboot destructive' /etc/dfuzzer.conf +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? diff --git a/test/units/testsuite-22.01.sh b/test/units/TEST-22-TMPFILES.01.sh index 2276b75..2276b75 100755 --- a/test/units/testsuite-22.01.sh +++ b/test/units/TEST-22-TMPFILES.01.sh diff --git a/test/units/testsuite-22.02.sh b/test/units/TEST-22-TMPFILES.02.sh index b883a96..f191ae3 100755 --- a/test/units/testsuite-22.02.sh +++ b/test/units/TEST-22-TMPFILES.02.sh @@ -14,6 +14,14 @@ mkdir /tmp/{C,d,D,e} 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 - - @@ -104,6 +112,14 @@ 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 diff --git a/test/units/testsuite-22.03.sh b/test/units/TEST-22-TMPFILES.03.sh index 6fce4c0..d158498 100755 --- a/test/units/testsuite-22.03.sh +++ b/test/units/TEST-22-TMPFILES.03.sh @@ -12,6 +12,14 @@ 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 @@ -189,6 +197,11 @@ 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 @@ -200,6 +213,12 @@ 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 @@ -207,6 +226,12 @@ 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 @@ -223,6 +248,10 @@ 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 diff --git a/test/units/testsuite-22.04.sh b/test/units/TEST-22-TMPFILES.04.sh index 7bf2b28..2c0ffe3 100755 --- a/test/units/testsuite-22.04.sh +++ b/test/units/TEST-22-TMPFILES.04.sh @@ -9,6 +9,12 @@ 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 diff --git a/test/units/testsuite-22.05.sh b/test/units/TEST-22-TMPFILES.05.sh index cde9b5d..d78e7ee 100755 --- a/test/units/testsuite-22.05.sh +++ b/test/units/TEST-22-TMPFILES.05.sh @@ -12,6 +12,15 @@ mkdir /tmp/{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 - - diff --git a/test/units/testsuite-22.06.sh b/test/units/TEST-22-TMPFILES.06.sh index f64a95c..45aea9c 100755 --- a/test/units/testsuite-22.06.sh +++ b/test/units/TEST-22-TMPFILES.06.sh @@ -6,7 +6,14 @@ set -eux set -o pipefail test_snippet() { - systemd-tmpfiles "$@" - <<EOF + # 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 diff --git a/test/units/testsuite-22.07.sh b/test/units/TEST-22-TMPFILES.07.sh index de20d5e..de20d5e 100755 --- a/test/units/testsuite-22.07.sh +++ b/test/units/TEST-22-TMPFILES.07.sh diff --git a/test/units/testsuite-22.08.sh b/test/units/TEST-22-TMPFILES.08.sh index 40fafd3..40fafd3 100755 --- a/test/units/testsuite-22.08.sh +++ b/test/units/TEST-22-TMPFILES.08.sh diff --git a/test/units/testsuite-22.09.sh b/test/units/TEST-22-TMPFILES.09.sh index 0857773..0857773 100755 --- a/test/units/testsuite-22.09.sh +++ b/test/units/TEST-22-TMPFILES.09.sh diff --git a/test/units/testsuite-22.10.sh b/test/units/TEST-22-TMPFILES.10.sh index 99052c8..99052c8 100755 --- a/test/units/testsuite-22.10.sh +++ b/test/units/TEST-22-TMPFILES.10.sh diff --git a/test/units/testsuite-22.11.sh b/test/units/TEST-22-TMPFILES.11.sh index f71a95f..6f75888 100755 --- a/test/units/testsuite-22.11.sh +++ b/test/units/TEST-22-TMPFILES.11.sh @@ -12,6 +12,19 @@ mkdir /tmp/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 @@ -34,6 +47,19 @@ test ! -f /tmp/x/z2 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 @@ -56,6 +82,20 @@ test ! -f /tmp/x/z2 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] @@ -79,6 +119,20 @@ test -f /tmp/x/z2 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] @@ -102,6 +156,19 @@ test -f /tmp/x/z2 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 @@ -125,17 +192,58 @@ test -f /tmp/x/2/a/y2 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 -# X is not supposed to influence R +# 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 -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 diff --git a/test/units/testsuite-22.12.sh b/test/units/TEST-22-TMPFILES.12.sh index b8c4da8..57788da 100755 --- a/test/units/testsuite-22.12.sh +++ b/test/units/TEST-22-TMPFILES.12.sh @@ -52,6 +52,16 @@ sleep 1 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 - diff --git a/test/units/testsuite-22.13.sh b/test/units/TEST-22-TMPFILES.13.sh index 33ef451..33ef451 100755 --- a/test/units/testsuite-22.13.sh +++ b/test/units/TEST-22-TMPFILES.13.sh diff --git a/test/units/testsuite-22.14.sh b/test/units/TEST-22-TMPFILES.14.sh index 2132de7..2132de7 100755 --- a/test/units/testsuite-22.14.sh +++ b/test/units/TEST-22-TMPFILES.14.sh diff --git a/test/units/testsuite-22.15.sh b/test/units/TEST-22-TMPFILES.15.sh index 6cbb498..6ee0e63 100755 --- a/test/units/testsuite-22.15.sh +++ b/test/units/TEST-22-TMPFILES.15.sh @@ -13,6 +13,12 @@ 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 @@ -26,6 +32,12 @@ 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 diff --git a/test/units/testsuite-22.16.sh b/test/units/TEST-22-TMPFILES.16.sh index 555e07f..3d3d0c8 100755 --- a/test/units/testsuite-22.16.sh +++ b/test/units/TEST-22-TMPFILES.16.sh @@ -12,6 +12,11 @@ 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 diff --git a/test/units/testsuite-22.17.sh b/test/units/TEST-22-TMPFILES.17.sh index f43aba5..f43aba5 100755 --- a/test/units/testsuite-22.17.sh +++ b/test/units/TEST-22-TMPFILES.17.sh diff --git a/test/units/TEST-22-TMPFILES.18.sh b/test/units/TEST-22-TMPFILES.18.sh new file mode 100755 index 0000000..5d24197 --- /dev/null +++ b/test/units/TEST-22-TMPFILES.18.sh @@ -0,0 +1,34 @@ +#!/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 diff --git a/test/units/TEST-22-TMPFILES.19.sh b/test/units/TEST-22-TMPFILES.19.sh new file mode 100755 index 0000000..c5a0966 --- /dev/null +++ b/test/units/TEST-22-TMPFILES.19.sh @@ -0,0 +1,29 @@ +#!/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 diff --git a/test/units/testsuite-22.sh b/test/units/TEST-22-TMPFILES.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-22.sh +++ b/test/units/TEST-22-TMPFILES.sh diff --git a/test/units/TEST-23-UNIT-FILE-openfile-child.sh b/test/units/TEST-23-UNIT-FILE-openfile-child.sh new file mode 100755 index 0000000..4828b9d --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE-openfile-child.sh @@ -0,0 +1,15 @@ +#!/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 diff --git a/test/units/TEST-23-UNIT-FILE-short-lived.sh b/test/units/TEST-23-UNIT-FILE-short-lived.sh new file mode 100755 index 0000000..4b13070 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE-short-lived.sh @@ -0,0 +1,18 @@ +#!/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 diff --git a/test/units/testsuite-23.ExecReload.sh b/test/units/TEST-23-UNIT-FILE.ExecReload.sh index b497f73..d544ce6 100755 --- a/test/units/testsuite-23.ExecReload.sh +++ b/test/units/TEST-23-UNIT-FILE.ExecReload.sh @@ -14,7 +14,7 @@ SERVICE_NAME="${SERVICE_PATH##*/}" echo "[#1] Failing ExecReload= should not kill the service" cat >"$SERVICE_PATH" <<EOF [Service] -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity ExecReload=/bin/false EOF @@ -30,7 +30,7 @@ systemctl stop "$SERVICE_NAME" echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)" cat >"$SERVICE_PATH" <<EOF [Service] -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity ExecReload=/bin/true ExecReload=/bin/false ExecReload=/bin/true @@ -47,7 +47,7 @@ systemctl stop "$SERVICE_NAME" echo "[#3] Failing ExecReload=- should not affect reload's exit code" cat >"$SERVICE_PATH" <<EOF [Service] -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity ExecReload=-/bin/false EOF diff --git a/test/units/testsuite-23.ExecStopPost.sh b/test/units/TEST-23-UNIT-FILE.ExecStopPost.sh index aeaf3aa..aeaf3aa 100755 --- a/test/units/testsuite-23.ExecStopPost.sh +++ b/test/units/TEST-23-UNIT-FILE.ExecStopPost.sh diff --git a/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh b/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh new file mode 100755 index 0000000..4fd68d8 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh @@ -0,0 +1,31 @@ +#!/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 diff --git a/test/units/testsuite-23.RuntimeDirectoryPreserve.sh b/test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh index ca57702..ca57702 100755 --- a/test/units/testsuite-23.RuntimeDirectoryPreserve.sh +++ b/test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh diff --git a/test/units/testsuite-23.StandardOutput.sh b/test/units/TEST-23-UNIT-FILE.StandardOutput.sh index 50b9ac2..a951b13 100755 --- a/test/units/testsuite-23.StandardOutput.sh +++ b/test/units/TEST-23-UNIT-FILE.StandardOutput.sh @@ -7,7 +7,7 @@ set -o pipefail systemd-analyze log-level debug -systemd-run --wait --unit=testsuite-23-standard-output-one \ +systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-one \ -p StandardOutput=file:/tmp/stdout \ -p StandardError=file:/tmp/stderr \ -p Type=exec \ @@ -19,7 +19,7 @@ cmp /tmp/stderr <<EOF y EOF -systemd-run --wait --unit=testsuite-23-standard-output-two \ +systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-two \ -p StandardOutput=file:/tmp/stdout \ -p StandardError=file:/tmp/stderr \ -p Type=exec \ @@ -31,7 +31,7 @@ cmp /tmp/stderr <<EOF a EOF -systemd-run --wait --unit=testsuite-23-standard-output-three \ +systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-three \ -p StandardOutput=append:/tmp/stdout \ -p StandardError=append:/tmp/stderr \ -p Type=exec \ @@ -45,7 +45,7 @@ a c EOF -systemd-run --wait --unit=testsuite-23-standard-output-four \ +systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-four \ -p StandardOutput=truncate:/tmp/stdout \ -p StandardError=truncate:/tmp/stderr \ -p Type=exec \ diff --git a/test/units/TEST-23-UNIT-FILE.Upholds.sh b/test/units/TEST-23-UNIT-FILE.Upholds.sh new file mode 100755 index 0000000..20d2d2b --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.Upholds.sh @@ -0,0 +1,99 @@ +#!/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 diff --git a/test/units/testsuite-23.clean-unit.sh b/test/units/TEST-23-UNIT-FILE.clean-unit.sh index a82b54f..a883243 100755 --- a/test/units/testsuite-23.clean-unit.sh +++ b/test/units/TEST-23-UNIT-FILE.clean-unit.sh @@ -26,7 +26,7 @@ StateDirectory=test-service CacheDirectory=test-service LogsDirectory=test-service RuntimeDirectoryPreserve=yes -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity Type=exec EOF @@ -97,7 +97,7 @@ StateDirectory=test-service CacheDirectory=test-service LogsDirectory=test-service RuntimeDirectoryPreserve=yes -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity Type=exec EOF diff --git a/test/units/testsuite-23.exec-command-ex.sh b/test/units/TEST-23-UNIT-FILE.exec-command-ex.sh index f926e7d..f926e7d 100755 --- a/test/units/testsuite-23.exec-command-ex.sh +++ b/test/units/TEST-23-UNIT-FILE.exec-command-ex.sh diff --git a/test/units/TEST-23-UNIT-FILE.oneshot-restart.sh b/test/units/TEST-23-UNIT-FILE.oneshot-restart.sh new file mode 100755 index 0000000..d06dbaa --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.oneshot-restart.sh @@ -0,0 +1,101 @@ +#!/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 diff --git a/test/units/TEST-23-UNIT-FILE.openfile.sh b/test/units/TEST-23-UNIT-FILE.openfile.sh new file mode 100755 index 0000000..644b6f4 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.openfile.sh @@ -0,0 +1,55 @@ +#!/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 diff --git a/test/units/testsuite-23.percentj-wantedby.sh b/test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh index e9ffaba..c091bd6 100755 --- a/test/units/testsuite-23.percentj-wantedby.sh +++ b/test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh @@ -8,8 +8,8 @@ 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" \ + --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 diff --git a/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh b/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh new file mode 100755 index 0000000..3a78234 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh @@ -0,0 +1,43 @@ +#!/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) diff --git a/test/units/testsuite-23.sh b/test/units/TEST-23-UNIT-FILE.sh index a929c8b..a929c8b 100755 --- a/test/units/testsuite-23.sh +++ b/test/units/TEST-23-UNIT-FILE.sh diff --git a/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh b/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh new file mode 100755 index 0000000..61a6592 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh @@ -0,0 +1,93 @@ +#!/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 diff --git a/test/units/testsuite-23.statedir.sh b/test/units/TEST-23-UNIT-FILE.statedir.sh index b592314..b592314 100755 --- a/test/units/testsuite-23.statedir.sh +++ b/test/units/TEST-23-UNIT-FILE.statedir.sh diff --git a/test/units/testsuite-23.success-failure.sh b/test/units/TEST-23-UNIT-FILE.success-failure.sh index 8fc9596..8fc9596 100755 --- a/test/units/testsuite-23.success-failure.sh +++ b/test/units/TEST-23-UNIT-FILE.success-failure.sh diff --git a/test/units/testsuite-23.type-exec.sh b/test/units/TEST-23-UNIT-FILE.type-exec.sh index 87f32cc..87f32cc 100755 --- a/test/units/testsuite-23.type-exec.sh +++ b/test/units/TEST-23-UNIT-FILE.type-exec.sh diff --git a/test/units/testsuite-23.utmp.sh b/test/units/TEST-23-UNIT-FILE.utmp.sh index 4f84315..4f84315 100755 --- a/test/units/testsuite-23.utmp.sh +++ b/test/units/TEST-23-UNIT-FILE.utmp.sh diff --git a/test/units/TEST-23-UNIT-FILE.verify-unit-files.sh b/test/units/TEST-23-UNIT-FILE.verify-unit-files.sh new file mode 100755 index 0000000..3e83d44 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.verify-unit-files.sh @@ -0,0 +1,45 @@ +#!/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 diff --git a/test/units/testsuite-23.whoami.sh b/test/units/TEST-23-UNIT-FILE.whoami.sh index a0c73b8..b538d94 100755 --- a/test/units/testsuite-23.whoami.sh +++ b/test/units/TEST-23-UNIT-FILE.whoami.sh @@ -5,11 +5,11 @@ set -eux set -o pipefail -test "$(systemctl whoami)" = testsuite-23.service -test "$(systemctl whoami $$)" = testsuite-23.service +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 -testsuite-23.service +TEST-23-UNIT-FILE.service init.scope EOF diff --git a/test/units/testsuite-24.sh b/test/units/TEST-24-CRYPTSETUP.sh index c815f90..b788c82 100755 --- a/test/units/testsuite-24.sh +++ b/test/units/TEST-24-CRYPTSETUP.sh @@ -5,8 +5,6 @@ set -o pipefail # TODO: # - /proc/cmdline parsing -# - figure out token support (apart from TPM2, as that's covered by TEST-70-TPM2) -# - this might help https://www.qemu.org/docs/master/system/devices/ccid.html # - expect + interactive auth? # We set up an encrypted /var partition which should get mounted automatically @@ -35,6 +33,7 @@ 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 @@ -42,6 +41,11 @@ cryptsetup_start_and_check() { shift fi + if [[ "${1:?}" == "-u" ]]; then + umount_header_and_key=1 + shift + fi + for volume in "$@"; do unit="systemd-cryptsetup@$volume.service" @@ -64,6 +68,12 @@ cryptsetup_start_and_check() { return 1 fi + if [[ "$umount_header_and_key" -ne 0 ]]; then + umount "$TMPFS_DETACHED_KEYFILE" + umount "$TMPFS_DETACHED_HEADER" + udevadm settle --timeout=60 + fi + systemctl status "$unit" test -e "/dev/mapper/$volume" systemctl stop "$unit" @@ -133,22 +143,31 @@ cryptsetup luksAddKey --batch-mode \ STORE_IMAGE="$WORKDIR/store.img" truncate -s 64M "$STORE_IMAGE" STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")" -sfdisk "$STORE_LOOP" <<EOF +udevadm lock --device "$STORE_LOOP" 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 +udevadm settle --timeout=60 mkdir -p /mnt -mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store" +udevadm lock --device "/dev/disk/by-partlabel/header_store" 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" +udevadm lock --device "/dev/disk/by-partlabel/keyfile_store" 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 -udevadm settle --timeout=30 + +# 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=60 # Prepare our test crypttab [[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak @@ -164,6 +183,7 @@ empty0 $IMAGE_EMPTY - headless=1, 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 @@ -177,6 +197,7 @@ detached_fail4 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1, 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 @@ -207,10 +228,46 @@ mkdir -p /run/cryptsetup-keys.d cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key cryptsetup_start_and_check empty_nokey +if [[ -d /usr/lib/softhsm/tokens ]]; 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 diff --git a/test/units/testsuite-25.sh b/test/units/TEST-25-IMPORT.sh index b298c50..b298c50 100755 --- a/test/units/testsuite-25.sh +++ b/test/units/TEST-25-IMPORT.sh diff --git a/test/units/testsuite-26.sh b/test/units/TEST-26-SYSTEMCTL.sh index 1e11c42..ae7a5d6 100755 --- a/test/units/testsuite-26.sh +++ b/test/units/TEST-26-SYSTEMCTL.sh @@ -16,12 +16,12 @@ at_exit() { return 0 } -trap at_exit EXIT - # 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 @@ -56,6 +56,20 @@ 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 @@ -127,7 +141,7 @@ 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" -systemctl kill "$UNIT_NAME" +timeout 10 systemctl kill --wait "$UNIT_NAME" (! systemctl is-active "$UNIT_NAME") systemctl restart "$UNIT_NAME" systemctl is-active "$UNIT_NAME" @@ -188,6 +202,24 @@ test_mask_unmask_revert() { 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" @@ -209,7 +241,7 @@ systemctl revert "$UNIT_NAME" 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/" +grep -r "CPUQuota=10.00%" "/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/") @@ -306,7 +338,7 @@ done # Aux verbs & assorted checks systemctl is-active "*-journald.service" -systemctl cat "*journal*" +systemctl cat "*udevd*" systemctl cat "$UNIT_NAME" systemctl help "$UNIT_NAME" systemctl service-watchdogs @@ -348,6 +380,10 @@ if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then # 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 diff --git a/test/units/testsuite-29.sh b/test/units/TEST-29-PORTABLE.sh index 676330c..27c24a0 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/TEST-29-PORTABLE.sh @@ -5,6 +5,11 @@ 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 @@ -31,9 +36,9 @@ 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 /usr/share/app0.raw | grep -q '✓ sysext for portable service' -systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ sysext for portable service' -systemd-dissect --no-pager /usr/share/conf0.raw | grep -q '✓ confext for 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/ @@ -117,7 +122,7 @@ 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 /usr/share/app0.raw /usr/share/minimal_0.raw app0 +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)" @@ -127,7 +132,7 @@ grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.atta 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 /usr/share/app0.raw /usr/share/minimal_1.raw app0 +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)" @@ -137,12 +142,12 @@ grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.atta 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 /usr/share/app0.raw /usr/share/minimal_1.raw app0 +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 /usr/share/app0.raw /tmp/app0_1.0.raw +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 @@ -152,28 +157,28 @@ status="$(portablectl is-attached --extension app0_1 minimal_0)" 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 /usr/share/app1.raw /usr/share/minimal_0.raw app1 +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 /usr/share/app1.raw /tmp/app1_2.raw +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 /usr/share/app1.raw /usr/share/minimal_1.raw app1 +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 /usr/share/app1.raw /usr/share/minimal_1.raw app1 -portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1 +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 @@ -181,7 +186,27 @@ systemctl is-active app1.service status="$(portablectl is-attached --extension app1 minimal_0)" [[ "${status}" == "running-runtime" ]] -portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1 +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 @@ -190,7 +215,7 @@ 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 /usr/share/app0.raw /tmp/app10.raw +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 @@ -206,21 +231,59 @@ 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 /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0 +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 /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw)" +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 /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /usr/share/conf0.raw" +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 --now --runtime --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0 +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 /usr/share/app0.raw /tmp/app0 -mount /usr/share/app1.raw /tmp/app1 +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 @@ -266,7 +329,22 @@ grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd 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 --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 +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 diff --git a/test/units/testsuite-30.sh b/test/units/TEST-30-ONCLOCKCHANGE.sh index 104c87b..83698b8 100755 --- a/test/units/testsuite-30.sh +++ b/test/units/TEST-30-ONCLOCKCHANGE.sh @@ -16,7 +16,7 @@ systemd-run --on-clock-change touch /tmp/clock-changed test ! -f /tmp/timezone-changed test ! -f /tmp/clock-changed -timedatectl set-timezone Europe/Kiev +timedatectl set-timezone Europe/Kyiv while test ! -f /tmp/timezone-changed ; do sleep .5 ; done diff --git a/test/units/testsuite-31.sh b/test/units/TEST-31-DEVICE-ENUMERATION.sh index 03aba36..5e99302 100755 --- a/test/units/testsuite-31.sh +++ b/test/units/TEST-31-DEVICE-ENUMERATION.sh @@ -3,7 +3,7 @@ set -eux set -o pipefail -if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then +if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then exit 1 fi diff --git a/test/units/testsuite-32.sh b/test/units/TEST-32-OOMPOLICY.sh index 83b548a..046b8b9 100755 --- a/test/units/testsuite-32.sh +++ b/test/units/TEST-32-OOMPOLICY.sh @@ -9,7 +9,7 @@ set -o pipefail # 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 +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 diff --git a/test/units/testsuite-34.sh b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh index d15b675..d15b675 100755 --- a/test/units/testsuite-34.sh +++ b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh diff --git a/test/units/testsuite-35.sh b/test/units/TEST-35-LOGIN.sh index 36e26da..78e0c1e 100755 --- a/test/units/testsuite-35.sh +++ b/test/units/TEST-35-LOGIN.sh @@ -25,14 +25,22 @@ setup_test_user() { trap cleanup_test_user EXIT } -test_enable_debug() { - mkdir -p /run/systemd/system/systemd-logind.service.d - cat >/run/systemd/system/systemd-logind.service.d/debug.conf <<EOF +test_write_dropin() { + systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF [Service] Environment=SYSTEMD_LOG_LEVEL=debug EOF - systemctl daemon-reload - systemctl stop systemd-logind.service + + # 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() { @@ -57,6 +65,25 @@ EOF 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 @@ -231,7 +258,7 @@ cleanup_session() ( systemctl stop getty@tty2.service - for s in $(loginctl --no-legend list-sessions | awk '$3 == "logind-test-user" { print $1 }'); do + 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 @@ -281,18 +308,18 @@ check_session() ( local seat session leader_pid - if [[ $(loginctl --no-legend | grep -c "logind-test-user") != 1 ]]; then + 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 'logind-test-user *seat' | awk '{ print $4 }') + 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 | awk '$3 == "logind-test-user" { print $1 }') + 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 @@ -337,7 +364,7 @@ EOF check_session && break done check_session - assert_eq "$(loginctl --no-legend | awk '$3=="logind-test-user" { print $5 }')" "tty2" + assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2" } testcase_sanity_check() { @@ -355,13 +382,15 @@ testcase_sanity_check() { # 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 | awk '$3 == "logind-test-user" { print $1; exit; }') + 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" @@ -428,7 +457,7 @@ EOF udevadm info "$dev" # trigger logind and activate session - loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')" + loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')" # check ACL sleep 1 @@ -469,7 +498,7 @@ testcase_lock_idle_action() { return fi - if loginctl --no-legend | grep -q logind-test-user; then + 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 @@ -478,6 +507,7 @@ testcase_lock_idle_action() { create_session + journalctl --sync ts="$(date '+%H:%M:%S')" mkdir -p /run/systemd/logind.conf.d @@ -493,18 +523,24 @@ EOF # 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" + journalctl --sync + set +o pipefail + timeout -v 35 journalctl -b -u systemd-logind.service --since="$ts" -n all --follow | grep -m 1 -q 'Sent message type=signal .* member=Lock' + set -o pipefail + + # 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 2 ]]; 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 + journalctl --sync + set +o pipefail + timeout -v 35 journalctl -b -u systemd-logind.service --since="$ts" -n all --follow | grep -m "$((locks + 1))" -q 'Sent message type=signal .* member=Lock' + timeout -v 35 journalctl -b -u systemd-logind.service --since="$ts" -n all --follow | grep -m 2 -q -F 'System idle. Will be locked now.' + set -o pipefail } testcase_session_properties() { @@ -518,7 +554,7 @@ testcase_session_properties() { trap cleanup_session RETURN create_session - s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }') + 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 } @@ -534,17 +570,17 @@ testcase_list_users_sessions_seats() { create_session # Activate the session - loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')" + loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')" - session=$(loginctl list-sessions --no-legend | 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 | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)" - seat=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $4 }') - assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $5 }')" tty2 - assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $6 }')" active - assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $7 }')" no - assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $8 }')" '-' + 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?}" @@ -555,15 +591,15 @@ testcase_list_users_sessions_seats() { 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 | awk '$3 == "logind-test-user" { print $1 }'); do + 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 -q logind-test-user; do sleep 1; done"; then + 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 + timeout 30 bash -c "until [[ \"\$(loginctl list-users --no-legend | awk '\$2 == \"logind-test-user\" { print \$4 }')\" == lingering ]]; do sleep 1; done" } teardown_stop_idle_session() ( @@ -586,7 +622,9 @@ testcase_stop_idle_session() { create_session trap teardown_stop_idle_session RETURN - id="$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1; }')" + id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')" + + journalctl --sync ts="$(date '+%H:%M:%S')" mkdir -p /run/systemd/logind.conf.d @@ -597,8 +635,9 @@ EOF systemctl restart systemd-logind.service sleep 5 + journalctl --sync 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 -c "logind-test-user")" 0 + assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0 } testcase_ambient_caps() { @@ -653,8 +692,53 @@ EOF 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_enable_debug +test_write_dropin run_testcases touch /testok diff --git a/test/units/testsuite-36.sh b/test/units/TEST-36-NUMAPOLICY.sh index 8a53b98..fcd3d6d 100755 --- a/test/units/testsuite-36.sh +++ b/test/units/TEST-36-NUMAPOLICY.sh @@ -3,6 +3,11 @@ set -eux set -o pipefail +if [[ -n "${ASAN_OPTIONS:-}" ]]; then + echo "This test does not support running with sanitizers, skipping the test" | tee --append /skipped + exit 77 +fi + # shellcheck disable=SC2317 at_exit() { # shellcheck disable=SC2181 @@ -29,7 +34,7 @@ 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=1 +sleepAfterStart=3 # Journal cursor for easier navigation journalCursorFile="jounalCursorFile" @@ -80,7 +85,7 @@ EOF writeTestUnit() { mkdir -p "$testUnitFile.d/" - printf "[Service]\nExecStart=/bin/sleep 3600\n" >"$testUnitFile" + printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile" } writeTestUnitNUMAPolicy() { diff --git a/test/units/testsuite-38-sleep.service b/test/units/TEST-38-FREEZER-sleep.service index c116c80..1bb9ddf 100644 --- a/test/units/testsuite-38-sleep.service +++ b/test/units/TEST-38-FREEZER-sleep.service @@ -1,3 +1,3 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/sleep 3600 +ExecStart=sleep 3600 diff --git a/test/units/testsuite-38.sh b/test/units/TEST-38-FREEZER.sh index 5fc87fc..0759784 100755 --- a/test/units/testsuite-38.sh +++ b/test/units/TEST-38-FREEZER.sh @@ -9,7 +9,7 @@ set -o pipefail systemd-analyze log-level debug -unit=testsuite-38-sleep.service +unit=TEST-38-FREEZER-sleep.service start_test_service() { systemctl daemon-reload @@ -94,7 +94,7 @@ check_freezer_state() { # Ignore the intermediate freezing & thawing states in case we check # the unit state too quickly - [[ "$state" =~ ^(freezing|thawing)$ ]] || break + [[ "$state" =~ ^(freezing|thawing) ]] || break sleep .5 done @@ -105,7 +105,14 @@ check_freezer_state() { } check_cgroup_state() { - grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events + # 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() { @@ -142,31 +149,6 @@ testcase_dbus_api() { echo } -testcase_jobs() { - local pid_before= - local pid_after= - echo "Test that it is possible to apply jobs on frozen units:" - - systemctl start "${unit}" - dbus_freeze "${unit}" - check_freezer_state "${unit}" "frozen" - - echo -n " - restart: " - pid_before=$(systemctl show -p MainPID "${unit}" --value) - systemctl restart "${unit}" - pid_after=$(systemctl show -p MainPID "${unit}" --value) - [ "$pid_before" != "$pid_after" ] && echo "[ OK ]" - - dbus_freeze "${unit}" - check_freezer_state "${unit}" "frozen" - - echo -n " - stop: " - timeout 5s systemctl stop "${unit}" - echo "[ OK ]" - - echo -} - testcase_systemctl() { echo "Test that systemctl freeze/thaw verbs:" @@ -224,22 +206,101 @@ testcase_recursive() { echo "Test recursive freezing:" - echo -n " - freeze: " + echo -n " - freeze/thaw parent: " systemctl freeze "$slice" - check_freezer_state "${slice}" "frozen" - check_freezer_state "${unit}" "frozen" - grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events - grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + 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 " - thaw: " + 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 "${unit}" "running" - check_freezer_state "${slice}" "running" - grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events - grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + 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" @@ -255,38 +316,39 @@ testcase_preserve_state() { 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 + 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" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "running" # However actual kernel state should be frozen - grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events - grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + 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 + echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze sleep 1 - check_freezer_state "${unit}" "running" - check_freezer_state "${slice}" "running" - grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events - grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + 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" + 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" diff --git a/test/units/testsuite-43.sh b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh index 4f31a33..165af47 100755 --- a/test/units/testsuite-43.sh +++ b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh @@ -6,9 +6,11 @@ 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 0 + exit 77 fi systemd-analyze log-level debug @@ -130,7 +132,7 @@ 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 /usr/share/app1.raw + 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 \ diff --git a/test/units/testsuite-44.sh b/test/units/TEST-44-LOG-NAMESPACE.sh index fbd4ae6..0819a4b 100755 --- a/test/units/testsuite-44.sh +++ b/test/units/TEST-44-LOG-NAMESPACE.sh @@ -4,12 +4,24 @@ 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) diff --git a/test/units/testsuite-45.sh b/test/units/TEST-45-TIMEDATE.sh index b426927..dff3ed0 100755 --- a/test/units/testsuite-45.sh +++ b/test/units/TEST-45-TIMEDATE.sh @@ -57,12 +57,12 @@ testcase_timezone() { assert_in "Local time:" "$(timedatectl --no-pager)" echo 'change timezone' - assert_eq "$(timedatectl --no-pager set-timezone Europe/Kiev 2>&1)" "" - assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kiev" + 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/Kiev" + assert_eq "$(cat /etc/timezone)" "Europe/Kyiv" fi - assert_in "Time zone: Europe/Kiev \(EES*T, \+0[0-9]00\)" "$(timedatectl)" + assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)" if [[ -n "$ORIG_TZ" ]]; then echo 'reset timezone to original' @@ -218,7 +218,7 @@ assert_ntp() { assert_timedated_signal() { local timestamp="${1:?}" local value="${2:?}" - local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor" + SYSLOG_IDENTIFIER="busctl-monitor") + local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service") journalctl --sync @@ -258,12 +258,12 @@ ConditionVirtualization= Type=simple AmbientCapabilities= ExecStart= -ExecStart=/bin/sleep infinity +ExecStart=sleep infinity EOF systemctl daemon-reload fi - systemd-run --unit busctl-monitor.service -p SyslogIdentifier=busctl-monitor --service-type=notify \ + 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' @@ -298,7 +298,7 @@ assert_timesyncd_signal() { local timestamp="${1:?}" local property="${2:?}" local value="${3:?}" - local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service" + SYSLOG_IDENTIFIER="busctl-monitor") + local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service") journalctl --sync @@ -359,7 +359,7 @@ EOF systemctl restart systemd-networkd networkctl status ntp99 - systemd-run --unit busctl-monitor.service -p SyslogIdentifier=busctl-monitor --service-type=notify \ + 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 diff --git a/test/units/TEST-46-HOMED.sh b/test/units/TEST-46-HOMED.sh new file mode 100755 index 0000000..61590a1 --- /dev/null +++ b/test/units/TEST-46-HOMED.sh @@ -0,0 +1,620 @@ +#!/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" /)" + +systemctl start systemd-homed.service systemd-userdbd.socket + +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 + +TMP_SKEL=$(mktemp -d) +echo hogehoge >"$TMP_SKEL"/hoge + +# 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 \ + --skel="$TMP_SKEL" +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) +[[ $(PASSWORD=xEhErW0ndafV4s homectl with test-user -- stat -c %U /home/test-user/hoge) == "test-user" ]] +[[ $(PASSWORD=xEhErW0ndafV4s homectl with test-user -- cat /home/test-user/hoge) == "$(cat "$TMP_SKEL"/hoge)" ]] + +# 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 diff --git a/test/units/TEST-50-DISSECT.DDI.sh b/test/units/TEST-50-DISSECT.DDI.sh new file mode 100755 index 0000000..42c9a43 --- /dev/null +++ b/test/units/TEST-50-DISSECT.DDI.sh @@ -0,0 +1,70 @@ +#!/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 diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh new file mode 100755 index 0000000..563206c --- /dev/null +++ b/test/units/TEST-50-DISSECT.dissect.sh @@ -0,0 +1,745 @@ +#!/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 +journalctl --sync +set +o pipefail +# "journalctl -u foo.service" may not work as expected, especially entries for _TRANSPORT=stdout, +# for short-living services or when the service manager generates debugging logs. +# Instead, SYSLOG_IDENTIFIER= should be reliable for stdout. Let's use it. +timeout -v 30s journalctl -b SYSLOG_IDENTIFIER=echo _TRANSPORT=stdout -o cat -n all --follow | grep -m 1 -q '^foo$' +set -o pipefail +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) diff --git a/test/units/TEST-50-DISSECT.mountfsd.sh b/test/units/TEST-50-DISSECT.mountfsd.sh new file mode 100755 index 0000000..604a9db --- /dev/null +++ b/test/units/TEST-50-DISSECT.mountfsd.sh @@ -0,0 +1,92 @@ +#!/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 - diff --git a/test/units/TEST-50-DISSECT.sh b/test/units/TEST-50-DISSECT.sh new file mode 100755 index 0000000..0e378a8 --- /dev/null +++ b/test/units/TEST-50-DISSECT.sh @@ -0,0 +1,215 @@ +#!/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" +udevadm settle --timeout=60 + +: "Run subtests" + +run_subtests + +touch /testok diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh new file mode 100755 index 0000000..3bc8899 --- /dev/null +++ b/test/units/TEST-50-DISSECT.sysext.sh @@ -0,0 +1,1012 @@ +#!/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 diff --git a/test/units/testsuite-52.sh b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh index 16ff507..16ff507 100755 --- a/test/units/testsuite-52.sh +++ b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh diff --git a/test/units/testsuite-53.sh b/test/units/TEST-53-ISSUE-16347.sh index 84cd661..84cd661 100755 --- a/test/units/testsuite-53.sh +++ b/test/units/TEST-53-ISSUE-16347.sh diff --git a/test/units/testsuite-54.sh b/test/units/TEST-54-CREDS.sh index bcbe7a1..fe410d5 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/TEST-54-CREDS.sh @@ -3,15 +3,25 @@ # shellcheck disable=SC2016 set -eux +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + systemd-analyze log-level debug -run_with_cred_compare() { +run_with_cred_compare() ( local cred="${1:?}" local exp="${2?}" + local log_file shift 2 - diff <(systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@") <(echo -ne "$exp") -} + 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 # @@ -197,6 +207,12 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then [ "$(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="" @@ -297,11 +313,40 @@ fi 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.service \ + --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" @@ -314,6 +359,44 @@ if ! systemd-detect-virt -q -c ; then 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 diff --git a/test/units/testsuite-55-testbloat.service b/test/units/TEST-55-OOMD-testbloat.service index 6c8e3c9..ba4f2bc 100644 --- a/test/units/testsuite-55-testbloat.service +++ b/test/units/TEST-55-OOMD-testbloat.service @@ -6,5 +6,5 @@ Description=Create a lot of memory pressure # 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 +Slice=TEST-55-OOMD-workload.slice ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1 diff --git a/test/units/testsuite-55-testchill.service b/test/units/TEST-55-OOMD-testchill.service index 369b802..1f708dd 100644 --- a/test/units/testsuite-55-testchill.service +++ b/test/units/TEST-55-OOMD-testchill.service @@ -4,5 +4,5 @@ Description=No memory pressure [Service] MemoryHigh=3M -Slice=testsuite-55-workload.slice +Slice=TEST-55-OOMD-workload.slice ExecStart=sleep infinity diff --git a/test/units/testsuite-55-testmunch.service b/test/units/TEST-55-OOMD-testmunch.service index 3730059..5659906 100644 --- a/test/units/testsuite-55-testmunch.service +++ b/test/units/TEST-55-OOMD-testmunch.service @@ -4,5 +4,5 @@ Description=Create some memory pressure [Service] MemoryHigh=12M -Slice=testsuite-55-workload.slice +Slice=TEST-55-OOMD-workload.slice ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1 diff --git a/test/units/testsuite-55-workload.slice b/test/units/TEST-55-OOMD-workload.slice index d117b75..d117b75 100644 --- a/test/units/testsuite-55-workload.slice +++ b/test/units/TEST-55-OOMD-workload.slice diff --git a/test/units/testsuite-55.sh b/test/units/TEST-55-OOMD.sh index 81617db..b04ebca 100755 --- a/test/units/testsuite-55.sh +++ b/test/units/TEST-55-OOMD.sh @@ -6,6 +6,14 @@ 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 @@ -16,13 +24,14 @@ test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max" [[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped [[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped if [[ -s /skipped ]]; then - exit 0 + exit 77 fi -rm -rf /run/systemd/system/testsuite-55-testbloat.service.d +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 @@ -78,35 +87,34 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then # 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. - mkdir -p /run/systemd/system/testsuite-55-testchill.service.d/ - cat >/run/systemd/system/testsuite-55-testchill.service.d/99-MemoryHigh.conf <<EOF + 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/testsuite-55-testchill.service.d/ /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 testsuite-55-testchill.service -systemctl start testsuite-55-testbloat.service +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 "/testsuite-55-workload.slice"; do sleep 1; done' -oomctl | grep "/testsuite-55-workload.slice" +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 testsuite-55-testchill.service +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 testsuite-55-testbloat.service; then + if ! systemctl status TEST-55-OOMD-testbloat.service; then break fi oomctl @@ -114,28 +122,28 @@ for _ in {0..59}; do 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 +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 testsuite-55-testchill.service -systemctl start --machine "testuser@.host" --user testsuite-55-testbloat.service +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 "/testsuite-55-workload.slice"; do sleep 1; done' -oomctl | grep -E "/user.slice.*/testsuite-55-workload.slice" +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 testsuite-55-testchill.service +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 testsuite-55-testbloat.service; then + if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then break fi oomctl @@ -143,8 +151,8 @@ for _ in {0..59}; do 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 +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 @@ -152,19 +160,19 @@ loginctl disable-linger testuser 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 + 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 testsuite-55-testchill.service - systemctl start testsuite-55-testmunch.service - systemctl start testsuite-55-testbloat.service + 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 testsuite-55-testmunch.service; then + if ! systemctl status TEST-55-OOMD-testmunch.service; then break fi oomctl @@ -172,9 +180,9 @@ EOF 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 + 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 diff --git a/test/units/testsuite-58.sh b/test/units/TEST-58-REPART.sh index c64b203..8a014ac 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/TEST-58-REPART.sh @@ -9,7 +9,7 @@ set -o pipefail if ! command -v systemd-repart >/dev/null; then echo "no systemd-repart" >/skipped - exit 0 + exit 77 fi # shellcheck source=test/units/test-control.sh @@ -87,7 +87,7 @@ elif [ "${machine}" = "ppc64le" ]; then usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB architecture="ppc64-le" else - echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me" + echo "Unexpected uname -m: ${machine} in TEST-58-REPART.sh, please fix me" exit 1 fi @@ -373,7 +373,7 @@ $imgs/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79 fi loop="$(losetup -P --show --find "$imgs/zzz")" - udevadm wait --timeout 60 --settle "${loop:?}" + udevadm wait --timeout 60 --settle "${loop:?}p7" volume="test-repart-$RANDOM" @@ -435,7 +435,7 @@ EOF "offset" : 1048576, "old_size" : 0, "raw_size" : 33554432, - "size" : "-> 32.0M", + "size" : "-> 32M", "old_padding" : 0, "raw_padding" : 0, "padding" : "-> 0B", @@ -496,7 +496,7 @@ EOF "offset" : 1048576, "old_size" : 0, "raw_size" : 33554432, - "size" : "-> 32.0M", + "size" : "-> 32M", "old_padding" : 0, "raw_padding" : 0, "padding" : "-> 0B", @@ -512,7 +512,7 @@ EOF "offset" : 34603008, "old_size" : 0, "raw_size" : 33554432, - "size" : "-> 32.0M", + "size" : "-> 32M", "old_padding" : 0, "raw_padding" : 0, "padding" : "-> 0B", @@ -961,7 +961,7 @@ EOF # shellcheck disable=SC2064 trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR - udevadm wait --timeout 60 --settle "${loop:?}" + udevadm wait --timeout 60 --settle "${loop:?}p1" "${loop:?}p2" # Check that the verity block sizes are as expected veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096' @@ -1026,7 +1026,7 @@ EOF fi loop=$(losetup -P --show -f "$imgs/zzz") - udevadm wait --timeout 60 --settle "${loop:?}" + udevadm wait --timeout 60 --settle "${loop:?}p1" "${loop:?}p2" # Test that /usr/def did not end up in the root partition but other files did. mkdir "$imgs/mnt" diff --git a/test/units/testsuite-59.sh b/test/units/TEST-59-RELOADING-RESTART.sh index 1b622b3..0e04403 100755 --- a/test/units/testsuite-59.sh +++ b/test/units/TEST-59-RELOADING-RESTART.sh @@ -36,7 +36,7 @@ Description=TEST-59-RELOADING-RESTART Normal exit [Service] Type=notify -ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1" +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 @@ -45,7 +45,7 @@ Description=TEST-59-RELOADING-RESTART Restart=on-failure [Service] Type=notify -ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1" +ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1" Restart=on-failure StartLimitBurst=1 EOF @@ -57,7 +57,7 @@ Description=TEST-59-RELOADING-RESTART Restart=on-abort [Service] Type=notify -ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1" +ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1" Restart=on-abort EOF @@ -104,6 +104,14 @@ 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 diff --git a/test/units/testsuite-60.sh b/test/units/TEST-60-MOUNT-RATELIMIT.sh index e800a7a..a0e99dc 100755 --- a/test/units/testsuite-60.sh +++ b/test/units/TEST-60-MOUNT-RATELIMIT.sh @@ -23,8 +23,8 @@ teardown_test_dependencies() ( losetup -d "${LOOP_1}" || : fi - rm -f /tmp/testsuite-60-dependencies-0.img - rm -f /tmp/testsuite-60-dependencies-1.img + 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 @@ -33,13 +33,13 @@ teardown_test_dependencies() ( ) setup_loop() { - truncate -s 30m "/tmp/testsuite-60-dependencies-${1?}.img" - sfdisk --wipe=always "/tmp/testsuite-60-dependencies-${1?}.img" <<EOF + 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/testsuite-60-dependencies-${1?}.img") + 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" } @@ -203,7 +203,7 @@ EOF } test_issue_23796() { - local mount_path mount_mytmpfs + local mount_path mount_mytmpfs since mount_path="$(command -v mount 2>/dev/null)" mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs" @@ -225,6 +225,9 @@ EOF # shellcheck disable=SC2064 trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN + journalctl --sync + since="$(date '+%H:%M:%S')" + for _ in {1..10}; do systemctl --no-block start tmp-hoge.mount sleep ".$RANDOM" @@ -233,7 +236,7 @@ EOF sleep 1 if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \ - journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then + journalctl --since="$since" -u tmp-hoge.mount -q --grep "but there is no mount"; then exit 1 fi @@ -249,6 +252,8 @@ 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")" + +journalctl --sync TS="$(date '+%H:%M:%S')" mkdir -p "$LONGPATH" @@ -271,6 +276,9 @@ for ((i = 0; i < NUM_DIRS; i++)); do mkdir "/tmp/meow${i}" done +# The following loop may produce many journal entries. +# Let's process all pending entries before testing. +journalctl --sync TS="$(date '+%H:%M:%S')" for ((i = 0; i < NUM_DIRS; i++)); do @@ -286,9 +294,13 @@ 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" +set +o pipefail +journalctl --sync +if timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) entered rate limit'; then + journalctl --sync + timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) left rate limit' fi +set -o pipefail # 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. diff --git a/test/units/TEST-62-RESTRICT-IFACES-1.service b/test/units/TEST-62-RESTRICT-IFACES-1.service new file mode 100644 index 0000000..16695c1 --- /dev/null +++ b/test/units/TEST-62-RESTRICT-IFACES-1.service @@ -0,0 +1,9 @@ +# 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 diff --git a/test/units/TEST-62-RESTRICT-IFACES-2.service b/test/units/TEST-62-RESTRICT-IFACES-2.service new file mode 100644 index 0000000..bce7e8e --- /dev/null +++ b/test/units/TEST-62-RESTRICT-IFACES-2.service @@ -0,0 +1,10 @@ +# 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 diff --git a/test/units/testsuite-62-3.service b/test/units/TEST-62-RESTRICT-IFACES-3.service index b6c8e7a..116530b 100644 --- a/test/units/testsuite-62-3.service +++ b/test/units/TEST-62-RESTRICT-IFACES-3.service @@ -2,9 +2,9 @@ [Unit] Description=TEST-62-RESTRICT-IFACES-deny-list [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' +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 diff --git a/test/units/testsuite-62-4.service b/test/units/TEST-62-RESTRICT-IFACES-4.service index 053e6d2..200a383 100644 --- a/test/units/testsuite-62-4.service +++ b/test/units/TEST-62-RESTRICT-IFACES-4.service @@ -2,9 +2,9 @@ [Unit] Description=TEST-62-RESTRICT-IFACES-empty-assignment [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' +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 diff --git a/test/units/testsuite-62-5.service b/test/units/TEST-62-RESTRICT-IFACES-5.service index a8f268d..51761ba 100644 --- a/test/units/testsuite-62-5.service +++ b/test/units/TEST-62-RESTRICT-IFACES-5.service @@ -2,9 +2,9 @@ [Unit] Description=TEST-62-RESTRICT-IFACES-invert-assignment [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' +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 diff --git a/test/units/testsuite-62-2.service b/test/units/TEST-62-RESTRICT-IFACES-6.service index b83362d..876d8f3 100644 --- a/test/units/testsuite-62-2.service +++ b/test/units/TEST-62-RESTRICT-IFACES-6.service @@ -1,10 +1,10 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -Description=TEST-62-RESTRICT-IFACES-allow-list +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 -RestrictNetworkInterfaces=veth1 +RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars +RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars Type=oneshot diff --git a/test/units/testsuite-62.sh b/test/units/TEST-62-RESTRICT-IFACES.sh index ed40821..35ab862 100755 --- a/test/units/testsuite-62.sh +++ b/test/units/TEST-62-RESTRICT-IFACES.sh @@ -17,6 +17,7 @@ setup() { 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 } @@ -33,31 +34,23 @@ teardown() { systemd-analyze log-level info } -KERNEL_VERSION="$(uname -r)" -KERNEL_MAJOR="${KERNEL_VERSION%%.*}" -KERNEL_MINOR="${KERNEL_VERSION#"$KERNEL_MAJOR".}" -KERNEL_MINOR="${KERNEL_MINOR%%.*}" - -MAJOR_REQUIRED=5 -MINOR_REQUIRED=7 - -if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then +if systemd-analyze compare-versions "$(uname -r)" lt 5.7; then echo "kernel is not 5.7+" >>/skipped - exit 0 + exit 77 fi if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then echo "bpf-framework is disabled" >>/skipped - exit 0 + 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 +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 diff --git a/test/units/testsuite-63.sh b/test/units/TEST-63-PATH.sh index ea8cd94..cdd323c 100755 --- a/test/units/testsuite-63.sh +++ b/test/units/TEST-63-PATH.sh @@ -118,7 +118,7 @@ timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P Acti 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' +timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied >/dev/null; do sleep .2; done' systemctl log-level info diff --git a/test/units/testsuite-64.sh b/test/units/TEST-64-UDEV-STORAGE.sh index 65e5f6c..5ddddf5 100755 --- a/test/units/testsuite-64.sh +++ b/test/units/TEST-64-UDEV-STORAGE.sh @@ -148,7 +148,7 @@ check_device_units() {( 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.+/ { print $1 }' | sed -e 's/\.device$//') + done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//') return 0 )} @@ -168,7 +168,7 @@ helper_check_device_units() {( check_device_units 1 "$@" )} -testcase_megasas2_basic() { +testcase_virtio_scsi_basic() { lsblk -S [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] } @@ -239,16 +239,28 @@ testcase_nvme_subsystem() { } testcase_virtio_scsi_identically_named_partitions() { - local num + local num_part num_disk i j if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then - num=$((4 * 4)) + num_part=4 + num_disk=4 else - num=$((16 * 8)) + num_part=8 + num_disk=16 fi + for ((i = 0; i < num_disk; i++)); do + udevadm lock --device "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive$i" \ + sfdisk "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive$i" <<EOF +label: gpt + +$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done) +EOF + done + + udevadm settle lsblk --noheadings -a -o NAME,PARTLABEL - [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$num" ]] + [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]] } testcase_multipath_basic_failover() { @@ -270,6 +282,18 @@ blacklist_exceptions { blacklist { } EOF + + udevadm lock --device /dev/disk/by-id/wwn-0xdeaddeadbeef0000 \ + sfdisk /dev/disk/by-id/wwn-0xdeaddeadbeef0000 <<EOF +label: gpt + +name="first_partition", size=5M +uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M +EOF + udevadm settle + udevadm lock --device /dev/disk/by-id/wwn-0xdeaddeadbeef0000-part2 \ + mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" /dev/disk/by-id/wwn-0xdeaddeadbeef0000-part2 + modprobe -v dm_multipath systemctl start multipathd.service systemctl status multipathd.service @@ -362,7 +386,7 @@ testcase_simultaneous_events_1() { else num_part=10 iterations=100 - timeout=30 + timeout=60 fi for disk in {0..9}; do @@ -521,9 +545,15 @@ testcase_lvm_basic() { local i iterations partitions part timeout local vgroup="MyTestGroup$RANDOM" local devices=( - /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3} + /dev/disk/by-id/scsi-0systemd_foobar_deadbeeflvm{0..3} ) + . /etc/os-release + if [[ "$ID" == "ubuntu" ]]; then + echo "LVM on Ubuntu is broken, skipping the test" | tee --append /skipped + exit 77 + fi + if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then timeout=180 else @@ -544,6 +574,7 @@ testcase_lvm_basic() { lvm lvs udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1" + udevadm trigger --settle "/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 @@ -599,6 +630,7 @@ testcase_lvm_basic() { 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 trigger --settle "/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 @@ -693,9 +725,14 @@ testcase_lvm_basic() { testcase_btrfs_basic() { local dev_stub i label mpoint uuid local devices=( - /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3} + /dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs{0..3} ) + if ! modinfo btrfs; then + echo "This test requires the btrfs kernel module but it is not installed, skipping the test" | tee --append /skipped + exit 77 + fi + ls -l "${devices[@]}" echo "Single device: default settings" @@ -731,10 +768,10 @@ EOF 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 \ + --device=/dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs0 \ + --device=/dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs1 \ + --device=/dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs2 \ + --device=/dev/disk/by-id/scsi-0systemd_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 @@ -755,9 +792,10 @@ EOF 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 lock --device="${devices[$i]}" \ + 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 @@ -824,14 +862,28 @@ testcase_iscsi_lvm() { local vgroup="iscsi_lvm$RANDOM" local expected_symlinks=() local devices=( - /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3} + /dev/disk/by-id/scsi-0systemd_foobar_deadbeefiscsi{0..3} ) + . /etc/os-release + if [[ "$ID" == "ubuntu" ]]; then + echo "LVM on Ubuntu is broken, skipping the test" | tee --append /skipped + exit 77 + fi + ls -l "${devices[@]}" - # Start the target daemon - systemctl start tgtd - systemctl status tgtd + # Start the target daemon (debian names it tgt.service so make sure we handle that) + if systemctl list-unit-files tgt.service; then + systemctl start tgt + systemctl status tgt + elif systemctl list-unit-files tgtd.service; then + systemctl start tgtd + systemctl status tgtd + else + echo "This test requires tgtd but it is not installed, skipping ..." | tee --append /skipped + exit 77 + fi echo "iSCSI LUNs backed by devices" # See RFC3721 and RFC7143 @@ -867,7 +919,7 @@ testcase_iscsi_lvm() { 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 lock --device "${devices[0]}" mkfs.ext4 -L iscsi_store "${devices[0]}" udevadm wait --settle --timeout=30 "${devices[0]}" mount "${devices[0]}" "$mpoint" for i in {1..4}; do @@ -903,6 +955,7 @@ testcase_iscsi_lvm() { lvm lvs udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1" + udevadm trigger --settle "/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 @@ -950,6 +1003,16 @@ testcase_long_sysfs_path() { stat /sys/block/vda readlink -f /sys/block/vda/dev + dev="/dev/vda" + udevadm lock --device "$dev" sfdisk "$dev" <<EOF +label: gpt + +name="test_swap", size=32M +uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M +EOF + udevadm settle + udevadm lock --device "${dev}1" mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1" + udevadm lock --device "${dev}2" 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) @@ -992,7 +1055,7 @@ 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} + /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadm{0..4} ) ls -l "${devices[@]}" @@ -1009,9 +1072,11 @@ testcase_mdadm_basic() { "/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 + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2 udevadm wait --settle --timeout=30 "$raid_dev" + # udevd does not lock md devices, hence we need to trigger uevent after creating filesystem. mkfs.ext4 -L "$part_name" "$raid_dev" + udevadm trigger --settle "$raid_dev" udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" for i in {0..9}; do echo "Disassemble - reassemble loop, iteration #$i" @@ -1038,9 +1103,10 @@ testcase_mdadm_basic() { "/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 + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_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 trigger --settle "$raid_dev" udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" for i in {0..9}; do echo "Disassemble - reassemble loop, iteration #$i" @@ -1078,10 +1144,11 @@ testcase_mdadm_basic() { "/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 + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_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. + # We need to trigger uevents after sfdisk and mkfs. sfdisk --wipe=always "$raid_dev" <<EOF label: gpt @@ -1089,8 +1156,10 @@ 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 trigger --settle --parent-match "$raid_dev" 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 trigger --settle "/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" @@ -1112,7 +1181,7 @@ 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} + /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadmlvm{0..4} ) ls -l "${devices[@]}" @@ -1131,7 +1200,7 @@ testcase_mdadm_lvm() { "/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 + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_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" @@ -1144,6 +1213,7 @@ testcase_mdadm_lvm() { lvm lvs udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" mkfs.ext4 -L "$part_name" "/dev/$vgroup/mypart2" + udevadm trigger --settle "/dev/$vgroup/mypart2" udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" # Disassemble the array lvm vgchange -an "$vgroup" diff --git a/test/units/testsuite-65.sh b/test/units/TEST-65-ANALYZE.sh index a6bb38d..18f5c4d 100755 --- a/test/units/testsuite-65.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -20,6 +20,7 @@ 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 || : @@ -30,6 +31,7 @@ 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)" @@ -52,6 +54,7 @@ systemd-analyze dot --order systemd-journald.service systemd-logind.service >/de 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 @@ -73,8 +76,11 @@ 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 malloc >/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 @@ -82,6 +88,7 @@ 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 @@ -91,11 +98,13 @@ 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" @@ -107,7 +116,7 @@ ConditionKernelVersion=>1.0 ConditionPathExists=/etc/os-release [Service] -ExecStart=/bin/true +ExecStart=true EOF systemctl daemon-reload systemd-analyze condition --unit="$UNIT_NAME" @@ -119,17 +128,20 @@ systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \ (! 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' @@ -144,6 +156,7 @@ systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*' (! 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 @@ -152,6 +165,7 @@ systemd-analyze timestamp yesterday now 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' @@ -159,6 +173,7 @@ systemd-analyze timespan 1s 300s '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 @@ -170,16 +185,21 @@ 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} @@ -282,6 +302,11 @@ systemd-analyze security --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" +# Make sure that running generators under systemd-analyze verify works. +# Note: sd-analyze spawns generators in a sandbox which makes gcov unhapy, so temporarily override +# $GCOV_PREFIX to make it skip generating any coverage reports +GCOV_PREFIX=/tmp systemd-analyze verify --generators /tmp/testfile.service + rm /tmp/testfile.service cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service @@ -334,6 +359,17 @@ 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 @@ -904,6 +940,13 @@ 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 diff --git a/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service b/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service new file mode 100644 index 0000000..85b3d0d --- /dev/null +++ b/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service @@ -0,0 +1,10 @@ +# 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" diff --git a/test/units/testsuite-66.sh b/test/units/TEST-66-DEVICE-ISOLATION.sh index 147335a..ccdfcb2 100755 --- a/test/units/testsuite-66.sh +++ b/test/units/TEST-66-DEVICE-ISOLATION.sh @@ -3,11 +3,11 @@ set -eux set -o pipefail -RESULTS_FILE=/tmp/testsuite66serviceresults +RESULTS_FILE=/tmp/TEST-66-DEVICE-ISOLATION.serviceresults systemd-analyze log-level debug -systemctl start testsuite-66-deviceisolation.service +systemctl start TEST-66-DEVICE-ISOLATION-device-isolation.service sleep 5 grep -q "Operation not permitted" "$RESULTS_FILE" @@ -15,7 +15,7 @@ grep -q "Operation not permitted" "$RESULTS_FILE" systemctl daemon-reload systemctl daemon-reexec -systemctl stop testsuite-66-deviceisolation.service +systemctl stop TEST-66-DEVICE-ISOLATION-device-isolation.service grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42 diff --git a/test/units/testsuite-67.sh b/test/units/TEST-67-INTEGRITY.sh index a42fd66..a42fd66 100755 --- a/test/units/testsuite-67.sh +++ b/test/units/TEST-67-INTEGRITY.sh diff --git a/test/units/testsuite-68.sh b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh index 11da48a..11da48a 100755 --- a/test/units/testsuite-68.sh +++ b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh diff --git a/test/units/TEST-69-SHUTDOWN.py b/test/units/TEST-69-SHUTDOWN.py new file mode 100755 index 0000000..eb790f4 --- /dev/null +++ b/test/units/TEST-69-SHUTDOWN.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later +# pylint: disable=broad-except + +import logging +import sys + +import pexpect + + +def main(): + logger = logging.getLogger("test-shutdown") + + consoles = [] + for _ in range(2): + # Use script to allocate a separate pseudo tty to run the login shell in. + console = pexpect.spawn( + "script", ["--quiet", "--return", "--flush", "--command", "login -f root", "/dev/null"], + logfile=sys.stdout, + env={"TERM": "dumb"}, + encoding="utf-8", + timeout=60, + ) + + logger.info("waiting for login prompt") + console.expect(".*# ", 10) + + consoles += [console] + + consoles[1].sendline("tty") + consoles[1].expect(r"/dev/(pts/\d+)") + pty = console.match.group(1) + logger.info("window 1 at tty %s", pty) + + logger.info("schedule reboot") + consoles[1].sendline("shutdown -r") + consoles[1].expect("Reboot scheduled for (?P<date>.*), use 'shutdown -c' to cancel", 2) + date = consoles[1].match.group("date") + logger.info("reboot scheduled for %s", date) + + logger.info("verify broadcast message") + consoles[0].expect(f"Broadcast message from root@H on {pty}", 2) + consoles[0].expect(f"The system will reboot at {date}!", 2) + + logger.info("check show output") + consoles[1].sendline("shutdown --show") + consoles[1].expect(f"Reboot scheduled for {date}, use 'shutdown -c' to cancel", 2) + + logger.info("cancel shutdown") + consoles[1].sendline("shutdown -c") + consoles[0].expect("System shutdown has been cancelled", 2) + + consoles[0].sendline("> /testok") + +if __name__ == "__main__": + main() + +# vim: sw=4 et diff --git a/test/units/testsuite-70.creds.sh b/test/units/TEST-70-TPM2.creds.sh index e66bfd1..e66bfd1 100755 --- a/test/units/testsuite-70.creds.sh +++ b/test/units/TEST-70-TPM2.creds.sh diff --git a/test/units/testsuite-70.cryptenroll.sh b/test/units/TEST-70-TPM2.cryptenroll.sh index 3f8c14e..f18ef02 100755 --- a/test/units/testsuite-70.cryptenroll.sh +++ b/test/units/TEST-70-TPM2.cryptenroll.sh @@ -59,6 +59,18 @@ 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") diff --git a/test/units/testsuite-70.cryptsetup.sh b/test/units/TEST-70-TPM2.cryptsetup.sh index 4cd627f..cb7c8b1 100755 --- a/test/units/testsuite-70.cryptsetup.sh +++ b/test/units/TEST-70-TPM2.cryptsetup.sh @@ -212,6 +212,7 @@ 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"p1 systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes mkdir /tmp/dditest.mnt mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt diff --git a/test/units/testsuite-70.measure.sh b/test/units/TEST-70-TPM2.measure.sh index 3336f38..3336f38 100755 --- a/test/units/testsuite-70.measure.sh +++ b/test/units/TEST-70-TPM2.measure.sh diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/TEST-70-TPM2.pcrextend.sh index 318fce0..318fce0 100755 --- a/test/units/testsuite-70.pcrextend.sh +++ b/test/units/TEST-70-TPM2.pcrextend.sh diff --git a/test/units/testsuite-70.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh index 3da9926..fd51161 100755 --- a/test/units/testsuite-70.pcrlock.sh +++ b/test/units/TEST-70-TPM2.pcrlock.sh @@ -74,7 +74,7 @@ if [[ -n "$SD_STUB" ]]; then "$SD_PCRLOCK" lock-uki <"$SD_STUB" fi -PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes +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 @@ -85,7 +85,7 @@ 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 --tpm2-public-key= --wipe-slot=tpm2 "$img" +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 @@ -102,7 +102,7 @@ systemd-cryptsetup detach pcrlock # 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=yes +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 @@ -110,6 +110,10 @@ 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 @@ -118,7 +122,20 @@ echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/92 systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless systemd-cryptsetup detach pcrlock -"$SD_PCRLOCK" remove-policy +# 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 @@ -143,4 +160,20 @@ systemd-cryptsetup detach pcrlock (! "$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 diff --git a/test/units/testsuite-70.sh b/test/units/TEST-70-TPM2.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-70.sh +++ b/test/units/TEST-70-TPM2.sh diff --git a/test/units/testsuite-70.tpm2-setup.sh b/test/units/TEST-70-TPM2.tpm2-setup.sh index faf6fe7..faf6fe7 100755 --- a/test/units/testsuite-70.tpm2-setup.sh +++ b/test/units/TEST-70-TPM2.tpm2-setup.sh diff --git a/test/units/testsuite-71.sh b/test/units/TEST-71-HOSTNAME.sh index da765a9..dc3f587 100755 --- a/test/units/testsuite-71.sh +++ b/test/units/TEST-71-HOSTNAME.sh @@ -61,6 +61,12 @@ get_chassis() ( 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 @@ -80,7 +86,7 @@ testcase_chassis() { assert_eq "$(get_chassis)" "$i" done - systemctl stop systemd-hostnamed.service + stop_hostnamed rm -f /etc/machine-info # fallback chassis type @@ -95,7 +101,7 @@ restore_sysfs_dmi() { umount /sys/class/dmi/id rm -rf /run/systemd/system/systemd-hostnamed.service.d systemctl daemon-reload - systemctl stop systemd-hostnamed + stop_hostnamed } testcase_firmware_date() { @@ -120,15 +126,15 @@ EOF echo '1' >/sys/class/dmi/id/uevent echo '09/08/2000' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed + stop_hostnamed assert_in '2000-09-08' "$(hostnamectl)" echo '2022' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed + stop_hostnamed assert_not_in 'Firmware Date' "$(hostnamectl)" echo 'garbage' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed + stop_hostnamed assert_not_in 'Firmware Date' "$(hostnamectl)" } @@ -223,6 +229,14 @@ testcase_nss-myhostname() { (! 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 diff --git a/test/units/testsuite-72.sh b/test/units/TEST-72-SYSUPDATE.sh index de657a2..5e658e0 100755 --- a/test/units/testsuite-72.sh +++ b/test/units/TEST-72-SYSUPDATE.sh @@ -6,16 +6,17 @@ set -eux set -o pipefail SYSUPDATE=/lib/systemd/systemd-sysupdate -SECTOR_SIZES="512 4096" -BACKING_FILE=/var/tmp/72-joined.raw -export SYSTEMD_ESP_PATH=/var/tmp/72-esp -export SYSTEMD_XBOOTLDR_PATH=/var/tmp/72-xbootldr +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 ! test -x "$SYSUPDATE"; then +if [[ ! -x "$SYSUPDATE" ]]; then echo "no systemd-sysupdate" >/skipped - exit 0 + exit 77 fi # Loopback devices may not be supported. They are used because sfdisk cannot @@ -24,93 +25,93 @@ fi # 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" + SECTOR_SIZES=(512) fi -trap cleanup ERR -cleanup() { - set +o pipefail - blockdev="$( losetup --list --output NAME,BACK-FILE | grep $BACKING_FILE | cut -d' ' -f1)" - [ -n "$blockdev" ] && losetup --detach "$blockdev" - rm -f "$BACKING_FILE" - rm -rf /var/tmp/72-{dirs,defs,source,xbootldr,esp} - rm -f /testok +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() { - # Inputs: - # $1: sector size - # $2: version + local sector_size="${1:?}" + local version="${2:?}" # Create a pair of random partition payloads, and compress one - dd if=/dev/urandom of="/var/tmp/72-source/part1-$2.raw" bs="$1" count=2048 - dd if=/dev/urandom of="/var/tmp/72-source/part2-$2.raw" bs="$1" count=2048 - gzip -k -f "/var/tmp/72-source/part2-$2.raw" + 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 >"/var/tmp/72-source/uki-$2.efi" + echo $RANDOM >"$WORKDIR/source/uki-$version.efi" # Create a random extra payload - echo $RANDOM >"/var/tmp/72-source/uki-extra-$2.efi" + echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi" # Create tarball of a directory - mkdir -p "/var/tmp/72-source/dir-$2" - echo $RANDOM >"/var/tmp/72-source/dir-$2/foo.txt" - echo $RANDOM >"/var/tmp/72-source/dir-$2/bar.txt" - tar --numeric-owner -C "/var/tmp/72-source/dir-$2/" -czf "/var/tmp/72-source/dir-$2.tar.gz" . + 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 /var/tmp/72-source/ && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS ) + (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=/var/tmp/72-defs --verify=no check-new - "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no update - ( ! "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new ) + "$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() { - # Inputs: - # $1: block device - # $2: sector size - # $3: version - # $4: partition number of part1 - # $5: partition number of part2 + 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 / $2 )) - part1_offset=$(( ( $4 - 1 ) * 2048 + gpt_reserved_sectors )) - part2_offset=$(( ( $5 - 1 ) * 2048 + gpt_reserved_sectors )) + 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="$1" bs="$2" skip="$part1_offset" count=2048 | cmp "/var/tmp/72-source/part1-$3.raw" - dd if="$1" bs="$2" skip="$part2_offset" count=2048 | cmp "/var/tmp/72-source/part2-$3.raw" + 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 "/var/tmp/72-source/uki-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3+3-0.efi" - test -z "$(ls -A /var/tmp/72-esp/EFI/Linux)" + 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 "/var/tmp/72-source/uki-extra-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3.efi.extra.d/extra.addon.efi" + cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi" # Check the directories - cmp "/var/tmp/72-source/dir-$3/foo.txt" /var/tmp/72-dirs/current/foo.txt - cmp "/var/tmp/72-source/dir-$3/bar.txt" /var/tmp/72-dirs/current/bar.txt + 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 +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 )) + 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 - # shellcheck disable=SC2086 - blockdev="$(losetup --find --show --sector-size $sector_size $BACKING_FILE)" + blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")" else blockdev="$BACKING_FILE" fi @@ -126,16 +127,15 @@ size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty EOF - rm -rf /var/tmp/72-dirs - mkdir -p /var/tmp/72-dirs + for d in "dirs" "defs"; do + rm -rf "${WORKDIR:?}/$d" + mkdir -p "$WORKDIR/$d" + done - rm -rf /var/tmp/72-defs - mkdir -p /var/tmp/72-defs - - cat >/var/tmp/72-defs/01-first.conf <<EOF + cat >"$WORKDIR/defs/01-first.conf" <<EOF [Source] Type=regular-file -Path=/var/tmp/72-source +Path=$WORKDIR/source MatchPattern=part1-@v.raw [Target] @@ -145,10 +145,10 @@ MatchPattern=part1-@v MatchPartitionType=root-x86-64 EOF - cat >/var/tmp/72-defs/02-second.conf <<EOF + cat >"$WORKDIR/defs/02-second.conf" <<EOF [Source] Type=regular-file -Path=/var/tmp/72-source +Path=$WORKDIR/source MatchPattern=part2-@v.raw.gz [Target] @@ -158,24 +158,24 @@ MatchPattern=part2-@v MatchPartitionType=root-x86-64-verity EOF - cat >/var/tmp/72-defs/03-third.conf <<EOF + cat >"$WORKDIR/defs/03-third.conf" <<EOF [Source] Type=directory -Path=/var/tmp/72-source +Path=$WORKDIR/source MatchPattern=dir-@v [Target] Type=directory -Path=/var/tmp/72-dirs -CurrentSymlink=/var/tmp/72-dirs/current +Path=$WORKDIR/dirs +CurrentSymlink=$WORKDIR/dirs/current MatchPattern=dir-@v InstancesMax=3 EOF - cat >/var/tmp/72-defs/04-fourth.conf <<EOF + cat >"$WORKDIR/defs/04-fourth.conf" <<EOF [Source] Type=regular-file -Path=/var/tmp/72-source +Path=$WORKDIR/source MatchPattern=uki-@v.efi [Target] @@ -191,10 +191,10 @@ TriesDone=0 InstancesMax=2 EOF - cat >/var/tmp/72-defs/05-fifth.conf <<EOF + cat >"$WORKDIR/defs/05-fifth.conf" <<EOF [Source] Type=regular-file -Path=/var/tmp/72-source +Path=$WORKDIR/source MatchPattern=uki-extra-@v.efi [Target] @@ -206,11 +206,8 @@ Mode=0444 InstancesMax=2 EOF - rm -rf /var/tmp/72-esp /var/tmp/72-xbootldr - mkdir -p /var/tmp/72-esp/EFI/Linux /var/tmp/72-xbootldr/EFI/Linux - - rm -rf /var/tmp/72-source - mkdir -p /var/tmp/72-source + 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 @@ -226,9 +223,9 @@ EOF new_version "$sector_size" v3 update_now verify_version "$blockdev" "$sector_size" v3 1 3 - test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1+3-0.efi" - test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi" - test ! -d "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d" + 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 @@ -238,10 +235,10 @@ EOF # see above) new_version "$sector_size" v4 - cat >/var/tmp/72-defs/02-second.conf <<EOF + cat >"$WORKDIR/defs/02-second.conf" <<EOF [Source] Type=url-file -Path=file:///var/tmp/72-source +Path=file://$WORKDIR/source MatchPattern=part2-@v.raw.gz [Target] @@ -251,16 +248,16 @@ MatchPattern=part2-@v MatchPartitionType=root-x86-64-verity EOF - cat >/var/tmp/72-defs/03-third.conf <<EOF + cat >"$WORKDIR/defs/03-third.conf" <<EOF [Source] Type=url-tar -Path=file:///var/tmp/72-source +Path=file://$WORKDIR/source MatchPattern=dir-@v.tar.gz [Target] Type=directory -Path=/var/tmp/72-dirs -CurrentSymlink=/var/tmp/72-dirs/current +Path=$WORKDIR/dirs +CurrentSymlink=$WORKDIR/dirs/current MatchPattern=dir-@v InstancesMax=3 EOF @@ -269,10 +266,8 @@ EOF verify_version "$blockdev" "$sector_size" v4 2 4 # Cleanup - [ -b "$blockdev" ] && losetup --detach "$blockdev" + [[ -b "$blockdev" ]] && losetup --detach "$blockdev" rm "$BACKING_FILE" done -rm -r /var/tmp/72-{dirs,defs,source,xbootldr,esp} - touch /testok diff --git a/test/units/testsuite-73.sh b/test/units/TEST-73-LOCALE.sh index df5af4b..18539b8 100755 --- a/test/units/testsuite-73.sh +++ b/test/units/TEST-73-LOCALE.sh @@ -28,31 +28,6 @@ EOF systemctl daemon-reload } -restore_locale() { - if [[ -d /usr/lib/locale/xx_XX.UTF-8 ]]; then - rmdir /usr/lib/locale/xx_XX.UTF-8 - fi - - if [[ -f /tmp/locale.conf.bak ]]; then - mv /tmp/locale.conf.bak /etc/locale.conf - else - rm -f /etc/locale.conf - fi - - if [[ -f /tmp/default-locale.bak ]]; then - mv /tmp/default-locale.bak /etc/default/locale - else - rm -f /etc/default/locale - rmdir --ignore-fail-on-non-empty /etc/default - fi - - if [[ -f /tmp/locale.gen.bak ]]; then - mv /tmp/locale.gen.bak /etc/locale.gen - else - rm -f /etc/locale.gen - fi -} - testcase_locale() { local i output @@ -77,13 +52,8 @@ testcase_locale() { mkdir -p /etc/default trap restore_locale RETURN - - if command -v locale-gen >/dev/null 2>&1 && - ! localectl list-locales | grep -F "en_US.UTF-8"; then - # ensure at least one utf8 locale exist - echo "en_US.UTF-8 UTF-8" >/etc/locale.gen - locale-gen en_US.UTF-8 - fi + # 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 diff --git a/test/units/testsuite-74.battery-check.sh b/test/units/TEST-74-AUX-UTILS.battery-check.sh index 52a92b8..52a92b8 100755 --- a/test/units/testsuite-74.battery-check.sh +++ b/test/units/TEST-74-AUX-UTILS.battery-check.sh diff --git a/test/units/testsuite-74.bootctl.sh b/test/units/TEST-74-AUX-UTILS.bootctl.sh index 4be7bfd..78c0e6e 100755 --- a/test/units/testsuite-74.bootctl.sh +++ b/test/units/TEST-74-AUX-UTILS.bootctl.sh @@ -59,8 +59,8 @@ basic_tests() { } testcase_bootctl_basic() { - assert_eq "$(bootctl --print-esp-path)" "/efi" - assert_eq "$(bootctl --print-boot-path)" "/boot" + assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$" + assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$" bootctl --print-root-device basic_tests @@ -263,4 +263,17 @@ EOF 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 diff --git a/test/units/testsuite-74.busctl.sh b/test/units/TEST-74-AUX-UTILS.busctl.sh index aaf96d0..aaf96d0 100755 --- a/test/units/testsuite-74.busctl.sh +++ b/test/units/TEST-74-AUX-UTILS.busctl.sh diff --git a/test/units/TEST-74-AUX-UTILS.capsule.sh b/test/units/TEST-74-AUX-UTILS.capsule.sh new file mode 100755 index 0000000..e7b5c87 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.capsule.sh @@ -0,0 +1,53 @@ +#!/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 ) diff --git a/test/units/testsuite-74.cgls.sh b/test/units/TEST-74-AUX-UTILS.cgls.sh index 9268f42..9268f42 100755 --- a/test/units/testsuite-74.cgls.sh +++ b/test/units/TEST-74-AUX-UTILS.cgls.sh diff --git a/test/units/testsuite-74.cgtop.sh b/test/units/TEST-74-AUX-UTILS.cgtop.sh index cf98279..cf98279 100755 --- a/test/units/testsuite-74.cgtop.sh +++ b/test/units/TEST-74-AUX-UTILS.cgtop.sh diff --git a/test/units/testsuite-74.coredump.sh b/test/units/TEST-74-AUX-UTILS.coredump.sh index 6552643..b9c8fde 100755 --- a/test/units/testsuite-74.coredump.sh +++ b/test/units/TEST-74-AUX-UTILS.coredump.sh @@ -77,24 +77,26 @@ 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" -# 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 +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 + systemctl daemon-reload + + [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60 -if cgroupfs_supports_user_xattrs; then machinectl start "$CONTAINER" - timeout 60 bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done" + 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" @@ -102,6 +104,10 @@ if cgroupfs_supports_user_xattrs; then 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" + + machinectl stop "$CONTAINER" + rm -rf "/var/lib/machines/$CONTAINER" + unset CONTAINER fi coredumpctl diff --git a/test/units/testsuite-74.delta.sh b/test/units/TEST-74-AUX-UTILS.delta.sh index a0e1cb5..dabe234 100755 --- a/test/units/testsuite-74.delta.sh +++ b/test/units/TEST-74-AUX-UTILS.delta.sh @@ -14,14 +14,14 @@ trap at_exit EXIT # Extended unit cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF [Service] -ExecStart=/bin/true +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=/bin/true +ExecStartPre=true EOF # Masked unit cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service diff --git a/test/units/testsuite-74.escape.sh b/test/units/TEST-74-AUX-UTILS.escape.sh index e398d40..e398d40 100755 --- a/test/units/testsuite-74.escape.sh +++ b/test/units/TEST-74-AUX-UTILS.escape.sh diff --git a/test/units/testsuite-74.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh index be08575..7bab009 100755 --- a/test/units/testsuite-74.firstboot.sh +++ b/test/units/TEST-74-AUX-UTILS.firstboot.sh @@ -3,6 +3,9 @@ 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 @@ -13,6 +16,8 @@ at_exit() { ls -lR "$ROOT" rm -fr "$ROOT" fi + + restore_locale } trap at_exit EXIT @@ -24,6 +29,23 @@ ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTq # 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" diff --git a/test/units/testsuite-74.id128.sh b/test/units/TEST-74-AUX-UTILS.id128.sh index c1b80d6..f91cd5f 100755 --- a/test/units/testsuite-74.id128.sh +++ b/test/units/TEST-74-AUX-UTILS.id128.sh @@ -22,6 +22,13 @@ 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 diff --git a/test/units/testsuite-74.machine-id-setup.sh b/test/units/TEST-74-AUX-UTILS.machine-id-setup.sh index c2b9db5..c2b9db5 100755 --- a/test/units/testsuite-74.machine-id-setup.sh +++ b/test/units/TEST-74-AUX-UTILS.machine-id-setup.sh diff --git a/test/units/testsuite-74.modules-load.sh b/test/units/TEST-74-AUX-UTILS.modules-load.sh index 3d00e07..ceac826 100755 --- a/test/units/testsuite-74.modules-load.sh +++ b/test/units/TEST-74-AUX-UTILS.modules-load.sh @@ -17,8 +17,10 @@ if systemd-detect-virt -cq; then 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 loop dummy +modprobe --all --resolve-alias dummy mkdir -p /run/modules-load.d/ @@ -27,62 +29,58 @@ mkdir -p /run/modules-load.d/ "$MODULES_LOAD_BIN" --version # Explicit config file -modprobe -v --all --remove loop dummy -printf "loop\ndummy" >"$CONFIG_FILE" +modprobe -v --all --remove dummy +printf "dummy" >"$CONFIG_FILE" "$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log -grep -E "Inserted module .*loop" /tmp/out.log grep -E "Inserted module .*dummy" /tmp/out.log # Implicit config file -modprobe -v --all --remove loop dummy -printf "loop\ndummy" >"$CONFIG_FILE" +modprobe -v --all --remove dummy +printf "dummy" >"$CONFIG_FILE" "$MODULES_LOAD_BIN" |& tee /tmp/out.log -grep -E "Inserted module .*loop" /tmp/out.log grep -E "Inserted module .*dummy" /tmp/out.log # Valid & invalid data mixed together -modprobe -v --all --remove loop dummy +modprobe -v --all --remove dummy cat >"$CONFIG_FILE" <<EOF -loop -loop -loop - loop +dummy +dummy +dummy + dummy dummy \\n\n\n\\\\\\ - -loo!@@123##2455 + +dumm!@@123##2455 # This is a comment $(printf "%.0sx" {0..4096}) dummy -loop +dummy foo-bar-baz 1 " ' EOF "$MODULES_LOAD_BIN" |& tee /tmp/out.log -grep -E "^Inserted module .*loop" /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 2 ]] +[[ "$(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 loop dummy +modprobe -v --all --remove dummy # Make sure we have no config files left over that might interfere with # following tests rm -fv "$CONFIG_FILE" -[[ -z "$(systemd-analyze cat-config modules-load.d)" ]] -CMDLINE="ro root= modules_load= modules_load=, / = modules_load=foo-bar-baz,dummy modules_load=loop,loop,loop" +[[ "$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 .*loop" /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 2 ]] +[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]] (! "$MODULES_LOAD_BIN" --nope) (! "$MODULES_LOAD_BIN" /foo/bar/baz) diff --git a/test/units/testsuite-74.mount.sh b/test/units/TEST-74-AUX-UTILS.mount.sh index 41c5c86..14253c3 100755 --- a/test/units/testsuite-74.mount.sh +++ b/test/units/TEST-74-AUX-UTILS.mount.sh @@ -6,13 +6,6 @@ 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 @@ -23,6 +16,7 @@ at_exit() { trap at_exit EXIT WORK_DIR="$(mktemp -d)" +mkdir -p "$WORK_DIR/mnt" systemd-mount --list systemd-mount --list --full @@ -30,15 +24,43 @@ systemd-mount --list --no-legend systemd-mount --list --no-pager systemd-mount --list --quiet +# 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" + +# 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 + # 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")" -mkfs.ext4 -L sd-mount-test "$LOOP" -mkdir "$WORK_DIR/mnt" +udevadm wait --timeout 60 --settle "$LOOP" +# Also wait for the .device unit for the loop device is active. Otherwise, the .device unit activation +# that is triggered by the .mount unit introduced by systemd-mount below may time out. +timeout 60 bash -c "until systemctl is-active $LOOP; do sleep 1; done" mount "$LOOP" "$WORK_DIR/mnt" touch "$WORK_DIR/mnt/foo.bar" umount "$LOOP" (! mountpoint "$WORK_DIR/mnt") +# Wait for the mount unit to be unloaded. Otherwise, creation of the transient unit below may fail. +MOUNT_UNIT=$(systemd-escape --path --suffix=mount "$WORK_DIR/mnt") +timeout 60 bash -c "while [[ -n \$(systemctl list-units --all --no-legend $MOUNT_UNIT) ]]; do sleep 1; done" # Mount with both source and destination set systemd-mount "$LOOP" "$WORK_DIR/mnt" @@ -123,15 +145,37 @@ 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)" +LOOP_AUTO=$(systemd-mount --list --full --no-legend | awk '$6 == "sd-mount-test" { print $1 }') +LOOP_AUTO_DEVPATH=$(udevadm info --query property --property DEVPATH --value "$LOOP_AUTO") systemd-umount "$WORK_DIR/simple.img" +# Wait for 'change' uevent for the device with DISK_MEDIA_CHANGE=1. +# After the event, the backing_file attribute should be removed. +timeout 60 bash -c "while [[ -e /sys/$LOOP_AUTO_DEVPATH/loop/backing_file ]]; do sleep 1; done" # --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")" -mkfs.vfat -n owner-vfat "$LOOP" +# If the synthesized uevent triggered by inotify event has been processed earlier than the kernel finishes to +# attach the backing file, then SYSTEMD_READY=0 is set for the device. As a workaround, monitor sysattr +# and re-trigger uevent after that. +LOOP_DEVPATH=$(udevadm info --query property --property DEVPATH --value "$LOOP") +timeout 60 bash -c "until [[ -e /sys/$LOOP_DEVPATH/loop/backing_file ]]; do sleep 1; done" +udevadm trigger --settle "$LOOP" +# Also wait for the .device unit for the loop device is active. Otherwise, the .device unit activation +# that is triggered by the .mount unit introduced by systemd-mount below may time out. +if ! timeout 60 bash -c "until systemctl is-active $LOOP; do sleep 1; done"; then + # For debugging issue like + # https://github.com/systemd/systemd/issues/32680#issuecomment-2120959238 + # https://github.com/systemd/systemd/issues/32680#issuecomment-2122074805 + udevadm info "$LOOP" + udevadm info --attribute-walk "$LOOP" + cat /sys/"$(udevadm info --query property --property DEVPATH --value "$LOOP")"/loop/backing_file || : + false +fi # Mount it and check the UID/GID [[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]] systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt" @@ -140,12 +184,3 @@ systemctl status "$WORK_DIR/mnt" 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" diff --git a/test/units/TEST-74-AUX-UTILS.network-generator.sh b/test/units/TEST-74-AUX-UTILS.network-generator.sh new file mode 100755 index 0000000..5b5b0a1 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.network-generator.sh @@ -0,0 +1,37 @@ +#!/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 diff --git a/test/units/testsuite-74.networkctl.sh b/test/units/TEST-74-AUX-UTILS.networkctl.sh index 0a687af..3e333a2 100755 --- a/test/units/testsuite-74.networkctl.sh +++ b/test/units/TEST-74-AUX-UTILS.networkctl.sh @@ -11,10 +11,12 @@ at_exit() { systemctl stop systemd-networkd if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then - rm -fvr {/usr/lib,/etc}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \ + 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 @@ -28,6 +30,16 @@ 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 @@ -35,9 +47,23 @@ cat >new <<EOF Name=test2 EOF -EDITOR='mv new' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null +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 @@ -80,7 +106,19 @@ 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)" diff --git a/test/units/testsuite-74.path.sh b/test/units/TEST-74-AUX-UTILS.path.sh index 79056a5..79056a5 100755 --- a/test/units/testsuite-74.path.sh +++ b/test/units/TEST-74-AUX-UTILS.path.sh diff --git a/test/units/testsuite-74.pstore.sh b/test/units/TEST-74-AUX-UTILS.pstore.sh index 9be8066..9be8066 100755 --- a/test/units/testsuite-74.pstore.sh +++ b/test/units/TEST-74-AUX-UTILS.pstore.sh diff --git a/test/units/testsuite-74.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh index e894932..b4ff72e 100755 --- a/test/units/testsuite-74.run.sh +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -218,19 +218,30 @@ for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} prop done # Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc - -A=$(cat /proc/sys/kernel/random/boot_id) -B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id) -assert_eq "$A" "$B" - -V="/tmp/version.$RANDOM" -A="$(cat /proc/version).piff" -echo "$A" > "$V" -mount --bind "$V" /proc/version - -B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version) - -assert_eq "$A" "$B" - +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 "$V" +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 diff --git a/test/units/testsuite-74.sh b/test/units/TEST-74-AUX-UTILS.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-74.sh +++ b/test/units/TEST-74-AUX-UTILS.sh diff --git a/test/units/TEST-74-AUX-UTILS.socket.sh b/test/units/TEST-74-AUX-UTILS.socket.sh new file mode 100755 index 0000000..7ef85fa --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.socket.sh @@ -0,0 +1,84 @@ +#!/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 ) diff --git a/test/units/TEST-74-AUX-UTILS.ssh.sh b/test/units/TEST-74-AUX-UTILS.ssh.sh new file mode 100755 index 0000000..5d87d9f --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.ssh.sh @@ -0,0 +1,60 @@ +#!/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 diff --git a/test/units/testsuite-74.varlinkctl.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh index 5a96269..1d5a98b 100755 --- a/test/units/testsuite-74.varlinkctl.sh +++ b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh @@ -32,8 +32,9 @@ 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 . + # We ignore the return value of the following two calls, since if no memberships are defined at all this will return a NotFound error, which is OK + (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 @@ -53,6 +54,32 @@ if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then 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" @@ -87,3 +114,7 @@ done (! 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 '{}' diff --git a/test/units/TEST-74-AUX-UTILS.vpick.sh b/test/units/TEST-74-AUX-UTILS.vpick.sh new file mode 100755 index 0000000..400097f --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.vpick.sh @@ -0,0 +1,116 @@ +#!/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) diff --git a/test/units/testsuite-75.sh b/test/units/TEST-75-RESOLVED.sh index 86d602d..a4417ce 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/TEST-75-RESOLVED.sh @@ -14,6 +14,12 @@ 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() { @@ -46,8 +52,7 @@ monitor_check_rr() ( # 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 - # v255-only: match against a syslog tag as well to work around systemd/systemd#30886 - timeout -v 30s journalctl --since "$since" -f --full _SYSTEMD_UNIT="resolvectl-monitor.service" + SYSLOG_IDENTIFIER="resolvectl-monitor" | grep -m1 "$match" + timeout -v 30s journalctl -u resolvectl-monitor.service --since "$since" -f --full | grep -m1 "$match" ) restart_resolved() { @@ -103,12 +108,21 @@ assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)" mkdir -p /run/systemd/resolved.conf.d { echo "[Resolve]" - echo "MulticastDNS=yes" - echo "LLMNR=yes" + 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)" @@ -131,7 +145,7 @@ assert_in 'no' "$(resolvectl llmnr hoge)" echo "MulticastDNS=resolve" echo "LLMNR=resolve" } >/run/systemd/resolved.conf.d/mdns-llmnr.conf -restart_resolved +systemctl reload systemd-resolved.service # set per-link setting resolvectl mdns hoge yes resolvectl llmnr hoge yes @@ -151,7 +165,7 @@ assert_in 'no' "$(resolvectl llmnr hoge)" echo "MulticastDNS=no" echo "LLMNR=no" } >/run/systemd/resolved.conf.d/mdns-llmnr.conf -restart_resolved +systemctl reload systemd-resolved.service # set per-link setting resolvectl mdns hoge yes resolvectl llmnr hoge yes @@ -181,23 +195,44 @@ fd00:dead:beef:cafe::1 ns1.unsigned.test 127.128.0.5 localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5 EOF -mkdir -p /etc/systemd/network -cat >/etc/systemd/network/10-dns0.netdev <<EOF +mkdir -p /run/systemd/network +cat >/run/systemd/network/10-dns0.netdev <<EOF [NetDev] Name=dns0 Kind=dummy EOF -cat >/etc/systemd/network/10-dns0.network <<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" @@ -217,6 +252,14 @@ ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf 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 @@ -235,8 +278,11 @@ ln -svf /etc/bind.keys /etc/bind/bind.keys # Start the services systemctl unmask systemd-networkd -systemctl start systemd-networkd -restart_resolved +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 @@ -247,17 +293,22 @@ 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 SyslogIdentifier=resolvectl-monitor -p Type=notify resolvectl monitor -systemd-run -u resolvectl-monitor-json.service -p SyslogIdentifier=resolvectl-monitor-json -p Type=notify resolvectl monitor --json=short +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 -# Check if all the zones are valid (zone-check always returns 0, so let's check -# if it produces any errors/warnings) -run knotc zone-check +# 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 @@ -273,6 +324,7 @@ done < <(keymgr onlinesign.test. ds) knotc zone-commit test. knotc reload +sleep 2 ### SETUP END ### @@ -348,6 +400,12 @@ 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 @@ -405,6 +463,27 @@ 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 @@ -418,6 +497,18 @@ 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 @@ -460,9 +551,9 @@ monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow 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" +#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) ---" @@ -545,6 +636,61 @@ grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT" #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 @@ -559,10 +705,10 @@ systemctl stop resolvectl-monitor-json.service # Issue: https://github.com/systemd/systemd/issues/29580 (part #2) # # Check for any warnings regarding malformed messages -(! journalctl -p warning --grep malformed _SYSTEMD_UNIT="resolvectl-monitor-json.service" + SYSLOG_IDENTIFIER="resolvectl-monitor-json") +(! 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" + SYSLOG_IDENTIFIER="resolvectl-monitor-json" | while read -r line; do +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, @@ -589,7 +735,9 @@ if command -v nft >/dev/null; then sleep 2 drop_dns_outbound_traffic set +e - run dig stale1.unsigned.test -t A + # 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 @@ -602,13 +750,14 @@ if command -v nft >/dev/null; then echo "StaleRetentionSec=1d" } >/run/systemd/resolved.conf.d/test.conf ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf - restart_resolved + systemctl reload systemd-resolved.service run dig stale1.unsigned.test -t A grep -qE "NOERROR" "$RUN_OUT" sleep 2 drop_dns_outbound_traffic - run dig stale1.unsigned.test -t A + # 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" @@ -725,6 +874,28 @@ 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 diff --git a/test/units/testsuite-76.sh b/test/units/TEST-76-SYSCTL.sh index 855d0ef..855d0ef 100755 --- a/test/units/testsuite-76.sh +++ b/test/units/TEST-76-SYSCTL.sh diff --git a/test/units/testsuite-78.sh b/test/units/TEST-78-SIGQUEUE.sh index 46afd3c..46afd3c 100755 --- a/test/units/testsuite-78.sh +++ b/test/units/TEST-78-SIGQUEUE.sh diff --git a/test/units/testsuite-79.sh b/test/units/TEST-79-MEMPRESS.sh index 205f7f3..2b1de20 100755 --- a/test/units/testsuite-79.sh +++ b/test/units/TEST-79-MEMPRESS.sh @@ -15,7 +15,7 @@ fi systemd-analyze log-level debug -CGROUP=/sys/fs/cgroup/"$(systemctl show testsuite-79.service -P ControlGroup)" +CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-MEMPRESS.service -P ControlGroup)" test -d "$CGROUP" if ! test -f "$CGROUP"/memory.pressure ; then @@ -49,7 +49,17 @@ 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" +systemd-run \ + -u "$UNIT" \ + -p Type=exec \ + -p ProtectControlGroups=1 \ + -p DynamicUser=1 \ + -p MemoryPressureWatch=on \ + -p MemoryPressureThresholdSec=123ms \ + -p BindPaths=$SCRIPT \ + `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \ + -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \ + --wait "$SCRIPT" rm "$SCRIPT" diff --git a/test/units/testsuite-80.sh b/test/units/TEST-80-NOTIFYACCESS.sh index 97b222a..97b222a 100755 --- a/test/units/testsuite-80.sh +++ b/test/units/TEST-80-NOTIFYACCESS.sh diff --git a/test/units/testsuite-81.debug-generator.sh b/test/units/TEST-81-GENERATORS.debug-generator.sh index fddf85a..ef1e205 100755 --- a/test/units/testsuite-81.debug-generator.sh +++ b/test/units/TEST-81-GENERATORS.debug-generator.sh @@ -62,6 +62,13 @@ 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" diff --git a/test/units/testsuite-81.environment-d-generator.sh b/test/units/TEST-81-GENERATORS.environment-d-generator.sh index 5bc3978..5bc3978 100755 --- a/test/units/testsuite-81.environment-d-generator.sh +++ b/test/units/TEST-81-GENERATORS.environment-d-generator.sh diff --git a/test/units/testsuite-81.fstab-generator.sh b/test/units/TEST-81-GENERATORS.fstab-generator.sh index 50c4b2f..0c1b27e 100755 --- a/test/units/testsuite-81.fstab-generator.sh +++ b/test/units/TEST-81-GENERATORS.fstab-generator.sh @@ -13,6 +13,7 @@ OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)" FSTAB="$(mktemp)" at_exit() { + mountpoint -q /proc/cmdline && umount /proc/cmdline rm -fr "${OUT_DIR:?}" "${FSTAB:?}" } @@ -148,7 +149,7 @@ check_fstab_mount_units() { fi if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then # Some options are not propagated to the generated unit - if [[ "$where" == / ]]; then + 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=")" @@ -325,7 +326,7 @@ SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check # Check the default stuff that we (almost) always create in initrd : "fstab-generator: initrd default" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR" +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" @@ -347,7 +348,7 @@ cat "$FSTAB" printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB" cat "$FSTAB" # Don't care about the exit code here -SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || : +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 ]] diff --git a/test/units/testsuite-81.getty-generator.sh b/test/units/TEST-81-GENERATORS.getty-generator.sh index 103e966..d1dd22c 100755 --- a/test/units/testsuite-81.getty-generator.sh +++ b/test/units/TEST-81-GENERATORS.getty-generator.sh @@ -85,5 +85,5 @@ 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 +umount /sys/class/tty/console/active --lazy rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99 diff --git a/test/units/testsuite-81.run-generator.sh b/test/units/TEST-81-GENERATORS.run-generator.sh index 9bd74ef..9bd74ef 100755 --- a/test/units/testsuite-81.run-generator.sh +++ b/test/units/TEST-81-GENERATORS.run-generator.sh diff --git a/test/units/testsuite-81.sh b/test/units/TEST-81-GENERATORS.sh index 9c2a033..9c2a033 100755 --- a/test/units/testsuite-81.sh +++ b/test/units/TEST-81-GENERATORS.sh diff --git a/test/units/testsuite-81.system-update-generator.sh b/test/units/TEST-81-GENERATORS.system-update-generator.sh index 8ee1fee..8ee1fee 100755 --- a/test/units/testsuite-81.system-update-generator.sh +++ b/test/units/TEST-81-GENERATORS.system-update-generator.sh diff --git a/test/units/testsuite-82.sh b/test/units/TEST-82-SOFTREBOOT.sh index b5e6ded..f86bc92 100755 --- a/test/units/testsuite-82.sh +++ b/test/units/TEST-82-SOFTREBOOT.sh @@ -23,11 +23,13 @@ systemd-analyze log-level debug export SYSTEMD_LOG_LEVEL=debug -if [ -f /run/testsuite82.touch3 ]; then +if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then echo "This is the fourth boot!" systemd-notify --status="Fourth Boot" - rm /run/testsuite82.touch3 + 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 @@ -37,22 +39,31 @@ if [ -f /run/testsuite82.touch3 ]; then 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" - + 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" + + [[ ! -e /run/credentials/TEST-82-SOFTREBOOT-nosurvive.service ]] + assert_eq "$(cat /run/credentials/TEST-82-SOFTREBOOT-survive-argv.service/preserve)" "yay" + + # There may be huge amount of pending messages in sockets. Processing them may cause journal rotation and + # removal of old archived journal files. If a journal file is removed during journalctl reading it, + # the command may fail. To mitigate such, sync before reading journals. Workaround for #32834. + journalctl --sync # 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 +elif [ -f /run/TEST-82-SOFTREBOOT.touch2 ]; then echo "This is the third boot!" systemd-notify --status="Third Boot" - rm /run/testsuite82.touch2 + 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 @@ -66,10 +77,10 @@ elif [ -f /run/testsuite82.touch2 ]; then 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 "$(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 @@ -82,21 +93,23 @@ elif [ -f /run/testsuite82.touch2 ]; then mount # Restart the unit that is not supposed to survive - systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity + 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/testsuite82.touch3 + 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/testsuite82.touch ]; then +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/testsuite82.touch + rm /run/TEST-82-SOFTREBOOT.touch # Check that the fdstore entry still exists test "$LISTEN_FDS" -eq 1 @@ -104,8 +117,11 @@ elif [ -f /run/testsuite82.touch ]; then 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" + 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" @@ -114,10 +130,10 @@ elif [ -f /run/testsuite82.touch ]; then 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 "$(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 @@ -138,11 +154,16 @@ elif [ -f /run/testsuite82.touch ]; then 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 + 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/testsuite82.touch2 + touch /run/TEST-82-SOFTREBOOT.touch2 systemctl --no-block reboot # Now block until the soft-boot killing spree kills us @@ -151,6 +172,8 @@ 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" @@ -178,39 +201,61 @@ EOF # 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 + 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 -p SetCredential=gone:hoge --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=testsuite-82-survive-argv.service \ + # By writing to stdout, which is connected to the journal, we also ensure logging doesn't break across + # soft reboots due to journald being temporarily stopped. + 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" \ + --property SetCredential=preserve:yay \ "$survive_argv" - systemd-run --service-type=exec --unit=testsuite-82-survive.service \ + # shellcheck disable=SC2016 + 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 + bash -c 'count=0; while echo "$count"; do count=$[$count +1]; sleep 1; done' # 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 \ + 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/testsuite82.touch + touch /run/TEST-82-SOFTREBOOT.touch systemctl --no-block --check-inhibitors=yes soft-reboot # Now block until the soft-boot killing spree kills us @@ -220,4 +265,4 @@ fi systemd-analyze log-level info touch /testok -systemctl --no-block poweroff +systemctl --no-block exit 123 diff --git a/test/units/testsuite-83.sh b/test/units/TEST-83-BTRFS.sh index a722c79..4f10ae7 100755 --- a/test/units/testsuite-83.sh +++ b/test/units/TEST-83-BTRFS.sh @@ -3,6 +3,13 @@ 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 diff --git a/test/units/testsuite-84.sh b/test/units/TEST-84-STORAGETM.sh index eae87d5..eae87d5 100755 --- a/test/units/testsuite-84.sh +++ b/test/units/TEST-84-STORAGETM.sh diff --git a/test/units/a-conj.service b/test/units/a-conj.service index 3a7c9e1..13927e7 100644 --- a/test/units/a-conj.service +++ b/test/units/a-conj.service @@ -6,4 +6,4 @@ After=a.service Before=a.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/a.service b/test/units/a.service index ec5d059..0cc1320 100644 --- a/test/units/a.service +++ b/test/units/a.service @@ -5,4 +5,4 @@ Requires=b.service Before=b.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/autorelabel.service b/test/units/autorelabel.service index 7e5f9a2..5f8386e 100644 --- a/test/units/autorelabel.service +++ b/test/units/autorelabel.service @@ -3,17 +3,17 @@ Description=Relabel all filesystems DefaultDependencies=no Requires=local-fs.target -Conflicts=shutdown.target After=local-fs.target -Before=sysinit.target shutdown.target +Conflicts=shutdown.target +Before=shutdown.target basic.target ConditionSecurity=selinux ConditionPathExists=|/.autorelabel +SuccessAction=reboot [Service] -ExecStart=sh -xec 'echo 0 >/sys/fs/selinux/enforce; fixfiles -f -F relabel; rm /.autorelabel; systemctl --force reboot' +ExecStart=sh -xec 'echo 0 >/sys/fs/selinux/enforce; fixfiles -f -F relabel; rm /.autorelabel;' Type=oneshot TimeoutSec=infinity -RemainAfterExit=yes [Install] WantedBy=basic.target diff --git a/test/units/b.service b/test/units/b.service index 4503cf3..e875714 100644 --- a/test/units/b.service +++ b/test/units/b.service @@ -4,4 +4,4 @@ Description=B Wants=f.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/c.service b/test/units/c.service index a1ce28c..3fc3717 100644 --- a/test/units/c.service +++ b/test/units/c.service @@ -4,4 +4,4 @@ Description=C Requires=a.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/d.service b/test/units/d.service index 8202325..0438607 100644 --- a/test/units/d.service +++ b/test/units/d.service @@ -6,4 +6,4 @@ Before=a.service Requires=a.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/daughter.service b/test/units/daughter.service index 385fbed..0ee4f24 100644 --- a/test/units/daughter.service +++ b/test/units/daughter.service @@ -5,5 +5,5 @@ Description=Daughter Service [Service] Slice=parent.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true CPUAccounting=true diff --git a/test/units/delegated_cgroup_filtering_payload.sh b/test/units/delegated_cgroup_filtering_payload.sh index 50d01a5..1181fbb 100755 --- a/test/units/delegated_cgroup_filtering_payload.sh +++ b/test/units/delegated_cgroup_filtering_payload.sh @@ -2,11 +2,8 @@ # SPDX-License-Identifier: LGPL-2.1-or-later mkdir /sys/fs/cgroup/system.slice/delegated-cgroup-filtering.service/the_child -/bin/sh /usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload_child.sh & +/bin/sh /usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload_child.sh -while true -do - echo "parent_process: hello, world!" - echo "parent_process: hello, people!" - sleep .15 -done +echo "parent_process: hello, world!" +echo "parent_process: hello, people!" +sleep 2 diff --git a/test/units/delegated_cgroup_filtering_payload_child.sh b/test/units/delegated_cgroup_filtering_payload_child.sh index b5635b5..94f0d3a 100755 --- a/test/units/delegated_cgroup_filtering_payload_child.sh +++ b/test/units/delegated_cgroup_filtering_payload_child.sh @@ -3,9 +3,6 @@ echo $$ >/sys/fs/cgroup/system.slice/delegated-cgroup-filtering.service/the_child/cgroup.procs -while true -do - echo "child_process: hello, world!" - echo "child_process: hello, people!" - sleep .15 -done +echo "child_process: hello, world!" +echo "child_process: hello, people!" +sleep .15 diff --git a/test/units/dml-discard-empty.service b/test/units/dml-discard-empty.service index 720c1da..c176989 100644 --- a/test/units/dml-discard-empty.service +++ b/test/units/dml-discard-empty.service @@ -5,4 +5,4 @@ Description=DML discard empty service [Service] Slice=dml-discard.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/dml-discard-set-ml.service b/test/units/dml-discard-set-ml.service index 93246ac..0fba2ac 100644 --- a/test/units/dml-discard-set-ml.service +++ b/test/units/dml-discard-set-ml.service @@ -5,5 +5,5 @@ Description=DML discard set ml service [Service] Slice=dml-discard.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true MemoryLow=15 diff --git a/test/units/dml-override-empty.service b/test/units/dml-override-empty.service index ac96de0..5f0c143 100644 --- a/test/units/dml-override-empty.service +++ b/test/units/dml-override-empty.service @@ -5,4 +5,4 @@ Description=DML override empty service [Service] Slice=dml-override.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/dml-passthrough-empty.service b/test/units/dml-passthrough-empty.service index 1e1ba34..8966226 100644 --- a/test/units/dml-passthrough-empty.service +++ b/test/units/dml-passthrough-empty.service @@ -5,4 +5,4 @@ Description=DML passthrough empty service [Service] Slice=dml-passthrough.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/dml-passthrough-set-dml.service b/test/units/dml-passthrough-set-dml.service index 9a15311..ec82174 100644 --- a/test/units/dml-passthrough-set-dml.service +++ b/test/units/dml-passthrough-set-dml.service @@ -5,5 +5,5 @@ Description=DML passthrough set DML service [Service] Slice=dml-passthrough.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true DefaultMemoryLow=15 diff --git a/test/units/dml-passthrough-set-ml.service b/test/units/dml-passthrough-set-ml.service index 65083bc..63ec305 100644 --- a/test/units/dml-passthrough-set-ml.service +++ b/test/units/dml-passthrough-set-ml.service @@ -5,5 +5,5 @@ Description=DML passthrough set ML service [Service] Slice=dml-passthrough.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true MemoryLow=0 diff --git a/test/units/e.service b/test/units/e.service index 5bbcde2..c2acb92 100644 --- a/test/units/e.service +++ b/test/units/e.service @@ -6,4 +6,4 @@ Before=a.service Wants=a.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/end.sh b/test/units/end.sh index 230b716..cc1d7ee 100755 --- a/test/units/end.sh +++ b/test/units/end.sh @@ -4,10 +4,14 @@ set -eux set -o pipefail -(! journalctl -q -o short-monotonic --grep "didn't pass validation" >>/failed) +(! journalctl -q -o short-monotonic --grep "didn't pass validation" | grep -v "test-varlink-idl" >>/failed) -# Here, the redundant '[.]' at the end is for making not the logged self command hit the grep. -(! journalctl -q -o short-monotonic --grep 'Attempted to close sd-bus after fork whose connection is opened before the fork, this should not happen[.]' >>/failed) +# Here, the redundant '[ ]' in the pattern is required in order not to match the logged command itself. +(! journalctl -q -o short-monotonic --grep 'Warning: cannot close sd-bus connection[ ].*after fork' >>/failed) + +# Check if sd-executor doesn't complain about not being able to (de)serialize stuff +(! journalctl -q -o short-monotonic --grep "[F]ailed to parse serialized line" >>/failed) +(! journalctl -q -o short-monotonic --grep "[F]ailed to (de)?serialize \w+" >>/failed) systemctl poweroff --no-block exit 0 diff --git a/test/units/f.service b/test/units/f.service index ca20053..a66043e 100644 --- a/test/units/f.service +++ b/test/units/f.service @@ -3,4 +3,4 @@ Description=F [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/g.service b/test/units/g.service index 5fd794d..bfb3d78 100644 --- a/test/units/g.service +++ b/test/units/g.service @@ -4,4 +4,4 @@ Description=G Conflicts=e.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/generator-utils.sh b/test/units/generator-utils.sh index fb62747..97a63d8 100755 --- a/test/units/generator-utils.sh +++ b/test/units/generator-utils.sh @@ -72,7 +72,7 @@ run_and_list() { ls -lR "$out_dir" if [[ -n "${environ:-}" ]]; then - umount /proc/1/environ + umount /proc/1/environ --lazy rm -f "$environ" fi } diff --git a/test/units/grandchild.service b/test/units/grandchild.service index 4fe77b4..bdccfe1 100644 --- a/test/units/grandchild.service +++ b/test/units/grandchild.service @@ -5,4 +5,4 @@ Description=Grandchild Service [Service] Slice=parent-deep.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/h.service b/test/units/h.service index 5361d42..1c4dbb4 100644 --- a/test/units/h.service +++ b/test/units/h.service @@ -4,4 +4,4 @@ Description=H Wants=g.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/i.service b/test/units/i.service index 2b5e821..783ac65 100644 --- a/test/units/i.service +++ b/test/units/i.service @@ -6,4 +6,4 @@ Wants=b.service After=b.service [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/loopy.service b/test/units/loopy.service index 7fc0e42..4c1a4a3 100644 --- a/test/units/loopy.service +++ b/test/units/loopy.service @@ -1,3 +1,3 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/loopy2.service b/test/units/loopy2.service index 7fc0e42..4c1a4a3 100644 --- a/test/units/loopy2.service +++ b/test/units/loopy2.service @@ -1,3 +1,3 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/loopy3.service b/test/units/loopy3.service index b2af20a..f7a2f67 100644 --- a/test/units/loopy3.service +++ b/test/units/loopy3.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/true +ExecStart=true [Unit] Conflicts=loopy4.service diff --git a/test/units/loopy4.service b/test/units/loopy4.service index b2af20a..f7a2f67 100644 --- a/test/units/loopy4.service +++ b/test/units/loopy4.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Service] -ExecStart=/bin/true +ExecStart=true [Unit] Conflicts=loopy4.service diff --git a/test/units/nomemleaf.service b/test/units/nomemleaf.service index 14ce5ad..2e5c8ce 100644 --- a/test/units/nomemleaf.service +++ b/test/units/nomemleaf.service @@ -5,6 +5,6 @@ Description=Nomem Leaf Service [Service] Slice=nomem.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true IOWeight=200 MemoryAccounting=true diff --git a/test/units/sched_idle_bad.service b/test/units/sched_idle_bad.service index be8f1c2..d9fd6cc 100644 --- a/test/units/sched_idle_bad.service +++ b/test/units/sched_idle_bad.service @@ -3,5 +3,5 @@ Description=Bad sched priority for Idle [Service] -ExecStart=/bin/true +ExecStart=true CPUSchedulingPriority=1 diff --git a/test/units/sched_idle_ok.service b/test/units/sched_idle_ok.service index 5a1d809..a7238ed 100644 --- a/test/units/sched_idle_ok.service +++ b/test/units/sched_idle_ok.service @@ -3,5 +3,5 @@ Description=Sched idle with prio 0 [Service] -ExecStart=/bin/true +ExecStart=true CPUSchedulingPriority=0 diff --git a/test/units/sched_rr_bad.service b/test/units/sched_rr_bad.service index b51b868..3f3bf39 100644 --- a/test/units/sched_rr_bad.service +++ b/test/units/sched_rr_bad.service @@ -3,7 +3,7 @@ Description=Bad sched priority for RR [Service] -ExecStart=/bin/true +ExecStart=true CPUSchedulingPriority=-1 CPUSchedulingPriority=100 CPUSchedulingPolicy=rr diff --git a/test/units/sched_rr_change.service b/test/units/sched_rr_change.service index 6ae1feb..3a72bd6 100644 --- a/test/units/sched_rr_change.service +++ b/test/units/sched_rr_change.service @@ -3,7 +3,7 @@ Description=Change prio [Service] -ExecStart=/bin/true +ExecStart=true CPUSchedulingPriority=1 CPUSchedulingPriority=2 CPUSchedulingPriority=99 diff --git a/test/units/sched_rr_ok.service b/test/units/sched_rr_ok.service index 00b9822..5c71f30 100644 --- a/test/units/sched_rr_ok.service +++ b/test/units/sched_rr_ok.service @@ -3,5 +3,5 @@ Description=Default prio for RR [Service] -ExecStart=/bin/true +ExecStart=true CPUSchedulingPolicy=rr diff --git a/test/units/son.service b/test/units/son.service index 2059118..0242509 100644 --- a/test/units/son.service +++ b/test/units/son.service @@ -5,5 +5,5 @@ Description=Son Service [Service] Slice=parent.slice Type=oneshot -ExecStart=/bin/true +ExecStart=true CPUShares=100 diff --git a/test/units/test-control.sh b/test/units/test-control.sh index 0a1611b..4cede74 100644 --- a/test/units/test-control.sh +++ b/test/units/test-control.sh @@ -107,7 +107,7 @@ run_subtests_with_signals() { _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 diff --git a/test/units/testsuite-01.service b/test/units/testsuite-01.service deleted file mode 100644 index 9074e09..0000000 --- a/test/units/testsuite-01.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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 diff --git a/test/units/testsuite-02.service b/test/units/testsuite-02.service deleted file mode 100644 index dea2c4f..0000000 --- a/test/units/testsuite-02.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-02.sh b/test/units/testsuite-02.sh deleted file mode 100755 index 2a3cb08..0000000 --- a/test/units/testsuite-02.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/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} -mapfile -t TEST_LIST < <(find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}") - -# Reset state -rm -fv /failed /skipped /testok - -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 -report_result() { - if [[ $# -ne 2 ]]; then - echo >&2 "check_result: missing arguments" - exit 1 - fi - - local name="${1##*/}" - local ret=$2 - - if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then - echo "$name failed with $ret" - echo "$name" >>/failed-tests - { - echo "--- $name begin ---" - cat "/$name.log" - echo "--- $name end ---" - } >>/failed - elif [[ $ret == 77 || $ret == 127 ]]; then - echo "$name skipped" - echo "$name" >>/skipped-tests - { - echo "--- $name begin ---" - cat "/$name.log" - echo "--- $name end ---" - } >>/skipped - else - echo "$name OK" - echo "$name" >>/testok - fi -} - -set +x -# Associative array for running tasks, where running[test-path]=PID -declare -A running=() -for task in "${TEST_LIST[@]}"; do - # If there's MAX_QUEUE_SIZE running tasks, keep checking the running queue - # until one of the tasks finishes, so we can replace it. - while [[ ${#running[@]} -ge $MAX_QUEUE_SIZE ]]; do - for key in "${!running[@]}"; do - if ! kill -0 "${running[$key]}" &>/dev/null; then - # Task has finished, report its result and drop it from the queue - wait "${running[$key]}" && ec=0 || ec=$? - report_result "$key" "$ec" - unset "running[$key]" - # Break from inner for loop and outer while loop to skip - # the sleep below when we find a free slot in the queue - break 2 - fi - done - - # Precisely* calculated constant to keep the spinlock from burning the CPU(s) - sleep 0.01 - done - - if [[ -x $task ]]; then - echo "Executing test '$task'" - log_file="/${task##*/}.log" - $task &>"$log_file" & - running[$task]=$! - fi -done - -# Wait for remaining running tasks -for key in "${!running[@]}"; do - echo "Waiting for test '$key' to finish" - wait "${running[$key]}" && ec=0 || ec=$? - report_result "$key" "$ec" - unset "running[$key]" -done - -set -x - -# Test logs are sometimes lost, as the system shuts down immediately after -journalctl --sync - -test ! -s /failed -touch /testok diff --git a/test/units/testsuite-03.service b/test/units/testsuite-03.service deleted file mode 100644 index 836f962..0000000 --- a/test/units/testsuite-03.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/testsuite-04.LogFilterPatterns.sh b/test/units/testsuite-04.LogFilterPatterns.sh deleted file mode 100755 index 2192e84..0000000 --- a/test/units/testsuite-04.LogFilterPatterns.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# This fails due to https://github.com/systemd/systemd/issues/30886 -# but it is too complex and risky to backport, so disable the test -exit 0 - -# 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 end - - start="$(date '+%Y-%m-%d %T.%6N')" - systemctl restart "$unit" - sleep .5 - journalctl --sync - end="$(date '+%Y-%m-%d %T.%6N')" - - journalctl -q -u "$unit" -S "$start" -U "$end" -p notice - systemctl stop "$unit" -} - -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 diff --git a/test/units/testsuite-04.journal-gatewayd.sh b/test/units/testsuite-04.journal-gatewayd.sh deleted file mode 100755 index 5755ef1..0000000 --- a/test/units/testsuite-04.journal-gatewayd.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -# pipefail is disabled intentionally, as `curl | grep -q` is very SIGPIPE happy - -if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then - echo "Built without systemd-journal-gatewayd support, skipping the test" - exit 0 -fi - -TEST_MESSAGE="-= This is a test message $RANDOM =-" -TEST_TAG="$(systemd-id128 new)" - -echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG" -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; }')" - -/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 -Lfs http://localhost:19531 | grep -qF "<title>Journal</title>" -curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>" -(! curl -Lfs http://localhost:19531/foo/bar/baz) -(! curl -Lfs http://localhost:19531/foo/../../../bar/../baz) - -# /entries -# Accept: text/plain should be the default -curl -Lfs http://localhost:19531/entries | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfs --header "Accept: text/plain" http://localhost:19531/entries | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfs --header "Accept: application/json" http://localhost:19531/entries | \ - jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" -curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?boot | \ - jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" -curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ - jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" -# Show 10 entries starting from $BOOT_CURSOR, skip the first 5 -curl -Lfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \ - jq -se "length == 10" -# Check if the specified cursor refers to an existing entry and return just that entry -curl -Lfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \ - jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" -# 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 -Lfs --header "Accept: text/event-stream" http://localhost:19531/entries | \ - awk '!/^(data: \{.+\}|)$/ { exit 1; }' -# Same thing as journalctl --output=export -mkdir /tmp/remote-journal -curl -Lfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \ - /usr/lib/systemd/systemd-journal-remote --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/* -# 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 -Lfs http://localhost:19531/machine | jq - -# /fields -curl -Lfs http://localhost:19531/fields/MESSAGE | grep -qE -- "$TEST_MESSAGE" -curl -Lfs http://localhost:19531/fields/_TRANSPORT -(! curl -Lfs http://localhost:19531/fields) -(! curl -Lfs 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 -Lfsk https://localhost:19531 | grep -qF "<title>Journal</title>" -curl -Lfsk https://localhost:19531/entries | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfsk --header "Accept: application/json" https://localhost:19531/entries | \ - jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" -curl -Lfsk https://localhost:19531/machine | jq -curl -Lfsk https://localhost:19531/fields/_TRANSPORT - -kill "$GATEWAYD_PID" diff --git a/test/units/testsuite-04.service b/test/units/testsuite-04.service deleted file mode 100644 index 63a0104..0000000 --- a/test/units/testsuite-04.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-05.service b/test/units/testsuite-05.service deleted file mode 100644 index ab72d8f..0000000 --- a/test/units/testsuite-05.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-06.service b/test/units/testsuite-06.service deleted file mode 100644 index c4c1d87..0000000 --- a/test/units/testsuite-06.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-08.service b/test/units/testsuite-08.service deleted file mode 100644 index 2db35cf..0000000 --- a/test/units/testsuite-08.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/testsuite-09.service b/test/units/testsuite-09.service deleted file mode 100644 index 6c957ec..0000000 --- a/test/units/testsuite-09.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/testsuite-13.service b/test/units/testsuite-13.service deleted file mode 100644 index 95c9a02..0000000 --- a/test/units/testsuite-13.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-15.service b/test/units/testsuite-15.service deleted file mode 100644 index 10af93f..0000000 --- a/test/units/testsuite-15.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-17.03.sh b/test/units/testsuite-17.03.sh deleted file mode 100755 index 56e352e..0000000 --- a/test/units/testsuite-17.03.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex - -TEST_RULE="/run/udev/rules.d/49-test.rules" -KILL_PID= - -setup() { - mkdir -p "${TEST_RULE%/*}" - [[ -e /etc/udev/udev.conf ]] && cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bak - # Don't bother storing the coredumps in journal for this particular test - mkdir -p /run/systemd/coredump.conf.d/ - echo -ne "[Coredump]\nStorage=external\n" >/run/systemd/coredump.conf.d/99-storage-journal.conf - - cat >"${TEST_RULE}" <<EOF -ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug" -ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60" -EOF - cat >/etc/udev/udev.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" - [[ -e /etc/udev/udev.conf.bak ]] && mv -f /etc/udev/udev.conf.bak /etc/udev/udev.conf - rm /run/systemd/coredump.conf.d/99-storage-journal.conf - systemctl restart systemd-udevd.service -} - -run_test() { - local since - - since="$(date '+%F %T')" - - 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 coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then - 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 - return 0 - fi - sleep .5 - done - - return 1 -} - -trap teardown EXIT - -setup -run_test - -exit 0 diff --git a/test/units/testsuite-17.service b/test/units/testsuite-17.service deleted file mode 100644 index d218d72..0000000 --- a/test/units/testsuite-17.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-18.service b/test/units/testsuite-18.service deleted file mode 100644 index 16d90a1..0000000 --- a/test/units/testsuite-18.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-19.service b/test/units/testsuite-19.service deleted file mode 100644 index 9ee5fc9..0000000 --- a/test/units/testsuite-19.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-21.service b/test/units/testsuite-21.service deleted file mode 100644 index a5f77d0..0000000 --- a/test/units/testsuite-21.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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 diff --git a/test/units/testsuite-22.service b/test/units/testsuite-22.service deleted file mode 100644 index a5ed660..0000000 --- a/test/units/testsuite-22.service +++ /dev/null @@ -1,11 +0,0 @@ -# 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 diff --git a/test/units/testsuite-23-short-lived.sh b/test/units/testsuite-23-short-lived.sh deleted file mode 100755 index 4a12c7f..0000000 --- a/test/units/testsuite-23-short-lived.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/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 diff --git a/test/units/testsuite-23.JoinsNamespaceOf.sh b/test/units/testsuite-23.JoinsNamespaceOf.sh deleted file mode 100755 index 68ba465..0000000 --- a/test/units/testsuite-23.JoinsNamespaceOf.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/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 diff --git a/test/units/testsuite-23.Upholds.sh b/test/units/testsuite-23.Upholds.sh deleted file mode 100755 index e62f9c6..0000000 --- a/test/units/testsuite-23.Upholds.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/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 diff --git a/test/units/testsuite-23.oneshot-restart.sh b/test/units/testsuite-23.oneshot-restart.sh deleted file mode 100755 index 433cd69..0000000 --- a/test/units/testsuite-23.oneshot-restart.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Test oneshot unit restart on failure - -# wait this many secs for each test service to succeed in what is being tested -MAX_SECS=60 - -systemd-analyze 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-41-oneshot-restart-test" - -: >$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 - -systemd-analyze log-level info diff --git a/test/units/testsuite-23.runtime-bind-paths.sh b/test/units/testsuite-23.runtime-bind-paths.sh deleted file mode 100755 index 65c2dbf..0000000 --- a/test/units/testsuite-23.runtime-bind-paths.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/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) diff --git a/test/units/testsuite-23.service b/test/units/testsuite-23.service deleted file mode 100644 index 26f5226..0000000 --- a/test/units/testsuite-23.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-23.start-stop-no-reload.sh b/test/units/testsuite-23.start-stop-no-reload.sh deleted file mode 100755 index 9c4f17d..0000000 --- a/test/units/testsuite-23.start-stop-no-reload.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/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=/bin/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=/bin/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=/bin/sleep infinity -EOF - -systemctl restart testsuite-23-no-reload.target - -systemctl is-active testsuite-23-no-reload.service diff --git a/test/units/testsuite-24.service b/test/units/testsuite-24.service deleted file mode 100644 index e192d1c..0000000 --- a/test/units/testsuite-24.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/testsuite-25.service b/test/units/testsuite-25.service deleted file mode 100644 index 503eabb..0000000 --- a/test/units/testsuite-25.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-26.service b/test/units/testsuite-26.service deleted file mode 100644 index d8fdaff..0000000 --- a/test/units/testsuite-26.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-29.service b/test/units/testsuite-29.service deleted file mode 100644 index 035c6bf..0000000 --- a/test/units/testsuite-29.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-30.service b/test/units/testsuite-30.service deleted file mode 100644 index 253f7b5..0000000 --- a/test/units/testsuite-30.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-31.service b/test/units/testsuite-31.service deleted file mode 100644 index f0e78a9..0000000 --- a/test/units/testsuite-31.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-32.service b/test/units/testsuite-32.service deleted file mode 100644 index 50f5823..0000000 --- a/test/units/testsuite-32.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/testsuite-34.service b/test/units/testsuite-34.service deleted file mode 100644 index 6917afe..0000000 --- a/test/units/testsuite-34.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-35.service b/test/units/testsuite-35.service deleted file mode 100644 index 0599f61..0000000 --- a/test/units/testsuite-35.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-36.service b/test/units/testsuite-36.service deleted file mode 100644 index 5746dc1..0000000 --- a/test/units/testsuite-36.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-38.service b/test/units/testsuite-38.service deleted file mode 100644 index ac77836..0000000 --- a/test/units/testsuite-38.service +++ /dev/null @@ -1,7 +0,0 @@ -# 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 diff --git a/test/units/testsuite-43.service b/test/units/testsuite-43.service deleted file mode 100644 index e36afea..0000000 --- a/test/units/testsuite-43.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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 diff --git a/test/units/testsuite-44.service b/test/units/testsuite-44.service deleted file mode 100644 index 4dffdea..0000000 --- a/test/units/testsuite-44.service +++ /dev/null @@ -1,12 +0,0 @@ -# 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 diff --git a/test/units/testsuite-45.service b/test/units/testsuite-45.service deleted file mode 100644 index b16ce99..0000000 --- a/test/units/testsuite-45.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-46.service b/test/units/testsuite-46.service deleted file mode 100644 index 5efb9cc..0000000 --- a/test/units/testsuite-46.service +++ /dev/null @@ -1,13 +0,0 @@ -# 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 diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh deleted file mode 100755 index a77683b..0000000 --- a/test/units/testsuite-46.sh +++ /dev/null @@ -1,319 +0,0 @@ -#!/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 0 -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\):' /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 -} - -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. -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 -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 - -PASSWORD=xEhErW0ndafV4s homectl activate test-user -inspect test-user - -homectl deactivate test-user -inspect test-user - -PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Offline test" -inspect test-user - -PASSWORD=xEhErW0ndafV4s homectl activate test-user -inspect test-user - -homectl deactivate test-user -inspect test-user - -# Do some resize tests, but only if we run on real kernels, as quota inside of containers will fail -if ! systemd-detect-virt -cq ; 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 - 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 -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) - -wait_for_state test-user inactive -homectl remove test-user - -if ! systemd-detect-virt -cq ; then - wait_for_state test-user2 active - homectl deactivate test-user2 - wait_for_state test-user2 inactive - homectl remove test-user2 -fi - -# 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 - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service deleted file mode 100644 index bcafe6e..0000000 --- a/test/units/testsuite-50.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh deleted file mode 100755 index 3726b32..0000000 --- a/test/units/testsuite-50.sh +++ /dev/null @@ -1,718 +0,0 @@ -#!/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 - -export SYSTEMD_LOG_LEVEL=debug - -# shellcheck disable=SC2317 -cleanup() {( - set +ex - - if [ -z "${image_dir}" ]; then - return - fi - umount "${image_dir}/app0" - umount "${image_dir}/app1" - umount "${image_dir}/app-nodistro" - umount "${image_dir}/service-scoped-test" - rm -rf "${image_dir}" -)} - -udevadm control --log-level=debug - -cd /tmp - -image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)" -if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then - echo "mktemp under /tmp failed" - exit 1 -fi - -trap cleanup EXIT - -cp /usr/share/minimal* "${image_dir}/" -image="${image_dir}/minimal_0" -roothash="$(cat "${image}.roothash")" - -os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)" - -systemd-dissect --json=short "${image}.raw" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"' -systemd-dissect "${image}.raw" | grep -q -F "MARKER=1" -systemd-dissect "${image}.raw" | grep -q -F -f <(sed 's/"//g' "$os_release") - -systemd-dissect --list "${image}.raw" | grep -q '^etc/os-release$' -systemd-dissect --mtree "${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 "${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 "${image}.raw" etc/os-release | sha256sum) -test "$SHA256SUM1" != "" -read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha256sum etc/os-release) -test "$SHA256SUM2" != "" -test "$SHA256SUM1" = "$SHA256SUM2" - -mv "${image}.verity" "${image}.fooverity" -mv "${image}.roothash" "${image}.foohash" -systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"' -systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F "MARKER=1" -systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F -f <(sed 's/"//g' "$os_release") -mv "${image}.fooverity" "${image}.verity" -mv "${image}.foohash" "${image}.roothash" - -mkdir -p "${image_dir}/mount" "${image_dir}/mount2" -systemd-dissect --mount "${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 "${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 ${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="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1" -mv "${image}.verity" "${image}.fooverity" -mv "${image}.roothash" "${image}.foohash" -systemd-run -P -p RootImage="${image}.raw" -p RootHash="${image}.foohash" -p RootVerity="${image}.fooverity" 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="${image}.raw" --property RootHash="${roothash}" --property RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1" -mv "${image}.fooverity" "${image}.verity" -mv "${image}.foohash" "${image}.roothash" - -# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2 -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" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "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" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "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}" = "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}" = "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}" = "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" -else - echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me" - exit 1 -fi -# du rounds up to block size, which is more helpful for partitioning -root_size="$(du -k "${image}.raw" | cut -f1)" -verity_size="$(du -k "${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="${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" - -HAVE_OPENSSL=0 -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 2>&1; then - echo "openssl missing" >/failed - exit 1 - fi - - HAVE_OPENSSL=1 - 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 - - # Create key pair - openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes - # Sign Verity root hash with it - openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s" - # Generate signature partition JSON data - echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 <"${image}.roothash.p7s")"'"}' >"${image}.verity-sig" - # Pad it - truncate -s "${signature_size}" "${image}.verity-sig" - # Register certificate in the (userspace) verity key ring - mkdir -p /run/verity.d - ln -s "${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 "${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 "${image}.gpt" -uuid="$(tail -c 32 "${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 "${image}.gpt" --append -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append -fi -sfdisk --part-label "${image}.gpt" 1 "Root Partition" -sfdisk --part-label "${image}.gpt" 2 "Verity Partition" -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - sfdisk --part-label "${image}.gpt" 3 "Signature Partition" -fi -loop="$(losetup --show -P -f "${image}.gpt")" -partitions=( - "${loop:?}p1" - "${loop:?}p2" -) -if [ "${HAVE_OPENSSL}" -eq 1 ]; 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="${image}.raw" of="${loop}p1" -udevadm lock --device="${loop}p2" dd if="${image}.verity" of="${loop}p2" -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - udevadm lock --device="${loop}p3" dd if="${image}.verity-sig" of="${loop}p3" -fi -losetup -d "${loop}" - -# Derive partition UUIDs from root hash, in UUID syntax -ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)" -VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)" - -systemd-dissect --json=short --root-hash "${roothash}" "${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 "${roothash}" "${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 [ "${HAVE_OPENSSL}" -eq 1 ]; then - systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"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 "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1" -systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release") - -# Test image policies -systemd-dissect --validate "${image}.gpt" -systemd-dissect --validate "${image}.gpt" --image-policy='*' -(! systemd-dissect --validate "${image}.gpt" --image-policy='~') -(! systemd-dissect --validate "${image}.gpt" --image-policy='-') -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=absent) -(! systemd-dissect --validate "${image}.gpt" --image-policy=swap=unprotected+encrypted+verity) -systemd-dissect --validate "${image}.gpt" --image-policy=root=unprotected -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity-sig=unused+absent -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent+unprotected -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity=unused+absent) -systemd-dissect --validate "${image}.gpt" --image-policy=root=signed -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity-sig=unused+absent) -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity=unused+absent) - -# Test RootImagePolicy= unit file setting -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='*' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='~' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='-' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=absent' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=verity' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=signed' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=encrypted' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") - -systemd-dissect --root-hash "${roothash}" --mount "${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 "${roothash}" --mount "${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="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" - -systemd-run -P -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" 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=${image}.raw -RootImageOptions=root:ro,noatime home:ro,dev relatime,dev -RootImageOptions=nosuid,dev -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=${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 -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="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" 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=${image}.raw -MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime -MountImages=${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=/bin/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 "${image}.raw" /tmp/img root:nosuid - -while systemctl show -P SubState testservice-50d.service | grep -q running -do - sleep 0.1 -done - -systemctl is-active testservice-50d.service - -# ExtensionImages will set up an overlay -systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0" -systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" -systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0" -systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" -systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2" -systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1" -systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" -systemd-run -P --property ExtensionImages=/etc/service-scoped-test.raw --property RootImage="${image}.raw" 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 /usr/share/symlink-test/ -cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw -ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw -systemd-run -P --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw --property RootImage="${image}.raw" 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="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123" -# And again mixing sysext and confext -systemd-run -P \ - --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \ - --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \ - --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123" -systemd-run -P \ - --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \ - --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \ - --property RootImage="${image}.raw" 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=${image}.raw -ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid -# Relevant only for sanitizer runs -UnsetEnvironment=LD_PRELOAD -ExecStart=/bin/bash -c '/opt/script0.sh | grep ID' -ExecStart=/bin/bash -c '/opt/script1.sh | grep ID' -Type=oneshot -RemainAfterExit=yes -EOF -systemctl start testservice-50e.service -systemctl is-active testservice-50e.service - -# 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="${image}.raw" cat /opt/script0.sh) -(! systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh) -systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0" -systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1" -systemd-dissect --mount /usr/share/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="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0" -systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" 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="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0" -systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" 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="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2" -systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1" -systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" -systemd-run -P --property ExtensionDirectories="${image_dir}/service-scoped-test" --property RootImage="${image}.raw" 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=${image}.raw -ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1 -# Relevant only for sanitizer runs -UnsetEnvironment=LD_PRELOAD -ExecStart=/bin/bash -c '/opt/script0.sh | grep ID' -ExecStart=/bin/bash -c '/opt/script1.sh | grep ID' -Type=oneshot -RemainAfterExit=yes -EOF -systemctl start testservice-50f.service -systemctl is-active testservice-50f.service -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 /usr/share/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 - -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 - -# Check that the /sbin/mount.ddi helper works -T="/tmp/mounthelper.$RANDOM" -mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard -umount -R "$T" -rmdir "$T" - -LOOP="$(systemd-dissect --attach --loop-ref=waldo "${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' "${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' "${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" "${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" "${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 "${image}.raw" -(! systemd-dissect --detach "${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 "${image}.raw" -systemd-run --unit=test-root-ephemeral \ - -p RootDirectory=/tmp/img \ - -p RootEphemeral=yes \ - -p Type=exec \ - 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 -systemd-dissect --list /tmp/img - -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="${image}.raw" cat /run/host/os-release | cmp "${os_release}" - -# Test that systemd-sysext reloads the daemon. -mkdir -p /var/lib/extensions/ -ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw -systemd-sysext merge --no-reload -# the service should not be running -if systemctl --quiet is-active foo.service; then - echo "foo.service should not be active" - exit 1 -fi -systemd-sysext unmerge --no-reload -systemd-sysext merge -for RETRY in $(seq 60) LAST; do - if [[ "$(journalctl --boot _SYSTEMD_UNIT="foo.service" + SYSLOG_IDENTIFIER="sysext-foo" -p info -o cat)" == "foo" ]]; then - break - fi - if [ "${RETRY}" = LAST ]; then - echo "Output of foo.service not found" - exit 1 - fi - sleep 0.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 - -# Test systemd-repart --make-ddi=: -if command -v mksquashfs >/dev/null 2>&1; then - - 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 - - read -r X < /etc/waldo - test "$X" = 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) -fi - -# 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) - -touch /testok diff --git a/test/units/testsuite-52.service b/test/units/testsuite-52.service deleted file mode 100644 index b9f2909..0000000 --- a/test/units/testsuite-52.service +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=Testsuite service - -[Service] -ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh -Type=oneshot diff --git a/test/units/testsuite-53.service b/test/units/testsuite-53.service deleted file mode 100644 index cf3adbb..0000000 --- a/test/units/testsuite-53.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-54.service b/test/units/testsuite-54.service deleted file mode 100644 index ba8cdad..0000000 --- a/test/units/testsuite-54.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-55.service b/test/units/testsuite-55.service deleted file mode 100644 index 00fb499..0000000 --- a/test/units/testsuite-55.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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 diff --git a/test/units/testsuite-58.service b/test/units/testsuite-58.service deleted file mode 100644 index f843527..0000000 --- a/test/units/testsuite-58.service +++ /dev/null @@ -1,7 +0,0 @@ -# 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 diff --git a/test/units/testsuite-59.service b/test/units/testsuite-59.service deleted file mode 100644 index f85cfab..0000000 --- a/test/units/testsuite-59.service +++ /dev/null @@ -1,7 +0,0 @@ -# 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 diff --git a/test/units/testsuite-60.service b/test/units/testsuite-60.service deleted file mode 100644 index 1a929e4..0000000 --- a/test/units/testsuite-60.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-62-1.service b/test/units/testsuite-62-1.service deleted file mode 100644 index fa3a7e7..0000000 --- a/test/units/testsuite-62-1.service +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=TEST-62-RESTRICT-IFACES-all-pings-work -[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= -Type=oneshot diff --git a/test/units/testsuite-62.service b/test/units/testsuite-62.service deleted file mode 100644 index 5c3f94d..0000000 --- a/test/units/testsuite-62.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service deleted file mode 100644 index 483c6a8..0000000 --- a/test/units/testsuite-63.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-64.service b/test/units/testsuite-64.service deleted file mode 100644 index f75a3d7..0000000 --- a/test/units/testsuite-64.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-65.service b/test/units/testsuite-65.service deleted file mode 100644 index 3610baf..0000000 --- a/test/units/testsuite-65.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-66-deviceisolation.service b/test/units/testsuite-66-deviceisolation.service deleted file mode 100644 index 2d815a9..0000000 --- a/test/units/testsuite-66-deviceisolation.service +++ /dev/null @@ -1,10 +0,0 @@ -# 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=/bin/bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done" diff --git a/test/units/testsuite-66.service b/test/units/testsuite-66.service deleted file mode 100644 index 7e9dc3b..0000000 --- a/test/units/testsuite-66.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-67.service b/test/units/testsuite-67.service deleted file mode 100644 index 82f998e..0000000 --- a/test/units/testsuite-67.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/testsuite-68.service b/test/units/testsuite-68.service deleted file mode 100644 index 2d86e1f..0000000 --- a/test/units/testsuite-68.service +++ /dev/null @@ -1,7 +0,0 @@ -# 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 diff --git a/test/units/testsuite-70.service b/test/units/testsuite-70.service deleted file mode 100644 index c13c2d5..0000000 --- a/test/units/testsuite-70.service +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=TEST-70-TPM2 - -[Service] -Type=oneshot -ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh diff --git a/test/units/testsuite-71.service b/test/units/testsuite-71.service deleted file mode 100644 index 1718629..0000000 --- a/test/units/testsuite-71.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-72.service b/test/units/testsuite-72.service deleted file mode 100644 index 1640350..0000000 --- a/test/units/testsuite-72.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-73.service b/test/units/testsuite-73.service deleted file mode 100644 index 3ebd24d..0000000 --- a/test/units/testsuite-73.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-74.service b/test/units/testsuite-74.service deleted file mode 100644 index f782132..0000000 --- a/test/units/testsuite-74.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-75.service b/test/units/testsuite-75.service deleted file mode 100644 index 111cde3..0000000 --- a/test/units/testsuite-75.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-76.service b/test/units/testsuite-76.service deleted file mode 100644 index 3c8a9e8..0000000 --- a/test/units/testsuite-76.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-77-client.sh b/test/units/testsuite-77-client.sh deleted file mode 100755 index 0d9487a..0000000 --- a/test/units/testsuite-77-client.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/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 - -assert_eq "$LISTEN_FDS" "1" -assert_eq "$LISTEN_FDNAMES" "socket" -read -r -u 3 text -assert_eq "$text" "Socket" diff --git a/test/units/testsuite-77-run.sh b/test/units/testsuite-77-run.sh deleted file mode 100755 index fadd34d..0000000 --- a/test/units/testsuite-77-run.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/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 - -assert_eq "$LISTEN_FDS" "1" -assert_eq "$LISTEN_FDNAMES" "new-file" -read -r -u 3 text -assert_eq "$text" "New" diff --git a/test/units/testsuite-77.service b/test/units/testsuite-77.service deleted file mode 100644 index 6ed8add..0000000 --- a/test/units/testsuite-77.service +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=TEST-77-OPENFILE - -[Service] -OpenFile=/test-77-open.dat:open:read-only -OpenFile=/test-77-file.dat -ExecStartPre=rm -f /failed /testok -ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh -Type=oneshot diff --git a/test/units/testsuite-77.sh b/test/units/testsuite-77.sh deleted file mode 100755 index 2b85a8c..0000000 --- a/test/units/testsuite-77.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/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 - -assert_eq "$LISTEN_FDS" "2" -assert_eq "$LISTEN_FDNAMES" "open:test-77-file.dat" -read -r -u 3 text -assert_eq "$text" "Open" -read -r -u 4 text -assert_eq "$text" "File" - -# Test for socket -systemctl start testsuite-77-server.socket -systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \ - --wait \ - --pipe \ - /usr/lib/systemd/tests/testdata/units/testsuite-77-client.sh - -# Tests for D-Bus -diff <(systemctl show -p OpenFile testsuite-77) - <<EOF -OpenFile=/test-77-open.dat:open:read-only -OpenFile=/test-77-file.dat -EOF -echo "New" >/test-77-new-file.dat -systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only "$(dirname "$0")"/testsuite-77-run.sh - -assert_rc 202 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only "$(dirname "$0")"/testsuite-77-run.sh - -assert_rc 0 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only,graceful "$(dirname "$0")"/testsuite-77-run.sh - -# End -touch /testok diff --git a/test/units/testsuite-78.service b/test/units/testsuite-78.service deleted file mode 100644 index 05f3eff..0000000 --- a/test/units/testsuite-78.service +++ /dev/null @@ -1,7 +0,0 @@ -# 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 diff --git a/test/units/testsuite-79.service b/test/units/testsuite-79.service deleted file mode 100644 index f2d24df..0000000 --- a/test/units/testsuite-79.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-80.service b/test/units/testsuite-80.service deleted file mode 100644 index 4c7f5d5..0000000 --- a/test/units/testsuite-80.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-81.service b/test/units/testsuite-81.service deleted file mode 100644 index 3b697b3..0000000 --- a/test/units/testsuite-81.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-83.service b/test/units/testsuite-83.service deleted file mode 100644 index 55ebb45..0000000 --- a/test/units/testsuite-83.service +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/test/units/testsuite-84.service b/test/units/testsuite-84.service deleted file mode 100644 index 2c25770..0000000 --- a/test/units/testsuite-84.service +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/test/units/unit-with-multiple-dashes.service b/test/units/unit-with-multiple-dashes.service index 4aca904..9ac3c8c 100644 --- a/test/units/unit-with-multiple-dashes.service +++ b/test/units/unit-with-multiple-dashes.service @@ -4,4 +4,4 @@ Description=A unit with multiple dashes Documentation=man:test [Service] -ExecStart=/bin/true +ExecStart=true diff --git a/test/units/util.sh b/test/units/util.sh index b5ed732..f5f6786 100755 --- a/test/units/util.sh +++ b/test/units/util.sh @@ -28,6 +28,15 @@ assert_eq() {( fi )} +assert_le() {( + set +ex + + if [[ "${1:?}" -gt "${2:?}" ]]; then + echo "FAIL: '$1' > '$2'" >&2 + exit 1 + fi +)} + assert_in() {( set +ex @@ -147,13 +156,13 @@ coverage_create_nspawn_dropin() { create_dummy_container() { local root="${1:?}" - if [[ ! -d /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 /testsuite-13-container-template/* "$root" + cp -a /usr/share/TEST-13-NSPAWN-container-template/* "$root" coverage_create_nspawn_dropin "$root" } @@ -216,3 +225,156 @@ kernel_supports_lsm() { return 1 } + +install_extension_images() { + local os_release + os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)" + + # Rolling distros like Arch do not set VERSION_ID + local version_id="" + if grep -q "^VERSION_ID=" "$os_release"; then + version_id="$(grep "^VERSION_ID=" "$os_release")" + fi + + local initdir="/var/tmp/app0" + mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" + grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0" + echo "$version_id" >>"$initdir/usr/lib/extension-release.d/extension-release.app0" + ( + echo "$version_id" + echo "SYSEXT_IMAGE_ID=app" + ) >>"$initdir/usr/lib/extension-release.d/extension-release.app0" + cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/opt/script0.sh +TemporaryFileSystem=/var/lib +StateDirectory=app0 +RuntimeDirectory=app0 +EOF + cat >"$initdir/opt/script0.sh" <<EOF +#!/bin/bash +set -e +test -e /usr/lib/os-release +echo bar >\${STATE_DIRECTORY}/foo +cat /usr/lib/extension-release.d/extension-release.app0 +EOF + chmod +x "$initdir/opt/script0.sh" + echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" + mksquashfs "$initdir" /tmp/app0.raw -noappend + + initdir="/var/tmp/conf0" + mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" "$initdir/opt" + grep "^ID=" "$os_release" >"$initdir/etc/extension-release.d/extension-release.conf0" + echo "$version_id" >>"$initdir/etc/extension-release.d/extension-release.conf0" + ( + echo "$version_id" + echo "CONFEXT_IMAGE_ID=app" + ) >>"$initdir/etc/extension-release.d/extension-release.conf0" + echo MARKER_1 >"$initdir/etc/systemd/system/some_file" + mksquashfs "$initdir" /tmp/conf0.raw -noappend + + initdir="/var/tmp/app1" + mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" + grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2" + ( + echo "$version_id" + echo "SYSEXT_SCOPE=portable" + echo "SYSEXT_IMAGE_ID=app" + echo "SYSEXT_IMAGE_VERSION=1" + echo "PORTABLE_PREFIXES=app1" + ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2" + setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2" + cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/opt/script1.sh +StateDirectory=app1 +RuntimeDirectory=app1 +EOF + cat >"$initdir/opt/script1.sh" <<EOF +#!/bin/bash +set -e +test -e /usr/lib/os-release +echo baz >\${STATE_DIRECTORY}/foo +cat /usr/lib/extension-release.d/extension-release.app2 +EOF + chmod +x "$initdir/opt/script1.sh" + echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file" + mksquashfs "$initdir" /tmp/app1.raw -noappend + + initdir="/var/tmp/app-nodistro" + mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" + ( + echo "ID=_any" + echo "ARCHITECTURE=_any" + ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" + echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" + mksquashfs "$initdir" /tmp/app-nodistro.raw -noappend + + initdir="/var/tmp/service-scoped-test" + mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" + ( + echo "ID=_any" + echo "ARCHITECTURE=_any" + ) >"$initdir/etc/extension-release.d/extension-release.service-scoped-test" + echo MARKER_CONFEXT_123 >"$initdir/etc/systemd/system/some_file" + mksquashfs "$initdir" /etc/service-scoped-test.raw -noappend + + # We need to create a dedicated sysext image to test the reload mechanism. If we share an image to install the + # 'foo.service' it will be loaded from another test run, which will impact the targeted test. + initdir="/var/tmp/app-reload" + mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" + ( + echo "ID=_any" + echo "ARCHITECTURE=_any" + echo "EXTENSION_RELOAD_MANAGER=1" + ) >"$initdir/usr/lib/extension-release.d/extension-release.app-reload" + mkdir -p "$initdir/usr/lib/systemd/system/multi-user.target.d" + cat >"$initdir/usr/lib/systemd/system/foo.service" <<EOF +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=echo foo + +[Install] +WantedBy=multi-user.target +EOF + echo -e "[Unit]\nUpholds=foo.service" >"$initdir/usr/lib/systemd/system/multi-user.target.d/10-foo-service.conf" + mksquashfs "$initdir" /tmp/app-reload.raw -noappend +} + +restore_locale() { + if [[ -d /usr/lib/locale/xx_XX.UTF-8 ]]; then + rmdir /usr/lib/locale/xx_XX.UTF-8 + fi + + if [[ -f /tmp/locale.conf.bak ]]; then + mv /tmp/locale.conf.bak /etc/locale.conf + else + rm -f /etc/locale.conf + fi + + if [[ -f /tmp/default-locale.bak ]]; then + mv /tmp/default-locale.bak /etc/default/locale + else + rm -rf /etc/default + fi + + if [[ -f /tmp/locale.gen.bak ]]; then + mv /tmp/locale.gen.bak /etc/locale.gen + else + rm -f /etc/locale.gen + fi +} + +generate_locale() { + local locale="${1:?}" + + if command -v locale-gen >/dev/null && ! localectl list-locales | grep -F "$locale"; then + echo "$locale UTF-8" >/etc/locale.gen + locale-gen "$locale" + fi +} |