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