tvheadend/src/trap.c

234 lines
4.8 KiB
C
Raw Normal View History

/**
* Crash handling
* Copyright (C) 2009 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2009-12-14 08:59:23 +00:00
#include "trap.h"
char tvh_binshasum[20];
2009-12-14 08:59:23 +00:00
#if defined(__i386__) || defined(__x86_64__)
// Only do this on x86 for now
#define _GNU_SOURCE
#include <link.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <ucontext.h>
#include <execinfo.h>
#include <stdio.h>
#include <stdarg.h>
2010-08-19 18:06:47 +00:00
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
2010-12-30 09:17:14 +00:00
#include <openssl/sha.h>
2010-11-29 19:25:24 +00:00
#include "tvheadend.h"
#define MAXFRAMES 100
2010-08-19 18:06:47 +00:00
static char line1[200];
static char tmpbuf[1024];
static char libs[1024];
static void
2010-08-19 18:06:47 +00:00
sappend(char *buf, size_t l, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
2010-08-19 18:06:47 +00:00
vsnprintf(buf + strlen(buf), l - strlen(buf), fmt, ap);
va_end(ap);
}
static void
traphandler(int sig, siginfo_t *si, void *UC)
{
ucontext_t *uc = UC;
static void *frames[MAXFRAMES];
int nframes = backtrace(frames, MAXFRAMES);
2010-08-19 18:06:47 +00:00
Dl_info dli;
int i;
2010-08-19 18:06:47 +00:00
const char *reason = NULL;
tvhlog_spawn(LOG_ALERT, "CRASH", "Signal: %d in %s ", sig, line1);
switch(sig) {
case SIGSEGV:
switch(si->si_code) {
case SEGV_MAPERR: reason = "Address not mapped"; break;
case SEGV_ACCERR: reason = "Access error"; break;
}
break;
case SIGFPE:
switch(si->si_code) {
case FPE_INTDIV: reason = "Integer division by zero"; break;
}
break;
}
tvhlog_spawn(LOG_ALERT, "CRASH", "Fault address %p (%s)",
si->si_addr, reason ?: "N/A");
tvhlog_spawn(LOG_ALERT, "CRASH", "Loaded libraries: %s ", libs);
snprintf(tmpbuf, sizeof(tmpbuf), "Register dump [%d]: ", NGREG);
for(i = 0; i < NGREG; i++) {
#if __WORDSIZE == 64
2010-08-19 18:06:47 +00:00
sappend(tmpbuf, sizeof(tmpbuf), "%016llx ", uc->uc_mcontext.gregs[i]);
#else
2010-08-19 18:06:47 +00:00
sappend(tmpbuf, sizeof(tmpbuf), "%08x ", uc->uc_mcontext.gregs[i]);
#endif
}
2010-08-19 18:06:47 +00:00
tvhlog_spawn(LOG_ALERT, "CRASH", "%s", tmpbuf);
2010-08-19 18:06:47 +00:00
tvhlog_spawn(LOG_ALERT, "CRASH", "STACKTRACE");
2010-08-19 18:06:47 +00:00
for(i = 0; i < nframes; i++) {
if(dladdr(frames[i], &dli)) {
if(dli.dli_sname != NULL && dli.dli_saddr != NULL) {
tvhlog_spawn(LOG_ALERT, "CRASH", "%s+0x%tx (%s)",
dli.dli_sname,
frames[i] - dli.dli_saddr,
dli.dli_fname);
continue;
}
if(dli.dli_fname != NULL && dli.dli_fbase != NULL) {
tvhlog_spawn(LOG_ALERT, "CRASH", "%s %p",
dli.dli_fname,
frames[i]);
continue;
}
tvhlog_spawn(LOG_ALERT, "CRASH", "%p", frames[i]);
}
}
}
static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
2010-08-19 18:06:47 +00:00
if(info->dlpi_name[0])
sappend(libs, sizeof(libs), "%s ", info->dlpi_name);
return 0;
}
extern const char *htsversion_full;
void
trap_init(const char *ver)
{
uint8_t digest[20];
struct sigaction sa, old;
char path[256];
2010-12-30 09:17:14 +00:00
SHA_CTX binsum;
2010-08-19 18:06:47 +00:00
int fd;
if((fd = open("/proc/self/exe", O_RDONLY)) != -1) {
struct stat st;
if(!fstat(fd, &st)) {
char *m = malloc(st.st_size);
if(m != NULL) {
if(read(fd, m, st.st_size) == st.st_size) {
2010-12-30 09:17:14 +00:00
SHA_Init(&binsum);
SHA_Update(&binsum, (void *)m, st.st_size);
SHA_Final(digest, &binsum);
2010-08-19 18:06:47 +00:00
}
free(m);
}
}
}
snprintf(line1, sizeof(line1),
"PRG: %s (%s) "
"[%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x] "
"CWD: %s ", ver, htsversion_full,
digest[0],
digest[1],
digest[2],
digest[3],
digest[4],
digest[5],
digest[6],
digest[7],
digest[8],
digest[9],
digest[10],
digest[11],
digest[12],
digest[13],
digest[14],
digest[15],
digest[16],
digest[17],
digest[18],
digest[19],
getcwd(path, sizeof(path)));
memcpy(tvh_binshasum, digest, 20);
dl_iterate_phdr(callback, NULL);
memset(&sa, 0, sizeof(sa));
2010-08-19 18:06:47 +00:00
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGSEGV);
sigaddset(&m, SIGBUS);
sigaddset(&m, SIGILL);
sigaddset(&m, SIGABRT);
sigaddset(&m, SIGFPE);
sa.sa_sigaction = traphandler;
sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
sigaction(SIGSEGV, &sa, &old);
sigaction(SIGBUS, &sa, &old);
sigaction(SIGILL, &sa, &old);
sigaction(SIGABRT, &sa, &old);
sigaction(SIGFPE, &sa, &old);
2010-08-19 18:06:47 +00:00
sigprocmask(SIG_UNBLOCK, &m, NULL);
}
2009-12-14 08:59:23 +00:00
#else
void
trap_init(const char *ver)
{
}
#endif