core: add fallback-default-target build option

Add a 'fallback-default-target' meson option that configures which unit
to activate when default.target is not installed. This makes things more
resilient in general and is also useful for minimal/statically-linked
installations that may not ship a default.target symlink.

'graphical.target' is used as the default value of the setting, because
that's what we symlink as default.target in units/meson.build.

In the initrd, we had a fallback to start default.target if initrd.target
cannot be started. This fallback is changed to only do that if it is
not found, not on other errors. This seems more correct (and makes
the two fallbacks symmetrical.)
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-07-02 17:19:18 +02:00
parent fff9c9ba46
commit a809b6e565
6 changed files with 34 additions and 7 deletions
+1
View File
@@ -15,6 +15,7 @@
<!ENTITY HIGH_RLIMIT_NOFILE "{{HIGH_RLIMIT_NOFILE}}">
<!ENTITY DEFAULT_DNSSEC_MODE "{{DEFAULT_DNSSEC_MODE_STR}}">
<!ENTITY DEFAULT_DNS_OVER_TLS_MODE "{{DEFAULT_DNS_OVER_TLS_MODE_STR}}">
<!ENTITY FALLBACK_DEFAULT_TARGET "{{FALLBACK_DEFAULT_TARGET}}">
<!ENTITY DEFAULT_TIMEOUT_SEC "{{DEFAULT_TIMEOUT_SEC}} s">
<!ENTITY DEFAULT_USER_TIMEOUT_SEC "{{DEFAULT_USER_TIMEOUT_SEC}} s">
<!ENTITY SYSTEMD_DEFAULT_KEYMAP "{{SYSTEMD_DEFAULT_KEYMAP}}">
+11 -1
View File
@@ -1,6 +1,9 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
%entities;
]>
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd.special" xmlns:xi="http://www.w3.org/2001/XInclude">
@@ -239,6 +242,13 @@
<varname>3</varname>, <varname>5</varname>, …; see
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
<para>If <filename>default.target</filename> is not present on disk and no unit was requested on
the kernel command line, the default set at compile time will be used
(<filename>&FALLBACK_DEFAULT_TARGET;</filename>).</para>
<para>If the default unit or one of the fallbacks cannot be started, systemd starts
<filename>rescue.target</filename>.</para>
<para>For typical unit files please set <literal>WantedBy=</literal> to a regular target (like
<filename>multi-user.target</filename> or <filename>graphical.target</filename>),
instead of <filename>default.target</filename>, since such a service will also be run on special
+4
View File
@@ -726,6 +726,9 @@ if fallback_hostname == '' or fallback_hostname[0] == '.' or fallback_hostname[0
endif
conf.set_quoted('FALLBACK_HOSTNAME', fallback_hostname)
fallback_default_target = get_option('fallback-default-target')
conf.set_quoted('FALLBACK_DEFAULT_TARGET', fallback_default_target)
extra_net_naming_schemes = []
extra_net_naming_map = []
foreach scheme: get_option('extra-net-naming-schemes').split(',')
@@ -2751,6 +2754,7 @@ summary({
'nobody user name' : nobody_user,
'nobody group name' : nobody_group,
'fallback hostname' : get_option('fallback-hostname'),
'fallback default target' : fallback_default_target,
'default compression method' : compression,
'default DNSSEC mode' : default_dnssec,
'default DNS-over-TLS mode' : default_dns_over_tls,
+2
View File
@@ -267,6 +267,8 @@ option('clock-valid-range-usec-max', type : 'integer', value : 473364000000000,
description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error ["0" disables]')
option('default-user-shell', type : 'string', value : '/bin/bash',
description : 'default interactive shell')
option('fallback-default-target', type : 'string', value : 'graphical.target',
description : 'unit to activate when default.target is not found on disk')
option('system-alloc-uid-min', type : 'integer', value : 0,
description : 'minimum system UID used when allocating')
+13 -6
View File
@@ -2768,15 +2768,22 @@ static int do_queue_default_job(
log_debug("Activating default unit: %s", unit);
r = manager_load_startable_unit_or_warn(m, unit, NULL, &target);
if (r < 0 && in_initrd() && !arg_default_unit) {
/* Fall back to default.target, which we used to always use by default. Only do this if no
* explicit configuration was given. */
if (r == -ENOENT && !arg_default_unit) {
if (in_initrd())
/* Fall back to default.target, which we used to always use by default.
* Only do this if no explicit configuration was given. */
unit = SPECIAL_DEFAULT_TARGET;
else
/* The default.target symlink was not found on disk and the target was not
* explicitly specified. Fall back to the target configured at build time
* via -Ddefault-target=. */
unit = FALLBACK_DEFAULT_TARGET;
log_info("Falling back to %s.", SPECIAL_DEFAULT_TARGET);
r = manager_load_startable_unit_or_warn(m, SPECIAL_DEFAULT_TARGET, NULL, &target);
log_info("Falling back to %s.", unit);
r = manager_load_startable_unit_or_warn(m, unit, NULL, &target);
}
if (r < 0) {
/* We failed. Activate rescue mode. */
log_info("Falling back to %s.", SPECIAL_RESCUE_TARGET);
r = manager_load_startable_unit_or_warn(m, SPECIAL_RESCUE_TARGET, NULL, &target);
+3
View File
@@ -88,6 +88,9 @@ TEST(unit_name_is_valid) {
test_unit_name_is_valid_one("foo.target.wants/plain.service", UNIT_NAME_ANY, false);
test_unit_name_is_valid_one("foo.target.conf/foo.conf", UNIT_NAME_ANY, false);
test_unit_name_is_valid_one("foo.target.requires/plain.socket", UNIT_NAME_ANY, false);
/* The build-time configured fallback for default.target must be a valid plain unit name. */
test_unit_name_is_valid_one(FALLBACK_DEFAULT_TARGET, UNIT_NAME_PLAIN, true);
}
static void test_unit_name_replace_instance_one(const char *pattern, const char *repl, const char *expected, int ret) {