#include <u.h>
#include <signal.h>
#define NOPLAN9DEFINES
#include <libc.h>

extern char *_p9sigstr(int, char*);

static struct {
	int sig;
	int restart;
	int dumb;
} sigs[] = {
	SIGHUP, 0, 0,
	SIGINT, 0, 0,
	SIGQUIT, 0, 0,
	SIGILL, 0, 0,
	SIGTRAP, 0, 0,
/*	SIGABRT,	*/
#ifdef SIGEMT
	SIGEMT, 0, 0,
#endif
	SIGFPE, 0, 0,
	SIGBUS, 0, 0,
/*	SIGSEGV,	*/
	SIGCHLD, 1, 1,
	SIGSYS, 0, 0,
	SIGPIPE, 0, 1,
	SIGALRM, 0, 0,
	SIGTERM, 0, 0,
	SIGTSTP, 1, 1,
	SIGTTIN, 1, 1,
	SIGTTOU, 1, 1,
	SIGXCPU, 0, 0,
	SIGXFSZ, 0, 0,
	SIGVTALRM, 0, 0,
	SIGUSR1, 0, 0,
	SIGUSR2, 0, 0,
	SIGWINCH, 1, 1,
#ifdef SIGINFO
	SIGINFO, 0, 0,
#endif
};

typedef struct Jmp Jmp;
struct Jmp
{
	p9jmp_buf b;
};

static Jmp onejmp;

static Jmp*
getonejmp(void)
{
	return &onejmp;
}

Jmp *(*_notejmpbuf)(void) = getonejmp;
static void (*notifyf)(void*, char*);
static int alldumb;

static void
nop(int sig)
{
	USED(sig);
}

static void
notifysigf(int sig)
{
	int v;
	char tmp[64];
	Jmp *j;

	j = (*_notejmpbuf)();
	v = p9setjmp(j->b);
	if(v == 0 && notifyf)
		(*notifyf)(nil, _p9sigstr(sig, tmp));
	else if(v == 2){
		if(0)print("HANDLED %d\n", sig);
		return;
	}
	if(0)print("DEFAULT %d\n", sig);
	signal(sig, SIG_DFL);
	raise(sig);
}

int
noted(int v)
{
	p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1);
	abort();
	return 0;
}

static void
handlesig(int s, int r, int skip)
{
	struct sigaction sa, osa;

	/*
	 * If someone has already installed a handler,
	 * It's probably some ld preload nonsense,
	 * like pct (a SIGVTALRM-based profiler).
	 * Leave it alone.
	 */
	sigaction(s, nil, &osa);
	if(osa.sa_handler != SIG_DFL && osa.sa_handler != notifysigf && osa.sa_handler != nop)
		return;

	memset(&sa, 0, sizeof sa);
	if(skip)
		sa.sa_handler = nop;
	else if(notifyf == 0)
		sa.sa_handler = SIG_DFL;
	else
		sa.sa_handler = notifysigf;

	/*
	 * We assume that one jump buffer per thread
	 * is okay, which means that we can't deal with 
	 * signal handlers called during signal handlers.
	 */
	sigfillset(&sa.sa_mask);
	if(r)
		sa.sa_flags |= SA_RESTART;
	else
		sa.sa_flags &= ~SA_RESTART;
	sigaction(s, &sa, nil);
}

int
notify(void (*f)(void*, char*))
{
	int i;

	notifyf = f;
	for(i=0; i<nelem(sigs); i++)
		handlesig(sigs[i].sig, sigs[i].restart, sigs[i].dumb && !alldumb);
	return 0;
}

void
notifyall(int all)
{
	int i;

	alldumb = all;
	for(i=0; i<nelem(sigs); i++)
		if(sigs[i].dumb)
			handlesig(sigs[i].sig, sigs[i].restart, !all);
}

void
notifyatsig(int sig, int use)
{
	int i;

	for(i=0; i<nelem(sigs); i++)
		if(sigs[i].sig == sig)
			handlesig(sigs[i].sig, sigs[i].restart, 0);
}