From: Lennart Poettering Date: Thu, 10 Mar 2022 08:33:05 +0000 (+0100) Subject: pid1: split out crash handler logic into its own .c/.h file X-Git-Tag: v251-rc1~177^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F22695%2Fhead;p=thirdparty%2Fsystemd.git pid1: split out crash handler logic into its own .c/.h file This stuff is sufficiently different from the rest of main.c, let's move it to its own .c/.h file, to make main.c a bit shorter. No code changes, just some refactoring. --- diff --git a/src/core/crash-handler.c b/src/core/crash-handler.c new file mode 100644 index 00000000000..561b7fc19c6 --- /dev/null +++ b/src/core/crash-handler.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "crash-handler.h" +#include "exit-status.h" +#include "macro.h" +#include "main.h" +#include "missing_syscall.h" +#include "process-util.h" +#include "raw-clone.h" +#include "rlimit-util.h" +#include "signal-util.h" +#include "terminal-util.h" +#include "virt.h" + +_noreturn_ void freeze_or_exit_or_reboot(void) { + + /* If we are running in a container, let's prefer exiting, after all we can propagate an exit code to + * the container manager, and thus inform it that something went wrong. */ + if (detect_container() > 0) { + log_emergency("Exiting PID 1..."); + _exit(EXIT_EXCEPTION); + } + + if (arg_crash_reboot) { + log_notice("Rebooting in 10s..."); + (void) sleep(10); + + log_notice("Rebooting now..."); + (void) reboot(RB_AUTOBOOT); + log_emergency_errno(errno, "Failed to reboot: %m"); + } + + log_emergency("Freezing execution."); + sync(); + freeze(); +} + +_noreturn_ static void crash(int sig, siginfo_t *siginfo, void *context) { + struct sigaction sa; + pid_t pid; + + /* NB: 💣 💣 💣 This is a signal handler, most likely executed in a situation where we have corrupted + * memory. Thus: please avoid any libc memory allocation here, or any functions that internally use + * memory allocation, as we cannot rely on memory allocation still working at this point! (Note that + * memory allocation is not async-signal-safe anyway — see signal-safety(7) for details —, and thus + * is not permissible in signal handlers.) */ + + if (getpid_cached() != 1) + /* Pass this on immediately, if this is not PID 1 */ + (void) raise(sig); + else if (!arg_dump_core) + log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); + else { + sa = (struct sigaction) { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; + + /* We want to wait for the core process, hence let's enable SIGCHLD */ + (void) sigaction(SIGCHLD, &sa, NULL); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); + else if (pid == 0) { + /* Enable default signal handler for core dump */ + + sa = (struct sigaction) { + .sa_handler = SIG_DFL, + }; + (void) sigaction(sig, &sa, NULL); + + /* Don't limit the coredump size */ + (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); + + /* Just to be sure... */ + (void) chdir("/"); + + /* Raise the signal again */ + pid = raw_getpid(); + (void) kill(pid, sig); /* raise() would kill the parent */ + + assert_not_reached(); + _exit(EXIT_EXCEPTION); + } else { + siginfo_t status; + int r; + + if (siginfo) { + if (siginfo->si_pid == 0) + log_emergency("Caught <%s> from unknown sender process.", signal_to_string(sig)); + else if (siginfo->si_pid == 1) + log_emergency("Caught <%s> from our own process.", signal_to_string(sig)); + else + log_emergency("Caught <%s> from PID "PID_FMT".", signal_to_string(sig), siginfo->si_pid); + } + + /* Order things nicely. */ + r = wait_for_terminate(pid, &status); + if (r < 0) + log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); + else if (status.si_code != CLD_DUMPED) { + const char *s = status.si_code == CLD_EXITED + ? exit_status_to_string(status.si_status, EXIT_STATUS_LIBC) + : signal_to_string(status.si_status); + + log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", + signal_to_string(sig), + pid, + sigchld_code_to_string(status.si_code), + status.si_status, strna(s)); + } else + log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", + signal_to_string(sig), pid); + } + } + + if (arg_crash_chvt >= 0) + (void) chvt(arg_crash_chvt); + + sa = (struct sigaction) { + .sa_handler = SIG_IGN, + .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, + }; + + /* Let the kernel reap children for us */ + (void) sigaction(SIGCHLD, &sa, NULL); + + if (arg_crash_shell) { + log_notice("Executing crash shell in 10s..."); + (void) sleep(10); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Failed to fork off crash shell: %m"); + else if (pid == 0) { + (void) setsid(); + (void) make_console_stdio(); + (void) rlimit_nofile_safe(); + (void) execle("/bin/sh", "/bin/sh", NULL, environ); + + log_emergency_errno(errno, "execle() failed: %m"); + _exit(EXIT_EXCEPTION); + } else { + log_info("Spawned crash shell as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } + } + + freeze_or_exit_or_reboot(); +} + +void install_crash_handler(void) { + static const struct sigaction sa = { + .sa_sigaction = crash, + .sa_flags = SA_NODEFER | SA_SIGINFO, /* So that we can raise the signal again from the signal handler */ + }; + int r; + + /* We ignore the return value here, since, we don't mind if we cannot set up a crash handler */ + r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER); + if (r < 0) + log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); +} diff --git a/src/core/crash-handler.h b/src/core/crash-handler.h new file mode 100644 index 00000000000..dc143354d10 --- /dev/null +++ b/src/core/crash-handler.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro.h" + +_noreturn_ void freeze_or_exit_or_reboot(void); +void install_crash_handler(void); diff --git a/src/core/main.c b/src/core/main.c index fc524867ef6..81db601a2cb 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #if HAVE_SECCOMP #include @@ -33,6 +32,7 @@ #include "clock-util.h" #include "conf-parser.h" #include "cpu-set-util.h" +#include "crash-handler.h" #include "dbus-manager.h" #include "dbus.h" #include "def.h" @@ -57,6 +57,7 @@ #include "log.h" #include "loopback-setup.h" #include "machine-id-setup.h" +#include "main.h" #include "manager.h" #include "manager-dump.h" #include "manager-serialize.h" @@ -71,7 +72,6 @@ #include "proc-cmdline.h" #include "process-util.h" #include "random-util.h" -#include "raw-clone.h" #include "rlimit-util.h" #if HAVE_SECCOMP #include "seccomp-util.h" @@ -116,10 +116,10 @@ static const char *arg_bus_introspect = NULL; * defaults are assigned in reset_arguments() below. */ static char *arg_default_unit; static bool arg_system; -static bool arg_dump_core; -static int arg_crash_chvt; -static bool arg_crash_shell; -static bool arg_crash_reboot; +bool arg_dump_core; +int arg_crash_chvt; +bool arg_crash_shell; +bool arg_crash_reboot; static char *arg_confirm_spawn; static ShowStatus arg_show_status; static StatusUnitFormat arg_status_unit_format; @@ -205,157 +205,6 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) { return 0; } -_noreturn_ static void freeze_or_exit_or_reboot(void) { - - /* If we are running in a container, let's prefer exiting, after all we can propagate an exit code to - * the container manager, and thus inform it that something went wrong. */ - if (detect_container() > 0) { - log_emergency("Exiting PID 1..."); - _exit(EXIT_EXCEPTION); - } - - if (arg_crash_reboot) { - log_notice("Rebooting in 10s..."); - (void) sleep(10); - - log_notice("Rebooting now..."); - (void) reboot(RB_AUTOBOOT); - log_emergency_errno(errno, "Failed to reboot: %m"); - } - - log_emergency("Freezing execution."); - sync(); - freeze(); -} - -_noreturn_ static void crash(int sig, siginfo_t *siginfo, void *context) { - struct sigaction sa; - pid_t pid; - - /* NB: 💣 💣 💣 This is a signal handler, most likely executed in a situation where we have corrupted - * memory. Thus: please avoid any libc memory allocation here, or any functions that internally use - * memory allocation, as we cannot rely on memory allocation still working at this point! (Note that - * memory allocation is not async-signal-safe anyway — see signal-safety(7) for details —, and thus - * is not permissible in signal handlers.) */ - - if (getpid_cached() != 1) - /* Pass this on immediately, if this is not PID 1 */ - (void) raise(sig); - else if (!arg_dump_core) - log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); - else { - sa = (struct sigaction) { - .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP|SA_RESTART, - }; - - /* We want to wait for the core process, hence let's enable SIGCHLD */ - (void) sigaction(SIGCHLD, &sa, NULL); - - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); - else if (pid == 0) { - /* Enable default signal handler for core dump */ - - sa = (struct sigaction) { - .sa_handler = SIG_DFL, - }; - (void) sigaction(sig, &sa, NULL); - - /* Don't limit the coredump size */ - (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); - - /* Just to be sure... */ - (void) chdir("/"); - - /* Raise the signal again */ - pid = raw_getpid(); - (void) kill(pid, sig); /* raise() would kill the parent */ - - assert_not_reached(); - _exit(EXIT_EXCEPTION); - } else { - siginfo_t status; - int r; - - if (siginfo) { - if (siginfo->si_pid == 0) - log_emergency("Caught <%s> from unknown sender process.", signal_to_string(sig)); - else if (siginfo->si_pid == 1) - log_emergency("Caught <%s> from our own process.", signal_to_string(sig)); - else - log_emergency("Caught <%s> from PID "PID_FMT".", signal_to_string(sig), siginfo->si_pid); - } - - /* Order things nicely. */ - r = wait_for_terminate(pid, &status); - if (r < 0) - log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); - else if (status.si_code != CLD_DUMPED) { - const char *s = status.si_code == CLD_EXITED - ? exit_status_to_string(status.si_status, EXIT_STATUS_LIBC) - : signal_to_string(status.si_status); - - log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", - signal_to_string(sig), - pid, - sigchld_code_to_string(status.si_code), - status.si_status, strna(s)); - } else - log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", - signal_to_string(sig), pid); - } - } - - if (arg_crash_chvt >= 0) - (void) chvt(arg_crash_chvt); - - sa = (struct sigaction) { - .sa_handler = SIG_IGN, - .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, - }; - - /* Let the kernel reap children for us */ - (void) sigaction(SIGCHLD, &sa, NULL); - - if (arg_crash_shell) { - log_notice("Executing crash shell in 10s..."); - (void) sleep(10); - - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off crash shell: %m"); - else if (pid == 0) { - (void) setsid(); - (void) make_console_stdio(); - (void) rlimit_nofile_safe(); - (void) execle("/bin/sh", "/bin/sh", NULL, environ); - - log_emergency_errno(errno, "execle() failed: %m"); - _exit(EXIT_EXCEPTION); - } else { - log_info("Spawned crash shell as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } - - freeze_or_exit_or_reboot(); -} - -static void install_crash_handler(void) { - static const struct sigaction sa = { - .sa_sigaction = crash, - .sa_flags = SA_NODEFER | SA_SIGINFO, /* So that we can raise the signal again from the signal handler */ - }; - int r; - - /* We ignore the return value here, since, we don't mind if we cannot set up a crash handler */ - r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER); - if (r < 0) - log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); -} - static int console_setup(void) { _cleanup_close_ int tty_fd = -1; int r; diff --git a/src/core/main.h b/src/core/main.h new file mode 100644 index 00000000000..b12a1ccfd78 --- /dev/null +++ b/src/core/main.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +extern bool arg_dump_core; +extern int arg_crash_chvt; +extern bool arg_crash_shell; +extern bool arg_crash_reboot; diff --git a/src/core/meson.build b/src/core/meson.build index 94cb3132e8b..f5e04b37ca2 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -197,7 +197,12 @@ libcore = shared_library( core_includes = [includes, include_directories('.')] -systemd_sources = files('main.c') +systemd_sources = files( + 'main.c', + 'main.h', + 'crash-handler.c', + 'crash-handler.h', +) in_files = [['system.conf', pkgsysconfdir], ['user.conf', pkgsysconfdir],