/*
** signal.c
**
** A MAD-approved signal handler.
*/

#include "rc.h"

#include <signal.h>
#include <stdlib.h>

#include "sigmsgs.h"
#include "posix.h"

/* from job.c */
extern void sigchld (int nohang);

void (*sighandlers[NUMOFSIGNALS])(int);

static sig_atomic_t sigcount, caught[NUMOFSIGNALS];

extern void
catcher ( int s )
{
	if (caught[s] == 0) {
		sigcount++;
		caught[s] = 1;
	}
	if (s == SIGCHLD) sigchld (WNOHANG);	/* must clear SIGCHLD immediately or looping occurs */
}


extern void
sigchk()
{
	void (*h)(int);
	int s, i;
	if (sigcount == 0)
		return; /* ho hum; life as usual */
	for (i = 0, s = -1; i < NUMOFSIGNALS; i++) {
		if (caught[i] != 0) {
			s = i;
			--sigcount;
			caught[s] = 0;
			break;
		}
	}
	if (forked && s != SIGCHLD) {
	/* exit on a signal in a child process, unless an information signal -
	** SIGCHLD. */
		exit(EXIT_FAILURE);
	}
	if (s == -1) panic("all-zero sig vector with nonzero sigcount");
	h = sighandlers [s];

	if (s == SIGCHLD) {	/* job control always intercepts SIGCHLD to keep tabs on background processes but an extra trap in fn sigchld may be added */
		if (h != SIG_DFL && h != SIG_IGN) (*h) (s);
	} else {
		if (h == SIG_DFL) panic("caught signal set to SIG_DFL");
		if (h == SIG_IGN) panic("caught signal set to SIG_IGN");
		(*h)(s);
	}
}

extern void
(*rc_signal(int s, void (*h)(int)))(int)
{
	struct sigaction act;
	void (*old)(int);
	sigchk ();
	old = sighandlers[s];
	act.sa_flags = 0;
	if (s != SIGCHLD && (h == SIG_DFL || h == SIG_IGN)) {	/* SIGCHLD always trapped */
		act.sa_handler = h;
		sigaction (s, &act, NULL);
		sighandlers[s] = h;
	} else {
		sighandlers[s] = h;
		act.sa_handler = catcher;
		sigemptyset (&act.sa_mask);
		sigaction (s, &act, NULL);
	}
	return old;
}

extern void
initsignal (void)
{
	struct sigaction oact;
	int i;
	for (i = 1; i < NUMOFSIGNALS; i++) {
		sigaction (i, NULL, &oact);
		sighandlers[i] = oact.sa_handler;
	}
}
