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