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.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/reboot.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
@@ -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);
|
||||
+6
-157
@@ -6,7 +6,6 @@
|
||||
#include <linux/oom.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <unistd.h>
|
||||
#if HAVE_SECCOMP
|
||||
#include <seccomp.h>
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
extern bool arg_dump_core;
|
||||
extern int arg_crash_chvt;
|
||||
extern bool arg_crash_shell;
|
||||
extern bool arg_crash_reboot;
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user