#include <sys/cdefs.h>
#if !defined(lint)
__RCSID("$NetBSD: refuse_signals.c,v 1.1 2022/01/22 07:53:06 pho Exp $");
#endif
#include <assert.h>
#include <fuse_internal.h>
#if defined(MULTITHREADED_REFUSE)
# include <pthread.h>
#endif
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#if defined(MULTITHREADED_REFUSE)
static pthread_mutex_t signal_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
static struct sigaction* saved_actions[NSIG];
struct refuse_obj_elem {
struct fuse* fuse;
struct refuse_obj_elem* next;
};
static struct refuse_obj_elem* fuse_head;
#if defined(MULTITHREADED_REFUSE)
static int
block_signals(sigset_t* oset) {
sigset_t set;
if (sigemptyset(&set) != 0)
return -1;
if (sigaddset(&set, SIGHUP) != 0)
return -1;
if (sigaddset(&set, SIGINT) != 0)
return -1;
if (sigaddset(&set, SIGTERM) != 0)
return -1;
return pthread_sigmask(SIG_BLOCK, &set, oset);
}
static int
unblock_signals(const sigset_t* oset) {
return pthread_sigmask(SIG_SETMASK, oset, NULL);
}
#endif
static int
set_signal_handler(int sig, void (*handler)(int, siginfo_t*, void*)) {
struct sigaction* saved;
struct sigaction act;
saved = malloc(sizeof(*saved));
if (!saved)
return -1;
if (sigaction(sig, NULL, saved) != 0) {
free(saved);
return -1;
}
saved_actions[sig] = saved;
memset(&act, 0, sizeof(act));
if (handler) {
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
}
else {
if (!(saved->sa_flags & SA_SIGINFO) && saved->sa_handler == SIG_DFL)
act.sa_handler = SIG_IGN;
else
return 0;
}
if (sigemptyset(&act.sa_mask) != 0) {
free(saved);
saved_actions[sig] = NULL;
return -1;
}
return sigaction(sig, &act, NULL);
}
static int
restore_signal_handler(int sig, void (*handler)(int, siginfo_t*, void*)) {
struct sigaction oact;
struct sigaction* saved;
saved = saved_actions[sig];
assert(saved != NULL);
if (sigaction(sig, NULL, &oact) != 0)
return -1;
if (handler) {
if (!(oact.sa_flags & SA_SIGINFO) || oact.sa_sigaction != handler)
goto done;
}
else {
if (oact.sa_handler != SIG_IGN)
goto done;
}
if (sigaction(sig, saved, NULL) != 0)
return -1;
done:
free(saved);
saved_actions[sig] = NULL;
return 0;
}
static void
exit_handler(int sig, siginfo_t* info, void* ctx) {
struct refuse_obj_elem* elem;
struct sigaction* saved;
#if defined(MULTITHREADED_REFUSE)
int rv;
rv = pthread_mutex_lock(&signal_mutex);
assert(rv == 0);
#endif
for (elem = fuse_head; elem != NULL; elem = elem->next)
fuse_exit(elem->fuse);
#if defined(MULTITHREADED_REFUSE)
rv = pthread_mutex_unlock(&signal_mutex);
assert(rv == 0);
#endif
saved = saved_actions[sig];
assert(saved != NULL);
if (saved->sa_handler != SIG_DFL && saved->sa_handler != SIG_IGN) {
if (saved->sa_flags & SA_SIGINFO)
saved->sa_sigaction(sig, info, ctx);
else
saved->sa_handler(sig);
}
}
int
__fuse_set_signal_handlers(struct fuse* fuse) {
int ret = 0;
struct refuse_obj_elem* elem;
#if defined(MULTITHREADED_REFUSE)
int rv;
sigset_t oset;
rv = block_signals(&oset);
assert(rv == 0);
rv = pthread_mutex_lock(&signal_mutex);
assert(rv == 0);
#endif
if (fuse_head == NULL) {
if (set_signal_handler(SIGHUP, exit_handler) != 0 ||
set_signal_handler(SIGINT, exit_handler) != 0 ||
set_signal_handler(SIGTERM, exit_handler) != 0 ||
set_signal_handler(SIGPIPE, NULL) != 0) {
ret = -1;
goto done;
}
}
for (elem = fuse_head; elem != NULL; elem = elem->next) {
if (elem->fuse == fuse)
goto done;
}
elem = malloc(sizeof(*elem));
if (!elem) {
ret = -1;
goto done;
}
elem->fuse = fuse;
elem->next = fuse_head;
fuse_head = elem;
done:
#if defined(MULTITHREADED_REFUSE)
rv = pthread_mutex_unlock(&signal_mutex);
assert(rv == 0);
rv = unblock_signals(&oset);
assert(rv == 0);
#endif
return ret;
}
int
__fuse_remove_signal_handlers(struct fuse* fuse) {
int ret = 0;
struct refuse_obj_elem* prev;
struct refuse_obj_elem* elem;
#if defined(MULTITHREADED_REFUSE)
int rv;
sigset_t oset;
rv = block_signals(&oset);
assert(rv == 0);
rv = pthread_mutex_lock(&signal_mutex);
assert(rv == 0);
#endif
for (prev = NULL, elem = fuse_head;
elem != NULL;
prev = elem, elem = elem->next) {
if (elem->fuse == fuse) {
if (prev)
prev->next = elem->next;
else
fuse_head = elem->next;
free(elem);
}
}
if (fuse_head == NULL) {
if (restore_signal_handler(SIGHUP, exit_handler) == -1 ||
restore_signal_handler(SIGINT, exit_handler) == -1 ||
restore_signal_handler(SIGTERM, exit_handler) == -1 ||
restore_signal_handler(SIGPIPE, NULL) == -1) {
ret = -1;
}
}
#if defined(MULTITHREADED_REFUSE)
rv = pthread_mutex_unlock(&signal_mutex);
assert(rv == 0);
rv = unblock_signals(&oset);
assert(rv == 0);
#endif
return ret;
}