a530492cd3
Since version 256, systemd will taint the system if /usr/bin and
/usr/sbin are not merged, known as merged-bin:
# systemctl --no-pager status
● buildroot
State: running
Units: 166 loaded (incl. loaded aliases)
Jobs: 0 queued
Failed: 0 units
Since: Mon 2025-07-07 19:48:05 UTC; 19s ago
systemd: 257.7
Tainted: unmerged-bin
CGroup: /
├─init.scope
[...]
Although this is not yet an error, it will be in the future.
To be as ready as we can be when that happens, forcibly enable
merged-bin, like we did when we initially added merged-usr (except
this time we carry the select from systemd, even though it is not
yet strictly required, rather than from the init entry).
Extend the runtime test to catch any tainted flag.
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
Cc: Arnout Vandecappelle <arnout@mind.be>
Cc: Norbert Lange <nolange79@gmail.com>
Cc: Sen Hastings <sen@hastings.org>
Signed-off-by: Romain Naour <romain.naour@smile.fr>
428 lines
14 KiB
Python
428 lines
14 KiB
Python
import infra.basetest
|
|
import re
|
|
from tests.init.base import InitSystemBase as InitSystemBase
|
|
|
|
|
|
# In the following tests, the read-only cases use the default settings,
|
|
# which historically used both a factory to populate a tmpfs on /var,
|
|
# and pre-populated /var at buildtime. Since these are the default
|
|
# settings, and they proved to generate a system that ultimately boots,
|
|
# we still want to keep testing that. See later, below, for the
|
|
# specialised test cases.
|
|
|
|
class InitSystemSystemdBase(InitSystemBase):
|
|
config = \
|
|
"""
|
|
BR2_arm=y
|
|
BR2_cortex_a9=y
|
|
BR2_ARM_ENABLE_VFP=y
|
|
BR2_TOOLCHAIN_EXTERNAL=y
|
|
BR2_TOOLCHAIN_EXTERNAL_BOOTLIN=y
|
|
BR2_INIT_SYSTEMD=y
|
|
BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
|
|
# BR2_TARGET_ROOTFS_TAR is not set
|
|
BR2_PER_PACKAGE_DIRECTORIES=y
|
|
"""
|
|
|
|
def check_systemd(self, fs):
|
|
if "BR2_LINUX_KERNEL=y" in self.config:
|
|
self.start_emulator(fs, "zImage", "vexpress-v2p-ca9")
|
|
else:
|
|
self.start_emulator(fs)
|
|
self.check_init("/lib/systemd/systemd")
|
|
|
|
# Test there is no tainted flag.
|
|
output, ret = self.emulator.run("systemctl --no-pager status")
|
|
self.assertEqual(ret, 0, f"'systemctl status' failed with exit code {ret}, with:\n{output}")
|
|
try:
|
|
# 'support-ended' tainted flag is only set based on the
|
|
# SUPPORT_END variable in /etc/os-release; as we don't set
|
|
# it in Buildroot, we can't get that flag in the runtime
|
|
# tests, even on our maintenance branches, so we don't need
|
|
# to filter it out.
|
|
tainted_flags = [
|
|
"".join(line.split(":")[1:]).strip()
|
|
for line in output
|
|
if line.strip().startswith("Tainted: ")
|
|
][0]
|
|
raise RuntimeError(f"Tainted flags: {tainted_flags}")
|
|
except IndexError:
|
|
# No tainted flag \o/
|
|
pass
|
|
|
|
# Test all units are OK
|
|
output, _ = self.emulator.run("systemctl --no-pager --failed --no-legend")
|
|
self.assertEqual(len(output), 0)
|
|
|
|
# Test we can reach the DBus daemon
|
|
self.assertRunOk("busctl --no-pager")
|
|
|
|
# Test we can read at least one line from the journal
|
|
output, _ = self.emulator.run("journalctl --no-pager --lines 1 --quiet")
|
|
self.assertEqual(len(output), 1)
|
|
|
|
# Check the network is up
|
|
self.check_network("eth0")
|
|
|
|
|
|
class TestInitSystemSystemdRoNetworkd(InitSystemSystemdBase):
|
|
config = InitSystemSystemdBase.config + \
|
|
"""
|
|
BR2_SYSTEM_DHCP="eth0"
|
|
# BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set
|
|
BR2_TARGET_ROOTFS_SQUASHFS=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
self.check_systemd("squashfs")
|
|
|
|
|
|
class TestInitSystemSystemdRwNetworkd(InitSystemSystemdBase):
|
|
config = InitSystemSystemdBase.config + \
|
|
"""
|
|
BR2_SYSTEM_DHCP="eth0"
|
|
BR2_TARGET_ROOTFS_EXT2=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
self.check_systemd("ext2")
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdown(InitSystemSystemdBase):
|
|
config = InitSystemSystemdBase.config + \
|
|
"""
|
|
BR2_SYSTEM_DHCP="eth0"
|
|
# BR2_PACKAGE_SYSTEMD_NETWORKD is not set
|
|
# BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set
|
|
BR2_TARGET_ROOTFS_SQUASHFS=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
self.check_systemd("squashfs")
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownDbusbroker(TestInitSystemSystemdRoIfupdown):
|
|
config = TestInitSystemSystemdRoIfupdown.config + \
|
|
"""
|
|
BR2_PACKAGE_DBUS_BROKER=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
# Parent class' test_run() method does exactly that, no more:
|
|
self.check_systemd("squashfs")
|
|
|
|
# Check that the dbus-broker daemon is running as non-root
|
|
cmd = "find /proc/$(pidof dbus-broker) -maxdepth 1 -name exe -user dbus"
|
|
out, _ = self.emulator.run(cmd)
|
|
self.assertEqual(len(out), 1)
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownDbusbrokerDbus(TestInitSystemSystemdRoIfupdownDbusbroker):
|
|
config = TestInitSystemSystemdRoIfupdownDbusbroker.config + \
|
|
"""
|
|
BR2_PACKAGE_DBUS=y
|
|
"""
|
|
|
|
|
|
class TestInitSystemSystemdRwIfupdown(InitSystemSystemdBase):
|
|
config = InitSystemSystemdBase.config + \
|
|
"""
|
|
BR2_SYSTEM_DHCP="eth0"
|
|
# BR2_PACKAGE_SYSTEMD_NETWORKD is not set
|
|
BR2_TARGET_ROOTFS_EXT2=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
self.check_systemd("ext2")
|
|
|
|
|
|
class TestInitSystemSystemdRwIfupdownDbusbroker(TestInitSystemSystemdRwIfupdown):
|
|
config = TestInitSystemSystemdRwIfupdown.config + \
|
|
"""
|
|
BR2_PACKAGE_DBUS_BROKER=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
# Parent class' test_run() method does exactly that, no more:
|
|
self.check_systemd("ext2")
|
|
|
|
# Check that the dbus-broker daemon is running as non-root
|
|
cmd = "find /proc/$(pidof dbus-broker) -maxdepth 1 -name exe -user dbus"
|
|
out, _ = self.emulator.run(cmd)
|
|
self.assertEqual(len(out), 1)
|
|
|
|
|
|
class TestInitSystemSystemdRwIfupdownDbusbrokerDbus(TestInitSystemSystemdRwIfupdownDbusbroker):
|
|
config = TestInitSystemSystemdRwIfupdownDbusbroker.config + \
|
|
"""
|
|
BR2_PACKAGE_DBUS=y
|
|
"""
|
|
|
|
|
|
class TestInitSystemSystemdRoFull(InitSystemSystemdBase):
|
|
config = InitSystemSystemdBase.config + \
|
|
"""
|
|
BR2_SYSTEM_DHCP="eth0"
|
|
# BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set
|
|
BR2_PACKAGE_SYSTEMD_JOURNAL_REMOTE=y
|
|
BR2_PACKAGE_SYSTEMD_BACKLIGHT=y
|
|
BR2_PACKAGE_SYSTEMD_BINFMT=y
|
|
BR2_PACKAGE_SYSTEMD_COREDUMP=y
|
|
BR2_PACKAGE_SYSTEMD_FIRSTBOOT=y
|
|
BR2_PACKAGE_SYSTEMD_HIBERNATE=y
|
|
BR2_PACKAGE_SYSTEMD_IMPORTD=y
|
|
BR2_PACKAGE_SYSTEMD_LOCALED=y
|
|
BR2_PACKAGE_SYSTEMD_LOGIND=y
|
|
BR2_PACKAGE_SYSTEMD_MACHINED=y
|
|
BR2_PACKAGE_SYSTEMD_POLKIT=y
|
|
BR2_PACKAGE_SYSTEMD_QUOTACHECK=y
|
|
BR2_PACKAGE_SYSTEMD_RANDOMSEED=y
|
|
BR2_PACKAGE_SYSTEMD_RFKILL=y
|
|
BR2_PACKAGE_SYSTEMD_SMACK_SUPPORT=y
|
|
BR2_PACKAGE_SYSTEMD_SYSUSERS=y
|
|
BR2_PACKAGE_SYSTEMD_VCONSOLE=y
|
|
BR2_TARGET_ROOTFS_SQUASHFS=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
self.check_systemd("squashfs")
|
|
|
|
|
|
class TestInitSystemSystemdRwFull(InitSystemSystemdBase):
|
|
config = InitSystemSystemdBase.config + \
|
|
"""
|
|
BR2_SYSTEM_DHCP="eth0"
|
|
BR2_PACKAGE_SYSTEMD_JOURNAL_REMOTE=y
|
|
BR2_PACKAGE_SYSTEMD_BACKLIGHT=y
|
|
BR2_PACKAGE_SYSTEMD_BINFMT=y
|
|
BR2_PACKAGE_SYSTEMD_COREDUMP=y
|
|
BR2_PACKAGE_SYSTEMD_FIRSTBOOT=y
|
|
BR2_PACKAGE_SYSTEMD_HIBERNATE=y
|
|
BR2_PACKAGE_SYSTEMD_IMPORTD=y
|
|
BR2_PACKAGE_SYSTEMD_LOCALED=y
|
|
BR2_PACKAGE_SYSTEMD_LOGIND=y
|
|
BR2_PACKAGE_SYSTEMD_MACHINED=y
|
|
BR2_PACKAGE_SYSTEMD_POLKIT=y
|
|
BR2_PACKAGE_SYSTEMD_QUOTACHECK=y
|
|
BR2_PACKAGE_SYSTEMD_RANDOMSEED=y
|
|
BR2_PACKAGE_SYSTEMD_RFKILL=y
|
|
BR2_PACKAGE_SYSTEMD_SMACK_SUPPORT=y
|
|
BR2_PACKAGE_SYSTEMD_SYSUSERS=y
|
|
BR2_PACKAGE_SYSTEMD_VCONSOLE=y
|
|
BR2_TARGET_ROOTFS_EXT2=y
|
|
"""
|
|
|
|
def test_run(self):
|
|
self.check_systemd("ext2")
|
|
|
|
|
|
# The following tests are all about read-only rootfs, and exercise either
|
|
# using an un-populated factory for /var, or an overlaysfs on top of a
|
|
# pre-populated /var. They all specialise the TestInitSystemSystemdRo*
|
|
# test cases above.
|
|
|
|
|
|
# Helper class for factory-based tests
|
|
class InitSystemSystemdBaseFactory():
|
|
config = \
|
|
"""
|
|
# BR2_INIT_SYSTEMD_POPULATE_TMPFILES is not set
|
|
BR2_ROOTFS_OVERLAY="{}"
|
|
""".format(infra.filepath("tests/init/systemd-factory"))
|
|
|
|
def test_run(self):
|
|
super().test_run()
|
|
|
|
# This one must be executed on the target, to check that
|
|
# the factory feature works as expected
|
|
out, exit_code = self.emulator.run("cat /var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "foobar")
|
|
|
|
# /var/foo/bar is from the /var factory
|
|
_, exit_code = self.emulator.run("test -e /usr/share/factory/var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
|
|
# We can write in /var/foo/bar
|
|
_, exit_code = self.emulator.run("echo barfoo >/var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
# ... and it contains the new content
|
|
out, exit_code = self.emulator.run("cat /var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "barfoo")
|
|
# ... but the factory is umodified
|
|
out, exit_code = self.emulator.run("cat /usr/share/factory/var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "foobar")
|
|
|
|
|
|
class TestInitSystemSystemdRoNetworkdFactory(
|
|
InitSystemSystemdBaseFactory,
|
|
TestInitSystemSystemdRoNetworkd,
|
|
):
|
|
config = InitSystemSystemdBaseFactory.config + \
|
|
TestInitSystemSystemdRoNetworkd.config
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownFactory(
|
|
InitSystemSystemdBaseFactory,
|
|
TestInitSystemSystemdRoIfupdown,
|
|
):
|
|
config = InitSystemSystemdBaseFactory.config + \
|
|
TestInitSystemSystemdRoIfupdown.config
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownDbusbrokerFactory(
|
|
InitSystemSystemdBaseFactory,
|
|
TestInitSystemSystemdRoIfupdownDbusbroker,
|
|
):
|
|
config = InitSystemSystemdBaseFactory.config + \
|
|
TestInitSystemSystemdRoIfupdownDbusbroker.config
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownDbusbrokerDbusFactory(
|
|
InitSystemSystemdBaseFactory,
|
|
TestInitSystemSystemdRoIfupdownDbusbrokerDbus,
|
|
):
|
|
config = InitSystemSystemdBaseFactory.config + \
|
|
TestInitSystemSystemdRoIfupdownDbusbrokerDbus.config
|
|
|
|
|
|
class TestInitSystemSystemdRoFullFactory(
|
|
InitSystemSystemdBaseFactory,
|
|
TestInitSystemSystemdRoFull,
|
|
):
|
|
config = InitSystemSystemdBaseFactory.config + \
|
|
TestInitSystemSystemdRoFull.config
|
|
|
|
|
|
# Helper class for overlayfs-based tests
|
|
class InitSystemSystemdBaseOverlayfs():
|
|
config = \
|
|
"""
|
|
# BR2_INIT_SYSTEMD_VAR_FACTORY is not set
|
|
BR2_INIT_SYSTEMD_VAR_OVERLAYFS=y
|
|
BR2_ROOTFS_OVERLAY="{}"
|
|
BR2_LINUX_KERNEL=y
|
|
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
|
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="5.10.202"
|
|
BR2_LINUX_KERNEL_DEFCONFIG="vexpress"
|
|
BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
|
|
BR2_LINUX_KERNEL_DTS_SUPPORT=y
|
|
BR2_LINUX_KERNEL_INTREE_DTS_NAME="vexpress-v2p-ca9"
|
|
""".format(infra.filepath("tests/init/systemd-factory"),
|
|
infra.filepath("conf/overlayfs-kernel-fragment.config"))
|
|
|
|
def test_run(self):
|
|
super().test_run()
|
|
|
|
# This one must be executed on the target, to check that
|
|
# the tmpfiles pre-populate works as expected
|
|
out, exit_code = self.emulator.run("cat /var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "foobar")
|
|
|
|
# /var/foo/bar is from the pre-populated /var, so it should
|
|
# not be present in the upper of the overlay
|
|
_, exit_code = self.emulator.run("test -e /run/buildroot/mounts/var/upper/foo/bar")
|
|
self.assertNotEqual(exit_code, 0)
|
|
|
|
# We can write in /var/foo/bar
|
|
_, exit_code = self.emulator.run("echo barfoo >/var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
# ... and it contains the new content
|
|
out, exit_code = self.emulator.run("cat /var/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "barfoo")
|
|
# ... and it to appears in the upper
|
|
_, exit_code = self.emulator.run("test -e /run/buildroot/mounts/var/upper/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
# ... with the new content
|
|
out, exit_code = self.emulator.run("cat /run/buildroot/mounts/var/upper/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "barfoo")
|
|
# ... while the lower still has the oldcontent
|
|
out, exit_code = self.emulator.run("cat /run/buildroot/mounts/var/lower/foo/bar")
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(out[0], "foobar")
|
|
|
|
|
|
class TestInitSystemSystemdRoNetworkdOverlayfs(
|
|
InitSystemSystemdBaseOverlayfs,
|
|
TestInitSystemSystemdRoNetworkd,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfs.config + \
|
|
TestInitSystemSystemdRoNetworkd.config
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownOverlayfs(
|
|
InitSystemSystemdBaseOverlayfs,
|
|
TestInitSystemSystemdRoIfupdown,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfs.config + \
|
|
TestInitSystemSystemdRoIfupdown.config
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownDbusbrokerOverlayfs(
|
|
InitSystemSystemdBaseOverlayfs,
|
|
TestInitSystemSystemdRoIfupdownDbusbroker,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfs.config + \
|
|
TestInitSystemSystemdRoIfupdownDbusbroker.config
|
|
|
|
|
|
class TestInitSystemSystemdRoIfupdownDbusbrokerDbusOverlayfs(
|
|
InitSystemSystemdBaseOverlayfs,
|
|
TestInitSystemSystemdRoIfupdownDbusbrokerDbus,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfs.config + \
|
|
TestInitSystemSystemdRoIfupdownDbusbrokerDbus.config
|
|
|
|
|
|
class TestInitSystemSystemdRoFullOverlayfs(
|
|
InitSystemSystemdBaseOverlayfs,
|
|
TestInitSystemSystemdRoFull,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfs.config + \
|
|
TestInitSystemSystemdRoFull.config
|
|
|
|
|
|
class InitSystemSystemdBaseOverlayfsVarBacking(InitSystemBase):
|
|
@classmethod
|
|
def gen_config(cls, overlaydir: str) -> str:
|
|
return re.sub(
|
|
r'^\s*BR2_ROOTFS_OVERLAY="(.*)"$',
|
|
'BR2_ROOTFS_OVERLAY="\\1 {}"'.format(infra.filepath(overlaydir)),
|
|
TestInitSystemSystemdRoFullOverlayfs.config,
|
|
flags=re.MULTILINE,
|
|
)
|
|
|
|
def check_var_mounted(self):
|
|
self.assertRunOk("grep '^other-var-backing-store /run/buildroot/mounts/var tmpfs' /proc/mounts")
|
|
|
|
|
|
class TestInitSystemSystemdRoFullOverlayfsVarBackingMountUnit(
|
|
TestInitSystemSystemdRoFullOverlayfs,
|
|
InitSystemSystemdBaseOverlayfsVarBacking,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfsVarBacking.gen_config(
|
|
'tests/init/systemd-overlay-mount-unit',
|
|
)
|
|
|
|
def test_run(self):
|
|
super().test_run()
|
|
self.check_var_mounted()
|
|
|
|
|
|
class TestInitSystemSystemdRoFullOverlayfsVarBackingFstab(
|
|
TestInitSystemSystemdRoFullOverlayfs,
|
|
InitSystemSystemdBaseOverlayfsVarBacking,
|
|
):
|
|
config = InitSystemSystemdBaseOverlayfsVarBacking.gen_config(
|
|
'tests/init/systemd-overlay-fstab',
|
|
)
|
|
|
|
def test_run(self):
|
|
super().test_run()
|
|
self.check_var_mounted()
|