core: embed essential units as built-in fallbacks

Compile the contents of a few essential target unit files
(graphical.target, multi-user.target, rescue.target, etc.) into the
manager binary, with comments stripped, and fall back to those built-in
copies when no fragment is found on disk. This lets the manager reach a
usable state even on a system that ships none of these unit files, e.g.
in minimal environments or one-off chroots.

On-disk files always take precedence (masks included), as the fallback is
only consulted when nothing is found in the lookup paths. The unit file
list is defined in units/meson.build and embedded via a small generator
script; 'units' is now processed before 'src/core' so the list is
available there.

The set of units and services is selected that should allow for a normal
operation of starting/stopping/restarting, of the machine. The services
all use SuccessAction=…, so they don't require any binaries to be
installed.

sigpwr.target is included, even though it doesn't do anything useful.
The same is true in normal installations. We might want to change it
do something useful there too.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-07-02 17:19:18 +02:00
parent a809b6e565
commit a0146fe807
6 changed files with 140 additions and 9 deletions
+35 -3
View File
@@ -1070,6 +1070,33 @@ units = [
udev_units = []
hwdb_units = []
# Unit files whose contents are embedded into the manager binary, so that it can
# fall back to a built-in copy when the file is not present on disk. Referenced
# from src/core/meson.build.
embedded_unit_names = [
'basic.target',
'exit.target',
'final.target',
'graphical.target',
'halt.target',
'kexec.target',
'multi-user.target',
'poweroff.target',
'reboot.target',
'shutdown.target',
'sigpwr.target',
'soft-reboot.target',
'sysinit.target',
'systemd-exit.service',
'systemd-halt.service',
'systemd-kexec.service',
'systemd-poweroff.service',
'systemd-reboot.service',
'systemd-soft-reboot.service',
'umount.target',
]
embedded_units = []
foreach unit : units
source = unit.get('file')
@@ -1092,7 +1119,7 @@ foreach unit : units
endforeach
if needs_jinja
t = custom_target(
processed = custom_target(
input : source,
output : name,
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
@@ -1100,9 +1127,9 @@ foreach unit : units
install_dir : systemunitdir,
install_tag : unit.get('install_tag', ''))
if unit.get('install_tag', '') == 'udev'
udev_units += t
udev_units += processed
elif unit.get('install_tag', '') == 'hwdb'
hwdb_units += t
hwdb_units += processed
endif
elif install
install_data(source,
@@ -1110,6 +1137,11 @@ foreach unit : units
install_tag : unit.get('install_tag', ''))
endif
if name in embedded_unit_names
# Embed either the processed output or the original file.
embedded_units += needs_jinja ? processed : source
endif
if install
foreach target : unit.get('symlinks', [])
if target.endswith('/')