2022-06-10 07:55:54 +03:00
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
2023-05-16 20:09:13 +03:00
# Utility functions for shell tests
2022-06-10 07:55:54 +03:00
2023-09-23 19:17:04 +03:00
# shellcheck disable=SC2034
[ [ -e /var/tmp/.systemd_reboot_count ] ] && REBOOT_COUNT = " $( </var/tmp/.systemd_reboot_count) " || REBOOT_COUNT = 0
2024-10-16 08:31:16 +03:00
assert_ok( ) { (
2022-06-14 03:00:00 +03:00
set +ex
2022-06-28 00:31:49 +03:00
local rc
2022-06-10 07:55:54 +03:00
" $@ "
rc = $?
2024-10-16 08:31:16 +03:00
if [ [ " $rc " -ne 0 ] ] ; then
2022-06-10 07:55:54 +03:00
echo " FAIL: command ' $* ' failed with exit code $rc " >& 2
exit 1
fi
2022-06-14 03:00:00 +03:00
) }
2024-10-16 08:31:16 +03:00
assert_fail( ) { (
set +ex
local rc
if " $@ " ; then
echo " FAIL: command ' $* ' unexpectedly succeeded " >& 2
exit 1
fi
) }
2022-06-14 03:00:00 +03:00
assert_eq( ) { (
set +ex
2022-06-10 07:55:54 +03:00
2022-06-14 03:00:00 +03:00
if [ [ " ${ 1 ? } " != " ${ 2 ? } " ] ] ; then
2022-06-10 07:55:54 +03:00
echo " FAIL: expected: ' $2 ' actual: ' $1 ' " >& 2
exit 1
fi
2022-06-14 03:00:00 +03:00
) }
2024-10-25 10:29:38 +03:00
assert_neq( ) { (
set +ex
if [ [ " ${ 1 ? } " = " ${ 2 ? } " ] ] ; then
echo " FAIL: not expected: ' $2 ' actual: ' $1 ' " >& 2
exit 1
fi
) }
2023-08-09 23:43:31 +03:00
assert_le( ) { (
set +ex
if [ [ " ${ 1 : ? } " -gt " ${ 2 : ? } " ] ] ; then
echo " FAIL: ' $1 ' > ' $2 ' " >& 2
exit 1
fi
) }
2022-06-14 03:00:00 +03:00
assert_in( ) { (
set +ex
2022-06-10 07:55:54 +03:00
2022-06-14 03:00:00 +03:00
if ! [ [ " ${ 2 ? } " = ~ ${ 1 ? } ] ] ; then
2022-06-10 07:55:54 +03:00
echo " FAIL: ' $1 ' not found in: " >& 2
echo " $2 " >& 2
exit 1
fi
2022-06-14 03:00:00 +03:00
) }
assert_not_in( ) { (
set +ex
if [ [ " ${ 2 ? } " = ~ ${ 1 ? } ] ] ; then
echo " FAIL: ' $1 ' found in: " >& 2
echo " $2 " >& 2
exit 1
fi
) }
assert_rc( ) { (
set +ex
2022-06-10 07:55:54 +03:00
2022-06-28 00:31:49 +03:00
local rc exp = " ${ 1 ? } "
2022-06-10 07:55:54 +03:00
shift
" $@ "
rc = $?
assert_eq " $rc " " $exp "
2022-06-14 03:00:00 +03:00
) }
2023-05-16 20:11:51 +03:00
2023-09-23 19:17:04 +03:00
assert_not_reached( ) {
echo >& 2 " Code should not be reached at ${ BASH_SOURCE [1] } : ${ BASH_LINENO [1] } , function ${ FUNCNAME [1] } () "
exit 1
}
2023-06-27 17:15:24 +03:00
run_and_grep( ) { (
set +ex
local expression
local log ec
local exp_ec = 0
# Invert the grep condition - i.e. check if the expression is _not_ in command's output
if [ [ " ${ 1 : ? } " = = "-n" ] ] ; then
exp_ec = 1
shift
fi
expression = " ${ 1 : ? } "
shift
if [ [ $# -eq 0 ] ] ; then
echo >& 2 " FAIL: Not enough arguments for ${ FUNCNAME [0] } () "
return 1
fi
log = " $( mktemp) "
if ! " $@ " | & tee " ${ log : ? } " ; then
echo >& 2 " FAIL: Command ' $* ' failed "
return 1
fi
grep -qE " $expression " " $log " && ec = 0 || ec = $?
if [ [ " $exp_ec " -eq 0 && " $ec " -ne 0 ] ] ; then
echo >& 2 " FAIL: Expression ' $expression ' not found in the output of ' $* ' "
return 1
elif [ [ " $exp_ec " -ne 0 && " $ec " -eq 0 ] ] ; then
echo >& 2 " FAIL: Expression ' $expression ' found in the output of ' $* ' "
return 1
fi
rm -f " $log "
) }
2023-05-16 20:11:51 +03:00
get_cgroup_hierarchy( ) {
case " $( stat -c '%T' -f /sys/fs/cgroup) " in
cgroup2fs)
echo "unified"
; ;
tmpfs)
if [ [ -d /sys/fs/cgroup/unified && " $( stat -c '%T' -f /sys/fs/cgroup/unified) " = = cgroup2fs ] ] ; then
echo "hybrid"
else
echo "legacy"
fi
; ;
*)
echo >& 2 "Failed to determine host's cgroup hierarchy"
exit 1
esac
}
2023-05-16 23:41:03 +03:00
runas( ) {
local userid = " ${ 1 : ? } "
shift
XDG_RUNTIME_DIR = /run/user/" $( id -u " $userid " ) " setpriv --reuid= " $userid " --init-groups " $@ "
}
2023-05-17 20:10:55 +03:00
2023-05-17 22:49:20 +03:00
coverage_create_nspawn_dropin( ) {
# If we're collecting coverage, bind mount the $BUILD_DIR into the nspawn
# container so gcov can update the counters. This is mostly for standalone
# containers, as machinectl stuff is handled by overriding the systemd-nspawn@.service
# (see test/test-functions:install_systemd())
local root = " ${ 1 : ? } "
local container
if [ [ -z " ${ COVERAGE_BUILD_DIR :- } " ] ] ; then
return 0
fi
container = " $( basename " $root " ) "
mkdir -p "/run/systemd/nspawn"
echo -ne " [Files]\nBind= $COVERAGE_BUILD_DIR \n " >" /run/systemd/nspawn/ ${ container : ? } .nspawn "
}
2023-05-17 20:10:55 +03:00
create_dummy_container( ) {
local root = " ${ 1 : ? } "
2024-05-11 20:17:13 +03:00
if [ [ ! -d /usr/share/TEST-13-NSPAWN-container-template ] ] ; then
2023-05-17 20:10:55 +03:00
echo >& 2 "Missing container template, probably not running in TEST-13-NSPAWN?"
exit 1
fi
mkdir -p " $root "
2024-11-27 12:17:36 +03:00
chmod 555 " $root "
2024-05-11 20:17:13 +03:00
cp -a /usr/share/TEST-13-NSPAWN-container-template/* " $root "
2023-05-17 22:49:20 +03:00
coverage_create_nspawn_dropin " $root "
2023-05-17 20:10:55 +03:00
}
2023-09-23 19:17:04 +03:00
# Bump the reboot counter and call systemctl with the given arguments
systemctl_final( ) {
local counter
if [ [ $# -eq 0 ] ] ; then
echo >& 2 "Missing arguments"
exit 1
fi
[ [ -e /var/tmp/.systemd_reboot_count ] ] && counter = " $( </var/tmp/.systemd_reboot_count) " || counter = 0
echo " $(( counter + 1 )) " >/var/tmp/.systemd_reboot_count
systemctl " $@ "
}
2023-10-19 13:28:37 +03:00
cgroupfs_supports_user_xattrs( ) {
local xattr
xattr = " user.supported_ $RANDOM "
# shellcheck disable=SC2064
trap " setfattr --remove= $xattr /sys/fs/cgroup || : " RETURN
setfattr --name= " $xattr " --value= 254 /sys/fs/cgroup
[ [ " $( getfattr --name= " $xattr " --absolute-names --only-values /sys/fs/cgroup) " -eq 254 ] ]
}
2023-10-25 15:48:30 +03:00
tpm_has_pcr( ) {
local algorithm = " ${ 1 : ? } "
local pcr = " ${ 2 : ? } "
[ [ -f " /sys/class/tpm/tpm0/pcr- $algorithm / $pcr " ] ]
}
2023-11-13 22:35:29 +03:00
openssl_supports_kdf( ) {
local kdf = " ${ 1 : ? } "
# The arguments will need to be adjusted to make this work for other KDFs than SSKDF,
# but let's do that when/if the need arises
openssl kdf -keylen 16 -kdfopt digest:SHA2-256 -kdfopt key:foo -out /dev/null " $kdf "
}
2023-11-24 18:00:15 +03:00
kernel_supports_lsm( ) {
local lsm = " ${ 1 : ? } "
local items item
if [ [ ! -e /sys/kernel/security/lsm ] ] ; then
echo " /sys/kernel/security/lsm doesn't exist, assuming $lsm is not supported "
return 1
fi
mapfile -t -d, items </sys/kernel/security/lsm
for item in " ${ items [@] } " ; do
if [ [ " $item " = = " $lsm " ] ] ; then
return 0
fi
done
return 1
}
2023-12-08 21:56:25 +03:00
2024-04-24 22:21:34 +03:00
install_extension_images( ) {
local os_release
os_release = " $( test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release) "
# Rolling distros like Arch do not set VERSION_ID
local version_id = ""
if grep -q "^VERSION_ID=" " $os_release " ; then
version_id = " $( grep "^VERSION_ID=" " $os_release " ) "
fi
local initdir = "/var/tmp/app0"
mkdir -p " $initdir /usr/lib/extension-release.d " " $initdir /usr/lib/systemd/system " " $initdir /opt "
grep "^ID=" " $os_release " >" $initdir /usr/lib/extension-release.d/extension-release.app0 "
echo " $version_id " >>" $initdir /usr/lib/extension-release.d/extension-release.app0 "
(
echo " $version_id "
echo "SYSEXT_IMAGE_ID=app"
) >>" $initdir /usr/lib/extension-release.d/extension-release.app0 "
cat >" $initdir /usr/lib/systemd/system/app0.service " <<EOF
[ Service]
Type = oneshot
RemainAfterExit = yes
ExecStart = /opt/script0.sh
TemporaryFileSystem = /var/lib
StateDirectory = app0
RuntimeDirectory = app0
EOF
cat >" $initdir /opt/script0.sh " <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
echo bar >\$ { STATE_DIRECTORY} /foo
cat /usr/lib/extension-release.d/extension-release.app0
EOF
chmod +x " $initdir /opt/script0.sh "
echo MARKER = 1 >" $initdir /usr/lib/systemd/system/some_file "
mksquashfs " $initdir " /tmp/app0.raw -noappend
2024-12-12 03:50:45 +03:00
veritysetup format /tmp/app0.raw /tmp/app0.verity --root-hash-file /tmp/app0.roothash
2024-04-24 22:21:34 +03:00
initdir = "/var/tmp/conf0"
mkdir -p " $initdir /etc/extension-release.d " " $initdir /etc/systemd/system " " $initdir /opt "
grep "^ID=" " $os_release " >" $initdir /etc/extension-release.d/extension-release.conf0 "
echo " $version_id " >>" $initdir /etc/extension-release.d/extension-release.conf0 "
(
echo " $version_id "
echo "CONFEXT_IMAGE_ID=app"
) >>" $initdir /etc/extension-release.d/extension-release.conf0 "
echo MARKER_1 >" $initdir /etc/systemd/system/some_file "
mksquashfs " $initdir " /tmp/conf0.raw -noappend
2024-12-12 03:50:45 +03:00
veritysetup format /tmp/conf0.raw /tmp/conf0.verity --root-hash-file /tmp/conf0.roothash
2024-04-24 22:21:34 +03:00
initdir = "/var/tmp/app1"
mkdir -p " $initdir /usr/lib/extension-release.d " " $initdir /usr/lib/systemd/system " " $initdir /opt "
grep "^ID=" " $os_release " >" $initdir /usr/lib/extension-release.d/extension-release.app2 "
(
echo " $version_id "
echo "SYSEXT_SCOPE=portable"
echo "SYSEXT_IMAGE_ID=app"
echo "SYSEXT_IMAGE_VERSION=1"
echo "PORTABLE_PREFIXES=app1"
) >>" $initdir /usr/lib/extension-release.d/extension-release.app2 "
setfattr -n user.extension-release.strict -v false " $initdir /usr/lib/extension-release.d/extension-release.app2 "
cat >" $initdir /usr/lib/systemd/system/app1.service " <<EOF
[ Service]
Type = oneshot
RemainAfterExit = yes
ExecStart = /opt/script1.sh
StateDirectory = app1
RuntimeDirectory = app1
EOF
cat >" $initdir /opt/script1.sh " <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
echo baz >\$ { STATE_DIRECTORY} /foo
cat /usr/lib/extension-release.d/extension-release.app2
EOF
chmod +x " $initdir /opt/script1.sh "
echo MARKER = 1 >" $initdir /usr/lib/systemd/system/other_file "
mksquashfs " $initdir " /tmp/app1.raw -noappend
initdir = "/var/tmp/app-nodistro"
mkdir -p " $initdir /usr/lib/extension-release.d " " $initdir /usr/lib/systemd/system "
(
echo "ID=_any"
echo "ARCHITECTURE=_any"
) >" $initdir /usr/lib/extension-release.d/extension-release.app-nodistro "
echo MARKER = 1 >" $initdir /usr/lib/systemd/system/some_file "
mksquashfs " $initdir " /tmp/app-nodistro.raw -noappend
initdir = "/var/tmp/service-scoped-test"
mkdir -p " $initdir /etc/extension-release.d " " $initdir /etc/systemd/system "
(
echo "ID=_any"
echo "ARCHITECTURE=_any"
) >" $initdir /etc/extension-release.d/extension-release.service-scoped-test "
echo MARKER_CONFEXT_123 >" $initdir /etc/systemd/system/some_file "
mksquashfs " $initdir " /etc/service-scoped-test.raw -noappend
# We need to create a dedicated sysext image to test the reload mechanism. If we share an image to install the
# 'foo.service' it will be loaded from another test run, which will impact the targeted test.
initdir = "/var/tmp/app-reload"
mkdir -p " $initdir /usr/lib/extension-release.d " " $initdir /usr/lib/systemd/system "
(
echo "ID=_any"
echo "ARCHITECTURE=_any"
echo "EXTENSION_RELOAD_MANAGER=1"
) >" $initdir /usr/lib/extension-release.d/extension-release.app-reload "
mkdir -p " $initdir /usr/lib/systemd/system/multi-user.target.d "
cat >" $initdir /usr/lib/systemd/system/foo.service " <<EOF
[ Service]
Type = oneshot
RemainAfterExit = yes
ExecStart = echo foo
[ Install]
WantedBy = multi-user.target
EOF
echo -e "[Unit]\nUpholds=foo.service" >" $initdir /usr/lib/systemd/system/multi-user.target.d/10-foo-service.conf "
mksquashfs " $initdir " /tmp/app-reload.raw -noappend
}
2024-05-02 10:16:28 +03:00
restore_locale( ) {
if [ [ -d /usr/lib/locale/xx_XX.UTF-8 ] ] ; then
rmdir /usr/lib/locale/xx_XX.UTF-8
fi
if [ [ -f /tmp/locale.conf.bak ] ] ; then
mv /tmp/locale.conf.bak /etc/locale.conf
else
rm -f /etc/locale.conf
fi
if [ [ -f /tmp/default-locale.bak ] ] ; then
mv /tmp/default-locale.bak /etc/default/locale
else
rm -rf /etc/default
fi
if [ [ -f /tmp/locale.gen.bak ] ] ; then
mv /tmp/locale.gen.bak /etc/locale.gen
else
rm -f /etc/locale.gen
fi
}
generate_locale( ) {
local locale = " ${ 1 : ? } "
if command -v locale-gen >/dev/null && ! localectl list-locales | grep -F " $locale " ; then
echo " $locale UTF-8 " >/etc/locale.gen
locale-gen " $locale "
fi
}