diff options
author | Bernd Schubert <bschubert@ddn.com> | 2024-07-10 23:04:46 +0200 |
---|---|---|
committer | Bernd Schubert <bernd.schubert@fastmail.fm> | 2024-07-14 14:28:44 +0200 |
commit | dae1184302834b52cff438fbf5322cd1c9c79c06 (patch) | |
tree | 3c71a7f60f442d460f5968e239ab0ed2a0defe27 /lib/fuse_signals.c | |
parent | 67ce439e2d73e73426b68695288729c6ffd63e5b (diff) | |
download | libfuse-dae1184302834b52cff438fbf5322cd1c9c79c06.tar.gz |
Add syslog and fatal signal handler feature
I see random ENOTCONN failures in xfstest generic/013 and generic/014
in my branch, but earliest on the 2nd run - takes ~12hours to get
the issue, but then there are no further information logged.
ENOTCONN points to a daemon crash - I need backtraces and a core dump.
This adds optional handling of fatal signals to print a core dump
and optional syslog logging with these new public functions:
fuse_set_fail_signal_handlers()
In addition to the existing fuse_set_signal_handlers(). This is not
enabled together with fuse_set_signal_handlers(), as it is change
in behavior and file systems might already have their own fatal
handlers.
fuse_log_enable_syslog
Print logs to syslog instead of stderr
fuse_log_close_syslog
Close syslog (for now just does closelog())
Code in fuse_signals.c is also updated, to be an array of signals,
and setting signal handlers is now down with a for-loop instead
of one hand coded set_one_signal_handler() per signal.
Diffstat (limited to 'lib/fuse_signals.c')
-rw-r--r-- | lib/fuse_signals.c | 146 |
1 files changed, 119 insertions, 27 deletions
diff --git a/lib/fuse_signals.c b/lib/fuse_signals.c index 4c1d172..0380c82 100644 --- a/lib/fuse_signals.c +++ b/lib/fuse_signals.c @@ -17,35 +17,72 @@ #include <signal.h> #include <stdlib.h> #include <execinfo.h> +#include <errno.h> +static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM }; +static int ignore_sigs[] = { SIGPIPE}; +static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV }; static struct fuse_session *fuse_instance; +#define BT_STACK_SZ (1024 * 1024) +static void *backtrace_buffer[BT_STACK_SZ]; + static void dump_stack(void) { + fprintf(stderr, "%s:%d\n", __func__, __LINE__); #ifdef HAVE_BACKTRACE - const size_t backtrace_sz = 1024 * 1024; - void* backtrace_buffer[backtrace_sz]; + char **strings; + + int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ); + strings = backtrace_symbols(backtrace_buffer, nptrs); + + fprintf(stderr, "%s: nptrs=%d\n", __func__, nptrs); + + if (strings == NULL) { + fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n", + strerror(errno)); + return; + } - int err_fd = fileno(stderr); + for (int idx = 0; idx < nptrs; idx++) + fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]); - int trace_len = backtrace(backtrace_buffer, backtrace_sz); - backtrace_symbols_fd(backtrace_buffer, trace_len, err_fd); + free(strings); #endif } static void exit_handler(int sig) { - if (fuse_instance) { - fuse_session_exit(fuse_instance); - if(sig <= 0) { - fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); - dump_stack(); - abort(); - } + if (fuse_instance == NULL) + return; + + fuse_session_exit(fuse_instance); + + if (sig < 0) { + fuse_log(FUSE_LOG_ERR, + "assertion error: signal value <= 0\n"); + dump_stack(); + abort(); fuse_instance->error = sig; } + + fuse_instance->error = sig; } +static void exit_backtrace(int sig) +{ + if (fuse_instance == NULL) + return; + + fuse_session_exit(fuse_instance); + + fuse_remove_signal_handlers(fuse_instance); + fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig); + dump_stack(); + abort(); +} + + static void do_nothing(int sig) { (void) sig; @@ -74,33 +111,88 @@ static int set_one_signal_handler(int sig, void (*handler)(int), int remove) return 0; } +static int _fuse_set_signal_handlers(int signals[], int nr_signals, + void (*handler)(int)) +{ + for (int idx = 0; idx < nr_signals; idx++) { + int signal = signals[idx]; + + /* + * If we used SIG_IGN instead of the do_nothing function, + * then we would be unable to tell if we set SIG_IGN (and + * thus should reset to SIG_DFL in fuse_remove_signal_handlers) + * or if it was already set to SIG_IGN (and should be left + * untouched. + */ + if (set_one_signal_handler(signal, handler, 0) == -1) { + fuse_log(FUSE_LOG_ERR, + "Failed to install signal handler for sig %d\n", + signal); + return -1; + } + } + + return 0; +} + int fuse_set_signal_handlers(struct fuse_session *se) { - /* If we used SIG_IGN instead of the do_nothing function, - then we would be unable to tell if we set SIG_IGN (and - thus should reset to SIG_DFL in fuse_remove_signal_handlers) - or if it was already set to SIG_IGN (and should be left - untouched. */ - if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || - set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || - set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || - set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) - return -1; + size_t nr_signals; + int rc; + + nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]); + rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler); + if (rc < 0) + return rc; - fuse_instance = se; + nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]); + rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing); + if (rc < 0) + return rc; + + if (fuse_instance == NULL) + fuse_instance = se; return 0; } +int fuse_set_fail_signal_handlers(struct fuse_session *se) +{ + size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]); + + int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals, + exit_backtrace); + if (rc < 0) + return rc; + + if (fuse_instance == NULL) + fuse_instance = se; + + return 0; +} + +static void _fuse_remove_signal_handlers(int signals[], int nr_signals, + void (*handler)(int)) +{ + for (int idx = 0; idx < nr_signals; idx++) + set_one_signal_handler(signals[idx], handler, 1); +} + void fuse_remove_signal_handlers(struct fuse_session *se) { + size_t nr_signals; + if (fuse_instance != se) fuse_log(FUSE_LOG_ERR, "fuse: fuse_remove_signal_handlers: unknown session\n"); else fuse_instance = NULL; - set_one_signal_handler(SIGHUP, exit_handler, 1); - set_one_signal_handler(SIGINT, exit_handler, 1); - set_one_signal_handler(SIGTERM, exit_handler, 1); - set_one_signal_handler(SIGPIPE, do_nothing, 1); + nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]); + _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler); + + nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]); + _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing); + + nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]); + _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace); } |