diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:00:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:00:47 +0000 |
commit | 2cb7e0aaedad73b076ea18c6900b0e86c5760d79 (patch) | |
tree | da68ca54bb79f4080079bf0828acda937593a4e1 /test/units | |
parent | Initial commit. (diff) | |
download | systemd-2cb7e0aaedad73b076ea18c6900b0e86c5760d79.tar.xz systemd-2cb7e0aaedad73b076ea18c6900b0e86c5760d79.zip |
Adding upstream version 247.3.upstream/247.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/units')
173 files changed, 5384 insertions, 0 deletions
diff --git a/test/units/a-conj.service b/test/units/a-conj.service new file mode 100644 index 0000000..db37ae7 --- /dev/null +++ b/test/units/a-conj.service @@ -0,0 +1,8 @@ +[Unit] +Description=A conjugate +Requires=a.service +After=a.service +Before=a.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/a.service b/test/units/a.service new file mode 100644 index 0000000..4168d2d --- /dev/null +++ b/test/units/a.service @@ -0,0 +1,7 @@ +[Unit] +Description=A +Requires=b.service +Before=b.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/autorelabel.service b/test/units/autorelabel.service new file mode 100644 index 0000000..cb38849 --- /dev/null +++ b/test/units/autorelabel.service @@ -0,0 +1,18 @@ +[Unit] +Description=Relabel all filesystems +DefaultDependencies=no +Requires=local-fs.target +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target +ConditionSecurity=selinux +ConditionPathExists=|/.autorelabel + +[Service] +ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot' +Type=oneshot +TimeoutSec=0 +RemainAfterExit=yes + +[Install] +WantedBy=basic.target diff --git a/test/units/b.service b/test/units/b.service new file mode 100644 index 0000000..e03bae3 --- /dev/null +++ b/test/units/b.service @@ -0,0 +1,6 @@ +[Unit] +Description=B +Wants=f.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/basic.target b/test/units/basic.target new file mode 100644 index 0000000..d8cdd5a --- /dev/null +++ b/test/units/basic.target @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Basic System +Documentation=man:systemd.special(7) +Requires=sysinit.target +Wants=sockets.target timers.target paths.target slices.target +After=sysinit.target sockets.target paths.target slices.target tmp.mount + +# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in +# remote-fs.target by default, hence pull them in explicitly here. Note that we +# 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 diff --git a/test/units/c.service b/test/units/c.service new file mode 100644 index 0000000..e2f60a8 --- /dev/null +++ b/test/units/c.service @@ -0,0 +1,6 @@ +[Unit] +Description=C +Requires=a.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/d.service b/test/units/d.service new file mode 100644 index 0000000..921fd2e --- /dev/null +++ b/test/units/d.service @@ -0,0 +1,8 @@ +[Unit] +Description=D:Cyclic +After=b.service +Before=a.service +Requires=a.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/daughter.service b/test/units/daughter.service new file mode 100644 index 0000000..c790b9d --- /dev/null +++ b/test/units/daughter.service @@ -0,0 +1,8 @@ +[Unit] +Description=Daughter Service + +[Service] +Slice=parent.slice +Type=oneshot +ExecStart=/bin/true +CPUAccounting=true diff --git a/test/units/dml-discard-empty.service b/test/units/dml-discard-empty.service new file mode 100644 index 0000000..75228f6 --- /dev/null +++ b/test/units/dml-discard-empty.service @@ -0,0 +1,7 @@ +[Unit] +Description=DML discard empty service + +[Service] +Slice=dml-discard.slice +Type=oneshot +ExecStart=/bin/true diff --git a/test/units/dml-discard-set-ml.service b/test/units/dml-discard-set-ml.service new file mode 100644 index 0000000..591c992 --- /dev/null +++ b/test/units/dml-discard-set-ml.service @@ -0,0 +1,8 @@ +[Unit] +Description=DML discard set ml service + +[Service] +Slice=dml-discard.slice +Type=oneshot +ExecStart=/bin/true +MemoryLow=15 diff --git a/test/units/dml-discard.slice b/test/units/dml-discard.slice new file mode 100644 index 0000000..e26d868 --- /dev/null +++ b/test/units/dml-discard.slice @@ -0,0 +1,5 @@ +[Unit] +Description=DML discard slice + +[Slice] +DefaultMemoryLow= diff --git a/test/units/dml-override-empty.service b/test/units/dml-override-empty.service new file mode 100644 index 0000000..142c987 --- /dev/null +++ b/test/units/dml-override-empty.service @@ -0,0 +1,7 @@ +[Unit] +Description=DML override empty service + +[Service] +Slice=dml-override.slice +Type=oneshot +ExecStart=/bin/true diff --git a/test/units/dml-override.slice b/test/units/dml-override.slice new file mode 100644 index 0000000..feb6773 --- /dev/null +++ b/test/units/dml-override.slice @@ -0,0 +1,5 @@ +[Unit] +Description=DML override slice + +[Slice] +DefaultMemoryLow=10 diff --git a/test/units/dml-passthrough-empty.service b/test/units/dml-passthrough-empty.service new file mode 100644 index 0000000..34832de --- /dev/null +++ b/test/units/dml-passthrough-empty.service @@ -0,0 +1,7 @@ +[Unit] +Description=DML passthrough empty service + +[Service] +Slice=dml-passthrough.slice +Type=oneshot +ExecStart=/bin/true diff --git a/test/units/dml-passthrough-set-dml.service b/test/units/dml-passthrough-set-dml.service new file mode 100644 index 0000000..5bdf4ed --- /dev/null +++ b/test/units/dml-passthrough-set-dml.service @@ -0,0 +1,8 @@ +[Unit] +Description=DML passthrough set DML service + +[Service] +Slice=dml-passthrough.slice +Type=oneshot +ExecStart=/bin/true +DefaultMemoryLow=15 diff --git a/test/units/dml-passthrough-set-ml.service b/test/units/dml-passthrough-set-ml.service new file mode 100644 index 0000000..2e568b5 --- /dev/null +++ b/test/units/dml-passthrough-set-ml.service @@ -0,0 +1,8 @@ +[Unit] +Description=DML passthrough set ML service + +[Service] +Slice=dml-passthrough.slice +Type=oneshot +ExecStart=/bin/true +MemoryLow=0 diff --git a/test/units/dml-passthrough.slice b/test/units/dml-passthrough.slice new file mode 100644 index 0000000..1b1a848 --- /dev/null +++ b/test/units/dml-passthrough.slice @@ -0,0 +1,5 @@ +[Unit] +Description=DML passthrough slice + +[Slice] +MemoryLow=100 diff --git a/test/units/dml.slice b/test/units/dml.slice new file mode 100644 index 0000000..84e333e --- /dev/null +++ b/test/units/dml.slice @@ -0,0 +1,5 @@ +[Unit] +Description=DML slice + +[Slice] +DefaultMemoryLow=50 diff --git a/test/units/e.service b/test/units/e.service new file mode 100644 index 0000000..5ba98c7 --- /dev/null +++ b/test/units/e.service @@ -0,0 +1,8 @@ +[Unit] +Description=E:Cyclic +After=b.service +Before=a.service +Wants=a.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/end.service b/test/units/end.service new file mode 100644 index 0000000..e7ed75e --- /dev/null +++ b/test/units/end.service @@ -0,0 +1,10 @@ +[Unit] +Description=End the test +After=testsuite.target +OnFailure=poweroff.target +OnFailureJobMode=replace-irreversibly + +[Service] +Type=oneshot +ExecStart=/bin/sh -x -c 'systemctl poweroff --no-block' +TimeoutStartSec=5m diff --git a/test/units/f.service b/test/units/f.service new file mode 100644 index 0000000..7dde681 --- /dev/null +++ b/test/units/f.service @@ -0,0 +1,5 @@ +[Unit] +Description=F + +[Service] +ExecStart=/bin/true diff --git a/test/units/g.service b/test/units/g.service new file mode 100644 index 0000000..cbfa82a --- /dev/null +++ b/test/units/g.service @@ -0,0 +1,6 @@ +[Unit] +Description=G +Conflicts=e.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/grandchild.service b/test/units/grandchild.service new file mode 100644 index 0000000..ab64130 --- /dev/null +++ b/test/units/grandchild.service @@ -0,0 +1,7 @@ +[Unit] +Description=Grandchild Service + +[Service] +Slice=parent-deep.slice +Type=oneshot +ExecStart=/bin/true diff --git a/test/units/h.service b/test/units/h.service new file mode 100644 index 0000000..74a7751 --- /dev/null +++ b/test/units/h.service @@ -0,0 +1,6 @@ +[Unit] +Description=H +Wants=g.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/hello-after-sleep.target b/test/units/hello-after-sleep.target new file mode 100644 index 0000000..526fbd2 --- /dev/null +++ b/test/units/hello-after-sleep.target @@ -0,0 +1,5 @@ +[Unit] +Description=Sleep for a minute, then say hello. +Wants=sleep.service hello.service +After=sleep.service +Before=hello.service diff --git a/test/units/hello.service b/test/units/hello.service new file mode 100644 index 0000000..82907b6 --- /dev/null +++ b/test/units/hello.service @@ -0,0 +1,5 @@ +[Unit] +Description=Hello World + +[Service] +ExecStart=/bin/echo "Hello World" diff --git a/test/units/i.service b/test/units/i.service new file mode 100644 index 0000000..938ea77 --- /dev/null +++ b/test/units/i.service @@ -0,0 +1,8 @@ +[Unit] +Description=I +Conflicts=a.service d.service +Wants=b.service +After=b.service + +[Service] +ExecStart=/bin/true diff --git a/test/units/loopy.service b/test/units/loopy.service new file mode 100644 index 0000000..9eb6457 --- /dev/null +++ b/test/units/loopy.service @@ -0,0 +1,2 @@ +[Service] +ExecStart=/bin/true diff --git a/test/units/loopy.service.d/compat.conf b/test/units/loopy.service.d/compat.conf new file mode 100644 index 0000000..51b84b8 --- /dev/null +++ b/test/units/loopy.service.d/compat.conf @@ -0,0 +1,5 @@ +[Unit] +BindsTo=loopy2.service + +[Install] +Also=loopy2.service diff --git a/test/units/loopy2.service b/test/units/loopy2.service new file mode 100644 index 0000000..9eb6457 --- /dev/null +++ b/test/units/loopy2.service @@ -0,0 +1,2 @@ +[Service] +ExecStart=/bin/true diff --git a/test/units/loopy3.service b/test/units/loopy3.service new file mode 100644 index 0000000..606e26b --- /dev/null +++ b/test/units/loopy3.service @@ -0,0 +1,5 @@ +[Service] +ExecStart=/bin/true + +[Unit] +Conflicts=loopy4.service diff --git a/test/units/loopy4.service b/test/units/loopy4.service new file mode 100644 index 0000000..606e26b --- /dev/null +++ b/test/units/loopy4.service @@ -0,0 +1,5 @@ +[Service] +ExecStart=/bin/true + +[Unit] +Conflicts=loopy4.service diff --git a/test/units/nomem.slice b/test/units/nomem.slice new file mode 100644 index 0000000..9c5d208 --- /dev/null +++ b/test/units/nomem.slice @@ -0,0 +1,5 @@ +[Unit] +Description=Nomem Parent Slice + +[Slice] +DisableControllers=memory diff --git a/test/units/nomemleaf.service b/test/units/nomemleaf.service new file mode 100644 index 0000000..3cbaccb --- /dev/null +++ b/test/units/nomemleaf.service @@ -0,0 +1,9 @@ +[Unit] +Description=Nomem Leaf Service + +[Service] +Slice=nomem.slice +Type=oneshot +ExecStart=/bin/true +IOWeight=200 +MemoryAccounting=true diff --git a/test/units/parent-deep.slice b/test/units/parent-deep.slice new file mode 100644 index 0000000..79b302f --- /dev/null +++ b/test/units/parent-deep.slice @@ -0,0 +1,5 @@ +[Unit] +Description=Deeper Parent Slice + +[Slice] +MemoryLimit=3G diff --git a/test/units/parent.slice b/test/units/parent.slice new file mode 100644 index 0000000..a95f903 --- /dev/null +++ b/test/units/parent.slice @@ -0,0 +1,5 @@ +[Unit] +Description=Parent Slice + +[Slice] +IOWeight=200 diff --git a/test/units/sched_idle_bad.service b/test/units/sched_idle_bad.service new file mode 100644 index 0000000..589a87c --- /dev/null +++ b/test/units/sched_idle_bad.service @@ -0,0 +1,6 @@ +[Unit] +Description=Bad sched priority for Idle + +[Service] +ExecStart=/bin/true +CPUSchedulingPriority=1 diff --git a/test/units/sched_idle_ok.service b/test/units/sched_idle_ok.service new file mode 100644 index 0000000..262ef3e --- /dev/null +++ b/test/units/sched_idle_ok.service @@ -0,0 +1,6 @@ +[Unit] +Description=Sched idle with prio 0 + +[Service] +ExecStart=/bin/true +CPUSchedulingPriority=0 diff --git a/test/units/sched_rr_bad.service b/test/units/sched_rr_bad.service new file mode 100644 index 0000000..0be534a --- /dev/null +++ b/test/units/sched_rr_bad.service @@ -0,0 +1,8 @@ +[Unit] +Description=Bad sched priority for RR + +[Service] +ExecStart=/bin/true +CPUSchedulingPolicy=rr +CPUSchedulingPriority=0 +CPUSchedulingPriority=100 diff --git a/test/units/sched_rr_change.service b/test/units/sched_rr_change.service new file mode 100644 index 0000000..b3e3a00 --- /dev/null +++ b/test/units/sched_rr_change.service @@ -0,0 +1,9 @@ +[Unit] +Description=Change prio + +[Service] +ExecStart=/bin/true +CPUSchedulingPolicy=rr +CPUSchedulingPriority=1 +CPUSchedulingPriority=2 +CPUSchedulingPriority=99 diff --git a/test/units/sched_rr_ok.service b/test/units/sched_rr_ok.service new file mode 100644 index 0000000..b88adc5 --- /dev/null +++ b/test/units/sched_rr_ok.service @@ -0,0 +1,6 @@ +[Unit] +Description=Default prio for RR + +[Service] +ExecStart=/bin/true +CPUSchedulingPolicy=rr diff --git a/test/units/shutdown.target b/test/units/shutdown.target new file mode 100644 index 0000000..582ae6b --- /dev/null +++ b/test/units/shutdown.target @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Shutdown +Documentation=man:systemd.special(7) +DefaultDependencies=no +RefuseManualStart=yes diff --git a/test/units/sleep.service b/test/units/sleep.service new file mode 100644 index 0000000..946c44b --- /dev/null +++ b/test/units/sleep.service @@ -0,0 +1,6 @@ +[Unit] +Description=Sleep for 1 minute + +[Service] +Type=oneshot +ExecStart=/bin/sleep 60 diff --git a/test/units/sockets.target b/test/units/sockets.target new file mode 100644 index 0000000..c6e20d7 --- /dev/null +++ b/test/units/sockets.target @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Sockets +Documentation=man:systemd.special(7) diff --git a/test/units/son.service b/test/units/son.service new file mode 100644 index 0000000..50bb96a --- /dev/null +++ b/test/units/son.service @@ -0,0 +1,8 @@ +[Unit] +Description=Son Service + +[Service] +Slice=parent.slice +Type=oneshot +ExecStart=/bin/true +CPUShares=100 diff --git a/test/units/sysinit.target b/test/units/sysinit.target new file mode 100644 index 0000000..eed3d16 --- /dev/null +++ b/test/units/sysinit.target @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Initialization +Documentation=man:systemd.special(7) +Conflicts=emergency.service emergency.target +Wants=local-fs.target swap.target +After=local-fs.target swap.target emergency.service emergency.target diff --git a/test/units/test-honor-first-shutdown.service b/test/units/test-honor-first-shutdown.service new file mode 100644 index 0000000..3170f97 --- /dev/null +++ b/test/units/test-honor-first-shutdown.service @@ -0,0 +1,11 @@ +[Unit] +Description=Honor First Shutdown feature +After=multi-user.target + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStop=sh -c 'kill -KILL $MAINPID' +FailureAction=reboot + +[Install] +WantedBy=multi-user.target diff --git a/test/units/test-honor-first-shutdown.sh b/test/units/test-honor-first-shutdown.sh new file mode 100755 index 0000000..17c1ec9 --- /dev/null +++ b/test/units/test-honor-first-shutdown.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Honor first shutdown test script" +sleep infinity; diff --git a/test/units/testsuite-01.service b/test/units/testsuite-01.service new file mode 100644 index 0000000..85b9cf5 --- /dev/null +++ b/test/units/testsuite-01.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-01-BASIC +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -e -x -c 'systemctl --state=failed --no-legend --no-pager >/failed ; systemctl daemon-reload ; echo OK >/testok' +Type=oneshot diff --git a/test/units/testsuite-02.service b/test/units/testsuite-02.service new file mode 100644 index 0000000..075e979 --- /dev/null +++ b/test/units/testsuite-02.service @@ -0,0 +1,7 @@ +[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 new file mode 100755 index 0000000..1ff1c33 --- /dev/null +++ b/test/units/testsuite-02.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +#set -ex +#set -o pipefail + +NPROC=$(nproc) +MAX_QUEUE_SIZE=${NPROC:-2} +IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*)) + +# reset state +rm /failed-tests /skipped-tests /skipped + +# Check & report test results +# Arguments: +# $1: test path +# $2: test exit code +function 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 ]]; then + echo "$name failed with $ret" + echo "$name" >>/failed-tests + { + echo "--- $name begin ---" + cat "/$name.log" + echo "--- $name end ---" + } >>/failed + elif [[ $ret == 77 ]]; 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 + + systemd-cat echo "--- $name ---" + systemd-cat cat "/$name.log" +} + +# 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=$? + 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 + log_file="/${task##*/}.log" + $task &>"$log_file" & + running[$task]=$! + fi +done + +# Wait for remaining running tasks +for key in "${!running[@]}"; do + wait ${running[$key]} + ec=$? + report_result "$key" $ec + unset running["$key"] +done + +exit 0 diff --git a/test/units/testsuite-03.service b/test/units/testsuite-03.service new file mode 100644 index 0000000..fe18fdc --- /dev/null +++ b/test/units/testsuite-03.service @@ -0,0 +1,8 @@ +[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-03.sh b/test/units/testsuite-03.sh new file mode 100755 index 0000000..85efeeb --- /dev/null +++ b/test/units/testsuite-03.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +set -ex + +# Test merging of a --job-mode=ignore-dependencies job into a previously +# installed job. + +systemctl start --no-block hello-after-sleep.target + +systemctl list-jobs > /root/list-jobs.txt +while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do + systemctl list-jobs > /root/list-jobs.txt +done + +grep 'hello\.service.*waiting' /root/list-jobs.txt + +# This is supposed to finish quickly, not wait for sleep to finish. +START_SEC=$(date -u '+%s') +systemctl start --job-mode=ignore-dependencies hello +END_SEC=$(date -u '+%s') +ELAPSED=$(($END_SEC-$START_SEC)) + +[ "$ELAPSED" -lt 3 ] + +# sleep should still be running, hello not. +systemctl list-jobs > /root/list-jobs.txt +grep 'sleep\.service.*running' /root/list-jobs.txt +grep 'hello\.service' /root/list-jobs.txt && exit 1 +systemctl stop sleep.service hello-after-sleep.target + +# Some basic testing that --show-transaction does something useful +! systemctl is-active systemd-importd +systemctl -T start systemd-importd +systemctl is-active systemd-importd +systemctl --show-transaction stop systemd-importd +! systemctl is-active systemd-importd + +# Test for a crash when enqueuing a JOB_NOP when other job already exists +systemctl start --no-block hello-after-sleep.target +# hello.service should still be waiting, so these try-restarts will collapse +# into NOPs. +systemctl try-restart --job-mode=fail hello.service +systemctl try-restart hello.service +systemctl stop hello.service sleep.service hello-after-sleep.target + +# TODO: add more job queueing/merging tests here. + +# Test for irreversible jobs +systemctl start unstoppable.service + +# This is expected to fail with 'job cancelled' +systemctl stop unstoppable.service && exit 1 +# But this should succeed +systemctl stop --job-mode=replace-irreversibly unstoppable.service + +# We're going to shutdown soon. Let's see if it succeeds when +# there's an active service that tries to be unstoppable. +# Shutdown of the container/VM will hang if not. +systemctl start unstoppable.service + +# Test waiting for a started unit(s) to terminate again +cat <<EOF > /run/systemd/system/wait2.service +[Unit] +Description=Wait for 2 seconds +[Service] +ExecStart=/bin/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' +EOF + +# wait2 succeeds +START_SEC=$(date -u '+%s') +systemctl start --wait wait2.service +END_SEC=$(date -u '+%s') +ELAPSED=$(($END_SEC-$START_SEC)) +[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1 + +# wait5fail fails, so systemctl should fail +START_SEC=$(date -u '+%s') +! systemctl start --wait wait2.service wait5fail.service || exit 1 +END_SEC=$(date -u '+%s') +ELAPSED=$(($END_SEC-$START_SEC)) +[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1 + +# Test time-limited scopes +START_SEC=$(date -u '+%s') +set +e +systemd-run --scope --property=RuntimeMaxSec=3s sleep 10 +RESULT=$? +END_SEC=$(date -u '+%s') +ELAPSED=$(($END_SEC-$START_SEC)) +[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1 +[[ "$RESULT" -ne 0 ]] || exit 1 + +touch /testok diff --git a/test/units/testsuite-04.service b/test/units/testsuite-04.service new file mode 100644 index 0000000..3d2b4a8 --- /dev/null +++ b/test/units/testsuite-04.service @@ -0,0 +1,7 @@ +[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-04.sh b/test/units/testsuite-04.sh new file mode 100755 index 0000000..3dce73b --- /dev/null +++ b/test/units/testsuite-04.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -x +set -e +set -o pipefail + +# Test stdout stream + +# Skip empty lines +ID=$(journalctl --new-id128 | sed -n 2p) +>/expected +printf $'\n\n\n' | systemd-cat -t "$ID" --level-prefix false +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output + +ID=$(journalctl --new-id128 | sed -n 2p) +>/expected +printf $'<5>\n<6>\n<7>\n' | systemd-cat -t "$ID" --level-prefix true +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output + +# Remove trailing spaces +ID=$(journalctl --new-id128 | sed -n 2p) +printf "Trailing spaces\n">/expected +printf $'<5>Trailing spaces \t \n' | systemd-cat -t "$ID" --level-prefix true +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output + +ID=$(journalctl --new-id128 | sed -n 2p) +printf "Trailing spaces\n">/expected +printf $'Trailing spaces \t \n' | systemd-cat -t "$ID" --level-prefix false +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output + +# Don't remove leading spaces +ID=$(journalctl --new-id128 | sed -n 2p) +printf $' \t Leading spaces\n'>/expected +printf $'<5> \t Leading spaces\n' | systemd-cat -t "$ID" --level-prefix true +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output + +ID=$(journalctl --new-id128 | sed -n 2p) +printf $' \t Leading spaces\n'>/expected +printf $' \t Leading spaces\n' | systemd-cat -t "$ID" --level-prefix false +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output + +# --output-fields restricts output +ID=$(journalctl --new-id128 | sed -n 2p) +printf $'foo' | systemd-cat -t "$ID" --level-prefix false +journalctl --sync +journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/output +[[ `grep -c . /output` -eq 6 ]] +grep -q '^__CURSOR=' /output +grep -q '^MESSAGE=foo$' /output +grep -q '^PRIORITY=6$' /output +! grep -q '^FOO=' /output +! grep -q '^SYSLOG_FACILITY=' /output + +# `-b all` negates earlier use of -b (-b and -m are otherwise exclusive) +journalctl -b -1 -b all -m > /dev/null + +# -b always behaves like -b0 +journalctl -q -b-1 -b0 | head -1 > /expected +journalctl -q -b-1 -b | head -1 > /output +cmp /expected /output +# ... even when another option follows (both of these should fail due to -m) +{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 > /expected +{ journalctl -ball -b -m 2>&1 || :; } | head -1 > /output +cmp /expected /output + +# https://github.com/systemd/systemd/issues/13708 +ID=$(systemd-id128 new) +systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' & +PID=$! +wait %% +journalctl --sync +# We can drop this grep when https://github.com/systemd/systemd/issues/13937 +# has a fix. +journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/output +[[ `grep -c . /output` -eq 2 ]] +grep -q "^_PID=$PID" /output +grep -vq "^_PID=$PID" /output + +# https://github.com/systemd/systemd/issues/15654 +ID=$(journalctl --new-id128 | sed -n 2p) +printf "This will\nusually fail\nand be truncated\n">/expected +systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;' +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]] + +# Add new tests before here, the journald restarts below +# may make tests flappy. + +# Don't lose streams on restart +systemctl start forever-print-hola +sleep 3 +systemctl restart systemd-journald +sleep 3 +systemctl stop forever-print-hola +[[ ! -f "/i-lose-my-logs" ]] + +# https://github.com/systemd/systemd/issues/4408 +rm -f /i-lose-my-logs +systemctl start forever-print-hola +sleep 3 +systemctl kill --signal=SIGKILL systemd-journald +sleep 3 +[[ ! -f "/i-lose-my-logs" ]] + +# https://github.com/systemd/systemd/issues/15528 +journalctl --follow --file=/var/log/journal/*/* | head -n1 || [[ $? -eq 1 ]] + +touch /testok diff --git a/test/units/testsuite-05.service b/test/units/testsuite-05.service new file mode 100644 index 0000000..66356fd --- /dev/null +++ b/test/units/testsuite-05.service @@ -0,0 +1,7 @@ +[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-05.sh b/test/units/testsuite-05.sh new file mode 100755 index 0000000..9168e72 --- /dev/null +++ b/test/units/testsuite-05.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -x +set -e +set -o pipefail + +P=/run/systemd/system.conf.d +mkdir $P + +cat >$P/rlimits.conf <<EOF +[Manager] +DefaultLimitNOFILE=10000:16384 +EOF + +systemctl daemon-reload + +[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]] +[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]] + +[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]] +[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]] + +systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]' +systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]' + +touch /testok diff --git a/test/units/testsuite-06.service b/test/units/testsuite-06.service new file mode 100644 index 0000000..3f8dad3 --- /dev/null +++ b/test/units/testsuite-06.service @@ -0,0 +1,10 @@ +[Unit] +Description=TEST-06-SELINUX + +Requires=load-systemd-test-module.service +After=load-systemd-test-module.service + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-06.sh b/test/units/testsuite-06.sh new file mode 100755 index 0000000..f9b106d --- /dev/null +++ b/test/units/testsuite-06.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -x +set -e +set -o pipefail + +echo 1 >/sys/fs/selinux/enforce || { + echo "Can't make selinux enforcing, skipping test" + touch /testok + exit +} + +runcon -t systemd_test_start_t systemctl start hola +runcon -t systemd_test_reload_t systemctl reload hola +runcon -t systemd_test_stop_t systemctl stop hola + +touch /testok diff --git a/test/units/testsuite-07.service b/test/units/testsuite-07.service new file mode 100644 index 0000000..2506c21 --- /dev/null +++ b/test/units/testsuite-07.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-07-ISSUE-1981 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh new file mode 100755 index 0000000..fbb2d1d --- /dev/null +++ b/test/units/testsuite-07.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -x +set -e + +>/failed + +cat <<'EOL' >/lib/systemd/system/my.service +[Service] +Type=oneshot +ExecStart=/bin/echo Timer runs me +EOL + +cat <<'EOL' >/lib/systemd/system/my.timer +[Timer] +OnBootSec=10s +OnUnitInactiveSec=1h +EOL + +systemctl unmask my.timer + +systemctl start my.timer + +mkdir -p /etc/systemd/system/my.timer.d/ +cat <<'EOL' >/etc/systemd/system/my.timer.d/override.conf +[Timer] +OnBootSec=10s +OnUnitInactiveSec=1h +EOL + +systemctl daemon-reload + +systemctl mask my.timer + +touch /testok +rm /failed diff --git a/test/units/testsuite-08.service b/test/units/testsuite-08.service new file mode 100644 index 0000000..d961dc7 --- /dev/null +++ b/test/units/testsuite-08.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-08-ISSUE-2730 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -x -c 'mount -o remount,rw /dev/sda1 && echo OK >/testok; systemctl poweroff' +Type=oneshot diff --git a/test/units/testsuite-09.service b/test/units/testsuite-09.service new file mode 100644 index 0000000..fc59e80 --- /dev/null +++ b/test/units/testsuite-09.service @@ -0,0 +1,10 @@ +[Unit] +Description=TEST-09-ISSUE-2691 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -c '>/testok' +ExecStop=sh -c 'kill -SEGV $$$$' +Type=oneshot +RemainAfterExit=yes +TimeoutStopSec=270s diff --git a/test/units/testsuite-10.service b/test/units/testsuite-10.service new file mode 100644 index 0000000..24f0da3 --- /dev/null +++ b/test/units/testsuite-10.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-10-ISSUE-2467 + +[Service] +ExecStartPre=rm -f /failed /testok +Type=oneshot +ExecStart=sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test10.socket; printf x >test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok' diff --git a/test/units/testsuite-11.service b/test/units/testsuite-11.service new file mode 100644 index 0000000..1544fd6 --- /dev/null +++ b/test/units/testsuite-11.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-11-ISSUE-3166 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-11.sh b/test/units/testsuite-11.sh new file mode 100755 index 0000000..708c7ce --- /dev/null +++ b/test/units/testsuite-11.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -x + +systemctl start fail-on-restart.service +active_state=$(systemctl show --value --property ActiveState fail-on-restart.service) +while [[ "$active_state" == "activating" || "$active_state" == "active" ]]; do + sleep 1 + active_state=$(systemctl show --value --property ActiveState fail-on-restart.service) +done +systemctl is-failed fail-on-restart.service || exit 1 +touch /testok diff --git a/test/units/testsuite-12.service b/test/units/testsuite-12.service new file mode 100644 index 0000000..72894ef --- /dev/null +++ b/test/units/testsuite-12.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-12-ISSUE-3171 +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-12.sh b/test/units/testsuite-12.sh new file mode 100755 index 0000000..b5888a2 --- /dev/null +++ b/test/units/testsuite-12.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -x +set -e +set -o pipefail + +U=/run/systemd/system/test12.socket +cat <<'EOF' >$U +[Unit] +Description=Test 12 socket +[Socket] +Accept=yes +ListenStream=/run/test12.socket +SocketGroup=adm +SocketMode=0660 +EOF + +cat <<'EOF' > /run/systemd/system/test12@.service +[Unit] +Description=Test service +[Service] +StandardInput=socket +ExecStart=/bin/sh -x -c cat +EOF + +systemctl start test12.socket +systemctl is-active test12.socket +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] +echo A | nc -w1 -U /run/test12.socket + +mv $U ${U}.disabled +systemctl daemon-reload +systemctl is-active test12.socket +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] +echo B | nc -w1 -U /run/test12.socket && exit 1 + +mv ${U}.disabled $U +systemctl daemon-reload +systemctl is-active test12.socket +echo C | nc -w1 -U /run/test12.socket && exit 1 +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] + +systemctl restart test12.socket +systemctl is-active test12.socket +echo D | nc -w1 -U /run/test12.socket +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] + +touch /testok diff --git a/test/units/testsuite-13.service b/test/units/testsuite-13.service new file mode 100644 index 0000000..5086793 --- /dev/null +++ b/test/units/testsuite-13.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-13-NSPAWN-SMOKE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh new file mode 100755 index 0000000..969ca4a --- /dev/null +++ b/test/units/testsuite-13.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +set -x +set -e +set -u +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +# check cgroup-v2 +is_v2_supported=no +mkdir -p /tmp/cgroup2 +if mount -t cgroup2 cgroup2 /tmp/cgroup2; then + is_v2_supported=yes + umount /tmp/cgroup2 +fi +rmdir /tmp/cgroup2 + +# check cgroup namespaces +is_cgns_supported=no +if [[ -f /proc/1/ns/cgroup ]]; then + is_cgns_supported=yes +fi + +is_user_ns_supported=no +# On some systems (e.g. CentOS 7) the default limit for user namespaces +# is set to 0, which causes the following unshare syscall to fail, even +# with enabled user namespaces support. By setting this value explicitly +# we can ensure the user namespaces support to be detected correctly. +sysctl -w user.max_user_namespaces=10000 +if unshare -U sh -c :; then + is_user_ns_supported=yes +fi + +SUSE_OPTS="" +ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' /etc/os-release) +if [[ "$ID_LIKE" = *"suse"* ]]; then + SUSE_OPTS="--bind /lib64 --bind /usr/lib64 " +fi + +function check_bind_tmp_path { + # https://github.com/systemd/systemd/issues/4789 + local _root="/var/lib/machines/testsuite-13.bind-tmp-path" + rm -rf "$_root" + /usr/lib/systemd/tests/testdata/create-busybox-container "$_root" + >/tmp/bind + systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind' +} + +function check_norbind { + # https://github.com/systemd/systemd/issues/13170 + local _root="/var/lib/machines/testsuite-13.norbind-path" + rm -rf "$_root" + mkdir -p /tmp/binddir/subdir + echo -n "outer" > /tmp/binddir/subdir/file + mount -t tmpfs tmpfs /tmp/binddir/subdir + echo -n "inner" > /tmp/binddir/subdir/file + /usr/lib/systemd/tests/testdata/create-busybox-container "$_root" + systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi' +} + +function check_notification_socket { + # https://github.com/systemd/systemd/issues/4944 + local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify' + # /testsuite-13.nc-container is prepared by test.sh + systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd" + systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd" +} + +function check_os_release { + local _cmd='. /tmp/os-release +if [ -n "${ID:+set}" ] && [ "${ID}" != "${container_host_id}" ]; then exit 1; fi +if [ -n "${VERSION_ID:+set}" ] && [ "${VERSION_ID}" != "${container_host_version_id}" ]; then exit 1; fi +if [ -n "${BUILD_ID:+set}" ] && [ "${BUILD_ID}" != "${container_host_build_id}" ]; then exit 1; fi +if [ -n "${VARIANT_ID:+set}" ] && [ "${VARIANT_ID}" != "${container_host_variant_id}" ]; then exit 1; fi +cd /tmp; (cd /run/host; md5sum os-release) | md5sum -c +if echo test >> /run/host/os-release; then exit 1; fi +' + + local _os_release_source="/etc/os-release" + if [ ! -r "${_os_release_source}" ]; then + _os_release_source="/usr/lib/os-release" + elif [ -L "${_os_release_source}" ] && rm /etc/os-release; then + # Ensure that /etc always wins if available + cp /usr/lib/os-release /etc + echo MARKER=1 >> /etc/os-release + fi + + systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd" + + if grep -q MARKER /etc/os-release; then + rm /etc/os-release + ln -s ../usr/lib/os-release /etc/os-release + fi +} + +function run { + if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then + printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2 + return 0 + fi + if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]]; then + printf "CGroup namespaces are not supported. Skipping.\n" >&2 + return 0 + fi + + local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3" + rm -rf "$_root" + /usr/lib/systemd/tests/testdata/create-busybox-container "$_root" + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" -b + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --private-network -b + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" -U -b; then + [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1 + else + [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --private-network -U -b; then + [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1 + else + [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1 + fi + + local _netns_opt="--network-namespace-path=/proc/self/ns/net" + + # --network-namespace-path and network-related options cannot be used together + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-veth -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then + return 1 + fi + + # allow combination of --network-namespace-path and --private-network + if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --private-network -b; then + return 1 + fi + + # test --network-namespace-path works with a network namespace created by "ip netns" + ip netns add nspawn_test + _netns_opt="--network-namespace-path=/run/netns/nspawn_test" + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP' + local r=$? + ip netns del nspawn_test + + if [ $r -ne 0 ]; then + return 1 + fi + + return 0 +} + +check_bind_tmp_path + +check_norbind + +check_notification_socket + +check_os_release + +for api_vfs_writable in yes no network; do + run no no $api_vfs_writable + run yes no $api_vfs_writable + run no yes $api_vfs_writable + run yes yes $api_vfs_writable +done + +touch /testok diff --git a/test/units/testsuite-14.service b/test/units/testsuite-14.service new file mode 100644 index 0000000..1606c68 --- /dev/null +++ b/test/units/testsuite-14.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-14-MACHINE-ID + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStart=/bin/sh -e -x -c 'systemctl --state=failed --no-legend --no-pager >/failed ; echo OK >/testok' +Type=oneshot diff --git a/test/units/testsuite-14.sh b/test/units/testsuite-14.sh new file mode 100755 index 0000000..95ac9b6 --- /dev/null +++ b/test/units/testsuite-14.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -e +set -x + +function setup_root { + local _root="$1" + mkdir -p "$_root" + mount -t tmpfs tmpfs "$_root" + mkdir -p "$_root/etc" "$_root/run" +} + +function check { + printf "Expected\n" + cat "$1" + printf "\nGot\n" + cat "$2" + cmp "$1" "$2" +} + +r="$(pwd)/overwrite-broken-machine-id" +setup_root "$r" +systemd-machine-id-setup --print --root "$r" +echo abc >>"$r/etc/machine-id" +id=$(systemd-machine-id-setup --print --root "$r") +echo $id >expected +check expected "$r/etc/machine-id" + +r="$(pwd)/transient-machine-id" +setup_root "$r" +systemd-machine-id-setup --print --root "$r" +echo abc >>"$r/etc/machine-id" +mount -o remount,ro "$r" +mount -t tmpfs tmpfs "$r/run" +transient_id=$(systemd-machine-id-setup --print --root "$r") +mount -o remount,rw "$r" +commited_id=$(systemd-machine-id-setup --print --commit --root "$r") +[[ "$transient_id" = "$commited_id" ]] +check "$r/etc/machine-id" "$r/run/machine-id" diff --git a/test/units/testsuite-15.service b/test/units/testsuite-15.service new file mode 100644 index 0000000..09571ed --- /dev/null +++ b/test/units/testsuite-15.service @@ -0,0 +1,7 @@ +[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-15.sh b/test/units/testsuite-15.sh new file mode 100755 index 0000000..b872a24 --- /dev/null +++ b/test/units/testsuite-15.sh @@ -0,0 +1,474 @@ +#! /bin/bash +set -e +set -x + +_clear_service () { + systemctl stop $1.service 2>/dev/null || : + rm -f /{etc,run,usr/lib}/systemd/system/$1.service + rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.d + rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.{wants,requires} + if [[ $1 == *@ ]]; then + systemctl stop $1*.service 2>/dev/null || : + rm -f /{etc,run,usr/lib}/systemd/system/$1*.service + rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.d + rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.{wants,requires} + fi +} + +clear_services () { + for u in $*; do + _clear_service $u + done + systemctl daemon-reload +} + +create_service () { + clear_services $1 + + cat >/etc/systemd/system/$1.service<<EOF +[Unit] +Description=$1 unit + +[Service] +ExecStart=/bin/sleep 100000 +EOF + mkdir -p /{etc,run,usr/lib}/systemd/system/$1.service.d + mkdir -p /etc/systemd/system/$1.service.{wants,requires} + mkdir -p /run/systemd/system/$1.service.{wants,requires} + mkdir -p /usr/lib/systemd/system/$1.service.{wants,requires} +} + +create_services () { + for u in $*; do + create_service $u + done +} + +check_ok () { + [ $# -eq 3 ] || return + + x="$(systemctl show --value -p $2 $1)" + case "$x" in + *$3*) return 0 ;; + *) return 1 ;; + esac +} + +check_ko () { + ! check_ok "$@" +} + +test_basic_dropins () { + echo "Testing basic dropins..." + + echo "*** test a wants b wants c" + create_services test15-a test15-b test15-c + ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/ + ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/ + check_ok test15-a Wants test15-b.service + check_ok test15-b Wants test15-c.service + + echo "*** test a wants,requires b" + create_services test15-a test15-b test15-c + ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/ + ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/ + check_ok test15-a Wants test15-b.service + check_ok test15-a Requires test15-b.service + + echo "*** test a wants nonexistent" + create_service test15-a + ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/ + check_ok test15-a Wants nonexistent.service + systemctl start test15-a + systemctl stop test15-a + + echo "*** test a requires nonexistent" + ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/ + systemctl daemon-reload + check_ok test15-a Requires nonexistent.service + + # 'b' is already loaded when 'c' pulls it in via a dropin. + echo "*** test a,c require b" + create_services test15-a test15-b test15-c + ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/ + ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/ + systemctl start test15-a + check_ok test15-c Requires test15-b.service + systemctl stop test15-a test15-b + + # 'b' is already loaded when 'c' pulls it in via an alias dropin. + echo "*** test a wants alias" + create_services test15-a test15-b test15-c + ln -sf test15-c.service /etc/systemd/system/test15-c1.service + ln -sf ../test15-c.service /etc/systemd/system/test15-a.service.wants/ + ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/ + systemctl start test15-a + check_ok test15-a Wants test15-c.service + check_ok test15-b Wants test15-c.service + systemctl stop test15-a test15-c + + echo "*** test service.d/ top level drop-in" + create_services test15-a test15-b + check_ko test15-a ExecCondition "/bin/echo a" + check_ko test15-b ExecCondition "/bin/echo b" + mkdir -p /usr/lib/systemd/system/service.d + cat >/usr/lib/systemd/system/service.d/override.conf <<EOF +[Service] +ExecCondition=/bin/echo %n +EOF + systemctl daemon-reload + check_ok test15-a ExecCondition "/bin/echo test15-a" + check_ok test15-b ExecCondition "/bin/echo test15-b" + rm -rf /usr/lib/systemd/system/service.d + + clear_services test15-a test15-b test15-c +} + +test_hierarchical_dropins () { + echo "Testing hierarchical dropins..." + echo "*** test service.d/ top level drop-in" + create_services a-b-c + check_ko a-b-c ExecCondition "/bin/echo service.d" + check_ko a-b-c ExecCondition "/bin/echo a-.service.d" + check_ko a-b-c ExecCondition "/bin/echo a-b-.service.d" + check_ko a-b-c ExecCondition "/bin/echo a-b-c.service.d" + + for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do + mkdir -p /usr/lib/systemd/system/$dropin + echo " +[Service] +ExecCondition=/bin/echo $dropin + " > /usr/lib/systemd/system/$dropin/override.conf + systemctl daemon-reload + check_ok a-b-c ExecCondition "/bin/echo $dropin" + done + for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do + rm -rf /usr/lib/systemd/system/$dropin + done + + clear_services a-b-c +} + +test_template_dropins () { + echo "Testing template dropins..." + + create_services foo bar@ yup@ + + # Declare some deps to check if the body was loaded + cat >>/etc/systemd/system/bar@.service <<EOF +[Unit] +After=bar-template-after.device +EOF + + cat >>/etc/systemd/system/yup@.service <<EOF +[Unit] +After=yup-template-after.device +EOF + + ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service + check_ok foo Wants bar@1.service + + echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***" + ln -s bar@.service /etc/systemd/system/bar-alias@.service + ln -s bar@1.service /etc/systemd/system/bar-alias@1.service + ln -s yup@.service /etc/systemd/system/bar-alias@2.service + ln -s yup@3.service /etc/systemd/system/bar-alias@3.service + + # create some dropin deps + mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/ + mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/ + mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/ + + ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/ + ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/ + ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/ + ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/ + ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/ + + ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/ + ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/ + ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/ + ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/ + ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/ + + ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/ + ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/ + ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/ + ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/ + ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/ + + systemctl daemon-reload + + echo '*** bar@0 is aliased by bar-alias@0 ***' + systemctl show -p Names,Requires bar@0 + systemctl show -p Names,Requires bar-alias@0 + check_ok bar@0 Names bar@0 + check_ok bar@0 Names bar-alias@0 + + check_ok bar@0 After bar-template-after.device + + check_ok bar@0 Requires bar-0-requires.device + check_ok bar@0 Requires bar-alias-0-requires.device + check_ok bar@0 Requires bar-template-requires.device + check_ok bar@0 Requires bar-alias-template-requires.device + check_ko bar@0 Requires yup-template-requires.device + + check_ok bar-alias@0 After bar-template-after.device + + check_ok bar-alias@0 Requires bar-0-requires.device + check_ok bar-alias@0 Requires bar-alias-0-requires.device + check_ok bar-alias@0 Requires bar-template-requires.device + check_ok bar-alias@0 Requires bar-alias-template-requires.device + check_ko bar-alias@0 Requires yup-template-requires.device + check_ko bar-alias@0 Requires yup-0-requires.device + + echo '*** bar@1 is aliased by bar-alias@1 ***' + systemctl show -p Names,Requires bar@1 + systemctl show -p Names,Requires bar-alias@1 + check_ok bar@1 Names bar@1 + check_ok bar@1 Names bar-alias@1 + + check_ok bar@1 After bar-template-after.device + + check_ok bar@1 Requires bar-1-requires.device + check_ok bar@1 Requires bar-alias-1-requires.device + check_ok bar@1 Requires bar-template-requires.device + # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418 + check_ok bar@1 Requires bar-alias-template-requires.device + check_ko bar@1 Requires yup-template-requires.device + check_ko bar@1 Requires yup-1-requires.device + + check_ok bar-alias@1 After bar-template-after.device + + check_ok bar-alias@1 Requires bar-1-requires.device + check_ok bar-alias@1 Requires bar-alias-1-requires.device + check_ok bar-alias@1 Requires bar-template-requires.device + check_ok bar-alias@1 Requires bar-alias-template-requires.device + check_ko bar-alias@1 Requires yup-template-requires.device + check_ko bar-alias@1 Requires yup-1-requires.device + + echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***' + systemctl show -p Names,Requires bar@2 + systemctl show -p Names,Requires bar-alias@2 + check_ok bar@2 Names bar@2 + check_ko bar@2 Names bar-alias@2 + + check_ok bar@2 After bar-template-after.device + + check_ok bar@2 Requires bar-2-requires.device + check_ko bar@2 Requires bar-alias-2-requires.device + check_ok bar@2 Requires bar-template-requires.device + check_ko bar@2 Requires bar-alias-template-requires.device + check_ko bar@2 Requires yup-template-requires.device + check_ko bar@2 Requires yup-2-requires.device + + check_ko bar-alias@2 After bar-template-after.device + + check_ko bar-alias@2 Requires bar-2-requires.device + check_ok bar-alias@2 Requires bar-alias-2-requires.device + check_ko bar-alias@2 Requires bar-template-requires.device + check_ok bar-alias@2 Requires bar-alias-template-requires.device + check_ok bar-alias@2 Requires yup-template-requires.device + check_ok bar-alias@2 Requires yup-2-requires.device + + echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***' + systemctl show -p Names,Requires bar@3 + systemctl show -p Names,Requires bar-alias@3 + check_ok bar@3 Names bar@3 + check_ko bar@3 Names bar-alias@3 + + check_ok bar@3 After bar-template-after.device + + check_ok bar@3 Requires bar-3-requires.device + check_ko bar@3 Requires bar-alias-3-requires.device + check_ok bar@3 Requires bar-template-requires.device + check_ko bar@3 Requires bar-alias-template-requires.device + check_ko bar@3 Requires yup-template-requires.device + check_ko bar@3 Requires yup-3-requires.device + + check_ko bar-alias@3 After bar-template-after.device + + check_ko bar-alias@3 Requires bar-3-requires.device + check_ok bar-alias@3 Requires bar-alias-3-requires.device + check_ko bar-alias@3 Requires bar-template-requires.device + check_ok bar-alias@3 Requires bar-alias-template-requires.device + check_ok bar-alias@3 Requires yup-template-requires.device + check_ok bar-alias@3 Requires yup-3-requires.device + + clear_services foo {bar,yup,bar-alias}@{,1,2,3} +} + +test_alias_dropins () { + echo "Testing alias dropins..." + + echo "*** test a wants b1 alias of b" + create_services test15-a test15-b + ln -sf test15-b.service /etc/systemd/system/test15-b1.service + ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/ + check_ok test15-a Wants test15-b.service + systemctl start test15-a + systemctl --quiet is-active test15-b + systemctl stop test15-a test15-b + rm /etc/systemd/system/test15-b1.service + clear_services test15-a test15-b + + # Check that dependencies don't vary. + echo "*** test 2" + create_services test15-a test15-x test15-y + mkdir -p /etc/systemd/system/test15-a1.service.wants/ + ln -sf test15-a.service /etc/systemd/system/test15-a1.service + ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/ + ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/ + check_ok test15-a1 Wants test15-x.service # see [1] + check_ok test15-a1 Wants test15-y.service + systemctl start test15-a + check_ok test15-a1 Wants test15-x.service # see [2] + check_ok test15-a1 Wants test15-y.service + systemctl stop test15-a test15-x test15-y + rm /etc/systemd/system/test15-a1.service + + clear_services test15-a test15-x test15-y +} + +test_masked_dropins () { + echo "Testing masked dropins..." + + create_services test15-a test15-b + + # 'b' is masked for both deps + echo "*** test a wants,requires b is masked" + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service + check_ko test15-a Wants test15-b.service + check_ko test15-a Requires test15-b.service + + # 'a' wants 'b' and 'b' is masked at a lower level + echo "*** test a wants b, mask override" + ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service + check_ok test15-a Wants test15-b.service + + # 'a' wants 'b' and 'b' is masked at a higher level + echo "*** test a wants b, mask" + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service + check_ko test15-a Wants test15-b.service + + # 'a' is masked but has an override config file + echo "*** test a is masked but has an override" + create_services test15-a test15-b + ln -sf /dev/null /etc/systemd/system/test15-a.service + cat >/usr/lib/systemd/system/test15-a.service.d/override.conf <<EOF +[Unit] +After=test15-b.service +EOF + check_ok test15-a UnitFileState masked + + # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep + echo "*** test a wants b, b1, and one is masked" + create_services test15-a test15-b + ln -sf test15-b.service /etc/systemd/system/test15-b1.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf ../test15-b1.service /usr/lib/systemd/system/test15-a.service.wants/test15-b1.service + systemctl cat test15-a + systemctl show -p Wants,Requires test15-a + systemctl cat test15-b1 + systemctl show -p Wants,Requires test15-b1 + check_ok test15-a Wants test15-b.service + check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units + rm /etc/systemd/system/test15-b1.service + + # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep + echo "*** test a wants b, alias dep is masked" + create_services test15-a test15-b + ln -sf test15-b.service /etc/systemd/system/test15-b1.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b1.service + ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service + check_ok test15-a Wants test15-b.service + check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units + rm /etc/systemd/system/test15-b1.service + + # 'a' has Wants=b.service but also has a masking + # dropin 'b': 'b' should still be pulled in. + echo "*** test a wants b both ways" + create_services test15-a test15-b + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service + cat >/usr/lib/systemd/system/test15-a.service.d/wants-b.conf<<EOF +[Unit] +Wants=test15-b.service +EOF + check_ok test15-a Wants test15-b.service + + # mask a dropin that points to an nonexistent unit. + echo "*** test a wants nonexistent is masked" + create_services test15-a + ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/nonexistent.service + ln -sf ../nonexistent.service /usr/lib/systemd/system/test15-a.service.requires/ + check_ko test15-a Requires nonexistent.service + + # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is + # masked at a higher level. + echo "*** test a wants b is masked" + create_services test15-a test15-b test15-c + ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/ + ln -sf ../test15-b.service /run/systemd/system/test15-c.service.requires/ + ln -sf /dev/null /etc/systemd/system/test15-c.service.requires/test15-b.service + systemctl start test15-a + check_ko test15-c Requires test15-b.service + systemctl stop test15-a test15-b + + # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is + # masked at a lower level. + echo "*** test a requires b is masked" + create_services test15-a test15-b test15-c + ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/ + ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/ + ln -sf /dev/null /run/systemd/system/test15-c.service.requires/test15-b.service + systemctl start test15-a + check_ok test15-c Requires test15-b.service + systemctl stop test15-a test15-b + + # 'a' requires 2 aliases of 'b' and one of them is a mask. + echo "*** test a requires alias of b, other alias masked" + create_services test15-a test15-b + ln -sf test15-b.service /etc/systemd/system/test15-b1.service + ln -sf test15-b.service /etc/systemd/system/test15-b2.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b1.service + ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/ + ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/ + check_ok test15-a Requires test15-b + + # Same as above but now 'b' is masked. + echo "*** test a requires alias of b, b dep masked" + create_services test15-a test15-b + ln -sf test15-b.service /etc/systemd/system/test15-b1.service + ln -sf test15-b.service /etc/systemd/system/test15-b2.service + ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/ + ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/ + ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service + check_ok test15-a Requires test15-b + + clear_services test15-a test15-b +} + +test_invalid_dropins () { + echo "Testing invalid dropins..." + # Assertion failed on earlier versions, command exits unsuccessfully on later versions + systemctl cat nonexistent@.service || true + create_services a + systemctl daemon-reload + # Assertion failed on earlier versions, command exits unsuccessfully on later versions + systemctl cat a@.service || true + systemctl stop a + clear_services a + return 0 +} + +test_basic_dropins +test_hierarchical_dropins +test_template_dropins +test_alias_dropins +test_masked_dropins +test_invalid_dropins + +touch /testok diff --git a/test/units/testsuite-16.service b/test/units/testsuite-16.service new file mode 100644 index 0000000..34e89ff --- /dev/null +++ b/test/units/testsuite-16.service @@ -0,0 +1,19 @@ +[Unit] +Description=TEST-16-EXTEND-TIMEOUT +# Testsuite: Assess all other testsuite-*.services worked as expected + +Wants=success-all.service +Wants=success-start.service +Wants=success-runtime.service +Wants=success-stop.service +Wants=fail-start.service +Wants=fail-stop.service +Wants=fail-runtime.service +StopWhenUnneeded=yes + +[Service] +ExecStartPre=rm -f /failed /testok +Type=exec +TimeoutStartSec=infinity +ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStart=true diff --git a/test/units/testsuite-16.sh b/test/units/testsuite-16.sh new file mode 100755 index 0000000..68e5561 --- /dev/null +++ b/test/units/testsuite-16.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -v -x + +rm -f /test.log + +TL=/test.log.XXXXXXXX + +function wait_for() +{ + service=${1} + result=${2:-success} + time=${3:-45} + + while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]] + do + sleep 1 + time=$(( $time - 1 )) + done + + if [[ ! -f /${service}.${result} ]] + then + journalctl -u ${service/_/-}.service >> "${TL}" + fi +} + +# This checks all stages, start, runtime and stop, can be extended by +# EXTEND_TIMEOUT_USEC + +wait_for success_all + +# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the +# extend timeout interval but less then the stage limit (TimeoutStartSec, +# RuntimeMaxSec, TimeoutStopSec) still succeed. + +wait_for success_start +wait_for success_runtime +wait_for success_stop + +# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the +# approprate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC +# message isn't sent within the extend timeout interval. + +wait_for fail_start startfail +wait_for fail_stop stopfail +wait_for fail_runtime runtimefail + +if [[ -f "${TL}" ]] +then + # no mv + cp "${TL}" /test.log + exit 1 +else + touch /testok + exit 0 +fi diff --git a/test/units/testsuite-17.service b/test/units/testsuite-17.service new file mode 100644 index 0000000..ed2017a --- /dev/null +++ b/test/units/testsuite-17.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-17-UDEV-WANTS + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-17.sh b/test/units/testsuite-17.sh new file mode 100755 index 0000000..7f8740b --- /dev/null +++ b/test/units/testsuite-17.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +mkdir -p /run/udev/rules.d/ + +rm -f /run/udev/rules.d/50-testsuite.rules +udevadm control --reload +udevadm trigger /dev/sda + +while : ; do + ( + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service + systemctl show -p WantedBy foobar.service | grep -q -v sda + systemctl show -p WantedBy waldo.service | grep -q -v sda + ) && break + + sleep .5 +done + +cat > /run/udev/rules.d/50-testsuite.rules <<EOF +ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service" +EOF +udevadm control --reload +udevadm trigger /dev/sda + +while : ; do + ( + udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service + systemctl show -p WantedBy foobar.service | grep -q sda + systemctl show -p WantedBy waldo.service | grep -q -v sda + ) && break + + sleep .5 +done + +cat > /run/udev/rules.d/50-testsuite.rules <<EOF +ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service" +EOF +udevadm control --reload +udevadm trigger /dev/sda + +while : ; do + ( + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service + udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service + systemctl show -p WantedBy foobar.service | grep -q -v sda + systemctl show -p WantedBy waldo.service | grep -q sda + ) && break + + sleep .5 +done + +rm /run/udev/rules.d/50-testsuite.rules + +udevadm control --reload +udevadm trigger /dev/sda + +while : ; do + ( + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service + systemctl show -p WantedBy foobar.service | grep -q -v sda + systemctl show -p WantedBy waldo.service | grep -q -v sda + ) && break + + sleep .5 +done + +echo OK >/testok + +exit 0 diff --git a/test/units/testsuite-18.service b/test/units/testsuite-18.service new file mode 100644 index 0000000..e4a945d --- /dev/null +++ b/test/units/testsuite-18.service @@ -0,0 +1,7 @@ +[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-18.sh b/test/units/testsuite-18.sh new file mode 100755 index 0000000..e471cda --- /dev/null +++ b/test/units/testsuite-18.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-run --wait -p FailureAction=poweroff true +! systemd-run --wait -p SuccessAction=poweroff false + +if ! test -f /firstphase ; then + echo OK > /firstphase + systemd-run --wait -p SuccessAction=reboot true +else + echo OK > /testok + systemd-run --wait -p FailureAction=poweroff false +fi + +sleep infinity diff --git a/test/units/testsuite-19.service b/test/units/testsuite-19.service new file mode 100644 index 0000000..d6ad5be --- /dev/null +++ b/test/units/testsuite-19.service @@ -0,0 +1,7 @@ +[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-19.sh b/test/units/testsuite-19.sh new file mode 100755 index 0000000..57831c2 --- /dev/null +++ b/test/units/testsuite-19.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +if grep -q cgroup2 /proc/filesystems ; then + systemd-run --wait --unit=test0.service -p "DynamicUser=1" -p "Delegate=" \ + test -w /sys/fs/cgroup/system.slice/test0.service/ -a \ + -w /sys/fs/cgroup/system.slice/test0.service/cgroup.procs -a \ + -w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control + + systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \ + grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers + + systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \ + grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers + + # "io" is not among the controllers enabled by default for all units, verify that + grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers + + # Run a service with "io" enabled, and verify it works + systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice" \ + grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers + + # We want to check if "io" is removed again from the controllers + # list. However, PID 1 (rightfully) does this asynchronously. In order + # to force synchronization on this, let's start a short-lived service + # which requires PID 1 to refresh the cgroup tree, so that we can + # verify that this all works. + systemd-run --wait --unit=test4.service true + + # And now check again, "io" should have vanished + grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers +else + echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2 +fi + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-20.service b/test/units/testsuite-20.service new file mode 100644 index 0000000..d31d531 --- /dev/null +++ b/test/units/testsuite-20.service @@ -0,0 +1,10 @@ +[Unit] +Description=TEST-20-MAINPIDGAMES +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 +NotifyAccess=all diff --git a/test/units/testsuite-20.sh b/test/units/testsuite-20.sh new file mode 100755 index 0000000..d94f6b2 --- /dev/null +++ b/test/units/testsuite-20.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +test `systemctl show -P MainPID testsuite-20.service` -eq $$ + +# Start a test process inside of our own cgroup +sleep infinity & +INTERNALPID=$! +disown + +# Start a test process outside of our own cgroup +systemd-run -p DynamicUser=1 --unit=test20-sleep.service /bin/sleep infinity +EXTERNALPID=`systemctl show -P MainPID test20-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-20.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-20.service` -eq $INTERNALPID + +# Update it back to our own PID, this should also work +systemd-notify MAINPID=$$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ + +# 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-20.service` -eq $$ + +# Try to set it to PID 0, which is invalid and should be ignored +systemd-notify MAINPID=0 +test `systemctl show -P MainPID testsuite-20.service` -eq $$ + +# 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-20.service` -eq $$ + +# 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-20.service` -eq $$ + +# 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-20.service` -eq $INTERNALPID + +# Update it back to our own PID, this should also work +systemd-notify --uid=1000 MAINPID=$$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ + +cat >/tmp/test20-mainpid.sh <<EOF +#!/usr/bin/env bash + +set -eux +set -o pipefail + +# Create a number of children, and make one the main one +sleep infinity & +disown + +sleep infinity & +MAINPID=\$! +disown + +sleep infinity & +disown + +echo \$MAINPID > /run/mainpidsh/pid +EOF +chmod +x /tmp/test20-mainpid.sh + +systemd-run --unit=test20-mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/test20-mainpid.sh +test `systemctl show -P MainPID test20-mainpidsh.service` -eq `cat /run/mainpidsh/pid` + +cat >/tmp/test20-mainpid2.sh <<EOF +#!/usr/bin/env bash + +set -eux +set -o pipefail + +# Create a number of children, and make one the main one +sleep infinity & +disown + +sleep infinity & +MAINPID=\$! +disown + +sleep infinity & +disown + +echo \$MAINPID > /run/mainpidsh2/pid +chown 1001:1001 /run/mainpidsh2/pid +EOF +chmod +x /tmp/test20-mainpid2.sh + +systemd-run --unit=test20-mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/test20-mainpid2.sh +test `systemctl show -P MainPID test20-mainpidsh2.service` -eq `cat /run/mainpidsh2/pid` + +cat >/dev/shm/test20-mainpid3.sh <<EOF +#!/usr/bin/env bash + +set -eux +set -o pipefail + +sleep infinity & +disown + +sleep infinity & +disown + +sleep infinity & +disown + +# Let's try to play games, and link up a privileged PID file +ln -s ../mainpidsh/pid /run/mainpidsh3/pid + +# Quick assertion that the link isn't dead +test -f /run/mainpidsh3/pid +EOF +chmod 755 /dev/shm/test20-mainpid3.sh + +# This has to fail, as we shouldn't accept the dangerous PID file, and then inotify-wait on it to be corrected which we never do +! systemd-run --unit=test20-mainpidsh3.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh3 -p PIDFile=/run/mainpidsh3/pid -p DynamicUser=1 -p TimeoutStartSec=2s /dev/shm/test20-mainpid3.sh + +# Test that this failed due to timeout, and not some other error +test `systemctl show -P Result test20-mainpidsh3.service` = timeout + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-22.01.sh b/test/units/testsuite-22.01.sh new file mode 100755 index 0000000..d233e37 --- /dev/null +++ b/test/units/testsuite-22.01.sh @@ -0,0 +1,13 @@ +#! /bin/bash +# +# With "e" don't attempt to set permissions when file doesn't exist, see +# https://github.com/systemd/systemd/pull/6682. +# + +set -e + +rm -fr /tmp/test + +echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create - + +! test -e /tmp/test diff --git a/test/units/testsuite-22.02.sh b/test/units/testsuite-22.02.sh new file mode 100755 index 0000000..d1bf1ea --- /dev/null +++ b/test/units/testsuite-22.02.sh @@ -0,0 +1,122 @@ +#! /bin/bash +# +# Basic tests for types creating directories +# + +set -e +set -x + +rm -fr /tmp/{C,d,D,e} +mkdir /tmp/{C,d,D,e} + +# +# 'd' +# +mkdir /tmp/d/2 +chmod 777 /tmp/d/2 + +systemd-tmpfiles --create - <<EOF +d /tmp/d/1 0755 daemon daemon - - +d /tmp/d/2 0755 daemon daemon - - +EOF + +test -d /tmp/d/1 +test $(stat -c %U:%G:%a /tmp/d/1) = "daemon:daemon:755" + +test -d /tmp/d/2 +test $(stat -c %U:%G:%a /tmp/d/2) = "daemon:daemon:755" + +# +# 'D' +# +mkdir /tmp/D/2 +chmod 777 /tmp/D/2 +touch /tmp/D/2/foo + +systemd-tmpfiles --create - <<EOF +D /tmp/D/1 0755 daemon daemon - - +D /tmp/D/2 0755 daemon daemon - - +EOF + +test -d /tmp/D/1 +test $(stat -c %U:%G:%a /tmp/D/1) = "daemon:daemon:755" + +test -d /tmp/D/2 +test $(stat -c %U:%G:%a /tmp/D/2) = "daemon:daemon:755" + +systemd-tmpfiles --remove - <<EOF +D /tmp/D/2 0755 daemon daemon - - +EOF + +# the content of '2' should be removed +test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*" + +# +# 'e' +# +mkdir -p /tmp/e/2/{d1,d2} +chmod 777 /tmp/e/2 +chmod 777 /tmp/e/2/d* + +systemd-tmpfiles --create - <<EOF +e /tmp/e/1 0755 daemon daemon - - +e /tmp/e/2/* 0755 daemon daemon - - +EOF + +! test -d /tmp/e/1 + +test -d /tmp/e/2 +test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777" + +test -d /tmp/e/2/d1 +test $(stat -c %U:%G:%a /tmp/e/2/d1) = "daemon:daemon:755" +test -d /tmp/e/2/d2 +test $(stat -c %U:%G:%a /tmp/e/2/d2) = "daemon:daemon:755" + +# 'e' operates on directories only +mkdir -p /tmp/e/3/{d1,d2} +chmod 777 /tmp/e/3 +chmod 777 /tmp/e/3/d* +touch /tmp/e/3/f1 +chmod 644 /tmp/e/3/f1 + +! systemd-tmpfiles --create - <<EOF +e /tmp/e/3/* 0755 daemon daemon - - +EOF + +# the directories should have been processed although systemd-tmpfiles failed +# previously due to the presence of a file. +test -d /tmp/e/3/d1 +test $(stat -c %U:%G:%a /tmp/e/3/d1) = "daemon:daemon:755" +test -d /tmp/e/3/d2 +test $(stat -c %U:%G:%a /tmp/e/3/d2) = "daemon:daemon:755" + +test -f /tmp/e/3/f1 +test $(stat -c %U:%G:%a /tmp/e/3/f1) = "root:root:644" + +# +# 'C' +# + +mkdir /tmp/C/{1,2,3}-origin +touch /tmp/C/{1,2,3}-origin/f1 +chmod 755 /tmp/C/{1,2,3}-origin/f1 + +mkdir /tmp/C/{2,3} +touch /tmp/C/3/f1 + +systemd-tmpfiles --create - <<EOF +C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin +C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin +EOF + +test -d /tmp/C/1 +test $(stat -c %U:%G:%a /tmp/C/1/f1) = "daemon:daemon:755" +test -d /tmp/C/2 +test $(stat -c %U:%G:%a /tmp/C/2/f1) = "daemon:daemon:755" + +! systemd-tmpfiles --create - <<EOF +C /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin +EOF + +test $(stat -c %U:%G:%a /tmp/C/3/f1) = "root:root:644" diff --git a/test/units/testsuite-22.03.sh b/test/units/testsuite-22.03.sh new file mode 100755 index 0000000..8d009fb --- /dev/null +++ b/test/units/testsuite-22.03.sh @@ -0,0 +1,236 @@ +#! /bin/bash +# +# Basic tests for types creating/writing files +# + +set -e +set -x + +rm -fr /tmp/{f,F,w} +mkdir /tmp/{f,F,w} +touch /tmp/file-owned-by-root + +# +# 'f' +# +systemd-tmpfiles --create - <<EOF +f /tmp/f/1 0644 - - - - +f /tmp/f/2 0644 - - - This string should be written +EOF + +### '1' should exist and be empty +test -f /tmp/f/1; ! test -s /tmp/f/1 +test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644" + +test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644" +test "$(< /tmp/f/2)" = "This string should be written" + +### The perms are supposed to be updated even if the file already exists. +systemd-tmpfiles --create - <<EOF +f /tmp/f/1 0666 daemon daemon - This string should not be written +EOF + +# file should be empty +! test -s /tmp/f/1 +test $(stat -c %U:%G:%a /tmp/f/1) = "daemon:daemon:666" + +### But we shouldn't try to set perms on an existing file which is not a +### regular one. +mkfifo /tmp/f/fifo +chmod 644 /tmp/f/fifo + +! systemd-tmpfiles --create - <<EOF +f /tmp/f/fifo 0666 daemon daemon - This string should not be written +EOF + +test -p /tmp/f/fifo +test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644" + +### 'f' should not follow symlinks. +ln -s missing /tmp/f/dangling +ln -s /tmp/file-owned-by-root /tmp/f/symlink + +! systemd-tmpfiles --create - <<EOF +f /tmp/f/dangling 0644 daemon daemon - - +f /tmp/f/symlink 0644 daemon daemon - - +EOF +! test -e /tmp/f/missing +test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644" + +### Handle read-only filesystem gracefully: we shouldn't fail if the target +### already exists and have the correct perms. +mkdir /tmp/f/rw-fs +mkdir /tmp/f/ro-fs + +touch /tmp/f/rw-fs/foo +chmod 644 /tmp/f/rw-fs/foo + +mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs + +systemd-tmpfiles --create - <<EOF +f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written +EOF +test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo + +! systemd-tmpfiles --create - <<EOF +f /tmp/f/ro-fs/foo 0666 - - - - +EOF +test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644" + +! systemd-tmpfiles --create - <<EOF +f /tmp/f/ro-fs/bar 0644 - - - - +EOF +! test -e /tmp/f/ro-fs/bar + +### 'f' shouldn't follow unsafe paths. +mkdir /tmp/f/daemon +ln -s /root /tmp/f/daemon/unsafe-symlink +chown -R --no-dereference daemon:daemon /tmp/f/daemon + +! systemd-tmpfiles --create - <<EOF +f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - - +EOF +! test -e /tmp/f/daemon/unsafe-symlink/exploit + +# +# 'F' +# +echo "This should be truncated" >/tmp/F/truncated +echo "This should be truncated" >/tmp/F/truncated-with-content + +systemd-tmpfiles --create - <<EOF +F /tmp/F/created 0644 - - - - +F /tmp/F/created-with-content 0644 - - - new content +F /tmp/F/truncated 0666 daemon daemon - - +F /tmp/F/truncated-with-content 0666 daemon daemon - new content +EOF + +test -f /tmp/F/created; ! test -s /tmp/F/created +test -f /tmp/F/created-with-content +test "$(< /tmp/F/created-with-content)" = "new content" +test -f /tmp/F/truncated; ! test -s /tmp/F/truncated +test $(stat -c %U:%G:%a /tmp/F/truncated) = "daemon:daemon:666" +test -s /tmp/F/truncated-with-content +test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "daemon:daemon:666" + +### We shouldn't try to truncate anything but regular files since the behavior is +### unspecified in the other cases. +mkfifo /tmp/F/fifo + +! systemd-tmpfiles --create - <<EOF +F /tmp/F/fifo 0644 - - - - +EOF + +test -p /tmp/F/fifo + +### 'F' should not follow symlinks. +ln -s missing /tmp/F/dangling +ln -s /tmp/file-owned-by-root /tmp/F/symlink + +! systemd-tmpfiles --create - <<EOF +f /tmp/F/dangling 0644 daemon daemon - - +f /tmp/F/symlink 0644 daemon daemon - - +EOF +! test -e /tmp/F/missing +test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644" + +### Handle read-only filesystem gracefully: we shouldn't fail if the target +### already exists and is empty. +mkdir /tmp/F/rw-fs +mkdir /tmp/F/ro-fs + +touch /tmp/F/rw-fs/foo +chmod 644 /tmp/F/rw-fs/foo + +mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs + +systemd-tmpfiles --create - <<EOF +F /tmp/F/ro-fs/foo 0644 - - - - +EOF +test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo + +echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo +! systemd-tmpfiles --create - <<EOF +F /tmp/F/ro-fs/foo 0644 - - - - +EOF + +! systemd-tmpfiles --create - <<EOF +F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written +EOF +test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo + +# Trying to change the perms should fail. +>/tmp/F/rw-fs/foo +! systemd-tmpfiles --create - <<EOF +F /tmp/F/ro-fs/foo 0666 - - - - +EOF +test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644" + +### Try to create a new file. +! systemd-tmpfiles --create - <<EOF +F /tmp/F/ro-fs/bar 0644 - - - - +EOF +! test -e /tmp/F/ro-fs/bar + +### 'F' shouldn't follow unsafe paths. +mkdir /tmp/F/daemon +ln -s /root /tmp/F/daemon/unsafe-symlink +chown -R --no-dereference daemon:daemon /tmp/F/daemon + +! systemd-tmpfiles --create - <<EOF +F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - - +EOF +! test -e /tmp/F/daemon/unsafe-symlink/exploit + +# +# 'w' +# +touch /tmp/w/overwritten + +### nop if the target does not exist. +systemd-tmpfiles --create - <<EOF +w /tmp/w/unexistent 0644 - - - new content +EOF +! test -e /tmp/w/unexistent + +### no argument given -> fails. +! systemd-tmpfiles --create - <<EOF +w /tmp/w/unexistent 0644 - - - - +EOF + +### write into an empty file. +systemd-tmpfiles --create - <<EOF +w /tmp/w/overwritten 0644 - - - old content +EOF +test -f /tmp/w/overwritten +test "$(< /tmp/w/overwritten)" = "old content" + +### new content is overwritten +systemd-tmpfiles --create - <<EOF +w /tmp/w/overwritten 0644 - - - new content +EOF +test -f /tmp/w/overwritten +test "$(< /tmp/w/overwritten)" = "new content" + +### writing into an 'exotic' file should be allowed. +systemd-tmpfiles --create - <<EOF +w /dev/null - - - - new content +EOF + +### 'w' follows symlinks +ln -s ./overwritten /tmp/w/symlink +systemd-tmpfiles --create - <<EOF +w /tmp/w/symlink - - - - $(readlink -e /tmp/w/symlink) +EOF +readlink -e /tmp/w/symlink +test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten" + +### 'w' shouldn't follow unsafe paths. +mkdir /tmp/w/daemon +ln -s /root /tmp/w/daemon/unsafe-symlink +chown -R --no-dereference daemon:daemon /tmp/w/daemon + +! systemd-tmpfiles --create - <<EOF +f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - - +EOF +! test -e /tmp/w/daemon/unsafe-symlink/exploit diff --git a/test/units/testsuite-22.04.sh b/test/units/testsuite-22.04.sh new file mode 100755 index 0000000..f916086 --- /dev/null +++ b/test/units/testsuite-22.04.sh @@ -0,0 +1,44 @@ +#! /bin/bash +# +# Basic tests for types creating fifos +# + +set -e +set -x + +rm -fr /tmp/p +mkdir /tmp/p +touch /tmp/p/f1 + +systemd-tmpfiles --create - <<EOF +p /tmp/p/fifo1 0666 - - - - +EOF + +test -p /tmp/p/fifo1 +test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666" + +# it should refuse to overwrite an existing file +! systemd-tmpfiles --create - <<EOF +p /tmp/p/f1 0666 - - - - +EOF + +test -f /tmp/p/f1 + +# unless '+' prefix is used +systemd-tmpfiles --create - <<EOF +p+ /tmp/p/f1 0666 - - - - +EOF + +test -p /tmp/p/f1 +test $(stat -c %U:%G:%a /tmp/p/f1) = "root:root:666" + +# +# Must be fixed +# +# mkdir /tmp/p/daemon +# #ln -s /root /tmp/F/daemon/unsafe-symlink +# chown -R --no-dereference daemon:daemon /tmp/p/daemon +# +# systemd-tmpfiles --create - <<EOF +# p /tmp/p/daemon/fifo2 0666 daemon daemon - - +# EOF diff --git a/test/units/testsuite-22.05.sh b/test/units/testsuite-22.05.sh new file mode 100755 index 0000000..13c4ac8 --- /dev/null +++ b/test/units/testsuite-22.05.sh @@ -0,0 +1,45 @@ +#! /bin/bash + +set -e +set -x + +rm -fr /tmp/{z,Z} +mkdir /tmp/{z,Z} + +# +# 'z' +# +mkdir /tmp/z/d{1,2} +touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21 + +systemd-tmpfiles --create - <<EOF +z /tmp/z/f1 0755 daemon daemon - - +z /tmp/z/d1 0755 daemon daemon - - +EOF + +test $(stat -c %U:%G /tmp/z/f1) = "daemon:daemon" +test $(stat -c %U:%G /tmp/z/d1) = "daemon:daemon" +test $(stat -c %U:%G /tmp/z/d1/f11) = "root:root" + +systemd-tmpfiles --create - <<EOF +z /tmp/z/d2/* 0755 daemon daemon - - +EOF + +test $(stat -c %U:%G /tmp/z/d2/f21) = "daemon:daemon" + +# +# 'Z' +# +mkdir /tmp/Z/d1 /tmp/Z/d1/d11 +touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111 + +systemd-tmpfiles --create - <<EOF +Z /tmp/Z/f1 0755 daemon daemon - - +Z /tmp/Z/d1 0755 daemon daemon - - +EOF + +test $(stat -c %U:%G /tmp/Z/f1) = "daemon:daemon" +test $(stat -c %U:%G /tmp/Z/d1) = "daemon:daemon" +test $(stat -c %U:%G /tmp/Z/d1/d11) = "daemon:daemon" +test $(stat -c %U:%G /tmp/Z/d1/f11) = "daemon:daemon" +test $(stat -c %U:%G /tmp/Z/d1/d11/f111) = "daemon:daemon" diff --git a/test/units/testsuite-22.06.sh b/test/units/testsuite-22.06.sh new file mode 100755 index 0000000..cd65ba6 --- /dev/null +++ b/test/units/testsuite-22.06.sh @@ -0,0 +1,38 @@ +#! /bin/bash +# +# Inspired by https://github.com/systemd/systemd/issues/9508 +# + +set -e + +test_snippet() { + systemd-tmpfiles "$@" - <<EOF +d /var/tmp/foobar-test-06 +d /var/tmp/foobar-test-06/important +R /var/tmp/foobar-test-06 +EOF +} + +test_snippet --create --remove +test -d /var/tmp/foobar-test-06 +test -d /var/tmp/foobar-test-06/important + +test_snippet --remove +! test -f /var/tmp/foobar-test-06 +! test -f /var/tmp/foobar-test-06/important + +test_snippet --create +test -d /var/tmp/foobar-test-06 +test -d /var/tmp/foobar-test-06/important + +touch /var/tmp/foobar-test-06/something-else + +test_snippet --create +test -d /var/tmp/foobar-test-06 +test -d /var/tmp/foobar-test-06/important +test -f /var/tmp/foobar-test-06/something-else + +test_snippet --create --remove +test -d /var/tmp/foobar-test-06 +test -d /var/tmp/foobar-test-06/important +! test -f /var/tmp/foobar-test-06/something-else diff --git a/test/units/testsuite-22.07.sh b/test/units/testsuite-22.07.sh new file mode 100755 index 0000000..39c04b9 --- /dev/null +++ b/test/units/testsuite-22.07.sh @@ -0,0 +1,31 @@ +#! /bin/bash +# +# Verifies the issues described by https://github.com/systemd/systemd/issues/10191 +# + +set -e +set -x + +rm -rf /tmp/test-prefix + +mkdir /tmp/test-prefix +touch /tmp/test-prefix/file + +systemd-tmpfiles --remove - <<EOF +r /tmp/test-prefix +r /tmp/test-prefix/file +EOF + +! test -f /tmp/test-prefix/file +! test -f /tmp/test-prefix + +mkdir /tmp/test-prefix +touch /tmp/test-prefix/file + +systemd-tmpfiles --remove - <<EOF +r /tmp/test-prefix/file +r /tmp/test-prefix +EOF + +! test -f /tmp/test-prefix/file +! test -f /tmp/test-prefix diff --git a/test/units/testsuite-22.08.sh b/test/units/testsuite-22.08.sh new file mode 100755 index 0000000..e7bf044 --- /dev/null +++ b/test/units/testsuite-22.08.sh @@ -0,0 +1,32 @@ +#! /bin/bash +# +# Verify tmpfiles can run in a root directory under a path prefix that contains +# directories owned by unprivileged users, for example when a root file system +# is mounted in a regular user's home directory. +# +# https://github.com/systemd/systemd/pull/11820 +# + +set -e + +rm -fr /tmp/root /tmp/user +mkdir -p /tmp/root /tmp/user/root +chown daemon:daemon /tmp/user + +# Verify the command works as expected with no prefix or a root-owned prefix. +echo 'd /tmp/root/test1' | systemd-tmpfiles --create - +test -d /tmp/root/test1 +echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create - +test -d /tmp/root/test2 + +# Verify the command fails to write to a root-owned subdirectory under an +# unprivileged user's directory when it's not part of the prefix, as expected +# by the unsafe_transition function. +! echo 'd /tmp/user/root/test' | systemd-tmpfiles --create - +! test -e /tmp/user/root/test +! echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create - +! test -e /tmp/user/root/test + +# Verify the above works when all user-owned directories are in the prefix. +echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create - +test -d /tmp/user/root/test diff --git a/test/units/testsuite-22.09.sh b/test/units/testsuite-22.09.sh new file mode 100755 index 0000000..c558dfd --- /dev/null +++ b/test/units/testsuite-22.09.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +set -e +set -x + +# Make sure that the "stat" output is not locale dependent. +export LANG=C LC_ALL=C + +# first, create file without suid/sgid +systemd-tmpfiles --create - <<EOF +f /tmp/xxx 0755 1 1 - - +f /tmp/yyy 0755 1 1 - - +EOF + +test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755" +test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755" + +# then, add suid/sgid +systemd-tmpfiles --create - <<EOF +f /tmp/xxx 04755 +f /tmp/yyy 02755 +EOF + +test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755" +test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755" + +# then, chown the files to somebody else +systemd-tmpfiles --create - <<EOF +f /tmp/xxx - 2 2 +f /tmp/yyy - 2 2 +EOF + +test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755" +test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755" + +# then, chown the files to a third user/group but also drop to a mask that has +# both more and fewer bits set +systemd-tmpfiles --create - <<EOF +f /tmp/xxx 0770 3 3 +f /tmp/yyy 0770 3 3 +EOF + +test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770" +test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770" + +# return to the beginning +systemd-tmpfiles --create - <<EOF +f /tmp/xxx 0755 1 1 - - +f /tmp/yyy 0755 1 1 - - +EOF + +test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755" +test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755" + +# remove everything +systemd-tmpfiles --remove - <<EOF +r /tmp/xxx +r /tmp/yyy +EOF diff --git a/test/units/testsuite-22.service b/test/units/testsuite-22.service new file mode 100644 index 0000000..55e3056 --- /dev/null +++ b/test/units/testsuite-22.service @@ -0,0 +1,12 @@ +[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 +StandardOutput=tty +StandardError=tty diff --git a/test/units/testsuite-22.sh b/test/units/testsuite-22.sh new file mode 100755 index 0000000..afce85a --- /dev/null +++ b/test/units/testsuite-22.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -x +set -e + +>/failed + +for t in ${0%.sh}.*.sh; do + echo "Running $t"; ./$t +done + +touch /testok +rm /failed diff --git a/test/units/testsuite-23.service b/test/units/testsuite-23.service new file mode 100644 index 0000000..b3b3297 --- /dev/null +++ b/test/units/testsuite-23.service @@ -0,0 +1,7 @@ +[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.sh b/test/units/testsuite-23.sh new file mode 100755 index 0000000..5e2966f --- /dev/null +++ b/test/units/testsuite-23.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +# Create a binary for which execve() will fail +touch /tmp/brokenbinary +chmod +x /tmp/brokenbinary + +# These three commands should succeed. +systemd-run --unit=one -p Type=simple /bin/sleep infinity +systemd-run --unit=two -p Type=simple -p User=idontexist /bin/sleep infinity +systemd-run --unit=three -p Type=simple /tmp/brokenbinary + +# And now, do the same with Type=exec, where the latter two should fail +systemd-run --unit=four -p Type=exec /bin/sleep infinity +! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity +! systemd-run --unit=six -p Type=exec /tmp/brokenbinary + +systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity +# Both TERM and SIGINT happen to have the same number on all architectures +test $(systemctl show --value -p KillSignal seven.service) -eq 15 +test $(systemctl show --value -p RestartKillSignal seven.service) -eq 2 + +systemctl restart seven.service +systemctl stop seven.service + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-24.service b/test/units/testsuite-24.service new file mode 100644 index 0000000..e66f613 --- /dev/null +++ b/test/units/testsuite-24.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-24-CRYPTSETUP +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -x -e -c 'mountpoint /var; systemctl --state=failed --no-legend --no-pager >/failed; echo OK >/testok' +Type=oneshot diff --git a/test/units/testsuite-25.service b/test/units/testsuite-25.service new file mode 100644 index 0000000..45d8b69 --- /dev/null +++ b/test/units/testsuite-25.service @@ -0,0 +1,7 @@ +[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-25.sh b/test/units/testsuite-25.sh new file mode 100755 index 0000000..e3dd43a --- /dev/null +++ b/test/units/testsuite-25.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +export SYSTEMD_PAGER=cat + +dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5 + +# Test import +machinectl import-raw /var/tmp/testimage.raw +machinectl image-status testimage +test -f /var/lib/machines/testimage.raw +cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw + +# Test export +machinectl export-raw testimage /var/tmp/testimage2.raw +cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw +rm /var/tmp/testimage2.raw + +# Test compressed export (gzip) +machinectl export-raw testimage /var/tmp/testimage2.raw.gz +gunzip /var/tmp/testimage2.raw.gz +cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw +rm /var/tmp/testimage2.raw + +# Test clone +machinectl clone testimage testimage3 +test -f /var/lib/machines/testimage3.raw +machinectl image-status testimage3 +test -f /var/lib/machines/testimage.raw +machinectl image-status testimage +cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw +cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw + +# Test removal +machinectl remove testimage +! test -f /var/lib/machines/testimage.raw +! machinectl image-status testimage + +# Test export of clone +machinectl export-raw testimage3 /var/tmp/testimage3.raw +cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw +rm /var/tmp/testimage3.raw + +# Test rename +machinectl rename testimage3 testimage4 +test -f /var/lib/machines/testimage4.raw +machinectl image-status testimage4 +! test -f /var/lib/machines/testimage3.raw +! machinectl image-status testimage3 +cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw + +# Test export of rename +machinectl export-raw testimage4 /var/tmp/testimage4.raw +cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw +rm /var/tmp/testimage4.raw + +# Test removal +machinectl remove testimage4 +! test -f /var/lib/machines/testimage4.raw +! machinectl image-status testimage4 + +# → And now, let's test directory trees ← # + +# Set up a directory we can import +mkdir /var/tmp/scratch +mv /var/tmp/testimage.raw /var/tmp/scratch/ +touch /var/tmp/scratch/anotherfile +mkdir /var/tmp/scratch/adirectory +echo "piep" > /var/tmp/scratch/adirectory/athirdfile + +# Test import-fs +machinectl import-fs /var/tmp/scratch/ +test -d /var/lib/machines/scratch +machinectl image-status scratch + +# Test export-tar +machinectl export-tar scratch /var/tmp/scratch.tar.gz +test -f /var/tmp/scratch.tar.gz +mkdir /var/tmp/extract +(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz) +diff -r /var/tmp/scratch/ /var/tmp/extract/ +rm -rf /var/tmp/extract + +# Test import-tar +machinectl import-tar /var/tmp/scratch.tar.gz scratch2 +test -d /var/lib/machines/scratch2 +machinectl image-status scratch2 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch2 + +# Test removal +machinectl remove scratch +! test -f /var/lib/machines/scratch +! machinectl image-status scratch + +# Test clone +machinectl clone scratch2 scratch3 +test -d /var/lib/machines/scratch2 +machinectl image-status scratch2 +test -d /var/lib/machines/scratch3 +machinectl image-status scratch3 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch3 + +# Test removal +machinectl remove scratch2 +! test -f /var/lib/machines/scratch2 +! machinectl image-status scratch2 + +# Test rename +machinectl rename scratch3 scratch4 +test -d /var/lib/machines/scratch4 +machinectl image-status scratch4 +! test -f /var/lib/machines/scratch3 +! machinectl image-status scratch3 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch4 + +# Test removal +machinectl remove scratch4 +! test -f /var/lib/machines/scratch4 +! machinectl image-status scratch4 + +# Test import-tar hyphen/stdin pipe behavior +cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5 +test -d /var/lib/machines/scratch5 +machinectl image-status scratch5 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch5 + +# Test export-tar hyphen/stdout pipe behavior +mkdir -p /var/tmp/extract +machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/ +diff -r /var/tmp/scratch/ /var/tmp/extract/ +rm -rf /var/tmp/extract + +rm -rf /var/tmp/scratch + +# Test removal +machinectl remove scratch5 +! test -f /var/lib/machines/scratch5 +! machinectl image-status scratch5 + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-26.service b/test/units/testsuite-26.service new file mode 100644 index 0000000..65b6683 --- /dev/null +++ b/test/units/testsuite-26.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-26-SETENV + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh new file mode 100755 index 0000000..89c0937 --- /dev/null +++ b/test/units/testsuite-26.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +# Make sure PATH is set +systemctl show-environment | grep -q '^PATH=' + +# Let's add an entry and override a built-in one +systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR + +# Check that both are set +systemctl show-environment | grep -q '^PATH=.*testaddition$' +systemctl show-environment | grep -q '^FOO=BAR$' + +systemctl daemon-reload + +# Check again after the reload +systemctl show-environment | grep -q '^PATH=.*testaddition$' +systemctl show-environment | grep -q '^FOO=BAR$' + +# Drop both +systemctl unset-environment FOO PATH + +# Check that one is gone and the other reverted to the built-in +! (systemctl show-environment | grep -q '^FOO=$') +! (systemctl show-environment | grep -q '^PATH=.*testaddition$') +systemctl show-environment | grep -q '^PATH=' + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-27.service b/test/units/testsuite-27.service new file mode 100644 index 0000000..52185f0 --- /dev/null +++ b/test/units/testsuite-27.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-27-STDOUTFILE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-27.sh b/test/units/testsuite-27.sh new file mode 100755 index 0000000..9d92e6e --- /dev/null +++ b/test/units/testsuite-27.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +systemd-run --wait --unit=test27-one \ + -p StandardOutput=file:/tmp/stdout \ + -p StandardError=file:/tmp/stderr \ + -p Type=exec \ + sh -c 'echo x ; echo y >&2' +cmp /tmp/stdout <<EOF +x +EOF +cmp /tmp/stderr <<EOF +y +EOF + +systemd-run --wait --unit=test27-two \ + -p StandardOutput=file:/tmp/stdout \ + -p StandardError=file:/tmp/stderr \ + -p Type=exec \ + sh -c 'echo z ; echo a >&2' +cmp /tmp/stdout <<EOF +z +EOF +cmp /tmp/stderr <<EOF +a +EOF + +systemd-run --wait --unit=test27-three \ + -p StandardOutput=append:/tmp/stdout \ + -p StandardError=append:/tmp/stderr \ + -p Type=exec \ + sh -c 'echo b ; echo c >&2' +cmp /tmp/stdout <<EOF +z +b +EOF +cmp /tmp/stderr <<EOF +a +c +EOF + +systemd-analyze log-level info + +echo OK >/testok + +exit 0 diff --git a/test/units/testsuite-28.service b/test/units/testsuite-28.service new file mode 100644 index 0000000..7ea8630 --- /dev/null +++ b/test/units/testsuite-28.service @@ -0,0 +1,11 @@ +[Unit] +Description=TEST-28-PERCENTJ-WANTEDBY +# Testsuite: Ensure %j Wants directives work +Wants=specifier-j-wants.service +After=specifier-j-wants.service +Requires=testsuite-28-pre.service +After=testsuite-28-pre.service + +[Service] +ExecStart=true +Type=oneshot diff --git a/test/units/testsuite-29.service b/test/units/testsuite-29.service new file mode 100644 index 0000000..90c2187 --- /dev/null +++ b/test/units/testsuite-29.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-29-UDEV-ID_RENAMING + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh new file mode 100755 index 0000000..5c62556 --- /dev/null +++ b/test/units/testsuite-29.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +mkdir -p /run/udev/rules.d/ + +cat > /run/udev/rules.d/50-testsuite.rules <<EOF +ACTION=="remove", GOTO="lo_end" + +SUBSYSTEM=="net", KERNEL=="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/lo" + +ACTION!="change", GOTO="lo_end" + +SUBSYSTEM=="net", KERNEL=="lo", ENV{ID_RENAMING}="1" + +LABEL="lo_end" +EOF + +udevadm control --log-priority=debug --reload --timeout=600 +udevadm trigger --action=add --settle /sys/devices/virtual/net/lo +udevadm info /sys/devices/virtual/net/lo +sleep 1 +STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device) +[[ $STATE == "active" ]] || exit 1 + +udevadm trigger --action=change --settle /sys/devices/virtual/net/lo +udevadm info /sys/devices/virtual/net/lo +sleep 1 +STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device) +[[ $STATE == "inactive" ]] || exit 1 + +udevadm trigger --action=move --settle /sys/devices/virtual/net/lo +udevadm info /sys/devices/virtual/net/lo +sleep 1 +STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device) +[[ $STATE == "active" ]] || exit 1 + +rm -f /run/udev/rules.d/50-testsuite.rules +udevadm control --reload --timeout=600 + +# test for issue #16967 + +ip link add hoge type dummy +udevadm info --wait-for-initialization=10s /sys/devices/virtual/net/hoge +sleep 1 +if ! systemctl status sys-devices-virtual-net-hoge.device; then exit 1; fi +if ! systemctl status sys-subsystem-net-devices-hoge.device; then exit 1; fi + +ip link set hoge name foobar +udevadm info --wait-for-initialization=10s /sys/devices/virtual/net/foobar +sleep 1 +if systemctl status sys-devices-virtual-net-hoge.device; then exit 1; fi +if systemctl status sys-subsystem-net-devices-hoge.device; then exit 1; fi +if ! systemctl status sys-devices-virtual-net-foobar.device; then exit 1; fi +if ! systemctl status sys-subsystem-net-devices-foobar.device; then exit 1; fi + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-30.service b/test/units/testsuite-30.service new file mode 100644 index 0000000..eb342f3 --- /dev/null +++ b/test/units/testsuite-30.service @@ -0,0 +1,7 @@ +[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-30.sh b/test/units/testsuite-30.sh new file mode 100755 index 0000000..a507ffc --- /dev/null +++ b/test/units/testsuite-30.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +systemctl disable --now systemd-timesyncd.service + +timedatectl set-timezone Europe/Berlin +timedatectl set-time 1980-10-15 + +systemd-run --on-timezone-change touch /tmp/timezone-changed +systemd-run --on-clock-change touch /tmp/clock-changed + +! test -f /tmp/timezone-changed +! test -f /tmp/clock-changed + +timedatectl set-timezone Europe/Kiev + +while ! test -f /tmp/timezone-changed ; do sleep .5 ; done + +timedatectl set-time 2018-1-1 + +while ! test -f /tmp/clock-changed ; do sleep .5 ; done + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-31.service b/test/units/testsuite-31.service new file mode 100644 index 0000000..07dfb0b --- /dev/null +++ b/test/units/testsuite-31.service @@ -0,0 +1,7 @@ +[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-31.sh b/test/units/testsuite-31.sh new file mode 100755 index 0000000..fcff82d --- /dev/null +++ b/test/units/testsuite-31.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e +set -o pipefail + +if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then + exit 1 +fi + +echo OK > /testok +exit 0 diff --git a/test/units/testsuite-32.service b/test/units/testsuite-32.service new file mode 100644 index 0000000..aab95cb --- /dev/null +++ b/test/units/testsuite-32.service @@ -0,0 +1,8 @@ +[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-32.sh b/test/units/testsuite-32.sh new file mode 100755 index 0000000..c1704ab --- /dev/null +++ b/test/units/testsuite-32.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +# Let's run this test only if the "memory.oom.group" cgroupfs attribute +# exists. This test is a bit too strict, since the "memory.events"/"oom_kill" +# logic has been around since a longer time than "memory.oom.group", but it's +# an easier thing to test for, and also: let's not get confused by older +# kernels where the concept was still new. + +if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; then + + systemd-analyze log-level debug + systemd-analyze log-target console + + # Run a service that is guaranteed to be the first candidate for OOM killing + systemd-run --unit=oomtest.service \ + -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \ + sleep infinity + + # Trigger an OOM killer run + echo 1 >/proc/sys/kernel/sysrq + echo f >/proc/sysrq-trigger + + while : ; do + STATE=`systemctl show -P ActiveState oomtest.service` + [ "$STATE" = "failed" ] && break + sleep .5 + done + + RESULT=`systemctl show -P Result oomtest.service` + test "$RESULT" = "oom-kill" + + systemd-analyze log-level info +fi + +echo OK >/testok + +exit 0 diff --git a/test/units/testsuite-33.service b/test/units/testsuite-33.service new file mode 100644 index 0000000..b64f1e0 --- /dev/null +++ b/test/units/testsuite-33.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-33-CLEAN-UNIT + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-33.sh b/test/units/testsuite-33.sh new file mode 100755 index 0000000..0a6ee57 --- /dev/null +++ b/test/units/testsuite-33.sh @@ -0,0 +1,319 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex +set -o pipefail + +cat > /etc/systemd/system/testservice.service <<EOF +[Service] +ConfigurationDirectory=testservice +RuntimeDirectory=testservice +StateDirectory=testservice +CacheDirectory=testservice +LogsDirectory=testservice +RuntimeDirectoryPreserve=yes +ExecStart=/bin/sleep infinity +Type=exec +EOF + +systemctl daemon-reload + +! test -e /etc/testservice +! test -e /run/testservice +! test -e /var/lib/testservice +! test -e /var/cache/testservice +! test -e /var/log/testservice + +systemctl start testservice + +test -d /etc/testservice +test -d /run/testservice +test -d /var/lib/testservice +test -d /var/cache/testservice +test -d /var/log/testservice + +! systemctl clean testservice + +systemctl stop testservice + +test -d /etc/testservice +test -d /run/testservice +test -d /var/lib/testservice +test -d /var/cache/testservice +test -d /var/log/testservice + +systemctl clean testservice --what=configuration + +! test -e /etc/testservice +test -d /run/testservice +test -d /var/lib/testservice +test -d /var/cache/testservice +test -d /var/log/testservice + +systemctl clean testservice + +! test -e /etc/testservice +! test -e /run/testservice +test -d /var/lib/testservice +! test -e /var/cache/testservice +test -d /var/log/testservice + +systemctl clean testservice --what=logs + +! test -e /etc/testservice +! test -e /run/testservice +test -d /var/lib/testservice +! test -e /var/cache/testservice +! test -e /var/log/testservice + +systemctl clean testservice --what=all + +! test -e /etc/testservice +! test -e /run/testservice +! test -e /var/lib/testservice +! test -e /var/cache/testservice +! test -e /var/log/testservice + +cat > /etc/systemd/system/testservice.service <<EOF +[Service] +DynamicUser=yes +ConfigurationDirectory=testservice +RuntimeDirectory=testservice +StateDirectory=testservice +CacheDirectory=testservice +LogsDirectory=testservice +RuntimeDirectoryPreserve=yes +ExecStart=/bin/sleep infinity +Type=exec +EOF + +systemctl daemon-reload + +! test -e /etc/testservice +! test -e /run/testservice +! test -e /var/lib/testservice +! test -e /var/cache/testservice +! test -e /var/log/testservice + +systemctl restart testservice + +test -d /etc/testservice +test -d /run/private/testservice +test -d /var/lib/private/testservice +test -d /var/cache/private/testservice +test -d /var/log/private/testservice +test -L /run/testservice +test -L /var/lib/testservice +test -L /var/cache/testservice +test -L /var/log/testservice + +! systemctl clean testservice + +systemctl stop testservice + +test -d /etc/testservice +test -d /run/private/testservice +test -d /var/lib/private/testservice +test -d /var/cache/private/testservice +test -d /var/log/private/testservice +test -L /run/testservice +test -L /var/lib/testservice +test -L /var/cache/testservice +test -L /var/log/testservice + +systemctl clean testservice --what=configuration + +! test -d /etc/testservice +test -d /run/private/testservice +test -d /var/lib/private/testservice +test -d /var/cache/private/testservice +test -d /var/log/private/testservice +test -L /run/testservice +test -L /var/lib/testservice +test -L /var/cache/testservice +test -L /var/log/testservice + +systemctl clean testservice + +! test -d /etc/testservice +! test -d /run/private/testservice +test -d /var/lib/private/testservice +! test -d /var/cache/private/testservice +test -d /var/log/private/testservice +! test -L /run/testservice +test -L /var/lib/testservice +! test -L /var/cache/testservice +test -L /var/log/testservice + +systemctl clean testservice --what=logs + +! test -d /etc/testservice +! test -d /run/private/testservice +test -d /var/lib/private/testservice +! test -d /var/cache/private/testservice +! test -d /var/log/private/testservice +! test -L /run/testservice +test -L /var/lib/testservice +! test -L /var/cache/testservice +! test -L /var/log/testservice + +systemctl clean testservice --what=all + +! test -d /etc/testservice +! test -d /run/private/testservice +! test -d /var/lib/private/testservice +! test -d /var/cache/private/testservice +! test -d /var/log/private/testservice +! test -L /run/testservice +! test -L /var/lib/testservice +! test -L /var/cache/testservice +! test -L /var/log/testservice + +cat > /etc/systemd/system/tmp-hoge.mount <<EOF +[Mount] +What=tmpfs +Type=tmpfs +ConfigurationDirectory=hoge +RuntimeDirectory=hoge +StateDirectory=hoge +CacheDirectory=hoge +LogsDirectory=hoge +EOF + +systemctl daemon-reload + +! test -e /etc/hoge +! test -e /run/hoge +! test -e /var/lib/hoge +! test -e /var/cache/hoge +! test -e /var/log/hoge + +systemctl start tmp-hoge.mount + +test -d /etc/hoge +test -d /run/hoge +test -d /var/lib/hoge +test -d /var/cache/hoge +test -d /var/log/hoge + +! systemctl clean tmp-hoge.mount + +test -d /etc/hoge +test -d /run/hoge +test -d /var/lib/hoge +test -d /var/cache/hoge +test -d /var/log/hoge + +systemctl stop tmp-hoge.mount + +test -d /etc/hoge +! test -d /run/hoge +test -d /var/lib/hoge +test -d /var/cache/hoge +test -d /var/log/hoge + +systemctl clean tmp-hoge.mount --what=configuration + +! test -d /etc/hoge +! test -d /run/hoge +test -d /var/lib/hoge +test -d /var/cache/hoge +test -d /var/log/hoge + +systemctl clean tmp-hoge.mount + +! test -d /etc/hoge +! test -d /run/hoge +test -d /var/lib/hoge +! test -d /var/cache/hoge +test -d /var/log/hoge + +systemctl clean tmp-hoge.mount --what=logs + +! test -d /etc/hoge +! test -d /run/hoge +test -d /var/lib/hoge +! test -d /var/cache/hoge +! test -d /var/log/hoge + +systemctl clean tmp-hoge.mount --what=all + +! test -d /etc/hoge +! test -d /run/hoge +! test -d /var/lib/hoge +! test -d /var/cache/hoge +! test -d /var/log/hoge + +cat > /etc/systemd/system/testservice.socket <<EOF +[Socket] +ListenSequentialPacket=/run/testservice.socket +RemoveOnStop=yes +ExecStartPre=true +ConfigurationDirectory=testsocket +RuntimeDirectory=testsocket +StateDirectory=testsocket +CacheDirectory=testsocket +LogsDirectory=testsocket +EOF + +systemctl daemon-reload + +! test -e /etc/testsocket +! test -e /run/testsocket +! test -e /var/lib/testsocket +! test -e /var/cache/testsocket +! test -e /var/log/testsocket + +systemctl start testservice.socket + +test -d /etc/testsocket +! test -d /run/testsocket +test -d /var/lib/testsocket +test -d /var/cache/testsocket +test -d /var/log/testsocket + +! systemctl clean testservice.socket + +systemctl stop testservice.socket + +test -d /etc/testsocket +! test -d /run/testsocket +test -d /var/lib/testsocket +test -d /var/cache/testsocket +test -d /var/log/testsocket + +systemctl clean testservice.socket --what=configuration + +! test -e /etc/testsocket +! test -d /run/testsocket +test -d /var/lib/testsocket +test -d /var/cache/testsocket +test -d /var/log/testsocket + +systemctl clean testservice.socket + +! test -e /etc/testsocket +! test -e /run/testsocket +test -d /var/lib/testsocket +! test -e /var/cache/testsocket +test -d /var/log/testsocket + +systemctl clean testservice.socket --what=logs + +! test -e /etc/testsocket +! test -e /run/testsocket +test -d /var/lib/testsocket +! test -e /var/cache/testsocket +! test -e /var/log/testsocket + +systemctl clean testservice.socket --what=all + +! test -e /etc/testsocket +! test -e /run/testsocket +! test -e /var/lib/testsocket +! test -e /var/cache/testsocket +! test -e /var/log/testsocket + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-34.service b/test/units/testsuite-34.service new file mode 100644 index 0000000..361e328 --- /dev/null +++ b/test/units/testsuite-34.service @@ -0,0 +1,7 @@ +[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-34.sh b/test/units/testsuite-34.sh new file mode 100755 index 0000000..6d94886 --- /dev/null +++ b/test/units/testsuite-34.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +# Set everything up without DynamicUser=1 + +systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test +systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test +! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing + +test -d /var/lib/zzz +! test -L /var/lib/zzz +! test -e /var/lib/private/zzz +test -f /var/lib/zzz/test +! test -f /var/lib/zzz/test-missing + +# Convert to DynamicUser=1 + +systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test +! systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing + +test -L /var/lib/zzz +test -d /var/lib/private/zzz + +test -f /var/lib/zzz/test +! test -f /var/lib/zzz/test-missing + +# Convert back + +systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test +! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing + +test -d /var/lib/zzz +! test -L /var/lib/zzz +! test -e /var/lib/private/zzz +test -f /var/lib/zzz/test +! test -f /var/lib/zzz/test-missing + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-36.service b/test/units/testsuite-36.service new file mode 100644 index 0000000..a681153 --- /dev/null +++ b/test/units/testsuite-36.service @@ -0,0 +1,7 @@ +[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-36.sh b/test/units/testsuite-36.sh new file mode 100755 index 0000000..aed9384 --- /dev/null +++ b/test/units/testsuite-36.sh @@ -0,0 +1,341 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +at_exit() { + if [ $? -ne 0 ]; then + # We're exiting with a non-zero EC, let's dump test artifacts + # for easier debugging + [ -f "$straceLog" ] && cat "$straceLog" + [ -f "$journalLog" ] && cat "$journalLog" + fi +} + +trap at_exit EXIT + +systemd-analyze log-level debug +systemd-analyze log-target journal + +# Log files +straceLog='strace.log' +journalLog='journal.log' + +# Systemd config files +testUnit='numa-test.service' +testUnitFile="/run/systemd/system/$testUnit" +testUnitNUMAConf="$testUnitFile.d/numa.conf" + +# Sleep constants (we should probably figure out something better but nothing comes to mind) +journalSleep=5 +sleepAfterStart=1 + +# Journal cursor for easier navigation +journalCursorFile="jounalCursorFile" + +startStrace() { + coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1 + # Wait for strace to properly "initialize" + sleep $sleepAfterStart +} + +stopStrace() { + kill -s TERM $COPROC_PID + # Make sure the strace process is indeed dead + while kill -0 $COPROC_PID 2>/dev/null; do sleep 0.1; done +} + +startJournalctl() { + # Save journal's cursor for later navigation + journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat +} + +stopJournalctl() { + local unit="${1:-init.scope}" + # Using journalctl --sync should be better than using SIGRTMIN+1, as + # the --sync wait until the synchronization is complete + echo "Force journald to write all queued messages" + journalctl --sync + journalctl -u $unit --cursor-file="$journalCursorFile" > "$journalLog" +} + +checkNUMA() { + # NUMA enabled system should have at least NUMA node0 + test -e /sys/devices/system/node/node0 +} + +writePID1NUMAPolicy() { + echo [Manager] > $confDir/numa.conf + echo NUMAPolicy=$1 >> $confDir/numa.conf + echo NUMAMask=$2>> $confDir/numa.conf +} + +writeTestUnit() { + mkdir -p $testUnitFile.d/ + echo [Service] > $testUnitFile + echo ExecStart=/bin/sleep 3600 >> $testUnitFile +} + +writeTestUnitNUMAPolicy() { + echo [Service] > $testUnitNUMAConf + echo NUMAPolicy=$1 >> $testUnitNUMAConf + echo NUMAMask=$2>> $testUnitNUMAConf + systemctl daemon-reload +} + +pid1ReloadWithStrace() { + startStrace + systemctl daemon-reload + sleep $sleepAfterStart + stopStrace +} + +pid1ReloadWithJournal() { + startJournalctl + systemctl daemon-reload + stopJournalctl +} + +pid1StartUnitWithStrace() { + startStrace '-f' + systemctl start $1 + sleep $sleepAfterStart + stopStrace +} + +pid1StartUnitWithJournal() { + startJournalctl + systemctl start $1 + sleep $sleepAfterStart + stopJournalctl +} + +pid1StopUnit() { + systemctl stop $1 +} + +systemctlCheckNUMAProperties() { + local LOGFILE="$(mktemp)" + systemctl show -p NUMAPolicy $1 > "$LOGFILE" + grep "NUMAPolicy=$2" "$LOGFILE" + + > "$LOGFILE" + + if [ -n "$3" ]; then + systemctl show -p NUMAMask $1 > "$LOGFILE" + grep "NUMAMask=$3" "$LOGFILE" + fi +} + +writeTestUnit + +# Create systemd config drop-in directory +confDir="/run/systemd/system.conf.d/" +mkdir -p "$confDir" + +if ! checkNUMA; then + echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check" + + echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support" + writePID1NUMAPolicy "default" "0" + startJournalctl + systemctl daemon-reload + stopJournalctl + grep "NUMA support not available, ignoring" "$journalLog" + + echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support" + runUnit='numa-systemd-run-test.service' + startJournalctl + systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000 + sleep $sleepAfterStart + pid1StopUnit $runUnit + stopJournalctl $runUnit + grep "NUMA support not available, ignoring" "$journalLog" + +else + echo "PID1 NUMAPolicy support - Default policy w/o mask" + writePID1NUMAPolicy "default" + pid1ReloadWithStrace + # Kernel requires that nodemask argument is set to NULL when setting default policy + grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog + + echo "PID1 NUMAPolicy support - Default policy w/ mask" + writePID1NUMAPolicy "default" "0" + pid1ReloadWithStrace + grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog + + echo "PID1 NUMAPolicy support - Bind policy w/o mask" + writePID1NUMAPolicy "bind" + pid1ReloadWithJournal + grep "Failed to set NUMA memory policy: Invalid argument" $journalLog + + echo "PID1 NUMAPolicy support - Bind policy w/ mask" + writePID1NUMAPolicy "bind" "0" + pid1ReloadWithStrace + grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog + + echo "PID1 NUMAPolicy support - Interleave policy w/o mask" + writePID1NUMAPolicy "interleave" + pid1ReloadWithJournal + grep "Failed to set NUMA memory policy: Invalid argument" $journalLog + + echo "PID1 NUMAPolicy support - Interleave policy w/ mask" + writePID1NUMAPolicy "interleave" "0" + pid1ReloadWithStrace + grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog + + echo "PID1 NUMAPolicy support - Preferred policy w/o mask" + writePID1NUMAPolicy "preferred" + pid1ReloadWithJournal + # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default + ! grep "Failed to set NUMA memory policy: Invalid argument" $journalLog + + echo "PID1 NUMAPolicy support - Preferred policy w/ mask" + writePID1NUMAPolicy "preferred" "0" + pid1ReloadWithStrace + grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog + + echo "PID1 NUMAPolicy support - Local policy w/o mask" + writePID1NUMAPolicy "local" + pid1ReloadWithStrace + # Kernel requires that nodemask argument is set to NULL when setting default policy + # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and + # return a numerical constant instead (with a comment): + # set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0 + # Let's cover this scenario as well + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "PID1 NUMAPolicy support - Local policy w/ mask" + writePID1NUMAPolicy "local" "0" + pid1ReloadWithStrace + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "Unit file NUMAPolicy support - Default policy w/o mask" + writeTestUnitNUMAPolicy "default" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "default" + pid1StopUnit $testUnit + grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog + + echo "Unit file NUMAPolicy support - Default policy w/ mask" + writeTestUnitNUMAPolicy "default" "0" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "default" "0" + pid1StopUnit $testUnit + # Maks must be ignored + grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog + + echo "Unit file NUMAPolicy support - Bind policy w/o mask" + writeTestUnitNUMAPolicy "bind" + pid1StartUnitWithJournal $testUnit + pid1StopUnit $testUnit + grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog + + echo "Unit file NUMAPolicy support - Bind policy w/ mask" + writeTestUnitNUMAPolicy "bind" "0" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "bind" "0" + pid1StopUnit $testUnit + grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog + + echo "Unit file NUMAPolicy support - Interleave policy w/o mask" + writeTestUnitNUMAPolicy "interleave" + pid1StartUnitWithStrace $testUnit + pid1StopUnit $testUnit + grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog + + echo "Unit file NUMAPolicy support - Interleave policy w/ mask" + writeTestUnitNUMAPolicy "interleave" "0" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "interleave" "0" + pid1StopUnit $testUnit + grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog + + echo "Unit file NUMAPolicy support - Preferred policy w/o mask" + writeTestUnitNUMAPolicy "preferred" + pid1StartUnitWithJournal $testUnit + systemctlCheckNUMAProperties $testUnit "preferred" + pid1StopUnit $testUnit + ! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog + + echo "Unit file NUMAPolicy support - Preferred policy w/ mask" + writeTestUnitNUMAPolicy "preferred" "0" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "preferred" "0" + pid1StopUnit $testUnit + grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog + + echo "Unit file NUMAPolicy support - Local policy w/o mask" + writeTestUnitNUMAPolicy "local" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "local" + pid1StopUnit $testUnit + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "Unit file NUMAPolicy support - Local policy w/ mask" + writeTestUnitNUMAPolicy "local" "0" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "local" "0" + pid1StopUnit $testUnit + # Maks must be ignored + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "Unit file CPUAffinity=NUMA support" + writeTestUnitNUMAPolicy "bind" "0" + echo "CPUAffinity=numa" >> $testUnitNUMAConf + systemctl daemon-reload + systemctl start $testUnit + systemctlCheckNUMAProperties $testUnit "bind" "0" + pid=$(systemctl show --value -p MainPID $testUnit) + cpulist=$(cat /sys/devices/system/node/node0/cpulist) + affinity_systemd=$(systemctl show --value -p CPUAffinity $testUnit) + [ $cpulist = $affinity_systemd ] + pid1StopUnit $testUnit + + echo "systemd-run NUMAPolicy support" + runUnit='numa-systemd-run-test.service' + + systemd-run -p NUMAPolicy=default --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "default" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "default" "" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "bind" "0" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "interleave" "0" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "preferred" "0" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "local" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "local" "" + pid1StopUnit $runUnit + + systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "local" "" + systemctl cat $runUnit | grep -q 'CPUAffinity=numa' + pid1StopUnit $runUnit + +fi + +# Cleanup +rm -rf $testDir +rm -rf $confDir +systemctl daemon-reload + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-37.service b/test/units/testsuite-37.service new file mode 100644 index 0000000..d25c6d2 --- /dev/null +++ b/test/units/testsuite-37.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-37-RUNTIMEDIRECTORYPRESERVE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-37.sh b/test/units/testsuite-37.sh new file mode 100755 index 0000000..32a9dd8 --- /dev/null +++ b/test/units/testsuite-37.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex +set -o pipefail + +systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa + +touch /run/hoge/foo +touch /tmp/aaa/bbb + +systemctl restart tmp-aaa.mount + +test -e /run/hoge/foo +! test -e /tmp/aaa/bbb + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-38-sleep.service b/test/units/testsuite-38-sleep.service new file mode 100644 index 0000000..859f97b --- /dev/null +++ b/test/units/testsuite-38-sleep.service @@ -0,0 +1,2 @@ +[Service] +ExecStart=/bin/sleep 3600 diff --git a/test/units/testsuite-38.service b/test/units/testsuite-38.service new file mode 100644 index 0000000..c848840 --- /dev/null +++ b/test/units/testsuite-38.service @@ -0,0 +1,6 @@ +[Unit] +Description=TEST-38-FREEZER + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-38.sh b/test/units/testsuite-38.sh new file mode 100755 index 0000000..18b7bd6 --- /dev/null +++ b/test/units/testsuite-38.sh @@ -0,0 +1,297 @@ +#!/usr/bin/env bash + +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +unit=testsuite-38-sleep.service + +start_test_service() { + systemctl daemon-reload + systemctl start "${unit}" +} + +dbus_freeze() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Freeze +} + +dbus_thaw() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Thaw +} + +dbus_freeze_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + FreezeUnit \ + s \ + "$1" +} + +dbus_thaw_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + ThawUnit \ + s \ + "$1" +} + +dbus_can_freeze() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + CanFreeze +} + +check_freezer_state() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + state=$(busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + FreezerState | cut -d " " -f2 | tr -d '"') + + [ "$state" = "$2" ] || { + echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 + exit 1 + } +} + +check_cgroup_state() { + grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events +} + +test_dbus_api() { + echo "Test that DBus API works:" + echo -n " - Freeze(): " + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - Thaw(): " + dbus_thaw "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - FreezeUnit(): " + dbus_freeze_unit "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - ThawUnit(): " + dbus_thaw_unit "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - CanFreeze(): " + output=$(dbus_can_freeze "${unit}") + [ "$output" = "b true" ] + echo "[ OK ]" + + echo +} + +test_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 +} + +test_systemctl() { + echo "Test that systemctl freeze/thaw verbs:" + + systemctl start "$unit" + + echo -n " - freeze: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + # Freezing already frozen unit should be NOP and return quickly + timeout 3s systemctl freeze "$unit" + echo "[ OK ]" + + echo -n " - thaw: " + systemctl thaw "$unit" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + # Likewise thawing already running unit shouldn't block + timeout 3s systemctl thaw "$unit" + echo "[ OK ]" + + systemctl stop "$unit" + + echo +} + +test_systemctl_show() { + echo "Test systemctl show integration:" + + systemctl start "$unit" + + echo -n " - FreezerState property: " + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "running" ] + systemctl freeze "$unit" + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "frozen" ] + systemctl thaw "$unit" + echo "[ OK ]" + + echo -n " - CanFreeze property: " + state=$(systemctl show -p CanFreeze --value "$unit") + [ "$state" = "yes" ] + echo "[ OK ]" + + systemctl stop "$unit" + echo +} + +test_recursive() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test recursive freezing:" + + echo -n " - freeze: " + 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 + echo "[ OK ]" + + echo -n " - thaw: " + 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 + echo "[ OK ]" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +test_preserve_state() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" + + echo -n " - freeze from outside: " + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + # Give kernel some time to freeze the slice + sleep 1 + + # Our state should not be affected + check_freezer_state "${slice}" "running" + check_freezer_state "${unit}" "running" + + # However actual kernel state should be frozen + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw from outside: " + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + sleep 1 + + check_freezer_state "${unit}" "running" + check_freezer_state "${slice}" "running" + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw from outside while inner service is frozen: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + check_freezer_state "${slice}" "running" + check_freezer_state "${unit}" "frozen" + echo "[ OK ]" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +test -e /sys/fs/cgroup/system.slice/cgroup.freeze && { + start_test_service + test_dbus_api + test_systemctl + test_systemctl_show + test_jobs + test_recursive + test_preserve_state +} + +echo OK > /testok +exit 0 diff --git a/test/units/testsuite-39.service b/test/units/testsuite-39.service new file mode 100644 index 0000000..395fe80 --- /dev/null +++ b/test/units/testsuite-39.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-39-EXECRELOAD + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-39.sh b/test/units/testsuite-39.sh new file mode 100755 index 0000000..eb7363f --- /dev/null +++ b/test/units/testsuite-39.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +export SYSTEMD_PAGER= +SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)" +SERVICE_NAME="${SERVICE_PATH##*/}" + +echo "[#1] Failing ExecReload= should not kill the service" +cat > "$SERVICE_PATH" << EOF +[Service] +ExecStart=/bin/sleep infinity +ExecReload=/bin/false +EOF + +systemctl daemon-reload +systemctl start $SERVICE_NAME +systemctl status $SERVICE_NAME +# The reload SHOULD fail but SHOULD NOT affect the service state +! systemctl reload $SERVICE_NAME +systemctl status $SERVICE_NAME +systemctl stop $SERVICE_NAME + + +echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)" +cat > "$SERVICE_PATH" << EOF +[Service] +ExecStart=/bin/sleep infinity +ExecReload=/bin/true +ExecReload=/bin/false +ExecReload=/bin/true +EOF + +systemctl daemon-reload +systemctl start $SERVICE_NAME +systemctl status $SERVICE_NAME +# The reload SHOULD fail but SHOULD NOT affect the service state +! systemctl reload $SERVICE_NAME +systemctl status $SERVICE_NAME +systemctl stop $SERVICE_NAME + +echo "[#3] Failing ExecReload=- should not affect reload's exit code" +cat > "$SERVICE_PATH" << EOF +[Service] +ExecStart=/bin/sleep infinity +ExecReload=-/bin/false +EOF + +systemctl daemon-reload +systemctl start $SERVICE_NAME +systemctl status $SERVICE_NAME +systemctl reload $SERVICE_NAME +systemctl status $SERVICE_NAME +systemctl stop $SERVICE_NAME + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-40.service b/test/units/testsuite-40.service new file mode 100644 index 0000000..38b0bd8 --- /dev/null +++ b/test/units/testsuite-40.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-40-EXEC-COMMAND-EX + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-40.sh b/test/units/testsuite-40.sh new file mode 100755 index 0000000..957d220 --- /dev/null +++ b/test/units/testsuite-40.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +declare -A property + +property[1_one]=ExecCondition +property[2_two]=ExecStartPre +property[3_three]=ExecStart +property[4_four]=ExecStartPost +property[5_five]=ExecReload +property[6_six]=ExecStop +property[7_seven]=ExecStopPost + +# These should all get upgraded to the corresponding Ex property as the non-Ex variant +# does not support the ":" prefix (no-env-expand). +for c in "${!property[@]}"; do + systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true + systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no" + systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand" +done + +declare -A property_ex + +property_ex[1_one_ex]=ExecConditionEx +property_ex[2_two_ex]=ExecStartPreEx +property_ex[3_three_ex]=ExecStartEx +property_ex[4_four_ex]=ExecStartPostEx +property_ex[5_five_ex]=ExecReloadEx +property_ex[6_six_ex]=ExecStopEx +property_ex[7_seven_ex]=ExecStopPostEx + +for c in "${!property_ex[@]}"; do + systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true + systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no" + systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand" +done + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-41.service b/test/units/testsuite-41.service new file mode 100644 index 0000000..766cb4c --- /dev/null +++ b/test/units/testsuite-41.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-41-ONESHOT-RESTART + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-41.sh b/test/units/testsuite-41.sh new file mode 100755 index 0000000..81fa171 --- /dev/null +++ b/test/units/testsuite-41.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +# wait this many secs for each test service to succeed in what is being tested +MAX_SECS=60 + +systemd-analyze log-level debug +systemd-analyze log-target console + +# test one: Restart=on-failure should restart the service +! systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1" + +for ((secs=0; secs<$MAX_SECS; secs++)); do + [[ "$(systemctl show one.service -P NRestarts)" -le 0 ]] || break + sleep 1 +done +if [[ "$(systemctl show 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=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 + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-42.service b/test/units/testsuite-42.service new file mode 100644 index 0000000..a5504b5 --- /dev/null +++ b/test/units/testsuite-42.service @@ -0,0 +1,9 @@ +[Unit] +Description=TEST-42-EXECSTOPPOST +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-42.sh b/test/units/testsuite-42.sh new file mode 100755 index 0000000..154398d --- /dev/null +++ b/test/units/testsuite-42.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -ex + +systemd-analyze log-level debug + +systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple1' true +test -f /run/simple1 + +! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false +test -f /run/simple2 + +systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec1' sleep 1 +test -f /run/exec1 + +! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false' +test -f /run/exec2 + +cat > /tmp/forking1.sh <<EOF +#!/usr/bin/env bash + +set -eux + +sleep 4 & +MAINPID=\$! +disown + +systemd-notify MAINPID=\$MAINPID +EOF +chmod +x /tmp/forking1.sh + +systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh +test -f /run/forking1 + +cat > /tmp/forking2.sh <<EOF +#!/usr/bin/env bash + +set -eux + +( sleep 4; exit 1 ) & +MAINPID=\$! +disown + +systemd-notify MAINPID=\$MAINPID +EOF +chmod +x /tmp/forking2.sh + +! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh +test -f /run/forking2 + +systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot1' true +test -f /run/oneshot1 + +! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false +test -f /run/oneshot2 + +systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus1' \ + busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 \ + || : +test -f /run/dbus1 + +! systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true +test -f /run/dbus2 + +cat > /tmp/notify1.sh <<EOF +#!/usr/bin/env bash + +set -eux + +systemd-notify --ready +EOF +chmod +x /tmp/notify1.sh + +systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh +test -f /run/notify1 + +! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true +test -f /run/notify2 + +systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true +test -f /run/idle1 + +! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false +test -f /run/idle2 + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-43.service b/test/units/testsuite-43.service new file mode 100644 index 0000000..31248f1 --- /dev/null +++ b/test/units/testsuite-43.service @@ -0,0 +1,9 @@ +[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-43.sh b/test/units/testsuite-43.sh new file mode 100755 index 0000000..ec84868 --- /dev/null +++ b/test/units/testsuite-43.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug + +runas() { + declare userid=$1 + shift + su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@" +} + +runas testuser systemd-run --wait --user --unit=test-private-users \ + -p PrivateUsers=yes -P echo hello + +runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \ + -p PrivateUsers=yes -p PrivateTmp=yes \ + -P touch /tmp/innerfile.txt +# File should not exist outside the job's tmp directory. +test ! -e /tmp/innerfile.txt + +touch /tmp/outerfile.txt +# File should not appear in unit's private tmp. +runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \ + -p PrivateUsers=yes -p PrivateTmp=yes \ + -P test ! -e /tmp/outerfile.txt + +# Confirm that creating a file in home works +runas testuser systemd-run --wait --user --unit=test-unprotected-home \ + -P touch /home/testuser/works.txt +test -e /home/testuser/works.txt + +# Confirm that creating a file in home is blocked under read-only +runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \ + -p PrivateUsers=yes -p ProtectHome=read-only \ + -P bash -c ' + test -e /home/testuser/works.txt + ! touch /home/testuser/blocked.txt + ' +test ! -e /home/testuser/blocked.txt + +# Check that tmpfs hides the whole directory +runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \ + -p PrivateUsers=yes -p ProtectHome=tmpfs \ + -P test ! -e /home/testuser + +# Confirm that home, /root, and /run/user are inaccessible under "yes" +runas testuser systemd-run --wait --user --unit=test-protect-home-yes \ + -p PrivateUsers=yes -p ProtectHome=yes \ + -P bash -c ' + test "$(stat -c %a /home)" = "0" + test "$(stat -c %a /root)" = "0" + test "$(stat -c %a /run/user)" = "0" + ' + +# Confirm we cannot change groups because we only have one mapping in the user +# namespace (no CAP_SETGID in the parent namespace to write the additional +# mapping of the user supplied group and thus cannot change groups to an +# unmapped group ID) +! runas testuser systemd-run --wait --user --unit=test-group-fail \ + -p PrivateUsers=yes -p Group=daemon \ + -P true + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-44.service b/test/units/testsuite-44.service new file mode 100644 index 0000000..bd4dd72 --- /dev/null +++ b/test/units/testsuite-44.service @@ -0,0 +1,12 @@ +[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 +LogTarget=foobar diff --git a/test/units/testsuite-44.sh b/test/units/testsuite-44.sh new file mode 100755 index 0000000..9754163 --- /dev/null +++ b/test/units/testsuite-44.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -ex + +systemd-analyze log-level debug + +systemd-run -p LogNamespace=foobar echo "hello world" + +journalctl --namespace=foobar --sync +journalctl --namespace=foobar > /tmp/hello-world +journalctl > /tmp/no-hello-world + +grep "hello world" /tmp/hello-world +! grep "hello world" /tmp/no-hello-world + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-46.service b/test/units/testsuite-46.service new file mode 100644 index 0000000..7698f35 --- /dev/null +++ b/test/units/testsuite-46.service @@ -0,0 +1,12 @@ +[Unit] +Description=TEST-46-HOMED +Wants=getty-pre.target +Before=getty-pre.target +Wants=systemd-homed.service +After=systemd-homed.service + +[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 new file mode 100755 index 0000000..00bbdf5 --- /dev/null +++ b/test/units/testsuite-46.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +set -ex +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 OK > /testok + 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. + homectl inspect $1 | tee /tmp/a + userdbctl user $1 | tee /tmp/b + + local PATTERN='/^\s*Disk (Size|Free|Floor|Ceiling):/d' + diff <(sed -r "$PATTERN" /tmp/a) <(sed -r "$PATTERN" /tmp/b) + rm /tmp/a /tmp/b +} + +systemd-analyze log-level debug +systemd-analyze log-target console + +NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=20M +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 + +PASSWORD=xEhErW0ndafV4s 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 + +PASSWORD=xEhErW0ndafV4s homectl deactivate test-user +inspect test-user + +! 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 + +homectl remove test-user + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-47-repro.service b/test/units/testsuite-47-repro.service new file mode 100644 index 0000000..655eea6 --- /dev/null +++ b/test/units/testsuite-47-repro.service @@ -0,0 +1,7 @@ +[Unit] +Description=Issue 14566 Repro + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStopPost=/bin/true +KillMode=mixed diff --git a/test/units/testsuite-47-repro.sh b/test/units/testsuite-47-repro.sh new file mode 100755 index 0000000..8c34289 --- /dev/null +++ b/test/units/testsuite-47-repro.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +sleep infinity & +echo $! > /leakedtestpid +wait $! diff --git a/test/units/testsuite-47.service b/test/units/testsuite-47.service new file mode 100644 index 0000000..3816c57 --- /dev/null +++ b/test/units/testsuite-47.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-47-ISSUE-14566 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-47.sh b/test/units/testsuite-47.sh new file mode 100755 index 0000000..50034cf --- /dev/null +++ b/test/units/testsuite-47.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +systemctl start testsuite-47-repro +sleep 4 +systemctl status testsuite-47-repro + +leaked_pid=$(cat /leakedtestpid) + +systemctl stop testsuite-47-repro +sleep 4 + +# Leaked PID will still be around if we're buggy. +# I personally prefer to see 42. +ps -p "$leaked_pid" && exit 42 + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-48.service b/test/units/testsuite-48.service new file mode 100644 index 0000000..9dc50ab --- /dev/null +++ b/test/units/testsuite-48.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-48-START-STOP-NO-RELOAD + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-48.sh b/test/units/testsuite-48.sh new file mode 100755 index 0000000..03231e7 --- /dev/null +++ b/test/units/testsuite-48.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex + +cat > /run/systemd/system/testservice-48.target <<EOF +[Unit] +Wants=testservice-48.service +EOF + +systemctl daemon-reload + +systemctl start testservice-48.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 systemd-testsuite testsuite-48.sh[30]: + cat +# May 07 23:12:20 systemd-testsuite testsuite-48.sh[30]: + ls -l --full-time /etc/systemd/system/testservice-48.service +# May 07 23:12:20 systemd-testsuite testsuite-48.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 / +# May 07 23:12:20 systemd-testsuite testsuite-48.sh[30]: + stat -f --format=%t /etc/systemd/system/testservice-48.servic +# May 07 23:12:20 systemd-testsuite testsuite-48.sh[53]: ef53 +sleep 3.1 + +cat > /run/systemd/system/testservice-48.service <<EOF +[Service] +ExecStart=/bin/sleep infinity +EOF + +systemctl start testservice-48.service + +systemctl is-active testservice-48.service + +# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992 +systemctl stop testservice-48.service +rm -f /run/systemd/system/testservice-48.service +systemctl daemon-reload + +sleep 3.1 + +cat > /run/systemd/system/testservice-48.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 testservice-48-nonexistent.service || true + +systemctl start testservice-48.service + +systemctl is-active testservice-48.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 testservice-48.service testservice-48.target +rm -f /run/systemd/system/testservice-48.service /run/systemd/system/testservice-48.target +systemctl daemon-reload + +sleep 3.1 + +cat > /run/systemd/system/testservice-48.target <<EOF +[Unit] +Conflicts=shutdown.target +Wants=testservice-48.service +EOF + +systemctl daemon-reload + +systemctl start testservice-48.target + +cat > /run/systemd/system/testservice-48.service <<EOF +[Service] +ExecStart=/bin/sleep infinity +EOF + +systemctl restart testservice-48.target + +systemctl is-active testservice-48.service + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-49.service b/test/units/testsuite-49.service new file mode 100644 index 0000000..f471771 --- /dev/null +++ b/test/units/testsuite-49.service @@ -0,0 +1,6 @@ +[Unit] +Description=TEST-49-UDEV-EVENT-TIMEOUT + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-49.sh b/test/units/testsuite-49.sh new file mode 100755 index 0000000..ffa9801 --- /dev/null +++ b/test/units/testsuite-49.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -ex + +test_rule="/run/udev/rules.d/49-test.rules" + +setup() { + mkdir -p "${test_rule%/*}" + cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bckp + echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' > "${test_rule}" + echo "event_timeout=30" >> /etc/udev/udev.conf + echo "timeout_signal=SIGABRT" >> /etc/udev/udev.conf + + systemctl restart systemd-udevd.service +} + +teardown() { + set +e + + mv -f /etc/udev/udev.conf.bckp /etc/udev/udev.conf + rm -f "$test_rule" + systemctl restart systemd-udevd.service +} + +run_test() { + since="$(date +%T)" + + echo add > /sys/class/net/lo/uevent + + for n in {1..20}; do + sleep 5 + if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then + return 0 + fi + done + + return 1 +} + +trap teardown EXIT + +setup +run_test + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service new file mode 100644 index 0000000..5a10a64 --- /dev/null +++ b/test/units/testsuite-50.service @@ -0,0 +1,7 @@ +[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 new file mode 100755 index 0000000..d615ac2 --- /dev/null +++ b/test/units/testsuite-50.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +cleanup() +{ + if [ -z "${image_dir}" ]; then + return + fi + rm -rf "${image_dir}" +} + +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" +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,"fstype":"squashfs","architecture":null,"verity":"external"' +systemd-dissect ${image}.raw | grep -q -F "MARKER=1" +systemd-dissect ${image}.raw | grep -q -F -f $os_release + +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,"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 $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 +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f $os_release +cat ${image_dir}/mount/etc/os-release | grep -q -F -f $os_release +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1" +# Verity volume should be shared (opened only once) +systemd-dissect --mount ${image}.raw ${image_dir}/mount2 +verity_count=$(ls -1 /dev/mapper/ | grep -c verity) +# 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 +umount ${image_dir}/mount +umount ${image_dir}/mount2 + +systemd-run -t -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 -t -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 -t --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 + 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 + 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 + architecture="arm64" +elif [ "${machine}" = "arm" ]; then + root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3 + verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6 + architecture="arm" +elif [ "${machine}" = "ia64" ]; then + root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97 + verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571 + architecture="ia64" +elif [ "${machine}" = "ppc64le" ]; then + # There's no support of PPC in the discoverable partitions specification yet, so skip the rest for now + echo OK >/testok + exit 0 +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)" +# 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)) +# 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" +uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)" +echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt +uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)" +echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append +sfdisk --part-label ${image}.gpt 1 "Root Partition" +sfdisk --part-label ${image}.gpt 2 "Verity Partition" +loop="$(losetup --show -P -f ${image}.gpt)" +dd if=${image}.raw of=${loop}p1 +dd if=${image}.verity of=${loop}p2 +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'","fstype":"squashfs","architecture":"'$architecture'","verity":"yes","node":' +systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'$VERITY_UUID'","fstype":"DM_verity_hash","architecture":"'$architecture'","verity":null,"node":' +systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1" +systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f $os_release + +systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f $os_release +cat ${image_dir}/mount/etc/os-release | grep -q -F -f $os_release +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1" +umount ${image_dir}/mount + +# add explicit -p MountAPIVFS=yes once to test the parser +systemd-run -t -p RootImage=${image}.gpt -p RootHash=${roothash} -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" + +systemd-run -t -p RootImage=${image}.raw -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -t -p RootImage=${image}.gpt -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime" + +mkdir -p 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 -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -t -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -t -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -t -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -t -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 -t -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 -t -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" + +echo OK >/testok + +exit 0 diff --git a/test/units/testsuite-51-repro-1.service b/test/units/testsuite-51-repro-1.service new file mode 100644 index 0000000..96ecabe --- /dev/null +++ b/test/units/testsuite-51-repro-1.service @@ -0,0 +1,9 @@ +[Unit] +Description=Issue 16115 Repro with on-abnormal + +[Service] +Type=simple +Restart=on-abnormal +ExecCondition=/bin/false +ExecStart=sleep 100 +RestartSec=1 diff --git a/test/units/testsuite-51-repro-2.service b/test/units/testsuite-51-repro-2.service new file mode 100644 index 0000000..6015ad8 --- /dev/null +++ b/test/units/testsuite-51-repro-2.service @@ -0,0 +1,9 @@ +[Unit] +Description=Issue 16115 Repro with on-failure + +[Service] +Type=simple +Restart=on-failure +ExecCondition=/bin/false +ExecStart=sleep 100 +RestartSec=1 diff --git a/test/units/testsuite-51.service b/test/units/testsuite-51.service new file mode 100644 index 0000000..903dc9a --- /dev/null +++ b/test/units/testsuite-51.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-51-ISSUE-16115 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-51.sh b/test/units/testsuite-51.sh new file mode 100755 index 0000000..246412a --- /dev/null +++ b/test/units/testsuite-51.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemctl start testsuite-51-repro-1 +systemctl start testsuite-51-repro-2 +sleep 5 # wait a bit in case there are restarts so we can count them below + +[[ "$(systemctl show testsuite-51-repro-1 -P NRestarts)" == "0" ]] +[[ "$(systemctl show testsuite-51-repro-2 -P NRestarts)" == "0" ]] + +touch /testok diff --git a/test/units/testsuite-53.service b/test/units/testsuite-53.service new file mode 100644 index 0000000..d4dd8cc --- /dev/null +++ b/test/units/testsuite-53.service @@ -0,0 +1,7 @@ +[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-53.sh b/test/units/testsuite-53.sh new file mode 100755 index 0000000..3536c24 --- /dev/null +++ b/test/units/testsuite-53.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +>/failed + +# Reset host date to current time, 3 days in the past. +date -s "-3 days" + +# Run a timer for every 15 minutes. +systemd-run --unit test-timer --on-calendar "*:0/15:0" true + +next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value) +next_elapsed=$(date -d "${next_elapsed}" +%s) +now=$(date +%s) +time_delta=$((next_elapsed - now)) + +# Check that the timer will elapse in less than 20 minutes. +((0 < time_delta && time_delta < 1200)) || { + echo 'Timer elapse outside of the expected 20 minute window.' + echo " next_elapsed=${next_elapsed}" + echo " now=${now}" + echo " time_delta=${time_delta}" + echo '' +} >>/failed + +if test ! -s /failed ; then + rm -f /failed + touch /testok +fi diff --git a/test/units/testsuite-54.service b/test/units/testsuite-54.service new file mode 100644 index 0000000..862dd1c --- /dev/null +++ b/test/units/testsuite-54.service @@ -0,0 +1,7 @@ +[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-54.sh b/test/units/testsuite-54.sh new file mode 100755 index 0000000..aabc56f --- /dev/null +++ b/test/units/testsuite-54.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -ex + +systemd-analyze log-level debug + +# Verify that the creds are properly loaded and we can read them from the service's unpriv user +systemd-run -p LoadCredential=passwd:/etc/passwd \ + -p LoadCredential=shadow:/etc/shadow \ + -p SetCredential=dog:wuff \ + -p DynamicUser=1 \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' > /tmp/ts54-concat +( cat /etc/passwd /etc/shadow && echo -n wuff ) | cmp /tmp/ts54-concat +rm /tmp/ts54-concat + +# Verify that the creds are immutable +! systemd-run -p LoadCredential=passwd:/etc/passwd \ + -p DynamicUser=1 \ + --wait \ + touch '${CREDENTIALS_DIRECTORY}/passwd' +! systemd-run -p LoadCredential=passwd:/etc/passwd \ + -p DynamicUser=1 \ + --wait \ + rm '${CREDENTIALS_DIRECTORY}/passwd' + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-55.service b/test/units/testsuite-55.service new file mode 100644 index 0000000..2127a4d --- /dev/null +++ b/test/units/testsuite-55.service @@ -0,0 +1,7 @@ +[Unit] +Description=TESTSUITE-55-UDEV-TAGS + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-55.sh b/test/units/testsuite-55.sh new file mode 100755 index 0000000..ffceefb --- /dev/null +++ b/test/units/testsuite-55.sh @@ -0,0 +1,66 @@ +#!/bin/bash +set -ex +set -o pipefail + +mkdir -p /run/udev/rules.d/ + +! test -f /run/udev/tags/added/c1:3 && + ! test -f /run/udev/tags/changed/c1:3 && + udevadm info /dev/null | grep -q -v 'E: TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q -v 'E: TAGS=.*:changed:.*' && + udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' + +cat > /run/udev/rules.d/50-testsuite.rules <<EOF +ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added" +ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed" +EOF + +udevadm control --reload +udevadm trigger -c add /dev/null + +while : ; do + test -f /run/udev/tags/added/c1:3 && + ! test -f /run/udev/tags/changed/c1:3 && + udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q -v 'E: TAGS=.*:changed:.*' && + udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' && + break + + sleep .5 +done + +udevadm control --reload +udevadm trigger -c change /dev/null + +while : ; do + test -f /run/udev/tags/added/c1:3 && + test -f /run/udev/tags/changed/c1:3 && + udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && + udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && + break + + sleep .5 +done + +udevadm control --reload +udevadm trigger -c add /dev/null + +while : ; do + test -f /run/udev/tags/added/c1:3 && + test -f /run/udev/tags/changed/c1:3 && + udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' && + udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && + udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' && + break + + sleep .5 +done + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-56-slowgrowth.sh b/test/units/testsuite-56-slowgrowth.sh new file mode 100755 index 0000000..ff5a747 --- /dev/null +++ b/test/units/testsuite-56-slowgrowth.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +PAGE_SIZE=$(getconf PAGE_SIZE) +BLOAT_ITERATION_TARGET=$(( 100 << 20 )) # 100 MB +BLOAT_HOLDER=() +PID="$$" + +function bloat { + local set_size=$(cat "/proc/$PID/statm" | cut -d " " -f2) + local mem_usage=$(( "$set_size" * "$PAGE_SIZE" )) + local target_mem_size=$(( "$mem_usage" + "$1" )) + + BLOAT_HOLDER=() + while [[ "$mem_usage" -lt "$target_mem_size" ]]; do + echo "target $target_mem_size" + echo "mem usage $mem_usage" + BLOAT_HOLDER+=( $(printf "%0.sg" {1..1000000}) ) + set_size=$(cat "/proc/$PID/statm" | cut -d " " -f2) + mem_usage=$(( "$set_size" * "$PAGE_SIZE" )) + done +} + +function run { + local arr=() + + while [[ true ]]; do + bloat "$BLOAT_ITERATION_TARGET" + arr+=( "$BLOAT_HOLDER" ) + sleep 1 + done +} + +run diff --git a/test/units/testsuite-56-testbloat.service b/test/units/testsuite-56-testbloat.service new file mode 100644 index 0000000..40cf5a9 --- /dev/null +++ b/test/units/testsuite-56-testbloat.service @@ -0,0 +1,9 @@ +[Unit] +Description=Create a lot of memory pressure + +[Service] +# A very small memory.high will cause the script (trying to use a lot of memory) +# to throttle and be put under heavy pressure +MemoryHigh=2M +Slice=testsuite-56-workload.slice +ExecStart=/usr/lib/systemd/tests/testdata/units/testsuite-56-slowgrowth.sh diff --git a/test/units/testsuite-56-testchill.service b/test/units/testsuite-56-testchill.service new file mode 100644 index 0000000..6cae3d8 --- /dev/null +++ b/test/units/testsuite-56-testchill.service @@ -0,0 +1,6 @@ +[Unit] +Description=No memory pressure + +[Service] +Slice=testsuite-56-workload.slice +ExecStart=sleep infinity diff --git a/test/units/testsuite-56-workload.slice b/test/units/testsuite-56-workload.slice new file mode 100644 index 0000000..3d542ec --- /dev/null +++ b/test/units/testsuite-56-workload.slice @@ -0,0 +1,10 @@ +[Unit] +Description=Test slice for memory pressure kills + +[Slice] +CPUAccounting=true +MemoryAccounting=true +IOAccounting=true +TasksAccounting=true +ManagedOOMMemoryPressure=kill +ManagedOOMMemoryPressureLimitPercent=50% diff --git a/test/units/testsuite-56.service b/test/units/testsuite-56.service new file mode 100644 index 0000000..b53b090 --- /dev/null +++ b/test/units/testsuite-56.service @@ -0,0 +1,7 @@ +[Unit] +Description=TESTSUITE-56-OOMD + +[Service] +ExecStartPre=rm -f /failed /skipped /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-56.sh b/test/units/testsuite-56.sh new file mode 100755 index 0000000..37d62d9 --- /dev/null +++ b/test/units/testsuite-56.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +# Loose checks to ensure the environment has the necessary features for systemd-oomd +[[ "$( awk '/SwapTotal/ { print $2 }' /proc/meminfo )" != "0" ]] || echo "no swap" >> /skipped +[[ -e /proc/pressure ]] || echo "no PSI" >> /skipped +cgroup_type=$(stat -fc %T /sys/fs/cgroup/) +if [[ "$cgroup_type" != *"cgroup2"* ]] && [[ "$cgroup_type" != *"0x63677270"* ]]; then + echo "no cgroup2" >> /skipped +fi +[[ -e /skipped ]] && exit 0 || true + +systemctl start testsuite-56-testbloat.service +systemctl start testsuite-56-testchill.service + +# Verify systemd-oomd is monitoring the expected units +oomctl | grep "/testsuite-56-workload.slice" +oomctl | grep "50%" + +# systemd-oomd watches for elevated pressure for 30 seconds before acting. +# It can take time to build up pressure so either wait 5 minutes or for the service to fail. +timeout=$(date -ud "5 minutes" +%s) +while [[ $(date -u +%s) -le $timeout ]]; do + if ! systemctl status testsuite-56-testbloat.service; then + break + fi + sleep 15 +done + +# testbloat should be killed and testchill should be fine +if systemctl status testsuite-56-testbloat.service; then exit 42; fi +if ! systemctl status testsuite-56-testchill.service; then exit 24; fi + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite.target b/test/units/testsuite.target new file mode 100644 index 0000000..1a7e5b3 --- /dev/null +++ b/test/units/testsuite.target @@ -0,0 +1,6 @@ +[Unit] +Description=Testsuite target +Requires=multi-user.target +After=multi-user.target +Conflicts=rescue.target +AllowIsolate=yes diff --git a/test/units/timers.target b/test/units/timers.target new file mode 100644 index 0000000..99f82e3 --- /dev/null +++ b/test/units/timers.target @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Timers +Documentation=man:systemd.special(7) + +DefaultDependencies=no +Conflicts=shutdown.target diff --git a/test/units/unit-.service.d/10-override.conf b/test/units/unit-.service.d/10-override.conf new file mode 100644 index 0000000..916737d --- /dev/null +++ b/test/units/unit-.service.d/10-override.conf @@ -0,0 +1,2 @@ +[Unit] +Description=override0 diff --git a/test/units/unit-with-.service.d/20-override.conf b/test/units/unit-with-.service.d/20-override.conf new file mode 100644 index 0000000..c6c2438 --- /dev/null +++ b/test/units/unit-with-.service.d/20-override.conf @@ -0,0 +1,2 @@ +[Unit] +Documentation=man:override1 diff --git a/test/units/unit-with-multiple-.service.d/20-override.conf b/test/units/unit-with-multiple-.service.d/20-override.conf new file mode 100644 index 0000000..62fafd2 --- /dev/null +++ b/test/units/unit-with-multiple-.service.d/20-override.conf @@ -0,0 +1,2 @@ +[Unit] +Documentation=man:override2 diff --git a/test/units/unit-with-multiple-.service.d/30-override.conf b/test/units/unit-with-multiple-.service.d/30-override.conf new file mode 100644 index 0000000..b9616da --- /dev/null +++ b/test/units/unit-with-multiple-.service.d/30-override.conf @@ -0,0 +1,2 @@ +[Unit] +Documentation=man:override3 diff --git a/test/units/unit-with-multiple-dashes.service b/test/units/unit-with-multiple-dashes.service new file mode 100644 index 0000000..b38b360 --- /dev/null +++ b/test/units/unit-with-multiple-dashes.service @@ -0,0 +1,6 @@ +[Unit] +Description=A unit with multiple dashes +Documentation=man:test + +[Service] +ExecStart=/bin/true diff --git a/test/units/unit-with-multiple-dashes.service.d/10-override.conf b/test/units/unit-with-multiple-dashes.service.d/10-override.conf new file mode 100644 index 0000000..982c362 --- /dev/null +++ b/test/units/unit-with-multiple-dashes.service.d/10-override.conf @@ -0,0 +1,2 @@ +[Unit] +Description=override4 diff --git a/test/units/unstoppable.service b/test/units/unstoppable.service new file mode 100644 index 0000000..56b72c9 --- /dev/null +++ b/test/units/unstoppable.service @@ -0,0 +1,5 @@ +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/echo "I'm unstoppable!" +ExecStop=/bin/systemctl start --no-block unstoppable.service |