/*
 * Copyright (c) 2001-2003 The Trustees of Indiana University.  
 *                         All rights reserved.
 * Copyright (c) 1998-2001 University of Notre Dame. 
 *                         All rights reserved.
 * Copyright (c) 1994-1998 The Ohio State University.  
 *                         All rights reserved.
 * 
 * This file is part of the LAM/MPI software package.  For license
 * information, see the LICENSE file in the top level directory of the
 * LAM/MPI source distribution.
 * 
 * $HEADER$
 *	Ohio Trollius
 *	Copyright 1997 The Ohio State University
 *	GDB
 *
 *	$Id: ksignal.c,v 6.8 2003/11/02 19:48:48 brbarret Exp $
 *
 *	Function:	- signal package
 *			- Any kernel request may return one or more signals.
 *			  A signal is indicated by one bit in a word.  When a
 *			  signal is turned on, it is vectored through a table
 *			  of signal handlers.  By default most signals are
 *			  ignored.  Some cause the process to terminate and a
 *			  few may have meanings particular to specific Trollius
 *			  services, such as the network layer.  Most signals
 *			  may be redirected by the user to user supplied
 *			  handling routines.
 *			- for compatiblity with UNIX, the signals start at 1
 *			- based on Trollius 2.0 Copyright 1990
 *			  The Ohio State University and Cornell
 *			  Research Foundation
 */

#include <events.h>
#include <kio.h>
#include <kreq.h>
#include <terror.h>
#include <typical.h>

/*
 * external variables
 */
extern struct kio_t _kio;			/* Kernel Input/Output block */

/*
 * local functions
 */
static void sigign(void);		/* ignore signal */
static void sigexit(void);		/* terminate process */
static void sigflush(void);		/* flush route cache */
static void sigstop(int sig);		/* stop process */

/*
 * local typedef for clean code
 */
typedef void (*lam_void_func)();


/*
 *	_ksig_follow
 *
 *	Function:	- follows signals that may be set in signal word
 */
void
_ksig_follow(void)
{
	int		i;		/* favourite index variable */
	int4		sigbit;		/* signal bit */

	if (_kio.ki_signal) {
/*
 * Check every possible signal.
 */
		for (i = 1; i <= TNSIG; ++i) {
/*
 * If the signal is turned on, call the handling routine.
 * Block the signal from further occurances during handling routine.
 */
			sigbit = sigmask(i);

			if ((_kio.ki_signal & sigbit) &&
				!(_kio.ki_sigmask & sigbit)) {

				_kio.ki_sigmask |= sigbit;
				_kio.ki_signal &= ~sigbit;
				(*(_kio.ki_sig_handlers[i - 1]))(i, 0, 0);
				_kio.ki_sigmask &= ~sigbit;
			}
		}
	}
}

/*
 *	ksignal
 *
 *	Function:	- simple software signal director
 *			- redirects a signal to a user supplied handling
 *			  routine
 *	Accepts:	- signal number
 *			- handler function
 *	Returns:	- previous handler function
 */
lam_void_func 
ksignal(int signo, lam_void_func sigfunc)
{
	lam_void_func oldsigfunc;

	if ((signo < 1) || (signo > TNSIG)) {
		errno = EINVAL;
		return(((void (*)()) -1));
	}

	if (sigmask(signo) & SIG_PRIV) {
		errno = EINVAL;
		return(((void (*)()) -1));
	}

	--signo;
	oldsigfunc = _kio.ki_sig_handlers[signo];

	if (sigfunc == SIG_DFL) {
		_kio.ki_sig_handlers[signo] = _kio.ki_sig_default[signo];
	} else if (sigfunc == SIG_IGN) {
		_kio.ki_sig_handlers[signo] = sigign;
	} else {
		_kio.ki_sig_handlers[signo] = sigfunc;
	}

	return(oldsigfunc);
}

/*
 *	_ksig_init
 *
 *	Function:	- initializes signal handling table
 */
void
_ksig_init(void)
{
	int		i;			/* favourite index variable */
/*
 * Retry all signals except SIGRELEASE.
 * Don't block any signals.
 * No signals are currently pending.
 */
	_kio.ki_sigretry = (int4) ~(sigmask(SIGRELEASE));
	_kio.ki_sigmask = 0;
	_kio.ki_signal = 0;
/*
 * Ignore most signals...
 */
	for (i = 0; i < TNSIG; ++i) {
		_kio.ki_sig_handlers[i] = sigign;
	}
/*
 * ...except for a few special cases.
 */
	_kio.ki_sig_handlers[SIGUDIE - 1] = sigexit;
	_kio.ki_sig_handlers[SIGARREST - 1] = sigstop;
	_kio.ki_sig_handlers[SIGSHRINK - 1] = sigflush;
/*
 * Now copy the current handling table to the default table.
 */
	for (i = 0; i < TNSIG; ++i) {
		_kio.ki_sig_default[i] = _kio.ki_sig_handlers[i];
	}
}

