xref: /netbsd-src/lib/librefuse/refuse_signals.c (revision 9f391507388a6ffcba9f0cd322f6670b919e36ec)
1*9f391507Spho /* $NetBSD: refuse_signals.c,v 1.1 2022/01/22 07:53:06 pho Exp $ */
2*9f391507Spho 
3*9f391507Spho /*
4*9f391507Spho  * Copyright (c) 2021 The NetBSD Foundation, Inc.
5*9f391507Spho  * All rights reserved.
6*9f391507Spho  *
7*9f391507Spho  * Redistribution and use in source and binary forms, with or without
8*9f391507Spho  * modification, are permitted provided that the following conditions
9*9f391507Spho  * are met:
10*9f391507Spho  * 1. Redistributions of source code must retain the above copyright
11*9f391507Spho  *    notice, this list of conditions and the following disclaimer.
12*9f391507Spho  * 2. Redistributions in binary form must reproduce the above copyright
13*9f391507Spho  *    notice, this list of conditions and the following disclaimer in the
14*9f391507Spho  *    documentation and/or other materials provided with the distribution.
15*9f391507Spho  * 3. The name of the author may not be used to endorse or promote
16*9f391507Spho  *    products derived from this software without specific prior written
17*9f391507Spho  *    permission.
18*9f391507Spho  *
19*9f391507Spho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20*9f391507Spho  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21*9f391507Spho  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*9f391507Spho  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23*9f391507Spho  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*9f391507Spho  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25*9f391507Spho  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26*9f391507Spho  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27*9f391507Spho  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28*9f391507Spho  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29*9f391507Spho  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*9f391507Spho  */
31*9f391507Spho 
32*9f391507Spho #include <sys/cdefs.h>
33*9f391507Spho #if !defined(lint)
34*9f391507Spho __RCSID("$NetBSD: refuse_signals.c,v 1.1 2022/01/22 07:53:06 pho Exp $");
35*9f391507Spho #endif /* !lint */
36*9f391507Spho 
37*9f391507Spho #include <assert.h>
38*9f391507Spho #include <fuse_internal.h>
39*9f391507Spho #if defined(MULTITHREADED_REFUSE)
40*9f391507Spho #  include <pthread.h>
41*9f391507Spho #endif
42*9f391507Spho #include <signal.h>
43*9f391507Spho #include <stdlib.h>
44*9f391507Spho #include <string.h>
45*9f391507Spho 
46*9f391507Spho /* Signal handling routines
47*9f391507Spho  *
48*9f391507Spho  * FUSE only supports running a single filesystem per process. ReFUSE
49*9f391507Spho  * is going to allow a process to run a filesystem per thread. In
50*9f391507Spho  * order to support this, our implementation of
51*9f391507Spho  * fuse_set_signal_handlers() installs a set of signal handlers which,
52*9f391507Spho  * when invoked, terminates all the filesystems that called the
53*9f391507Spho  * function. This means our fuse_remove_signal_handlers() must not
54*9f391507Spho  * actually remove the signal handlers until the last thread calls the
55*9f391507Spho  * function.
56*9f391507Spho  *
57*9f391507Spho  * FUSE installs a signal handler for a signal only if its sa_handler
58*9f391507Spho  * is set to SIG_DFL. This obviously has a bad consequence: if the
59*9f391507Spho  * caller process already has a non-default signal handler for SIGINT,
60*9f391507Spho  * Ctrl-C will not stop the main loop of FUSE. See
61*9f391507Spho  * https://stackoverflow.com/q/5044375/3571336
62*9f391507Spho  *
63*9f391507Spho  * Maybe we should do the same knowing it's bad, but it's probably
64*9f391507Spho  * better to call our handler along with the old one. We may change
65*9f391507Spho  * this behavior if this turns out to cause serious compatibility
66*9f391507Spho  * issues.
67*9f391507Spho  *
68*9f391507Spho  * Also note that it is tempting to use puffs_unmountonsignal(3) but
69*9f391507Spho  * we can't, because there is no way to revert its effect.
70*9f391507Spho  */
71*9f391507Spho 
72*9f391507Spho #if defined(MULTITHREADED_REFUSE)
73*9f391507Spho /* A mutex to protect the global state regarding signal handlers. When
74*9f391507Spho  * a thread is going to lock this, it must block all the signals (with
75*9f391507Spho  * pthread_sigmask(3)) that we install a handler for, or otherwise it
76*9f391507Spho  * may deadlock for trying to acquire a lock that is already held by
77*9f391507Spho  * itself. */
78*9f391507Spho static pthread_mutex_t signal_mutex = PTHREAD_MUTEX_INITIALIZER;
79*9f391507Spho #endif
80*9f391507Spho 
81*9f391507Spho /* Saved sigaction for each signal before we modify them. */
82*9f391507Spho static struct sigaction* saved_actions[NSIG];
83*9f391507Spho 
84*9f391507Spho /* A linked list of "struct fuse*" which should be terminated upon
85*9f391507Spho  * receiving a signal. */
86*9f391507Spho struct refuse_obj_elem {
87*9f391507Spho     struct fuse* fuse;
88*9f391507Spho     struct refuse_obj_elem* next;
89*9f391507Spho };
90*9f391507Spho static struct refuse_obj_elem* fuse_head;
91*9f391507Spho 
92*9f391507Spho #if defined(MULTITHREADED_REFUSE)
93*9f391507Spho static int
block_signals(sigset_t * oset)94*9f391507Spho block_signals(sigset_t* oset) {
95*9f391507Spho     sigset_t set;
96*9f391507Spho 
97*9f391507Spho     if (sigemptyset(&set) != 0)
98*9f391507Spho         return -1;
99*9f391507Spho 
100*9f391507Spho     if (sigaddset(&set, SIGHUP) != 0)
101*9f391507Spho         return -1;
102*9f391507Spho 
103*9f391507Spho     if (sigaddset(&set, SIGINT) != 0)
104*9f391507Spho         return -1;
105*9f391507Spho 
106*9f391507Spho     if (sigaddset(&set, SIGTERM) != 0)
107*9f391507Spho         return -1;
108*9f391507Spho 
109*9f391507Spho     return pthread_sigmask(SIG_BLOCK, &set, oset);
110*9f391507Spho }
111*9f391507Spho 
112*9f391507Spho static int
unblock_signals(const sigset_t * oset)113*9f391507Spho unblock_signals(const sigset_t* oset) {
114*9f391507Spho     return pthread_sigmask(SIG_SETMASK, oset, NULL);
115*9f391507Spho }
116*9f391507Spho #endif /* defined(MULTITHREADED_REFUSE) */
117*9f391507Spho 
118*9f391507Spho /* handler == NULL means the signal should be ignored. */
119*9f391507Spho static int
set_signal_handler(int sig,void (* handler)(int,siginfo_t *,void *))120*9f391507Spho set_signal_handler(int sig, void (*handler)(int, siginfo_t*, void*)) {
121*9f391507Spho     struct sigaction* saved;
122*9f391507Spho     struct sigaction act;
123*9f391507Spho 
124*9f391507Spho     saved = malloc(sizeof(*saved));
125*9f391507Spho     if (!saved)
126*9f391507Spho         return -1;
127*9f391507Spho 
128*9f391507Spho     if (sigaction(sig, NULL, saved) != 0) {
129*9f391507Spho         free(saved);
130*9f391507Spho         return -1;
131*9f391507Spho     }
132*9f391507Spho 
133*9f391507Spho     saved_actions[sig] = saved;
134*9f391507Spho 
135*9f391507Spho     memset(&act, 0, sizeof(act));
136*9f391507Spho     if (handler) {
137*9f391507Spho         act.sa_sigaction = handler;
138*9f391507Spho         act.sa_flags = SA_SIGINFO;
139*9f391507Spho     }
140*9f391507Spho     else {
141*9f391507Spho         /* Ignore the signal only if the signal doesn't have a
142*9f391507Spho          * handler. */
143*9f391507Spho         if (!(saved->sa_flags & SA_SIGINFO) && saved->sa_handler == SIG_DFL)
144*9f391507Spho             act.sa_handler = SIG_IGN;
145*9f391507Spho         else
146*9f391507Spho             return 0;
147*9f391507Spho     }
148*9f391507Spho 
149*9f391507Spho     if (sigemptyset(&act.sa_mask) != 0) {
150*9f391507Spho         free(saved);
151*9f391507Spho         saved_actions[sig] = NULL;
152*9f391507Spho         return -1;
153*9f391507Spho     }
154*9f391507Spho 
155*9f391507Spho     return sigaction(sig, &act, NULL);
156*9f391507Spho }
157*9f391507Spho 
158*9f391507Spho static int
restore_signal_handler(int sig,void (* handler)(int,siginfo_t *,void *))159*9f391507Spho restore_signal_handler(int sig, void (*handler)(int, siginfo_t*, void*)) {
160*9f391507Spho     struct sigaction oact;
161*9f391507Spho     struct sigaction* saved;
162*9f391507Spho 
163*9f391507Spho     saved = saved_actions[sig];
164*9f391507Spho     assert(saved != NULL);
165*9f391507Spho 
166*9f391507Spho     if (sigaction(sig, NULL, &oact) != 0)
167*9f391507Spho         return -1;
168*9f391507Spho 
169*9f391507Spho     /* Has the sigaction changed since we installed our handler? Do
170*9f391507Spho      * nothing if so. */
171*9f391507Spho     if (handler) {
172*9f391507Spho         if (!(oact.sa_flags & SA_SIGINFO) || oact.sa_sigaction != handler)
173*9f391507Spho             goto done;
174*9f391507Spho     }
175*9f391507Spho     else {
176*9f391507Spho         if (oact.sa_handler != SIG_IGN)
177*9f391507Spho             goto done;
178*9f391507Spho     }
179*9f391507Spho 
180*9f391507Spho     if (sigaction(sig, saved, NULL) != 0)
181*9f391507Spho         return -1;
182*9f391507Spho 
183*9f391507Spho   done:
184*9f391507Spho     free(saved);
185*9f391507Spho     saved_actions[sig] = NULL;
186*9f391507Spho     return 0;
187*9f391507Spho }
188*9f391507Spho 
189*9f391507Spho static void
exit_handler(int sig,siginfo_t * info,void * ctx)190*9f391507Spho exit_handler(int sig, siginfo_t* info, void* ctx) {
191*9f391507Spho     struct refuse_obj_elem* elem;
192*9f391507Spho     struct sigaction* saved;
193*9f391507Spho #if defined(MULTITHREADED_REFUSE)
194*9f391507Spho     int rv;
195*9f391507Spho 
196*9f391507Spho     /* pthread_mutex_lock(3) is NOT an async-signal-safe function. We
197*9f391507Spho      * assume it's okay, as the thread running this handler shouldn't
198*9f391507Spho      * be locking this mutex. */
199*9f391507Spho     rv = pthread_mutex_lock(&signal_mutex);
200*9f391507Spho     assert(rv == 0);
201*9f391507Spho #endif
202*9f391507Spho 
203*9f391507Spho     for (elem = fuse_head; elem != NULL; elem = elem->next)
204*9f391507Spho         fuse_exit(elem->fuse);
205*9f391507Spho 
206*9f391507Spho #if defined(MULTITHREADED_REFUSE)
207*9f391507Spho     rv = pthread_mutex_unlock(&signal_mutex);
208*9f391507Spho     assert(rv == 0);
209*9f391507Spho #endif
210*9f391507Spho 
211*9f391507Spho     saved = saved_actions[sig];
212*9f391507Spho     assert(saved != NULL);
213*9f391507Spho 
214*9f391507Spho     if (saved->sa_handler != SIG_DFL && saved->sa_handler != SIG_IGN) {
215*9f391507Spho         if (saved->sa_flags & SA_SIGINFO)
216*9f391507Spho             saved->sa_sigaction(sig, info, ctx);
217*9f391507Spho         else
218*9f391507Spho             saved->sa_handler(sig);
219*9f391507Spho     }
220*9f391507Spho }
221*9f391507Spho 
222*9f391507Spho /* The original function appeared on FUSE 2.5 takes a pointer to
223*9f391507Spho  * "struct fuse_session" instead of "struct fuse". We have no such
224*9f391507Spho  * things as fuse sessions.
225*9f391507Spho  */
226*9f391507Spho int
__fuse_set_signal_handlers(struct fuse * fuse)227*9f391507Spho __fuse_set_signal_handlers(struct fuse* fuse) {
228*9f391507Spho     int ret = 0;
229*9f391507Spho     struct refuse_obj_elem* elem;
230*9f391507Spho #if defined(MULTITHREADED_REFUSE)
231*9f391507Spho     int rv;
232*9f391507Spho     sigset_t oset;
233*9f391507Spho 
234*9f391507Spho     rv = block_signals(&oset);
235*9f391507Spho     assert(rv == 0);
236*9f391507Spho 
237*9f391507Spho     rv = pthread_mutex_lock(&signal_mutex);
238*9f391507Spho     assert(rv == 0);
239*9f391507Spho #endif
240*9f391507Spho 
241*9f391507Spho     /* Have we already installed our signal handlers? If the list is
242*9f391507Spho      * empty, it means we have not. */
243*9f391507Spho     if (fuse_head == NULL) {
244*9f391507Spho         if (set_signal_handler(SIGHUP, exit_handler) != 0 ||
245*9f391507Spho             set_signal_handler(SIGINT, exit_handler) != 0 ||
246*9f391507Spho             set_signal_handler(SIGTERM, exit_handler) != 0 ||
247*9f391507Spho             set_signal_handler(SIGPIPE, NULL) != 0) {
248*9f391507Spho 
249*9f391507Spho             ret = -1;
250*9f391507Spho             goto done;
251*9f391507Spho         }
252*9f391507Spho     }
253*9f391507Spho 
254*9f391507Spho     /* Add ourselves to the list of filesystems that want to be
255*9f391507Spho      * terminated upon receiving a signal. But only if we aren't
256*9f391507Spho      * already in the list. */
257*9f391507Spho     for (elem = fuse_head; elem != NULL; elem = elem->next) {
258*9f391507Spho         if (elem->fuse == fuse)
259*9f391507Spho             goto done;
260*9f391507Spho     }
261*9f391507Spho 
262*9f391507Spho     elem = malloc(sizeof(*elem));
263*9f391507Spho     if (!elem) {
264*9f391507Spho         ret = -1;
265*9f391507Spho         goto done;
266*9f391507Spho     }
267*9f391507Spho     elem->fuse = fuse;
268*9f391507Spho     elem->next = fuse_head;
269*9f391507Spho     fuse_head  = elem;
270*9f391507Spho   done:
271*9f391507Spho 
272*9f391507Spho #if defined(MULTITHREADED_REFUSE)
273*9f391507Spho     rv = pthread_mutex_unlock(&signal_mutex);
274*9f391507Spho     assert(rv == 0);
275*9f391507Spho 
276*9f391507Spho     rv = unblock_signals(&oset);
277*9f391507Spho     assert(rv == 0);
278*9f391507Spho #endif
279*9f391507Spho     return ret;
280*9f391507Spho }
281*9f391507Spho 
282*9f391507Spho int
__fuse_remove_signal_handlers(struct fuse * fuse)283*9f391507Spho __fuse_remove_signal_handlers(struct fuse* fuse) {
284*9f391507Spho     int ret = 0;
285*9f391507Spho     struct refuse_obj_elem* prev;
286*9f391507Spho     struct refuse_obj_elem* elem;
287*9f391507Spho #if defined(MULTITHREADED_REFUSE)
288*9f391507Spho     int rv;
289*9f391507Spho     sigset_t oset;
290*9f391507Spho 
291*9f391507Spho     rv = block_signals(&oset);
292*9f391507Spho     assert(rv == 0);
293*9f391507Spho 
294*9f391507Spho     rv = pthread_mutex_lock(&signal_mutex);
295*9f391507Spho     assert(rv == 0);
296*9f391507Spho #endif
297*9f391507Spho 
298*9f391507Spho     /* Remove ourselves from the list. */
299*9f391507Spho     for (prev = NULL, elem = fuse_head;
300*9f391507Spho          elem != NULL;
301*9f391507Spho          prev = elem, elem = elem->next) {
302*9f391507Spho 
303*9f391507Spho         if (elem->fuse == fuse) {
304*9f391507Spho             if (prev)
305*9f391507Spho                 prev->next = elem->next;
306*9f391507Spho             else
307*9f391507Spho                 fuse_head = elem->next;
308*9f391507Spho             free(elem);
309*9f391507Spho         }
310*9f391507Spho     }
311*9f391507Spho 
312*9f391507Spho     /* Restore handlers if we were the last one. */
313*9f391507Spho     if (fuse_head == NULL) {
314*9f391507Spho         if (restore_signal_handler(SIGHUP, exit_handler) == -1 ||
315*9f391507Spho             restore_signal_handler(SIGINT, exit_handler) == -1 ||
316*9f391507Spho             restore_signal_handler(SIGTERM, exit_handler) == -1 ||
317*9f391507Spho             restore_signal_handler(SIGPIPE, NULL) == -1) {
318*9f391507Spho 
319*9f391507Spho             ret = -1;
320*9f391507Spho         }
321*9f391507Spho     }
322*9f391507Spho 
323*9f391507Spho #if defined(MULTITHREADED_REFUSE)
324*9f391507Spho     rv = pthread_mutex_unlock(&signal_mutex);
325*9f391507Spho     assert(rv == 0);
326*9f391507Spho 
327*9f391507Spho     rv = unblock_signals(&oset);
328*9f391507Spho     assert(rv == 0);
329*9f391507Spho #endif
330*9f391507Spho     return ret;
331*9f391507Spho }
332