#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: sig.c,v 1.4 2021/11/27 22:16:41 rillig Exp $");
#endif
#include <assert.h>
#include <util.h>
#include <sys/queue.h>
#include "rcv.h"
#include "extern.h"
#include "sig.h"
static sig_t sigarray[NSIG];
typedef struct q_entry_s {
int qe_signo;
sig_t qe_handler;
struct q_entry_s *qe_next;
} q_entry_t;
static struct {
q_entry_t *qe_first;
q_entry_t **qe_last;
} sigq = { NULL, &sigq.qe_first };
#define SIGQUEUE_INIT(p) do {\
(p)->qe_first = NULL;\
(p)->qe_last = &((p)->qe_first);\
} while (0)
static q_entry_t *
alloc_entry(int signo)
{
static q_entry_t entries[NSIG];
q_entry_t *e;
e = &entries[signo];
if (e->qe_signo != 0)
return NULL;
e->qe_signo = signo;
e->qe_handler = sigarray[signo];
e->qe_next = NULL;
return e;
}
static void
free_entry(q_entry_t *e)
{
e->qe_signo = 0;
e->qe_handler = NULL;
e->qe_next = NULL;
}
static void
sig_post(int signo)
{
q_entry_t *e;
if (sigarray[signo] == SIG_DFL || sigarray[signo] == SIG_IGN)
return;
e = alloc_entry(signo);
if (e != NULL) {
*sigq.qe_last = e;
sigq.qe_last = &e->qe_next;
}
}
PUBLIC void
sig_check(void)
{
q_entry_t *e;
sigset_t nset;
sigset_t oset;
void (*handler)(int);
int signo;
(void)sigfillset(&nset);
(void)sigprocmask(SIG_SETMASK, &nset, &oset);
while ((e = sigq.qe_first) != NULL) {
signo = e->qe_signo;
handler = e->qe_handler;
sigq.qe_first = e->qe_next;
if (sigq.qe_first == NULL)
sigq.qe_last = &sigq.qe_first;
free_entry(e);
if (handler == SIG_DFL || handler == SIG_IGN) {
assert( 0);
}
else {
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
handler(signo);
(void)sigprocmask(SIG_SETMASK, &nset, NULL);
}
}
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
}
PUBLIC sig_t
sig_current(int signo)
{
assert(signo > 0 && signo < NSIG);
return sigarray[signo];
}
PUBLIC sig_t
sig_signal(int signo, sig_t handler)
{
sig_t old_handler;
sigset_t nset;
sigset_t oset;
assert(signo > 0 && signo < NSIG);
(void)sigemptyset(&nset);
(void)sigaddset(&nset, signo);
(void)sigprocmask(SIG_BLOCK, &nset, &oset);
old_handler = sigarray[signo];
sigarray[signo] = handler;
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
return old_handler;
}
static void
do_default_handler(int signo, int flags)
{
struct sigaction nsa;
struct sigaction osa;
sigset_t nset;
sigset_t oset;
int save_errno;
save_errno = errno;
(void)sigemptyset(&nsa.sa_mask);
nsa.sa_flags = flags;
nsa.sa_handler = SIG_DFL;
(void)sigaction(signo, &nsa, &osa);
(void)sigemptyset(&nset);
(void)sigaddset(&nset, signo);
(void)sigprocmask(SIG_UNBLOCK, &nset, &oset);
(void)kill(0, signo);
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
(void)sigaction(signo, &osa, NULL);
errno = save_errno;
}
static void
sig_handler(int signo)
{
sigset_t nset;
sigset_t oset;
(void)sigfillset(&nset);
(void)sigprocmask(SIG_SETMASK, &nset, &oset);
assert (signo > 0 && signo < NSIG);
sig_post(signo);
switch (signo) {
case SIGCONT:
assert( 0);
do_default_handler(signo, 0);
break;
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
do_default_handler(signo, 0);
break;
case SIGINT:
case SIGHUP:
case SIGQUIT:
case SIGPIPE:
default:
if (sigarray[signo] == SIG_DFL)
do_default_handler(signo, SA_RESTART);
break;
}
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
}
PUBLIC void
sig_setup(void)
{
sigset_t nset;
sigset_t oset;
struct sigaction sa;
struct sigaction osa;
(void)sigfillset(&nset);
(void)sigprocmask(SIG_BLOCK, &nset, &oset);
(void)sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_handler;
(void)sigaction(SIGTSTP, &sa, NULL);
(void)sigaction(SIGTTIN, &sa, NULL);
(void)sigaction(SIGTTOU, &sa, NULL);
(void)sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_handler;
if (sigaction(SIGHUP, &sa, &osa) != -1 && osa.sa_handler == SIG_IGN)
(void)signal(SIGHUP, SIG_IGN);
if (sigaction(SIGINT, &sa, &osa) != -1 && osa.sa_handler == SIG_IGN)
(void)signal(SIGINT, SIG_IGN);
#if 0
if (signal(SIGQUIT, SIG_DFL) == SIG_IGN)
(void)signal(SIGQUIT, SIG_IGN);
#endif
(void)sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_handler;
(void)sigaction(SIGPIPE, &sa, NULL);
(void)memset(sigarray, 0, sizeof(sigarray));
SIGQUEUE_INIT(&sigq);
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
}
static struct {
int depth;
sigset_t oset;
} hold;
PUBLIC void
sig_hold(void)
{
sigset_t nset;
if (hold.depth++ == 0) {
(void)sigemptyset(&nset);
(void)sigaddset(&nset, SIGHUP);
(void)sigaddset(&nset, SIGINT);
(void)sigaddset(&nset, SIGQUIT);
(void)sigprocmask(SIG_BLOCK, &nset, &hold.oset);
}
}
PUBLIC void
sig_release(void)
{
if (--hold.depth == 0)
(void)sigprocmask(SIG_SETMASK, &hold.oset, NULL);
}
PUBLIC int
sig_ignore(int sig, struct sigaction *osa, sigset_t *oset)
{
struct sigaction act;
sigset_t nset;
int error;
(void)sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
act.sa_handler = SIG_IGN;
error = sigaction(sig, &act, osa);
if (error != -1) {
(void)sigemptyset(&nset);
(void)sigaddset(&nset, sig);
(void)sigprocmask(SIG_UNBLOCK, &nset, oset);
} else if (oset != NULL)
(void)sigprocmask(SIG_UNBLOCK, NULL, oset);
return error;
}
PUBLIC int
sig_restore(int sig, struct sigaction *osa, sigset_t *oset)
{
int error;
error = 0;
if (oset)
error = sigprocmask(SIG_SETMASK, oset, NULL);
if (osa)
error = sigaction(sig, osa, NULL);
return error;
}
PUBLIC int
sig_setflags(int signo, int flags, struct sigaction *osa)
{
struct sigaction sa;
if (sigaction(signo, NULL, &sa) == -1)
return -1;
if (osa)
*osa = sa;
sa.sa_flags = flags;
return sigaction(signo, &sa, NULL);
}