summaryrefslogtreecommitdiff
path: root/src/core/crash-handler.c
blob: 6983f2e2b7f83a84b7a73defef1e88e1cdc9b130 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* 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 */
                propagate_signal(sig, siginfo);
        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 */
                        propagate_signal(sig, siginfo);
                        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");
}