diff --git a/TODO b/TODO index 1d155910d0..6a04978b53 100644 --- a/TODO +++ b/TODO @@ -1306,10 +1306,6 @@ Features: * fstab-generator: default to tmpfs-as-root if only usr= is specified on the kernel cmdline -* initrd-parse-etc.service: can we skip daemon-reload if /sysroot/etc/fstab is missing? - Note that we start initrd-fs.target and initrd-cleanup.target there, so a straightforward - ConditionPathExists= is not enough. - * docs: bring https://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime up to date * add a job mode that will fail if a transaction would mean stopping diff --git a/man/bootup.xml b/man/bootup.xml index b29e494ee9..6c69c8a9bd 100644 --- a/man/bootup.xml +++ b/man/bootup.xml @@ -174,30 +174,30 @@ emergency.service | | | available to the user. - (various (various (various - timers...) paths...) sockets...) (sound devices) - | | | | - v v v v - timers.target paths.target sockets.target sound.target - | | | - \______________ _|_________________/ (bluetooth devices) - \ / | - V v - basic.target bluetooth.target - | - __________/ \_______ (smartcard devices) - / \ | - | | v - | v smartcard.target - v graphical-session-pre.target - (various user services) | (printers) - | v | - | (services for the graphical session) v - | | printer.target - v v - default.target graphical-session.target + (various (various (various + timers...) paths...) sockets...) (sound devices) + | | | | + v v v v + timers.target paths.target sockets.target sound.target + | | | + \______________ _|_________________/ (bluetooth devices) + \ / | + V v + basic.target bluetooth.target + | + __________/ \_______ (smartcard devices) + / \ | + | | v + | v smartcard.target + v graphical-session-pre.target +(various user services) | (printers) + | v | + | (services for the graphical session) v + | | printer.target + v v + default.target graphical-session.target - + Bootup in the Initial RAM Disk (initrd) @@ -239,59 +239,59 @@ emergency.service | | | /sysroot. - : (beginning identical to above) - : - v - basic.target - | emergency.service - ______________________/| | - / | v - | initrd-root-device.target emergency.target - | | - | v - | sysroot.mount - | | - | v - | initrd-root-fs.target - | | - | v - v initrd-parse-etc.service - (custom initrd | - services...) v - | (sysroot-usr.mount and - | various mounts marked - | with fstab option - | x-initrd.mount...) - | | - | v - | initrd-fs.target - \______________________ | - \| - v - initrd.target - | - v - initrd-cleanup.service - isolates to - initrd-switch-root.target - | - v - ______________________/| - / v - | initrd-udevadm-cleanup-db.service - v | - (custom initrd | - services...) | - \______________________ | - \| - v - initrd-switch-root.target - | - v - initrd-switch-root.service - | - v - Transition to Host OS + : (beginning identical to above) + : + v + basic.target + | emergency.service + ______________________/| | + / | v + | initrd-root-device.target emergency.target + | | + | v + | sysroot.mount + | | + | v + | initrd-root-fs.target + | | + | v + v initrd-parse-etc.service +(custom initrd | + services...) v + | (sysroot-usr.mount and + | various mounts marked + | with fstab option + | x-initrd.mount...) + | | + | v + | initrd-fs.target + \______________________ | + \| + v + initrd.target + | + v + initrd-cleanup.service + isolates to + initrd-switch-root.target + | + v + ______________________/| + / v + | initrd-udevadm-cleanup-db.service + v | +(custom initrd | + services...) | + \______________________ | + \| + v + initrd-switch-root.target + | + v + initrd-switch-root.service + | + v + Transition to Host OS @@ -300,33 +300,40 @@ emergency.service | | | System shutdown with systemd also consists of various target units with some minimal ordering structure applied: - (conflicts with (conflicts with - all system all file system - services) mounts, swaps, - | cryptsetup/ - | veritysetup - | devices, ...) - | | - v v - shutdown.target umount.target - | | - \_______ ______/ - \ / - v - (various low-level - services) - | - v - final.target - | - _____________________________________/ \_________________________________ - / | | \ - | | | | - v v v v -systemd-reboot.service systemd-poweroff.service systemd-halt.service systemd-kexec.service - | | | | - v v v v - reboot.target poweroff.target halt.target kexec.target + (conflicts with (conflicts with + all system all file system + services) mounts, swaps, + | cryptsetup/ + | veritysetup + | devices, ...) + | | + v v + shutdown.target umount.target + | | + \_______ ______/ + \ / + v + (various low-level + services) + | + v + final.target + | + ___________________________/ \_________________ + / | | \ + | | | | + v | | | +systemd-reboot.service | | | + | v | | + | systemd-poweroff.service | | + v | v | + reboot.target | systemd-halt.service | + v | v + poweroff.target | systemd-kexec.service + v | + halt.target | + v + kexec.target Commonly used system shutdown targets are emphasized. diff --git a/meson.build b/meson.build index a7e3e87744..aa2ae27a93 100644 --- a/meson.build +++ b/meson.build @@ -2324,6 +2324,10 @@ exe = executable( install : true, install_dir : systemgeneratordir) +meson.add_install_script(meson_make_symlink, + systemgeneratordir / 'systemd-fstab-generator', + rootlibexecdir / 'systemd-sysroot-fstab-check') + if want_tests != 'false' test('test-fstab-generator', test_fstab_generator_sh, diff --git a/src/basic/def.h b/src/basic/def.h index 0a1ae023a3..2b4de29021 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -5,6 +5,11 @@ #define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) #define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) +/* We use an extra-long timeout for the reload. This is because a reload or reexec means generators are rerun + * which are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the generators can + * have their timeout, and for everything else there's the same time budget in place. */ +#define DAEMON_RELOAD_TIMEOUT_SEC (DEFAULT_TIMEOUT_USEC * 2) + #define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) #define DEFAULT_START_LIMIT_BURST 5 diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index f04463157a..10da1c8d7f 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1838,8 +1838,11 @@ static int verb_status(int argc, char *argv[], void *userdata) { bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0; print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information"); - if (have_bootloader_esp_uuid && !sd_id128_equal(esp_uuid, bootloader_esp_uuid)) - printf("WARNING: The boot loader reports a different ESP UUID than detected!\n"); + if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) && + !sd_id128_equal(esp_uuid, bootloader_esp_uuid)) + printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n", + SD_ID128_FORMAT_VAL(bootloader_esp_uuid), + SD_ID128_FORMAT_VAL(esp_uuid)); if (stub) { printf(" Stub: %s\n", stub); diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index b7fe729836..f5f8a10c57 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -52,7 +52,7 @@ static void start_target(const char *target, const char *mode) { return; } - log_info("Running request %s/start/%s", target, mode); + log_info("Requesting %s/start/%s", target, mode); /* Start this unit only if we can replace basic.target with it */ r = sd_bus_call_method(bus, diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index da7ea627f1..e76de45a0f 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -5,6 +5,8 @@ #include #include "alloc-util.h" +#include "bus-error.h" +#include "bus-locator.h" #include "chase-symlinks.h" #include "fd-util.h" #include "fileio.h" @@ -20,6 +22,7 @@ #include "parse-util.h" #include "path-util.h" #include "proc-cmdline.h" +#include "process-util.h" #include "special.h" #include "specifier.h" #include "stat-util.h" @@ -39,6 +42,7 @@ typedef enum MountPointFlags { MOUNT_RW_ONLY = 1 << 5, } MountPointFlags; +static bool arg_sysroot_check = false; static const char *arg_dest = NULL; static const char *arg_dest_late = NULL; static bool arg_fstab_enabled = true; @@ -119,6 +123,11 @@ static int add_swap( return 0; } + if (arg_sysroot_check) { + log_info("%s should be enabled in the initrd, will request daemon-reload.", what); + return true; + } + r = unit_name_from_path(what, ".swap", &name); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); @@ -175,7 +184,7 @@ static int add_swap( return r; } - return 0; + return true; } static bool mount_is_network(struct mntent *me) { @@ -378,6 +387,11 @@ static int add_mount( mount_point_ignore(where)) return 0; + if (arg_sysroot_check) { + log_info("%s should be mounted in the initrd, will request daemon-reload.", where); + return true; + } + r = fstab_filter_options(opts, "x-systemd.wanted-by\0", NULL, NULL, &wanted_by, NULL); if (r < 0) return r; @@ -567,7 +581,52 @@ static int add_mount( return r; } - return 0; + return true; +} + +static int do_daemon_reload(void) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r, k; + + log_debug("Calling org.freedesktop.systemd1.Manager.Reload()..."); + + r = bus_connect_system_systemd(&bus); + if (r < 0) + return log_error_errno(r, "Failed to get D-Bus connection: %m"); + + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "Reload"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, DAEMON_RELOAD_TIMEOUT_SEC, &error, NULL); + if (r < 0) + return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); + + /* We need to requeue the two targets so that any new units which previously were not part of the + * targets, and which we now added, will be started. */ + + r = 0; + FOREACH_STRING(unit, SPECIAL_INITRD_FS_TARGET, SPECIAL_SWAP_TARGET) { + log_info("Requesting %s/start/replace...", unit); + + k = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + &error, + NULL, + "ss", unit, "replace"); + if (k < 0) { + log_error_errno(k, "Failed to (re)start %s: %s", unit, bus_error_message(&error, r)); + if (r == 0) + r = k; + } + } + + return r; } static const char* sysroot_fstab_path(void) { @@ -582,8 +641,10 @@ static int parse_fstab(bool initrd) { if (initrd) fstab = sysroot_fstab_path(); - else + else { fstab = fstab_path(); + assert(!arg_sysroot_check); + } log_debug("Parsing %s...", fstab); @@ -700,6 +761,8 @@ static int parse_fstab(bool initrd) { target_unit); } + if (arg_sysroot_check && k > 0) + return true; /* We found a mount or swap that would be started… */ if (r >= 0 && k < 0) r = k; } @@ -1126,12 +1189,14 @@ static int determine_usr(void) { return determine_device(&arg_usr_what, arg_usr_hash, "usr"); } -static int run(const char *dest, const char *dest_early, const char *dest_late) { +/* If arg_sysroot_check is false, run as generator in the usual fashion. + * If it is true, check /sysroot/etc/fstab for any units that we'd want to mount + * in the initrd, and call daemon-reload. We will get reinvoked as a generator, + * with /sysroot/etc/fstab available, and then we can write additional units based + * on that file. */ +static int run_generator(void) { int r, r2 = 0, r3 = 0; - assert_se(arg_dest = dest); - assert_se(arg_dest_late = dest_late); - r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); @@ -1139,6 +1204,15 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) (void) determine_root(); (void) determine_usr(); + if (arg_sysroot_check) { + r = parse_fstab(true); + if (r == 0) + log_debug("Nothing interesting found, not doing daemon-reload."); + if (r > 0) + r = do_daemon_reload(); + return r; + } + /* Always honour root= and usr= in the kernel command line if we are in an initrd */ if (in_initrd()) { r = add_sysroot_mount(); @@ -1164,4 +1238,32 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) return r < 0 ? r : r2 < 0 ? r2 : r3; } -DEFINE_MAIN_GENERATOR_FUNCTION(run); +static int run(int argc, char **argv) { + arg_sysroot_check = invoked_as(argv, "systemd-sysroot-fstab-check"); + + if (arg_sysroot_check) { + /* Run as in systemd-sysroot-fstab-check mode */ + log_setup(); + + if (strv_length(argv) > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This program takes no arguments."); + if (!in_initrd()) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This program is only useful in the initrd."); + } else { + /* Run in generator mode */ + log_setup_generator(); + + if (!IN_SET(strv_length(argv), 2, 4)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This program takes one or three arguments."); + + arg_dest = ASSERT_PTR(argv[1]); + arg_dest_late = ASSERT_PTR(argv[argc > 3 ? 3 : 1]); + } + + return run_generator(); +} + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 33e1a20de6..fa56a8322d 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -53,7 +53,8 @@ static int open_parent_block_device(dev_t devnum, int *ret_fd) { r = sd_device_new_from_devnum(&d, 'b', devnum); if (r < 0) - return log_debug_errno(r, "Failed to open device: %m"); + return log_debug_errno(r, "Failed to create device object for block device "DEVNUM_FORMAT_STR": %m", + DEVNUM_FORMAT_VAL(devnum)); if (sd_device_get_devname(d, &name) < 0) { r = sd_device_get_syspath(d, &name); diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index a48a8570c4..52b1ba199a 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -103,7 +103,7 @@ static int change_runlevel(Server *s, int runlevel) { else mode = "replace-irreversibly"; - log_debug("Running request %s/start/%s", target, mode); + log_debug("Requesting %s/start/%s", target, mode); r = sd_bus_call_method( s->bus, diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index f46260307a..c3c22220c2 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -249,7 +249,7 @@ static int maybe_reload(sd_bus **bus) { return bus_log_create_error(r); /* Reloading the daemon may take long, hence set a longer timeout here */ - r = sd_bus_call(*bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL); + r = sd_bus_call(*bus, m, DAEMON_RELOAD_TIMEOUT_SEC, &error, NULL); if (r < 0) return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c index 06f17c6a8e..5648dfd83b 100644 --- a/src/sulogin-shell/sulogin-shell.c +++ b/src/sulogin-shell/sulogin-shell.c @@ -33,11 +33,8 @@ static int reload_manager(sd_bus *bus) { if (r < 0) return bus_log_create_error(r); - /* Note we use an extra-long timeout here. This is because a reload or reexec means generators are rerun which - * are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the generators can have - * their timeout, and for everything else there's the same time budget in place. */ - - r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL); + /* Reloading the daemon may take long, hence set a longer timeout here */ + r = sd_bus_call(bus, m, DAEMON_RELOAD_TIMEOUT_SEC, &error, NULL); if (r < 0) return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); diff --git a/src/systemctl/systemctl-daemon-reload.c b/src/systemctl/systemctl-daemon-reload.c index 33de7d161e..4f025ac4d2 100644 --- a/src/systemctl/systemctl-daemon-reload.c +++ b/src/systemctl/systemctl-daemon-reload.c @@ -37,12 +37,8 @@ int daemon_reload(enum action action, bool graceful) { if (r < 0) return bus_log_create_error(r); - /* Note we use an extra-long timeout here. This is because a reload or reexec means generators are - * rerun which are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the - * generators can have their timeout, and for everything else there's the same time budget in - * place. */ - - r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL); + /* Reloading the daemon may take long, hence set a longer timeout here */ + r = sd_bus_call(bus, m, DAEMON_RELOAD_TIMEOUT_SEC, &error, NULL); /* On reexecution, we expect a disconnect, not a reply */ if (IN_SET(r, -ETIMEDOUT, -ECONNRESET) && action == ACTION_REEXEC) diff --git a/units/initrd-parse-etc.service b/units/initrd-parse-etc.service deleted file mode 100644 index 38df728355..0000000000 --- a/units/initrd-parse-etc.service +++ /dev/null @@ -1,24 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -# -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[Unit] -Description=Reload Configuration from the Real Root -DefaultDependencies=no -Requires=initrd-root-fs.target -After=initrd-root-fs.target -OnFailure=emergency.target -OnFailureJobMode=replace-irreversibly -AssertPathExists=/etc/initrd-release - -[Service] -Type=oneshot -ExecStartPre=-systemctl daemon-reload -# we have to retrigger initrd-fs.target after daemon-reload -ExecStart=-systemctl --no-block start initrd-fs.target -ExecStart=systemctl --no-block start initrd-cleanup.service diff --git a/units/initrd-parse-etc.service.in b/units/initrd-parse-etc.service.in new file mode 100644 index 0000000000..fe0e860150 --- /dev/null +++ b/units/initrd-parse-etc.service.in @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Mountpoints Configured in the Real Root +AssertPathExists=/etc/initrd-release + +DefaultDependencies=no +Requires=initrd-root-fs.target +After=initrd-root-fs.target + +OnFailure=emergency.target +OnFailureJobMode=replace-irreversibly + +[Service] +Type=oneshot + +# FIXME: once dracut is patched to install the symlink, change to: +# ExecStart={{ROOTLIBEXECDIR}}/systemd-sysroot-fstab-check +ExecStart=@{{SYSTEM_GENERATOR_DIR}}/systemd-fstab-generator systemd-sysroot-fstab-check + +# We want to enqueue initrd-cleanup.service/start after we finished the part +# above. It can't be part of the initial transaction, because non-oneshot units +# use Conflicts=initrd-cleanup.service to be terminated before we switch root. +# Effectively, initrd-parse-etc.service acts as a synchronization point after +# which cleanup of the initrd processes starts. +ExecStart=systemctl --no-block start initrd-cleanup.service diff --git a/units/meson.build b/units/meson.build index 40f784ec68..2010a5566f 100644 --- a/units/meson.build +++ b/units/meson.build @@ -36,7 +36,6 @@ units = [ ['suspend-then-hibernate.target', 'ENABLE_HIBERNATE'], ['initrd-cleanup.service', 'ENABLE_INITRD'], ['initrd-fs.target', 'ENABLE_INITRD'], - ['initrd-parse-etc.service', 'ENABLE_INITRD'], ['initrd-root-device.target', 'ENABLE_INITRD'], ['initrd-root-fs.target', 'ENABLE_INITRD'], ['initrd-switch-root.service', 'ENABLE_INITRD'], @@ -179,6 +178,7 @@ in_units = [ ['emergency.service', ''], ['getty@.service', '', 'autovt@.service'], + ['initrd-parse-etc.service', 'ENABLE_INITRD'], ['kmod-static-nodes.service', 'HAVE_KMOD ENABLE_TMPFILES', 'sysinit.target.wants/'], ['quotaon.service', 'ENABLE_QUOTACHECK'],