/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Utility functions for setting signal handlers. This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_i.h" #include #include #include #include #include #ifdef HAVE_BACKTRACE #include #endif /* * Must not handle SIGCANCEL, as that is used to wake up threads from * syscalls reading requests from /dev/fuse */ 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; #ifdef HAVE_BACKTRACE #define BT_STACK_SZ (1024 * 1024) static void *backtrace_buffer[BT_STACK_SZ]; #endif static void dump_stack(void) { #ifdef HAVE_BACKTRACE char **strings; int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ); strings = backtrace_symbols(backtrace_buffer, nptrs); if (strings == NULL) { fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n", strerror(errno)); return; } for (int idx = 0; idx < nptrs; idx++) fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]); free(strings); #endif } static void exit_handler(int sig) { if (fuse_instance == NULL) { fuse_log(FUSE_LOG_ERR, "fuse_instance is NULL\n"); return; } if (fuse_instance->debug) fuse_log(FUSE_LOG_ERR, "exit_handler called with sig %d\n", sig); 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; } static int set_one_signal_handler(int sig, void (*handler)(int), int remove) { struct sigaction sa; struct sigaction old_sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = remove ? SIG_DFL : handler; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; if (sigaction(sig, NULL, &old_sa) == -1) { perror("fuse: cannot get old signal handler"); return -1; } if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && sigaction(sig, &sa, NULL) == -1) { perror("fuse: cannot set signal handler"); return -1; } 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) { 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; 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; /* * needs to be set independently if already set, as some applications * may have multiple sessions and might rely on traditional behavior * that the last session is used. */ 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; /* See fuse_set_signal_handlers, why set unconditionally */ 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; 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); }