/*
 *	sigflush
 *
 *	Function:	- flushes the route cache
 */
static void
sigflush(void)
{
	int		i;

	_kio.ki_origin = NOTNODEID;

	for (i = 0; i < MAXRCACHE; ++i) {
		_kio.ki_route[i].r_nodeid = NOTNODEID;
	}
}

/*
 *	sigign
 *
 *	Function:	- ignores a signal
 */
static void
sigign(void) 
{
}

/*
 *	sigexit
 *
 *	Function:	- terminates the process
 */
static void
sigexit(void)
{
	kexit(1);
}

/*
 *	sigstop
 *
 *	Function:	- stops or pauses the process
 *			- uses the EVSTOP or EVPAUSE event to indefinitely
 *			  hang until a future signal is received
 *	Accepts:	- signal that caused sigstop to be called
 */
static void
sigstop(int sig)
{
	struct kmsg	forever;		/* local message descriptor */

	forever.k_event = (sig == SIGARREST) ? EVSTOP : EVPAUSE;
	forever.k_type = 0;
	forever.k_flags = 0;
	forever.k_length = 0;
	forever.k_msg = 0;
	krecv(&forever);
}

/*
 *	kpause
 *
 *	Function:	- waits for a signal to occur
 *			- implemented by blocking on an event
 *			- must disable all retry flags while pausing
 *	Returns:	- ERROR
 */
int
kpause(void)
{
	int		save_retry;

	save_retry = _kio.ki_sigretry;
	_kio.ki_sigretry = 0;
	sigstop(0);
	_kio.ki_sigretry = save_retry;
	errno = EINTR;
	return(LAMERROR);
}

/*
 *	ksigsetmask
 *	ksigblock
 *	ksigsetretry
 *	ksigretry
 *
 *	Function:	- operates on signal inhibiting mask and kernel request
 *			  retry flags
 *	Returns:	- old value of mask or flags
 */
int
ksigsetmask(int mask)
{
	int		oldmask;		/* previous signal mask */

	oldmask = _kio.ki_sigmask;
	mask &= ~SIG_PRIV;
	_kio.ki_sigmask = mask;
/*
 * Some pending signals may now be unblocked.
 */
	if (_kio.ki_signal) {
		_ksig_follow();
	}

	return(oldmask);
}

int
ksigblock(int mask)
{
	int		oldmask;		/* previous signal mask */

	oldmask = _kio.ki_sigmask;
	mask &= ~SIG_PRIV;
	_kio.ki_sigmask |= mask;
	return(oldmask);
}

int
ksigsetretry(int flags)
{
	int		oldflags;		/* previous retry flags */

	oldflags = _kio.ki_sigretry;
	flags &= ~(SIG_PRIV | SIGRELEASE);
	_kio.ki_sigretry = flags;
	return(oldflags);
}

int
ksigretry(int flags)
{
	int		oldflags;		/* previous retry flags */

	oldflags = _kio.ki_sigretry;
	flags &= ~(SIG_PRIV | SIGRELEASE);
	_kio.ki_sigretry |= flags;
	return(oldflags);
}

/*
 *	lam_ksignal
 *	lam_ksigsetmask
 *	lam_ksigblock
 *	lam_ksigsetretry
 *	lam_ksigretry
 *	
 *	Function:	- de-rigeur wrappers
 */
lam_void_func
lam_ksignal(int signo, lam_void_func sigfunc)
{ return(ksignal(signo, sigfunc)); }

int lam_kpause() { return(kpause()); }

int lam_ksigsetmask(int mask) { return(ksigsetmask(mask)); }

int lam_ksigblock(int mask) { return(ksigblock(mask)); }

int lam_ksigsetretry(int flags) { return(ksigsetretry(flags)); }

int lam_ksigretry(int flags) { return(ksigretry(flags)); }

/*
 * Fortran wrapper.
 */
#if LAM_FORTRANNOUNDERSCORE
#else
void kpause_()
{
    kpause();
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1