libfuse
fuse_signals.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Utility functions for setting signal handlers.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14
15#include <stdio.h>
16#include <string.h>
17#include <signal.h>
18#include <stdlib.h>
19#include <errno.h>
20
21#ifdef HAVE_BACKTRACE
22#include <execinfo.h>
23#endif
24
25static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM };
26static int ignore_sigs[] = { SIGPIPE};
27static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV };
28static struct fuse_session *fuse_instance;
29
30#define BT_STACK_SZ (1024 * 1024)
31static void *backtrace_buffer[BT_STACK_SZ];
32
33static void dump_stack(void)
34{
35#ifdef HAVE_BACKTRACE
36 char **strings;
37
38 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
39 strings = backtrace_symbols(backtrace_buffer, nptrs);
40
41 if (strings == NULL) {
42 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
43 strerror(errno));
44 return;
45 }
46
47 for (int idx = 0; idx < nptrs; idx++)
48 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
49
50 free(strings);
51#endif
52}
53
54static void exit_handler(int sig)
55{
56 if (fuse_instance == NULL)
57 return;
58
59 fuse_session_exit(fuse_instance);
60
61 if (sig < 0) {
62 fuse_log(FUSE_LOG_ERR,
63 "assertion error: signal value <= 0\n");
64 dump_stack();
65 abort();
66 fuse_instance->error = sig;
67 }
68
69 fuse_instance->error = sig;
70}
71
72static void exit_backtrace(int sig)
73{
74 if (fuse_instance == NULL)
75 return;
76
77 fuse_session_exit(fuse_instance);
78
79 fuse_remove_signal_handlers(fuse_instance);
80 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
81 dump_stack();
82 abort();
83}
84
85
86static void do_nothing(int sig)
87{
88 (void) sig;
89}
90
91static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
92{
93 struct sigaction sa;
94 struct sigaction old_sa;
95
96 memset(&sa, 0, sizeof(struct sigaction));
97 sa.sa_handler = remove ? SIG_DFL : handler;
98 sigemptyset(&(sa.sa_mask));
99 sa.sa_flags = 0;
100
101 if (sigaction(sig, NULL, &old_sa) == -1) {
102 perror("fuse: cannot get old signal handler");
103 return -1;
104 }
105
106 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
107 sigaction(sig, &sa, NULL) == -1) {
108 perror("fuse: cannot set signal handler");
109 return -1;
110 }
111 return 0;
112}
113
114static int _fuse_set_signal_handlers(int signals[], int nr_signals,
115 void (*handler)(int))
116{
117 for (int idx = 0; idx < nr_signals; idx++) {
118 int signal = signals[idx];
119
120 /*
121 * If we used SIG_IGN instead of the do_nothing function,
122 * then we would be unable to tell if we set SIG_IGN (and
123 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
124 * or if it was already set to SIG_IGN (and should be left
125 * untouched.
126 */
127 if (set_one_signal_handler(signal, handler, 0) == -1) {
128 fuse_log(FUSE_LOG_ERR,
129 "Failed to install signal handler for sig %d\n",
130 signal);
131 return -1;
132 }
133 }
134
135 return 0;
136}
137
138int fuse_set_signal_handlers(struct fuse_session *se)
139{
140 size_t nr_signals;
141 int rc;
142
143 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
144 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
145 if (rc < 0)
146 return rc;
147
148 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
149 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
150 if (rc < 0)
151 return rc;
152
153 /*
154 * needs to be set independently if already set, as some applications
155 * may have multiple sessions and might rely on traditional behavior
156 * that the last session is used.
157 */
158 fuse_instance = se;
159
160 return 0;
161}
162
163int fuse_set_fail_signal_handlers(struct fuse_session *se)
164{
165 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
166
167 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
168 exit_backtrace);
169 if (rc < 0)
170 return rc;
171
172 /* See fuse_set_signal_handlers, why set unconditionally */
173 fuse_instance = se;
174
175 return 0;
176}
177
178static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
179 void (*handler)(int))
180{
181 for (int idx = 0; idx < nr_signals; idx++)
182 set_one_signal_handler(signals[idx], handler, 1);
183}
184
185void fuse_remove_signal_handlers(struct fuse_session *se)
186{
187 size_t nr_signals;
188
189 if (fuse_instance != se)
190 fuse_log(FUSE_LOG_ERR,
191 "fuse: fuse_remove_signal_handlers: unknown session\n");
192 else
193 fuse_instance = NULL;
194
195 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
196 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
197
198 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
199 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
200
201 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
202 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
203}
int fuse_set_fail_signal_handlers(struct fuse_session *se)
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)