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 if (fuse_instance == NULL)
154 fuse_instance = se;
155 return 0;
156}
157
158int fuse_set_fail_signal_handlers(struct fuse_session *se)
159{
160 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
161
162 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
163 exit_backtrace);
164 if (rc < 0)
165 return rc;
166
167 if (fuse_instance == NULL)
168 fuse_instance = se;
169
170 return 0;
171}
172
173static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
174 void (*handler)(int))
175{
176 for (int idx = 0; idx < nr_signals; idx++)
177 set_one_signal_handler(signals[idx], handler, 1);
178}
179
180void fuse_remove_signal_handlers(struct fuse_session *se)
181{
182 size_t nr_signals;
183
184 if (fuse_instance != se)
185 fuse_log(FUSE_LOG_ERR,
186 "fuse: fuse_remove_signal_handlers: unknown session\n");
187 else
188 fuse_instance = NULL;
189
190 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
191 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
192
193 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
194 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
195
196 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
197 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
198}
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)