132 lines
2.9 KiB
C
132 lines
2.9 KiB
C
#include "psignal.h"
|
|
#include <execinfo.h>
|
|
#include <signal.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#define PSIGNAL_MAX_CLEANUPS 16
|
|
static void (*cleanup_fns[PSIGNAL_MAX_CLEANUPS])(void);
|
|
static int cleanup_count = 0;
|
|
|
|
void psignal_register_cleanup(void (*fn)(void)) {
|
|
if (cleanup_count < PSIGNAL_MAX_CLEANUPS)
|
|
cleanup_fns[cleanup_count++] = fn;
|
|
}
|
|
|
|
static volatile sig_atomic_t sigint_flag = 0;
|
|
static volatile sig_atomic_t sigterm_flag = 0;
|
|
static volatile sig_atomic_t sighup_flag = 0;
|
|
|
|
static void sigint_handler(int sig) {
|
|
sigint_flag = 1;
|
|
}
|
|
|
|
static void sigterm_handler(int sig) {
|
|
sigterm_flag = 1;
|
|
}
|
|
|
|
static void sighup_handler(int sig) {
|
|
sighup_flag = 1;
|
|
}
|
|
|
|
void panic(const char *msg) {
|
|
void *frames[64];
|
|
int nframes;
|
|
char **symbols;
|
|
|
|
fprintf(stderr, "PANIC: %s\n", msg);
|
|
|
|
nframes = backtrace(frames, 64);
|
|
symbols = backtrace_symbols(frames, nframes);
|
|
|
|
if (symbols) {
|
|
fprintf(stderr, "Backtrace:\n");
|
|
for (int i = 0; i < nframes; i++) {
|
|
fprintf(stderr, " %s\n", symbols[i]);
|
|
}
|
|
free(symbols);
|
|
}
|
|
|
|
signal(SIGSEGV, SIG_DFL);
|
|
signal(SIGABRT, SIG_DFL);
|
|
signal(SIGBUS, SIG_DFL);
|
|
|
|
for (int i = 0; i < cleanup_count; i++)
|
|
cleanup_fns[i]();
|
|
|
|
/* Use _exit() instead of abort(): _exit closes all file descriptors
|
|
* immediately, releasing POSIX advisory locks (including SQLite's WAL
|
|
* locks). abort() is intercepted by ASan which can hang indefinitely,
|
|
* leaving the process as a zombie and the DB locked. */
|
|
_exit(1);
|
|
}
|
|
|
|
static void panic_handler(int sig) {
|
|
const char *msg;
|
|
if (sig == SIGSEGV)
|
|
msg = "Segmentation fault";
|
|
else if (sig == SIGABRT)
|
|
msg = "Abort";
|
|
else if (sig == SIGBUS)
|
|
msg = "Bus error";
|
|
else
|
|
msg = "Unknown signal";
|
|
panic(msg);
|
|
}
|
|
|
|
void psignal_set_custom_handler(int sig, void (*handler)(int)) {
|
|
struct sigaction sa;
|
|
|
|
if (sigaction(sig, NULL, &sa) != 0)
|
|
return;
|
|
|
|
if (sa.sa_handler == SIG_DFL) {
|
|
sa.sa_handler = handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
if (sigaction(sig, &sa, NULL) != 0) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void psignal_register(int signum) {
|
|
void (*handler)(int) = NULL;
|
|
|
|
if (signum == SIGINT) {
|
|
handler = sigint_handler;
|
|
} else if (signum == SIGTERM) {
|
|
handler = sigterm_handler;
|
|
} else if (signum == SIGHUP) {
|
|
handler = sighup_handler;
|
|
} else if (signum == SIGSEGV || signum == SIGABRT || signum == SIGBUS) {
|
|
handler = panic_handler;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
psignal_set_custom_handler(signum, handler);
|
|
}
|
|
|
|
int psignal_check_pending(void) {
|
|
sig_atomic_t int_flag = sigint_flag;
|
|
sig_atomic_t term_flag = sigterm_flag;
|
|
sig_atomic_t hup_flag = sighup_flag;
|
|
|
|
if (int_flag) {
|
|
sigint_flag = 0;
|
|
return SIGINT;
|
|
}
|
|
if (term_flag) {
|
|
sigterm_flag = 0;
|
|
return SIGTERM;
|
|
}
|
|
if (hup_flag) {
|
|
sighup_flag = 0;
|
|
return SIGHUP;
|
|
}
|
|
return 0;
|
|
}
|