From: Zbigniew Jędrzejewski-Szmek Date: Tue, 23 Jun 2026 08:43:50 +0000 (+0200) Subject: core: add fallback-default-target build option X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a809b6e565024103958782789dfb5adca5b7f573;p=thirdparty%2Fsystemd.git 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.) --- diff --git a/man/custom-entities.ent.in b/man/custom-entities.ent.in index 4494a1c3ab1..beff1529a78 100644 --- a/man/custom-entities.ent.in +++ b/man/custom-entities.ent.in @@ -15,6 +15,7 @@ + diff --git a/man/systemd.special.xml b/man/systemd.special.xml index f6f35b861d0..deba4f8e4f5 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -1,6 +1,9 @@ + "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ + +%entities; +]> @@ -239,6 +242,13 @@ 3, 5, …; see systemd1. + If default.target is not present on disk and no unit was requested on + the kernel command line, the default set at compile time will be used + (&FALLBACK_DEFAULT_TARGET;). + + If the default unit or one of the fallbacks cannot be started, systemd starts + rescue.target. + For typical unit files please set WantedBy= to a regular target (like multi-user.target or graphical.target), instead of default.target, since such a service will also be run on special diff --git a/meson.build b/meson.build index abdde0040b3..423f9218344 100644 --- a/meson.build +++ b/meson.build @@ -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, diff --git a/meson_options.txt b/meson_options.txt index 8df682742d1..9c0cda4c38e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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') diff --git a/src/core/main.c b/src/core/main.c index c476400a93e..3dd227f2443 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -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. */ - - log_info("Falling back to %s.", SPECIAL_DEFAULT_TARGET); + 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; - 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); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 7f71f5fdc74..c218e015fae 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -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) {