Theodore Ts'o | 9b3018a | 2011-08-11 14:56:49 -0400 | [diff] [blame] | 1 | /* |
| 2 | * sigcatcher.c --- print a backtrace on a SIGSEGV, et. al |
| 3 | * |
| 4 | * Copyright (C) 2011 Theodore Ts'o. |
| 5 | * |
| 6 | * %Begin-Header% |
| 7 | * This file may be redistributed under the terms of the GNU Public |
| 8 | * License. |
| 9 | * %End-Header% |
| 10 | */ |
| 11 | |
Theodore Ts'o | d1154eb | 2011-09-18 17:34:37 -0400 | [diff] [blame^] | 12 | #include "config.h" |
Theodore Ts'o | 9b3018a | 2011-08-11 14:56:49 -0400 | [diff] [blame] | 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <signal.h> |
| 16 | #include <string.h> |
| 17 | #ifdef HAVE_EXECINFO_H |
| 18 | #include <execinfo.h> |
| 19 | #endif |
| 20 | |
| 21 | #include "e2fsck.h" |
| 22 | |
| 23 | struct str_table { |
| 24 | int num; |
| 25 | const char *name; |
| 26 | }; |
| 27 | |
| 28 | #define DEFINE_ENTRY(SYM) { SYM, #SYM }, |
| 29 | #define END_TABLE { 0, 0 } |
| 30 | |
| 31 | static struct str_table sig_table[] = { |
| 32 | DEFINE_ENTRY(SIGHUP) |
| 33 | DEFINE_ENTRY(SIGINT) |
| 34 | DEFINE_ENTRY(SIGQUIT) |
| 35 | DEFINE_ENTRY(SIGILL) |
| 36 | DEFINE_ENTRY(SIGTRAP) |
| 37 | DEFINE_ENTRY(SIGABRT) |
| 38 | DEFINE_ENTRY(SIGIOT) |
| 39 | DEFINE_ENTRY(SIGBUS) |
| 40 | DEFINE_ENTRY(SIGFPE) |
| 41 | DEFINE_ENTRY(SIGKILL) |
| 42 | DEFINE_ENTRY(SIGUSR1) |
| 43 | DEFINE_ENTRY(SIGSEGV) |
| 44 | DEFINE_ENTRY(SIGUSR2) |
| 45 | DEFINE_ENTRY(SIGPIPE) |
| 46 | DEFINE_ENTRY(SIGALRM) |
| 47 | DEFINE_ENTRY(SIGTERM) |
| 48 | DEFINE_ENTRY(SIGSTKFLT) |
| 49 | DEFINE_ENTRY(SIGCHLD) |
| 50 | DEFINE_ENTRY(SIGCONT) |
| 51 | DEFINE_ENTRY(SIGSTOP) |
| 52 | DEFINE_ENTRY(SIGTSTP) |
| 53 | DEFINE_ENTRY(SIGTTIN) |
| 54 | DEFINE_ENTRY(SIGTTOU) |
| 55 | DEFINE_ENTRY(SIGURG) |
| 56 | DEFINE_ENTRY(SIGXCPU) |
| 57 | DEFINE_ENTRY(SIGXFSZ) |
| 58 | DEFINE_ENTRY(SIGVTALRM) |
| 59 | DEFINE_ENTRY(SIGPROF) |
| 60 | DEFINE_ENTRY(SIGWINCH) |
| 61 | DEFINE_ENTRY(SIGIO) |
| 62 | DEFINE_ENTRY(SIGPOLL) |
| 63 | DEFINE_ENTRY(SIGPWR) |
| 64 | DEFINE_ENTRY(SIGSYS) |
| 65 | END_TABLE |
| 66 | }; |
| 67 | |
| 68 | static struct str_table generic_code_table[] = { |
| 69 | DEFINE_ENTRY(SI_ASYNCNL) |
| 70 | DEFINE_ENTRY(SI_TKILL) |
| 71 | DEFINE_ENTRY(SI_SIGIO) |
| 72 | DEFINE_ENTRY(SI_ASYNCIO) |
| 73 | DEFINE_ENTRY(SI_MESGQ) |
| 74 | DEFINE_ENTRY(SI_TIMER) |
| 75 | DEFINE_ENTRY(SI_QUEUE) |
| 76 | DEFINE_ENTRY(SI_USER) |
| 77 | DEFINE_ENTRY(SI_KERNEL) |
| 78 | END_TABLE |
| 79 | }; |
| 80 | |
| 81 | static struct str_table sigill_code_table[] = { |
| 82 | DEFINE_ENTRY(ILL_ILLOPC) |
| 83 | DEFINE_ENTRY(ILL_ILLOPN) |
| 84 | DEFINE_ENTRY(ILL_ILLADR) |
| 85 | DEFINE_ENTRY(ILL_ILLTRP) |
| 86 | DEFINE_ENTRY(ILL_PRVOPC) |
| 87 | DEFINE_ENTRY(ILL_PRVREG) |
| 88 | DEFINE_ENTRY(ILL_COPROC) |
| 89 | DEFINE_ENTRY(ILL_BADSTK) |
| 90 | DEFINE_ENTRY(BUS_ADRALN) |
| 91 | DEFINE_ENTRY(BUS_ADRERR) |
| 92 | DEFINE_ENTRY(BUS_OBJERR) |
| 93 | END_TABLE |
| 94 | }; |
| 95 | |
| 96 | static struct str_table sigfpe_code_table[] = { |
| 97 | DEFINE_ENTRY(FPE_INTDIV) |
| 98 | DEFINE_ENTRY(FPE_INTOVF) |
| 99 | DEFINE_ENTRY(FPE_FLTDIV) |
| 100 | DEFINE_ENTRY(FPE_FLTOVF) |
| 101 | DEFINE_ENTRY(FPE_FLTUND) |
| 102 | DEFINE_ENTRY(FPE_FLTRES) |
| 103 | DEFINE_ENTRY(FPE_FLTINV) |
| 104 | DEFINE_ENTRY(FPE_FLTSUB) |
| 105 | END_TABLE |
| 106 | }; |
| 107 | |
| 108 | static struct str_table sigsegv_code_table[] = { |
| 109 | DEFINE_ENTRY(SEGV_MAPERR) |
| 110 | DEFINE_ENTRY(SEGV_ACCERR) |
| 111 | END_TABLE |
| 112 | }; |
| 113 | |
| 114 | |
| 115 | static struct str_table sigbus_code_table[] = { |
| 116 | DEFINE_ENTRY(BUS_ADRALN) |
| 117 | DEFINE_ENTRY(BUS_ADRERR) |
| 118 | DEFINE_ENTRY(BUS_OBJERR) |
| 119 | END_TABLE |
| 120 | }; |
| 121 | |
| 122 | static struct str_table sigstrap_code_table[] = { |
| 123 | DEFINE_ENTRY(TRAP_BRKPT) |
| 124 | DEFINE_ENTRY(TRAP_TRACE) |
| 125 | END_TABLE |
| 126 | }; |
| 127 | |
| 128 | static struct str_table sigcld_code_table[] = { |
| 129 | DEFINE_ENTRY(CLD_EXITED) |
| 130 | DEFINE_ENTRY(CLD_KILLED) |
| 131 | DEFINE_ENTRY(CLD_DUMPED) |
| 132 | DEFINE_ENTRY(CLD_TRAPPED) |
| 133 | DEFINE_ENTRY(CLD_STOPPED) |
| 134 | DEFINE_ENTRY(CLD_CONTINUED) |
| 135 | END_TABLE |
| 136 | }; |
| 137 | |
| 138 | static struct str_table sigpoll_code_table[] = { |
| 139 | DEFINE_ENTRY(POLL_IN) |
| 140 | DEFINE_ENTRY(POLL_OUT) |
| 141 | DEFINE_ENTRY(POLL_MSG) |
| 142 | DEFINE_ENTRY(POLL_ERR) |
| 143 | DEFINE_ENTRY(POLL_PRI) |
| 144 | DEFINE_ENTRY(POLL_HUP) |
| 145 | END_TABLE |
| 146 | }; |
| 147 | |
| 148 | static const char *lookup_table(int num, struct str_table *table) |
| 149 | { |
| 150 | struct str_table *p; |
| 151 | |
| 152 | for (p=table; p->name; p++) |
| 153 | if (num == p->num) |
| 154 | return(p->name); |
| 155 | return NULL; |
| 156 | } |
| 157 | |
| 158 | static const char *lookup_table_fallback(int num, struct str_table *table) |
| 159 | { |
| 160 | static char buf[32]; |
| 161 | const char *ret = lookup_table(num, table); |
| 162 | |
| 163 | if (ret) |
| 164 | return ret; |
| 165 | snprintf(buf, sizeof(buf), "%d", num); |
| 166 | buf[sizeof(buf)-1] = 0; |
| 167 | return buf; |
| 168 | } |
| 169 | |
| 170 | static void die_signal_handler(int signum, siginfo_t *siginfo, void *context) |
| 171 | { |
| 172 | void *stack_syms[32]; |
| 173 | int frames; |
| 174 | const char *cp; |
| 175 | |
| 176 | fprintf(stderr, "Signal (%d) %s ", signum, |
| 177 | lookup_table_fallback(signum, sig_table)); |
| 178 | if (siginfo->si_code == SI_USER) |
| 179 | fprintf(stderr, "(sent from pid %u) ", siginfo->si_pid); |
| 180 | cp = lookup_table(siginfo->si_code, generic_code_table); |
| 181 | if (cp) |
| 182 | fprintf(stderr, "si_code=%s ", cp); |
| 183 | else if (signum == SIGILL) |
| 184 | fprintf(stderr, "si_code=%s ", |
| 185 | lookup_table_fallback(siginfo->si_code, |
| 186 | sigill_code_table)); |
| 187 | else if (signum == SIGFPE) |
| 188 | fprintf(stderr, "si_code=%s ", |
| 189 | lookup_table_fallback(siginfo->si_code, |
| 190 | sigfpe_code_table)); |
| 191 | else if (signum == SIGSEGV) |
| 192 | fprintf(stderr, "si_code=%s ", |
| 193 | lookup_table_fallback(siginfo->si_code, |
| 194 | sigsegv_code_table)); |
| 195 | else if (signum == SIGBUS) |
| 196 | fprintf(stderr, "si_code=%s ", |
| 197 | lookup_table_fallback(siginfo->si_code, |
| 198 | sigbus_code_table)); |
| 199 | else if (signum == SIGCLD) |
| 200 | fprintf(stderr, "si_code=%s ", |
| 201 | lookup_table_fallback(siginfo->si_code, |
| 202 | sigcld_code_table)); |
| 203 | else |
| 204 | fprintf(stderr, "si code=%d ", siginfo->si_code); |
| 205 | if ((siginfo->si_code != SI_USER) && |
| 206 | (signum == SIGILL || signum == SIGFPE || |
| 207 | signum == SIGSEGV || signum == SIGBUS)) |
| 208 | fprintf(stderr, "fault addr=%p", siginfo->si_addr); |
| 209 | fprintf(stderr, "\n"); |
| 210 | |
| 211 | #ifdef HAVE_BACKTRACE |
| 212 | frames = backtrace(stack_syms, 32); |
| 213 | backtrace_symbols_fd(stack_syms, frames, 2); |
| 214 | #endif |
| 215 | exit(FSCK_ERROR); |
| 216 | } |
| 217 | |
| 218 | void sigcatcher_setup(void) |
| 219 | { |
| 220 | struct sigaction sa; |
| 221 | |
Theodore Ts'o | dd62d85 | 2011-09-03 09:43:50 -0400 | [diff] [blame] | 222 | memset(&sa, 0, sizeof(struct sigaction)); |
Theodore Ts'o | 9b3018a | 2011-08-11 14:56:49 -0400 | [diff] [blame] | 223 | sa.sa_sigaction = die_signal_handler; |
| 224 | sa.sa_flags = SA_SIGINFO; |
| 225 | |
| 226 | sigaction(SIGFPE, &sa, 0); |
| 227 | sigaction(SIGILL, &sa, 0); |
| 228 | sigaction(SIGBUS, &sa, 0); |
| 229 | sigaction(SIGSEGV, &sa, 0); |
| 230 | } |
| 231 | |
| 232 | |
| 233 | #ifdef DEBUG |
| 234 | #include <getopt.h> |
| 235 | |
| 236 | void usage(void) |
| 237 | { |
| 238 | fprintf(stderr, "tst_sigcatcher: [-akfn]\n"); |
| 239 | exit(1); |
| 240 | } |
| 241 | |
| 242 | int main(int argc, char** argv) |
| 243 | { |
| 244 | struct sigaction sa; |
| 245 | char *p = 0; |
| 246 | int i, c; |
| 247 | volatile x=0; |
| 248 | |
| 249 | memset(&sa, 0, sizeof(struct sigaction)); |
| 250 | sa.sa_sigaction = die_signal_handler; |
| 251 | sa.sa_flags = SA_SIGINFO; |
| 252 | for (i=1; i < 31; i++) |
| 253 | sigaction(i, &sa, 0); |
| 254 | |
| 255 | while ((c = getopt (argc, argv, "afkn")) != EOF) |
| 256 | switch (c) { |
| 257 | case 'a': |
| 258 | abort(); |
| 259 | break; |
| 260 | case 'f': |
| 261 | printf("%d\n", 42/x); |
| 262 | case 'k': |
| 263 | kill(getpid(), SIGTERM); |
| 264 | break; |
| 265 | case 'n': |
| 266 | *p = 42; |
| 267 | default: |
| 268 | usage (); |
| 269 | } |
| 270 | |
| 271 | printf("Sleeping for 10 seconds, send kill signal to pid %u...\n", |
| 272 | getpid()); |
| 273 | fflush(stdout); |
| 274 | sleep(10); |
| 275 | exit(0); |
| 276 | } |
| 277 | #endif |