1import infra.basetest
2import re
3from tests.init.base import InitSystemBase as InitSystemBase
4
5
6# In the following tests, the read-only cases use the default settings,
7# which historically used both a factory to populate a tmpfs on /var,
8# and pre-populated /var at buildtime. Since these are the default
9# settings, and they proved to generate a system that ultimately boots,
10# we still want to keep testing that. See later, below, for the
11# specialised test cases.
12
13class InitSystemSystemdBase(InitSystemBase):
14    config = \
15        """
16        BR2_arm=y
17        BR2_cortex_a9=y
18        BR2_ARM_ENABLE_VFP=y
19        BR2_TOOLCHAIN_EXTERNAL=y
20        BR2_TOOLCHAIN_EXTERNAL_BOOTLIN=y
21        BR2_INIT_SYSTEMD=y
22        BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
23        # BR2_TARGET_ROOTFS_TAR is not set
24        BR2_PER_PACKAGE_DIRECTORIES=y
25        """
26
27    def check_systemd(self, fs):
28        if "BR2_LINUX_KERNEL=y" in self.config:
29            self.start_emulator(fs, "zImage", "vexpress-v2p-ca9")
30        else:
31            self.start_emulator(fs)
32        self.check_init("/lib/systemd/systemd")
33
34        # Test all units are OK
35        output, _ = self.emulator.run("systemctl --no-pager --failed --no-legend")
36        self.assertEqual(len(output), 0)
37
38        # Test we can reach the DBus daemon
39        self.assertRunOk("busctl --no-pager")
40
41        # Test we can read at least one line from the journal
42        output, _ = self.emulator.run("journalctl --no-pager --lines 1 --quiet")
43        self.assertEqual(len(output), 1)
44
45        # Check the network is up
46        self.check_network("eth0")
47
48
49class TestInitSystemSystemdRoNetworkd(InitSystemSystemdBase):
50    config = InitSystemSystemdBase.config + \
51        """
52        BR2_SYSTEM_DHCP="eth0"
53        # BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set
54        BR2_TARGET_ROOTFS_SQUASHFS=y
55        """
56
57    def test_run(self):
58        self.check_systemd("squashfs")
59
60
61class TestInitSystemSystemdRwNetworkd(InitSystemSystemdBase):
62    config = InitSystemSystemdBase.config + \
63        """
64        BR2_SYSTEM_DHCP="eth0"
65        BR2_TARGET_ROOTFS_EXT2=y
66        """
67
68    def test_run(self):
69        self.check_systemd("ext2")
70
71
72class TestInitSystemSystemdRoIfupdown(InitSystemSystemdBase):
73    config = InitSystemSystemdBase.config + \
74        """
75        BR2_SYSTEM_DHCP="eth0"
76        # BR2_PACKAGE_SYSTEMD_NETWORKD is not set
77        # BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set
78        BR2_TARGET_ROOTFS_SQUASHFS=y
79        """
80
81    def test_run(self):
82        self.check_systemd("squashfs")
83
84
85class TestInitSystemSystemdRoIfupdownDbusbroker(TestInitSystemSystemdRoIfupdown):
86    config = TestInitSystemSystemdRoIfupdown.config + \
87        """
88        BR2_PACKAGE_DBUS_BROKER=y
89        """
90
91    def test_run(self):
92        # Parent class' test_run() method does exactly that, no more:
93        self.check_systemd("squashfs")
94
95        # Check that the dbus-broker daemon is running as non-root
96        cmd = "find /proc/$(pidof dbus-broker) -maxdepth 1 -name exe -user dbus"
97        out, _ = self.emulator.run(cmd)
98        self.assertEqual(len(out), 1)
99
100
101class TestInitSystemSystemdRoIfupdownDbusbrokerDbus(TestInitSystemSystemdRoIfupdownDbusbroker):
102    config = TestInitSystemSystemdRoIfupdownDbusbroker.config + \
103        """
104        BR2_PACKAGE_DBUS=y
105        """
106
107
108class TestInitSystemSystemdRwIfupdown(InitSystemSystemdBase):
109    config = InitSystemSystemdBase.config + \
110        """
111        BR2_SYSTEM_DHCP="eth0"
112        # BR2_PACKAGE_SYSTEMD_NETWORKD is not set
113        BR2_TARGET_ROOTFS_EXT2=y
114        """
115
116    def test_run(self):
117        self.check_systemd("ext2")
118
119
120class TestInitSystemSystemdRwIfupdownDbusbroker(TestInitSystemSystemdRwIfupdown):
121    config = TestInitSystemSystemdRwIfupdown.config + \
122        """
123        BR2_PACKAGE_DBUS_BROKER=y
124        """
125
126    def test_run(self):
127        # Parent class' test_run() method does exactly that, no more:
128        self.check_systemd("ext2")
129
130        # Check that the dbus-broker daemon is running as non-root
131        cmd = "find /proc/$(pidof dbus-broker) -maxdepth 1 -name exe -user dbus"
132        out, _ = self.emulator.run(cmd)
133        self.assertEqual(len(out), 1)
134
135
136class TestInitSystemSystemdRwIfupdownDbusbrokerDbus(TestInitSystemSystemdRwIfupdownDbusbroker):
137    config = TestInitSystemSystemdRwIfupdownDbusbroker.config + \
138        """
139        BR2_PACKAGE_DBUS=y
140        """
141
142
143class TestInitSystemSystemdRoFull(InitSystemSystemdBase):
144    config = InitSystemSystemdBase.config + \
145        """
146        BR2_SYSTEM_DHCP="eth0"
147        # BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW is not set
148        BR2_PACKAGE_SYSTEMD_JOURNAL_REMOTE=y
149        BR2_PACKAGE_SYSTEMD_BACKLIGHT=y
150        BR2_PACKAGE_SYSTEMD_BINFMT=y
151        BR2_PACKAGE_SYSTEMD_COREDUMP=y
152        BR2_PACKAGE_SYSTEMD_FIRSTBOOT=y
153        BR2_PACKAGE_SYSTEMD_HIBERNATE=y
154        BR2_PACKAGE_SYSTEMD_IMPORTD=y
155        BR2_PACKAGE_SYSTEMD_LOCALED=y
156        BR2_PACKAGE_SYSTEMD_LOGIND=y
157        BR2_PACKAGE_SYSTEMD_MACHINED=y
158        BR2_PACKAGE_SYSTEMD_POLKIT=y
159        BR2_PACKAGE_SYSTEMD_QUOTACHECK=y
160        BR2_PACKAGE_SYSTEMD_RANDOMSEED=y
161        BR2_PACKAGE_SYSTEMD_RFKILL=y
162        BR2_PACKAGE_SYSTEMD_SMACK_SUPPORT=y
163        BR2_PACKAGE_SYSTEMD_SYSUSERS=y
164        BR2_PACKAGE_SYSTEMD_VCONSOLE=y
165        BR2_TARGET_ROOTFS_SQUASHFS=y
166        """
167
168    def test_run(self):
169        self.check_systemd("squashfs")
170
171
172class TestInitSystemSystemdRwFull(InitSystemSystemdBase):
173    config = InitSystemSystemdBase.config + \
174        """
175        BR2_SYSTEM_DHCP="eth0"
176        BR2_PACKAGE_SYSTEMD_JOURNAL_REMOTE=y
177        BR2_PACKAGE_SYSTEMD_BACKLIGHT=y
178        BR2_PACKAGE_SYSTEMD_BINFMT=y
179        BR2_PACKAGE_SYSTEMD_COREDUMP=y
180        BR2_PACKAGE_SYSTEMD_FIRSTBOOT=y
181        BR2_PACKAGE_SYSTEMD_HIBERNATE=y
182        BR2_PACKAGE_SYSTEMD_IMPORTD=y
183        BR2_PACKAGE_SYSTEMD_LOCALED=y
184        BR2_PACKAGE_SYSTEMD_LOGIND=y
185        BR2_PACKAGE_SYSTEMD_MACHINED=y
186        BR2_PACKAGE_SYSTEMD_POLKIT=y
187        BR2_PACKAGE_SYSTEMD_QUOTACHECK=y
188        BR2_PACKAGE_SYSTEMD_RANDOMSEED=y
189        BR2_PACKAGE_SYSTEMD_RFKILL=y
190        BR2_PACKAGE_SYSTEMD_SMACK_SUPPORT=y
191        BR2_PACKAGE_SYSTEMD_SYSUSERS=y
192        BR2_PACKAGE_SYSTEMD_VCONSOLE=y
193        BR2_TARGET_ROOTFS_EXT2=y
194        """
195
196    def test_run(self):
197        self.check_systemd("ext2")
198
199
200# The following tests are all about read-only rootfs, and exercise either
201# using an un-populated factory for /var, or an overlaysfs ontop of a
202# pre-populated /var. They all specialise the TestInitSystemSystemdRo*
203# test cases above.
204
205
206# Helper class for factory-based tests
207class InitSystemSystemdBaseFactory():
208    config = \
209        """
210        # BR2_INIT_SYSTEMD_POPULATE_TMPFILES is not set
211        BR2_ROOTFS_OVERLAY="{}"
212        """.format(infra.filepath("tests/init/systemd-factory"))
213
214    def test_run(self):
215        super().test_run()
216
217        # This one must be executed on the target, to check that
218        # the factory feature works as expected
219        out, exit_code = self.emulator.run("cat /var/foo/bar")
220        self.assertEqual(exit_code, 0)
221        self.assertEqual(out[0], "foobar")
222
223        # /var/foo/bar is from the /var factory
224        _, exit_code = self.emulator.run("test -e /usr/share/factory/var/foo/bar")
225        self.assertEqual(exit_code, 0)
226
227        # We can write in /var/foo/bar
228        _, exit_code = self.emulator.run("echo barfoo >/var/foo/bar")
229        self.assertEqual(exit_code, 0)
230        # ... and it contains the new content
231        out, exit_code = self.emulator.run("cat /var/foo/bar")
232        self.assertEqual(exit_code, 0)
233        self.assertEqual(out[0], "barfoo")
234        # ... but the factory is umodified
235        out, exit_code = self.emulator.run("cat /usr/share/factory/var/foo/bar")
236        self.assertEqual(exit_code, 0)
237        self.assertEqual(out[0], "foobar")
238
239
240class TestInitSystemSystemdRoNetworkdFactory(
241    InitSystemSystemdBaseFactory,
242    TestInitSystemSystemdRoNetworkd,
243):
244    config = InitSystemSystemdBaseFactory.config + \
245        TestInitSystemSystemdRoNetworkd.config
246
247
248class TestInitSystemSystemdRoIfupdownFactory(
249    InitSystemSystemdBaseFactory,
250    TestInitSystemSystemdRoIfupdown,
251):
252    config = InitSystemSystemdBaseFactory.config + \
253        TestInitSystemSystemdRoIfupdown.config
254
255
256class TestInitSystemSystemdRoIfupdownDbusbrokerFactory(
257    InitSystemSystemdBaseFactory,
258    TestInitSystemSystemdRoIfupdownDbusbroker,
259):
260    config = InitSystemSystemdBaseFactory.config + \
261        TestInitSystemSystemdRoIfupdownDbusbroker.config
262
263
264class TestInitSystemSystemdRoIfupdownDbusbrokerDbusFactory(
265    InitSystemSystemdBaseFactory,
266    TestInitSystemSystemdRoIfupdownDbusbrokerDbus,
267):
268    config = InitSystemSystemdBaseFactory.config + \
269        TestInitSystemSystemdRoIfupdownDbusbrokerDbus.config
270
271
272class TestInitSystemSystemdRoFullFactory(
273    InitSystemSystemdBaseFactory,
274    TestInitSystemSystemdRoFull,
275):
276    config = InitSystemSystemdBaseFactory.config + \
277        TestInitSystemSystemdRoFull.config
278
279
280# Helper class for overlayfs-based tests
281class InitSystemSystemdBaseOverlayfs():
282    config = \
283        """
284        # BR2_INIT_SYSTEMD_VAR_FACTORY is not set
285        BR2_INIT_SYSTEMD_VAR_OVERLAYFS=y
286        BR2_ROOTFS_OVERLAY="{}"
287        BR2_LINUX_KERNEL=y
288        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
289        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="5.10.202"
290        BR2_LINUX_KERNEL_DEFCONFIG="vexpress"
291        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
292        BR2_LINUX_KERNEL_DTS_SUPPORT=y
293        BR2_LINUX_KERNEL_INTREE_DTS_NAME="vexpress-v2p-ca9"
294        """.format(infra.filepath("tests/init/systemd-factory"),
295                   infra.filepath("conf/overlayfs-kernel-fragment.config"))
296
297    def test_run(self):
298        super().test_run()
299
300        # This one must be executed on the target, to check that
301        # the tmpfiles pre-populate works as expected
302        out, exit_code = self.emulator.run("cat /var/foo/bar")
303        self.assertEqual(exit_code, 0)
304        self.assertEqual(out[0], "foobar")
305
306        # /var/foo/bar is from the pre-populated /var, so it should
307        # not be present in the upper of the overlay
308        _, exit_code = self.emulator.run("test -e /run/buildroot/mounts/var/upper/foo/bar")
309        self.assertNotEqual(exit_code, 0)
310
311        # We can write in /var/foo/bar
312        _, exit_code = self.emulator.run("echo barfoo >/var/foo/bar")
313        self.assertEqual(exit_code, 0)
314        # ... and it contains the new content
315        out, exit_code = self.emulator.run("cat /var/foo/bar")
316        self.assertEqual(exit_code, 0)
317        self.assertEqual(out[0], "barfoo")
318        # ... and it to appears in the upper
319        _, exit_code = self.emulator.run("test -e /run/buildroot/mounts/var/upper/foo/bar")
320        self.assertEqual(exit_code, 0)
321        # ... with the new content
322        out, exit_code = self.emulator.run("cat /run/buildroot/mounts/var/upper/foo/bar")
323        self.assertEqual(exit_code, 0)
324        self.assertEqual(out[0], "barfoo")
325        # ... while the lower still has the oldcontent
326        out, exit_code = self.emulator.run("cat /run/buildroot/mounts/var/lower/foo/bar")
327        self.assertEqual(exit_code, 0)
328        self.assertEqual(out[0], "foobar")
329
330
331class TestInitSystemSystemdRoNetworkdOverlayfs(
332    InitSystemSystemdBaseOverlayfs,
333    TestInitSystemSystemdRoNetworkd,
334):
335    config = InitSystemSystemdBaseOverlayfs.config + \
336        TestInitSystemSystemdRoNetworkd.config
337
338
339class TestInitSystemSystemdRoIfupdownOverlayfs(
340    InitSystemSystemdBaseOverlayfs,
341    TestInitSystemSystemdRoIfupdown,
342):
343    config = InitSystemSystemdBaseOverlayfs.config + \
344        TestInitSystemSystemdRoIfupdown.config
345
346
347class TestInitSystemSystemdRoIfupdownDbusbrokerOverlayfs(
348    InitSystemSystemdBaseOverlayfs,
349    TestInitSystemSystemdRoIfupdownDbusbroker,
350):
351    config = InitSystemSystemdBaseOverlayfs.config + \
352        TestInitSystemSystemdRoIfupdownDbusbroker.config
353
354
355class TestInitSystemSystemdRoIfupdownDbusbrokerDbusOverlayfs(
356    InitSystemSystemdBaseOverlayfs,
357    TestInitSystemSystemdRoIfupdownDbusbrokerDbus,
358):
359    config = InitSystemSystemdBaseOverlayfs.config + \
360        TestInitSystemSystemdRoIfupdownDbusbrokerDbus.config
361
362
363class TestInitSystemSystemdRoFullOverlayfs(
364    InitSystemSystemdBaseOverlayfs,
365    TestInitSystemSystemdRoFull,
366):
367    config = InitSystemSystemdBaseOverlayfs.config + \
368        TestInitSystemSystemdRoFull.config
369
370
371class InitSystemSystemdBaseOverlayfsVarBacking(InitSystemBase):
372    @classmethod
373    def gen_config(cls, overlaydir: str) -> str:
374        return re.sub(
375            r'^\s*BR2_ROOTFS_OVERLAY="(.*)"$',
376            'BR2_ROOTFS_OVERLAY="\\1 {}"'.format(infra.filepath(overlaydir)),
377            TestInitSystemSystemdRoFullOverlayfs.config,
378            flags=re.MULTILINE,
379        )
380
381    def check_var_mounted(self):
382        self.assertRunOk("grep '^other-var-backing-store /run/buildroot/mounts/var tmpfs' /proc/mounts")
383
384
385class TestInitSystemSystemdRoFullOverlayfsVarBackingMountUnit(
386    TestInitSystemSystemdRoFullOverlayfs,
387    InitSystemSystemdBaseOverlayfsVarBacking,
388):
389    config = InitSystemSystemdBaseOverlayfsVarBacking.gen_config(
390        'tests/init/systemd-overlay-mount-unit',
391    )
392
393    def test_run(self):
394        super().test_run()
395        self.check_var_mounted()
396
397
398class TestInitSystemSystemdRoFullOverlayfsVarBackingFstab(
399    TestInitSystemSystemdRoFullOverlayfs,
400    InitSystemSystemdBaseOverlayfsVarBacking,
401):
402    config = InitSystemSystemdBaseOverlayfsVarBacking.gen_config(
403        'tests/init/systemd-overlay-fstab',
404    )
405
406    def test_run(self):
407        super().test_run()
408        self.check_var_mounted()
409