From: Mike Yuan Date: Sat, 8 Mar 2025 19:09:58 +0000 (+0100) Subject: core/main: preemptively check existence of init only if we're switching root X-Git-Tag: v257.5~117 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1df135924796cf6790ec3f7848527349a5b760cc;p=thirdparty%2Fsystemd.git core/main: preemptively check existence of init only if we're switching root Follow-up for aaa27e2e21c04339914f26b7125789087eb51166 The commit described about system potentially becoming undebuggable after switching into broken root or whatnot. But notably we can never activate emergency.target after do_reexecute() failure, since the Manager has been destructed. Plus, for a normal reexecution the fallback shell logic triggered on non-existent /sbin/init is kinda useful. Let's hence guard the extra check behind switch-root. Also, move the check below /run/nextroot/ detection. (cherry picked from commit 93e19483dc9fae94d713d036ecee669450bd002d) --- diff --git a/src/core/main.c b/src/core/main.c index 7d2b3a1ce69..ec69eb1cc83 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1971,37 +1971,10 @@ static int do_reexecute( assert(saved_rlimit_memlock); assert(ret_error_message); - if (switch_root_init) { - r = chase(switch_root_init, switch_root_dir, CHASE_PREFIX_ROOT, NULL, NULL); - if (r < 0) - log_warning_errno(r, "Failed to chase configured init %s/%s: %m", - strempty(switch_root_dir), switch_root_init); - } else { - r = chase(SYSTEMD_BINARY_PATH, switch_root_dir, CHASE_PREFIX_ROOT, NULL, NULL); - if (r < 0) - log_debug_errno(r, "Failed to chase our own binary %s/%s: %m", - strempty(switch_root_dir), SYSTEMD_BINARY_PATH); - } - - if (r < 0) { - r = chase("/sbin/init", switch_root_dir, CHASE_PREFIX_ROOT, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to chase %s/sbin/init", strempty(switch_root_dir)); - } - /* Close and disarm the watchdog, so that the new instance can reinitialize it, but doesn't get * rebooted while we do that */ watchdog_close(true); - /* Reset RLIMIT_NOFILE + RLIMIT_MEMLOCK back to the kernel defaults, so that the new systemd can pass - * the kernel default to its child processes */ - if (saved_rlimit_nofile->rlim_cur != 0) - (void) setrlimit(RLIMIT_NOFILE, saved_rlimit_nofile); - if (saved_rlimit_memlock->rlim_cur != RLIM_INFINITY) - (void) setrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock); - - finish_remaining_processes(objective); - if (!switch_root_dir && objective == MANAGER_SOFT_REBOOT) { /* If no switch root dir is specified, then check if /run/nextroot/ qualifies and use that */ r = path_is_os_tree("/run/nextroot"); @@ -2011,6 +1984,39 @@ static int do_reexecute( switch_root_dir = "/run/nextroot"; } + if (switch_root_dir) { + /* If we're supposed to switch root, preemptively check the existence of a usable init. + * Otherwise the system might end up in a completely undebuggable state afterwards. */ + if (switch_root_init) { + r = chase_and_access(switch_root_init, switch_root_dir, CHASE_PREFIX_ROOT, X_OK, /* ret_path = */ NULL); + if (r < 0) + log_warning_errno(r, "Failed to chase configured init %s/%s: %m", + switch_root_dir, switch_root_init); + } else { + r = chase_and_access(SYSTEMD_BINARY_PATH, switch_root_dir, CHASE_PREFIX_ROOT, X_OK, /* ret_path = */ NULL); + if (r < 0) + log_debug_errno(r, "Failed to chase our own binary %s/%s: %m", + switch_root_dir, SYSTEMD_BINARY_PATH); + } + + if (r < 0) { + r = chase_and_access("/sbin/init", switch_root_dir, CHASE_PREFIX_ROOT, X_OK, /* ret_path = */ NULL); + if (r < 0) { + *ret_error_message = "Switch root target contains no usable init"; + return log_error_errno(r, "Failed to chase %s/sbin/init", switch_root_dir); + } + } + } + + /* Reset RLIMIT_NOFILE + RLIMIT_MEMLOCK back to the kernel defaults, so that the new systemd can pass + * the kernel default to its child processes */ + if (saved_rlimit_nofile->rlim_cur != 0) + (void) setrlimit(RLIMIT_NOFILE, saved_rlimit_nofile); + if (saved_rlimit_memlock->rlim_cur != RLIM_INFINITY) + (void) setrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock); + + finish_remaining_processes(objective); + if (switch_root_dir) { r = switch_root(/* new_root= */ switch_root_dir, /* old_root_after= */ NULL, @@ -2117,16 +2123,17 @@ static int do_reexecute( ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, "Failed to execute /sbin/init"); - *ret_error_message = "Failed to execute fallback shell"; if (r == -ENOENT) { - log_warning("No /sbin/init, trying fallback"); + log_warning_errno(r, "No /sbin/init, trying fallback shell"); args[0] = "/bin/sh"; args[1] = NULL; (void) execve(args[0], (char* const*) args, saved_env); - return log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m"); - } else - return log_error_errno(r, "Failed to execute /sbin/init, giving up: %m"); + r = -errno; + } + + *ret_error_message = "Failed to execute fallback shell"; + return log_error_errno(r, "Failed to execute /bin/sh, giving up: %m"); } static int invoke_main_loop(