#include <sys/types.h>
#include <sys/signal.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>

#define NODEFINE
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include "sig.h"

static void pm_sigaction(int, siginfo_t*, void*);

static void
installhandler(int i)
{
	struct sigaction act;

	memset(&act, 0, sizeof act);
	if(i==SIGCHLD)
		act.sa_flags |= SA_NOCLDSTOP;
	if(pm_sigtab[i].handle){
		act.sa_sigaction = pm_sigaction;
		act.sa_flags |= SA_SIGINFO;
	}else
		act.sa_handler = SIG_DFL;
	sigaction(i, &act, nil);
	siginterrupt(i, !pm_sigtab[i].restart);
}

static void
installhandlers(void)
{
	int i;

	for(i=0; i<pm_nsigtab; i++)
		installhandler(i);
}

static void
default_notify(void *arg, char *msg)
{
	USED(arg);
	USED(msg);
	pm_noted(PM_NDFLT);
}
	
static void (*notifyf)(void*, char*) = default_notify;

static void
pm_sigaction(int sig, siginfo_t *info, void *context)
{
	int i;
	Pmproc *up;

	/*
	 * I am sure this is not supposed to be necessary.
	 * installhandler(sig);
	 */

	if(sig == SIGCHLD){
		if(pm_debug & PmDebugNote)
			pm_fprint(2, "pm_sigaction: got SIGCHLD\n");
//		pm_checkkids();
		return;
	}
	/*
	 * could pass info, context to handlers 
	 */
	USED(info);
	USED(context);

	if(sig < 0 || sig >= pm_nsigtab){
		if(pm_debug & PmDebugNote)
			pm_fprint(2, "pm_sigaction: got bad signal %d\n", sig);
		pm_abort();
	}

	up = pm_getup();
	up->sigres = -1;
	if(pm_debug & PmDebugNote)
		pm_fprint(2, "pm_sigaction: got signal %d (%s), calling handler\n", sig,
			pm_sigtab[sig].desc);
	if(_setjmp(up->sigbuf)==0)
		(*notifyf)(nil, pm_sigtab[sig].desc);
	switch(up->sigres){
	default:
		if(pm_debug & PmDebugNote)
			pm_fprint(2, "pm_sigaction: handler didn't call noted\n");
		pm_abort();
	case PM_NCONT:
		if(pm_debug & PmDebugNote)
			pm_fprint(2, "pm_sigaction: handler NCONT\n");
		return;
	case PM_NDFLT:
		if(pm_debug & PmDebugNote)
			pm_fprint(2, "pm_sigaction: handler NDFLT\n");
		break;
	}

	/* default handling */
	switch(sig){
	case SIGURG:
	case SIGSTOP:
	case SIGTSTP:
	case SIGCONT:
	case SIGCHLD:
	case SIGTTIN:
	case SIGTTOU:
	case SIGIO:
	case SIGWINCH:
	case SIGINFO:
		if(pm_debug & PmDebugNote)
			pm_fprint(2, "pm_sigaction: ignoring signal\n");
		return;
	}

	
	if(pm_debug & PmDebugNote)
		pm_fprint(2, "pm_sigaction: unhandled signal, dying\n");
	for(i=0; i<pm_nsigtab; i++)
		signal(i, SIG_DFL);

	pm_fprint(2, "%lud %s%ssuicide: %s\n",
		(ulong)getpid(), argv0 ? argv0 : "", argv0 ? " " : "",
		pm_sigtab[sig].desc);
	pm_abort();
}

void
pm_siginit(void)
{
	installhandlers();
}

int
pm_noted(int res)
{
	Pmproc *up;

	up = pm_getup();
	up->sigres = res;
	_longjmp(up->sigbuf, 1);
	abort();
	return -1;
}

void
pm_notejmp(void *arg, pm_jmp_buf jb, int val)
{
	USED(arg);
	siglongjmp(jb, val);
}

void
pm_longjmp(pm_jmp_buf jb, int val)
{
	siglongjmp(jb, val);
}

int
pm_notify(void (*fn)(void*, char*))
{
	if(fn == nil)
		fn = default_notify;
	notifyf = fn;
	return 0;
}
