/* * sigcatcher.c --- print a backtrace on a SIGSEGV, et. al * * Copyright (C) 2011 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #ifdef HAVE_EXECINFO_H #include <execinfo.h> #endif #include "e2fsck.h" struct str_table { int num; const char *name; }; #define DEFINE_ENTRY(SYM) { SYM, #SYM }, #define END_TABLE { 0, 0 } static struct str_table sig_table[] = { #ifdef SIGHUP DEFINE_ENTRY(SIGHUP) #endif #ifdef SIGINT DEFINE_ENTRY(SIGINT) #endif #ifdef SIGQUIT DEFINE_ENTRY(SIGQUIT) #endif #ifdef SIGILL DEFINE_ENTRY(SIGILL) #endif #ifdef SIGTRAP DEFINE_ENTRY(SIGTRAP) #endif #ifdef SIGABRT DEFINE_ENTRY(SIGABRT) #endif #ifdef SIGIOT DEFINE_ENTRY(SIGIOT) #endif #ifdef SIGBUS DEFINE_ENTRY(SIGBUS) #endif #ifdef SIGFPE DEFINE_ENTRY(SIGFPE) #endif #ifdef SIGKILL DEFINE_ENTRY(SIGKILL) #endif #ifdef SIGUSR1 DEFINE_ENTRY(SIGUSR1) #endif #ifdef SIGSEGV DEFINE_ENTRY(SIGSEGV) #endif #ifdef SIGUSR2 DEFINE_ENTRY(SIGUSR2) #endif #ifdef SIGPIPE DEFINE_ENTRY(SIGPIPE) #endif #ifdef SIGALRM DEFINE_ENTRY(SIGALRM) #endif #ifdef SIGTERM DEFINE_ENTRY(SIGTERM) #endif #ifdef SIGSTKFLT DEFINE_ENTRY(SIGSTKFLT) #endif #ifdef SIGCHLD DEFINE_ENTRY(SIGCHLD) #endif #ifdef SIGCONT DEFINE_ENTRY(SIGCONT) #endif #ifdef SIGSTOP DEFINE_ENTRY(SIGSTOP) #endif #ifdef SIGTSTP DEFINE_ENTRY(SIGTSTP) #endif #ifdef SIGTTIN DEFINE_ENTRY(SIGTTIN) #endif #ifdef SIGTTOU DEFINE_ENTRY(SIGTTOU) #endif #ifdef SIGURG DEFINE_ENTRY(SIGURG) #endif #ifdef SIGXCPU DEFINE_ENTRY(SIGXCPU) #endif #ifdef SIGXFSZ DEFINE_ENTRY(SIGXFSZ) #endif #ifdef SIGVTALRM DEFINE_ENTRY(SIGVTALRM) #endif #ifdef SIGPROF DEFINE_ENTRY(SIGPROF) #endif #ifdef SIGWINCH DEFINE_ENTRY(SIGWINCH) #endif #ifdef SIGIO DEFINE_ENTRY(SIGIO) #endif #ifdef SIGPOLL DEFINE_ENTRY(SIGPOLL) #endif #ifdef SIGPWR DEFINE_ENTRY(SIGPWR) #endif #ifdef SIGSYS DEFINE_ENTRY(SIGSYS) #endif END_TABLE }; static struct str_table generic_code_table[] = { #ifdef SI_ASYNCNL DEFINE_ENTRY(SI_ASYNCNL) #endif #ifdef SI_TKILL DEFINE_ENTRY(SI_TKILL) #endif #ifdef SI_SIGIO DEFINE_ENTRY(SI_SIGIO) #endif #ifdef SI_ASYNCIO DEFINE_ENTRY(SI_ASYNCIO) #endif #ifdef SI_MESGQ DEFINE_ENTRY(SI_MESGQ) #endif #ifdef SI_TIMER DEFINE_ENTRY(SI_TIMER) #endif #ifdef SI_QUEUE DEFINE_ENTRY(SI_QUEUE) #endif #ifdef SI_USER DEFINE_ENTRY(SI_USER) #endif #ifdef SI_KERNEL DEFINE_ENTRY(SI_KERNEL) #endif END_TABLE }; static struct str_table sigill_code_table[] = { #ifdef ILL_ILLOPC DEFINE_ENTRY(ILL_ILLOPC) #endif #ifdef ILL_ILLOPN DEFINE_ENTRY(ILL_ILLOPN) #endif #ifdef ILL_ILLADR DEFINE_ENTRY(ILL_ILLADR) #endif #ifdef ILL_ILLTRP DEFINE_ENTRY(ILL_ILLTRP) #endif #ifdef ILL_PRVOPC DEFINE_ENTRY(ILL_PRVOPC) #endif #ifdef ILL_PRVREG DEFINE_ENTRY(ILL_PRVREG) #endif #ifdef ILL_COPROC DEFINE_ENTRY(ILL_COPROC) #endif #ifdef ILL_BADSTK DEFINE_ENTRY(ILL_BADSTK) #endif #ifdef BUS_ADRALN DEFINE_ENTRY(BUS_ADRALN) #endif #ifdef BUS_ADRERR DEFINE_ENTRY(BUS_ADRERR) #endif #ifdef BUS_OBJERR DEFINE_ENTRY(BUS_OBJERR) #endif END_TABLE }; static struct str_table sigfpe_code_table[] = { #ifdef FPE_INTDIV DEFINE_ENTRY(FPE_INTDIV) #endif #ifdef FPE_INTOVF DEFINE_ENTRY(FPE_INTOVF) #endif #ifdef FPE_FLTDIV DEFINE_ENTRY(FPE_FLTDIV) #endif #ifdef FPE_FLTOVF DEFINE_ENTRY(FPE_FLTOVF) #endif #ifdef FPE_FLTUND DEFINE_ENTRY(FPE_FLTUND) #endif #ifdef FPE_FLTRES DEFINE_ENTRY(FPE_FLTRES) #endif #ifdef FPE_FLTINV DEFINE_ENTRY(FPE_FLTINV) #endif #ifdef FPE_FLTSUB DEFINE_ENTRY(FPE_FLTSUB) #endif END_TABLE }; static struct str_table sigsegv_code_table[] = { #ifdef SEGV_MAPERR DEFINE_ENTRY(SEGV_MAPERR) #endif #ifdef SEGV_ACCERR DEFINE_ENTRY(SEGV_ACCERR) #endif END_TABLE }; static struct str_table sigbus_code_table[] = { #ifdef BUS_ADRALN DEFINE_ENTRY(BUS_ADRALN) #endif #ifdef BUS_ADRERR DEFINE_ENTRY(BUS_ADRERR) #endif #ifdef BUS_OBJERR DEFINE_ENTRY(BUS_OBJERR) #endif END_TABLE }; #if 0 /* should this be hooked in somewhere? */ static struct str_table sigstrap_code_table[] = { #ifdef TRAP_BRKPT DEFINE_ENTRY(TRAP_BRKPT) #endif #ifdef TRAP_TRACE DEFINE_ENTRY(TRAP_TRACE) #endif END_TABLE }; #endif static struct str_table sigcld_code_table[] = { #ifdef CLD_EXITED DEFINE_ENTRY(CLD_EXITED) #endif #ifdef CLD_KILLED DEFINE_ENTRY(CLD_KILLED) #endif #ifdef CLD_DUMPED DEFINE_ENTRY(CLD_DUMPED) #endif #ifdef CLD_TRAPPED DEFINE_ENTRY(CLD_TRAPPED) #endif #ifdef CLD_STOPPED DEFINE_ENTRY(CLD_STOPPED) #endif #ifdef CLD_CONTINUED DEFINE_ENTRY(CLD_CONTINUED) #endif END_TABLE }; #if 0 /* should this be hooked in somewhere? */ static struct str_table sigpoll_code_table[] = { #ifdef POLL_IN DEFINE_ENTRY(POLL_IN) #endif #ifdef POLL_OUT DEFINE_ENTRY(POLL_OUT) #endif #ifdef POLL_MSG DEFINE_ENTRY(POLL_MSG) #endif #ifdef POLL_ERR DEFINE_ENTRY(POLL_ERR) #endif #ifdef POLL_PRI DEFINE_ENTRY(POLL_PRI) #endif #ifdef POLL_HUP DEFINE_ENTRY(POLL_HUP) #endif END_TABLE }; #endif static const char *lookup_table(int num, struct str_table *table) { struct str_table *p; for (p=table; p->name; p++) if (num == p->num) return(p->name); return NULL; } static const char *lookup_table_fallback(int num, struct str_table *table) { static char buf[32]; const char *ret = lookup_table(num, table); if (ret) return ret; snprintf(buf, sizeof(buf), "%d", num); buf[sizeof(buf)-1] = 0; return buf; } static void die_signal_handler(int signum, siginfo_t *siginfo, void *context EXT2FS_ATTR((unused))) { const char *cp; fprintf(stderr, "Signal (%d) %s ", signum, lookup_table_fallback(signum, sig_table)); if (siginfo->si_code == SI_USER) fprintf(stderr, "(sent from pid %u) ", siginfo->si_pid); cp = lookup_table(siginfo->si_code, generic_code_table); if (cp) fprintf(stderr, "si_code=%s ", cp); else if (signum == SIGILL) fprintf(stderr, "si_code=%s ", lookup_table_fallback(siginfo->si_code, sigill_code_table)); else if (signum == SIGFPE) fprintf(stderr, "si_code=%s ", lookup_table_fallback(siginfo->si_code, sigfpe_code_table)); else if (signum == SIGSEGV) fprintf(stderr, "si_code=%s ", lookup_table_fallback(siginfo->si_code, sigsegv_code_table)); else if (signum == SIGBUS) fprintf(stderr, "si_code=%s ", lookup_table_fallback(siginfo->si_code, sigbus_code_table)); else if (signum == SIGCHLD) fprintf(stderr, "si_code=%s ", lookup_table_fallback(siginfo->si_code, sigcld_code_table)); else fprintf(stderr, "si code=%d ", siginfo->si_code); if ((siginfo->si_code != SI_USER) && (signum == SIGILL || signum == SIGFPE || signum == SIGSEGV || signum == SIGBUS)) fprintf(stderr, "fault addr=%p", siginfo->si_addr); fprintf(stderr, "\n"); #if defined(HAVE_BACKTRACE) && !defined(DISABLE_BACKTRACE) { void *stack_syms[32]; int frames; frames = backtrace(stack_syms, 32); backtrace_symbols_fd(stack_syms, frames, 2); } #endif exit(FSCK_ERROR); } void sigcatcher_setup(void) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = die_signal_handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGFPE, &sa, 0); sigaction(SIGILL, &sa, 0); sigaction(SIGBUS, &sa, 0); sigaction(SIGSEGV, &sa, 0); sigaction(SIGABRT, &sa, 0); } #ifdef DEBUG #include <getopt.h> void usage(void) { fprintf(stderr, "tst_sigcatcher: [-akfn]\n"); exit(1); } int main(int argc, char** argv) { struct sigaction sa; char *p = 0; int i, c; volatile x=0; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = die_signal_handler; sa.sa_flags = SA_SIGINFO; for (i=1; i < 31; i++) sigaction(i, &sa, 0); while ((c = getopt (argc, argv, "afkn")) != EOF) switch (c) { case 'a': abort(); break; case 'f': printf("%d\n", 42/x); case 'k': kill(getpid(), SIGTERM); break; case 'n': *p = 42; default: usage (); } printf("Sleeping for 10 seconds, send kill signal to pid %u...\n", getpid()); fflush(stdout); sleep(10); exit(0); } #endif