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