/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1997 The Ohio State University
 *	GDB
 *
 *	$Id: ksignal.c,v 6.4 1999/07/28 00:31:58 jsquyres 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();		/* ignore signal */
static void		sigexit();		/* terminate process */
static void		sigflush();		/* flush route cache */
static void		sigstop();		/* stop process */

/*
 *	_ksig_follow
 *
 *	Function:	- follows signals that may be set in signal word
 */
void
_ksig_follow()

{
	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
 */
void
(*(ksignal(signo, sigfunc)))()

int			signo;
void			(*sigfunc)();

{
	void		(*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()

{
	int		i;			/* favourite index variable */
/*
 * Retry all signals except SIGRELEASE.
 * Don't block any signals.
 * No signals are currently pending.
 */
	_kio.ki_sigretry = ~(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()

{
	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() {}

/*
 *	sigexit
 *
 *	Function:	- terminates the process
 */
static void
sigexit()

{
	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(sig)

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()

{
	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(mask)

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(mask)

int			mask;

{
	int		oldmask;		/* previous signal mask */

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

int
ksigsetretry(flags)

int			flags;

{
	int		oldflags;		/* previous retry flags */

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

int
ksigretry(flags)

int			flags;			/* new retry 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
 */
void (*(lam_ksignal(signo, sigfunc)))() int signo; void (*sigfunc)();
{ return(ksignal(signo, sigfunc)); }

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

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

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

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

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

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