diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/fuse_log.c | 64 | ||||
-rw-r--r-- | lib/fuse_signals.c | 146 | ||||
-rw-r--r-- | lib/fuse_versionscript | 3 |
3 files changed, 182 insertions, 31 deletions
diff --git a/lib/fuse_log.c b/lib/fuse_log.c index 0d268ab..95d6379 100644 --- a/lib/fuse_log.c +++ b/lib/fuse_log.c @@ -12,12 +12,56 @@ #include <stdarg.h> #include <stdio.h> +#include <stdbool.h> +#include <syslog.h> -static void default_log_func( - __attribute__(( unused )) enum fuse_log_level level, - const char *fmt, va_list ap) +#define MAX_SYSLOG_LINE_LEN 512 + +static bool to_syslog = false; + +static void default_log_func(__attribute__((unused)) enum fuse_log_level level, + const char *fmt, va_list ap) { - vfprintf(stderr, fmt, ap); + if (to_syslog) { + int sys_log_level; + + /* + * with glibc fuse_log_level has identical values as + * syslog levels, but we also support BSD - better we convert to + * be sure. + */ + switch (level) { + case FUSE_LOG_DEBUG: + sys_log_level = LOG_DEBUG; + break; + case FUSE_LOG_INFO: + sys_log_level = LOG_INFO; + break; + case FUSE_LOG_NOTICE: + sys_log_level = LOG_NOTICE; + break; + case FUSE_LOG_WARNING: + sys_log_level = LOG_WARNING; + break; + case FUSE_LOG_ERR: + sys_log_level = LOG_ERR; + break; + case FUSE_LOG_CRIT: + sys_log_level = LOG_CRIT; + break; + case FUSE_LOG_ALERT: + sys_log_level = LOG_ALERT; + break; + case FUSE_LOG_EMERG: + sys_log_level = LOG_EMERG; + } + + char log[MAX_SYSLOG_LINE_LEN]; + vsnprintf(log, MAX_SYSLOG_LINE_LEN, fmt, ap); + syslog(sys_log_level, "%s", log); + } else { + vfprintf(stderr, fmt, ap); + } } static fuse_log_func_t log_func = default_log_func; @@ -38,3 +82,15 @@ void fuse_log(enum fuse_log_level level, const char *fmt, ...) log_func(level, fmt, ap); va_end(ap); } + +void fuse_log_enable_syslog(const char *ident, int option, int facility) +{ + to_syslog = true; + + openlog(ident, option, facility); +} + +void fuse_log_close_syslog(void) +{ + closelog(); +}
\ No newline at end of file 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); } diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 1058372..14cbca1 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -198,6 +198,9 @@ FUSE_3.17 { fuse_passthrough_close; fuse_session_custom_io_30; fuse_session_custom_io_317; + fuse_set_fail_signal_handlers; + fuse_log_enable_syslog; + fuse_log_close_syslog; } FUSE_3.12; # Local Variables: |