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