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 LGPL2.txt
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
25/*
26 * Must not handle SIGCANCEL, as that is used to wake up threads from
27 * syscalls reading requests from /dev/fuse
28 */
29static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM };
30
31static int ignore_sigs[] = { SIGPIPE};
32static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV };
33static struct fuse_session *fuse_instance;
34
35#ifdef HAVE_BACKTRACE
36#define BT_STACK_SZ (1024 * 1024)
37static void *backtrace_buffer[BT_STACK_SZ];
38#endif
39
40static void dump_stack(void)
41{
42#ifdef HAVE_BACKTRACE
43 char **strings;
44
45 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
46 strings = backtrace_symbols(backtrace_buffer, nptrs);
47
48 if (strings == NULL) {
49 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
50 strerror(errno));
51 return;
52 }
53
54 for (int idx = 0; idx < nptrs; idx++)
55 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
56
57 free(strings);
58#endif
59}
60
61static void exit_handler(int sig)
62{
63 if (fuse_instance == NULL) {
64 fuse_log(FUSE_LOG_ERR, "fuse_instance is NULL\n");
65 return;
66 }
67
68 if (fuse_instance->debug)
69 fuse_log(FUSE_LOG_ERR, "exit_handler called with sig %d\n",
70 sig);
71
72 fuse_session_exit(fuse_instance);
73
74 if (sig < 0) {
75 fuse_log(FUSE_LOG_ERR,
76 "assertion error: signal value <= 0\n");
77 dump_stack();
78 abort();
79 fuse_instance->error = sig;
80 }
81
82 fuse_instance->error = sig;
83}
84
85static void exit_backtrace(int sig)
86{
87 if (fuse_instance == NULL)
88 return;
89
90 fuse_session_exit(fuse_instance);
91
92 fuse_remove_signal_handlers(fuse_instance);
93 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
94 dump_stack();
95 abort();
96}
97
98
99static void do_nothing(int sig)
100{
101 (void) sig;
102}
103
104static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
105{
106 struct sigaction sa;
107 struct sigaction old_sa;
108
109 memset(&sa, 0, sizeof(struct sigaction));
110 sa.sa_handler = remove ? SIG_DFL : handler;
111 sigemptyset(&(sa.sa_mask));
112 sa.sa_flags = 0;
113
114 if (sigaction(sig, NULL, &old_sa) == -1) {
115 perror("fuse: cannot get old signal handler");
116 return -1;
117 }
118
119 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
120 sigaction(sig, &sa, NULL) == -1) {
121 perror("fuse: cannot set signal handler");
122 return -1;
123 }
124 return 0;
125}
126
127static int _fuse_set_signal_handlers(int signals[], int nr_signals,
128 void (*handler)(int))
129{
130 for (int idx = 0; idx < nr_signals; idx++) {
131 int signal = signals[idx];
132
133 /*
134 * If we used SIG_IGN instead of the do_nothing function,
135 * then we would be unable to tell if we set SIG_IGN (and
136 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
137 * or if it was already set to SIG_IGN (and should be left
138 * untouched.
139 */
140 if (set_one_signal_handler(signal, handler, 0) == -1) {
141 fuse_log(FUSE_LOG_ERR,
142 "Failed to install signal handler for sig %d\n",
143 signal);
144 return -1;
145 }
146 }
147
148 return 0;
149}
150
151int fuse_set_signal_handlers(struct fuse_session *se)
152{
153 size_t nr_signals;
154 int rc;
155
156 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
157 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
158 if (rc < 0)
159 return rc;
160
161 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
162 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
163 if (rc < 0)
164 return rc;
165
166 /*
167 * needs to be set independently if already set, as some applications
168 * may have multiple sessions and might rely on traditional behavior
169 * that the last session is used.
170 */
171 fuse_instance = se;
172
173 return 0;
174}
175
176int fuse_set_fail_signal_handlers(struct fuse_session *se)
177{
178 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
179
180 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
181 exit_backtrace);
182 if (rc < 0)
183 return rc;
184
185 /* See fuse_set_signal_handlers, why set unconditionally */
186 fuse_instance = se;
187
188 return 0;
189}
190
191static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
192 void (*handler)(int))
193{
194 for (int idx = 0; idx < nr_signals; idx++)
195 set_one_signal_handler(signals[idx], handler, 1);
196}
197
198void fuse_remove_signal_handlers(struct fuse_session *se)
199{
200 size_t nr_signals;
201
202 if (fuse_instance != se)
203 fuse_log(FUSE_LOG_ERR,
204 "fuse: fuse_remove_signal_handlers: unknown session\n");
205 else
206 fuse_instance = NULL;
207
208 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
209 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
210
211 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
212 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
213
214 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
215 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
216}
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)