#include <SignalManager.h>

#ifdef __linux

#ifndef SIGSYS
#define SIGSYS SIGUNUSED
#endif

#endif

// *****************************************************************************
// * SignalManager::reporter :
// *	This is the declaration for the error reporter.
// *****************************************************************************
ErrorReporter SignalManager::reporter;

// *****************************************************************************
// * SignalHandler::SignalHandler :
// *	This is the initializer for a SignalHandler class.  It holds the
// *	information needed to handle a single signal number.
// *****************************************************************************
_SignalHandler_::_SignalHandler_ ( void )
	: signo(0), handler(NULL), oldHandler(NULL)
	{
	}
	
// *****************************************************************************
// * SignalHandler::~SignalHandler :
// *	This is the destructor for the SignlaHandler class.  It will call the
// *	uninstall method to remove the handler that was imposed for the signal.
// *****************************************************************************
_SignalHandler_::~_SignalHandler_ ( void )
	{
	uninstall ();
	}
	
// *****************************************************************************
// * SignalHandler::install :
// *	This method will install the specified signal handler function on the
// *	signal number specified by signo.  The old handler will be preserved
// *	for later restoration.
// *****************************************************************************
void _SignalHandler_::install ( int newSigno, SignalHandlerFunc newHandler )
	{
	// *********************************************************************
	// * If a local signal handler has already been installed on the 
	// * specified signal number, uninstall it before proceeding.
	// *********************************************************************
	if(handler!=NULL && signo==newSigno) uninstall();
	
	// *********************************************************************
	// * Setup the control variables.
	// *********************************************************************
	signo            = newSigno;
	handler          = newHandler;
	
	// *********************************************************************
	// * Install the new signal handler, preserving the old handler in the
	// * oact sigaction structure.
	// *********************************************************************
	oldHandler = signal(signo, handler);
	}

// *****************************************************************************
// * SignalHandler::uninstall :
// *	This method will remove a user specified signal handler and will restore
// *	the previously installed handler.
// *****************************************************************************
void _SignalHandler_::uninstall ( void )
	{
	// *********************************************************************
	// * Check to see if the signal handler has been installed.  If so, 
	// * call sigaction to confirm that the current signal handler is the
	// * one that was installed.
	// *********************************************************************
	if(signo!=0 && handler!=NULL)
		{
		SignalHandlerFunc current = signal(signo, SIG_DFL);
		if(current==handler) signal(signo, oldHandler);
		else                 signal(signo, current);
		}

	signo   = 0;
	handler = NULL;
	}

// *****************************************************************************
// * SignalManager::installHandler :
// *	This method will install the specified signal handler.
// *****************************************************************************
void SignalManager::installHandler (int signo, SignalHandlerFunc handler )
	{
	if(signo>0 && signo<=MAXSIGNAL) signals[signo-1].install(signo, handler);
	}
	
// *****************************************************************************
// * SignalManager::uninstallHandler :
// *	This method will remove a signal handler that was previously installed.
// *****************************************************************************
void SignalManager::uninstallHandler ( int signo )
	{
	if(signo>0 && signo<=MAXSIGNAL) signals[signo-1].uninstall();
	}
	
// *****************************************************************************
// * SignalManager::installDefaults :
// * 	This method will install a default collection of signal handlers.
// *****************************************************************************	
void SignalManager::installDefaults ( void )
	{
	static int sigList[] =
		{
	#if !defined (_WIN32)
		SIGFPE,		// Erroneous arithmetic operation.
		SIGHUP,		// Hangup.
		SIGILL,		// Illegal instruction.
		SIGINT,		// Terminal interrupt signal.
		SIGPIPE,	// Write on a socket with no one to read
		SIGQUIT,	// Terminal quit signal.
		SIGSEGV,	// Invalid memory reference.
		SIGTERM,	// Termination signal.
		SIGBUS,		// Bus error.
		SIGSYS,		// Bad system call.
	#else
		SIGABRT,	// Process abort signal.
		SIGFPE,		// Erroneous arithmetic operation.
		SIGILL,		// Illegal instruction.
		SIGINT,		// Terminal interrupt signal.
		SIGSEGV,	// Invalid memory reference.
		SIGTERM,	// Termination signal.
	#endif		
		0
		};

	for(int i=0; sigList[i]!=0; i++) installHandler(sigList[i], defaultHandler);
	}

// *****************************************************************************
// * SignalManager::defaultHandler :
// * 	This is the default signal handler method.
// *****************************************************************************
void SignalManager::defaultHandler( int signo )
	{
	switch(signo)
		{
		// *************************************************************
		// * Process the interrupt signal by reporting it and exiting.
		// *************************************************************
		case SIGINT:
		reporter.outputError (
			 CDEV_SEVERITY_INFO, 
			"Signal Manager", 
			"[SIGINT] Terminating at user's request");
		exit(0);
		
		// *************************************************************
		// * Process the terminate signal by reporting it and exiting.
		// *************************************************************
		case SIGTERM:
		reporter.outputError (
			 CDEV_SEVERITY_INFO,
			"Signal Manager",
			"[SIGTERM] Terminating normally");
		exit(0);
		
		// *************************************************************
		// * Process abort signal by reporting the error and exiting.
		// *************************************************************
		case SIGABRT:		
		reporter.outputError (
			 CDEV_SEVERITY_SEVERE, 
			"Signal Manager", 
			"[SIGABRT] Application terminating due to abort signal");		
		abort();
				
		
		// *************************************************************
		// * Process erroneous arithmetic operations by reporting them
		// * and exiting.
		// *************************************************************
		case SIGFPE:
		reporter.outputError (
			 CDEV_SEVERITY_SEVERE, 
			"Signal Manager", 
			"[SIGFPE] Floating point error - terminating\n");
		abort();
		
			
		// *************************************************************
		// * Process illegal instructions by reporting them and exiting.
		// *************************************************************
		case SIGILL:
		reporter.outputError (
			 CDEV_SEVERITY_SEVERE, 
			"Signal Manager", 
			"[SIGILL] Illegal instruction");
		abort();
		
		// *************************************************************
		// * Process the segmentation fault signal by reporting the
		// * error and terminating.
		// *************************************************************
		case SIGSEGV:
		reporter.outputError (
			 CDEV_SEVERITY_SEVERE,
			"Signal Manager",
			"[SIGSEGV] Segmentation fault");
		abort();		
		
#if !defined(_WIN32)
		// *************************************************************
		// * Report pipe errors, however, do not terminate.
		// *************************************************************
		case SIGPIPE:
		reporter.outputError (
			 CDEV_SEVERITY_ERROR, 
			"Signal Manager",
			"[SIGPIPE] Write on a pipe with no one to read it");
		errno = SIGPIPE;
		break;

		// *************************************************************
		// * Process a bus error by reporting it and exiting.
		// *************************************************************
		case SIGBUS:
		reporter.outputError (
			 CDEV_SEVERITY_SEVERE,
			"Signal Manager",
			"[SIGBUS] Bus error");
		abort();
		
		// *************************************************************
		// * Process a bad system  call by reporting it and exiting.
		// *************************************************************
		case SIGSYS:
		reporter.outputError (
			 CDEV_SEVERITY_SEVERE,
			"Signal Manager",
			"[SIGSYS] Bad system call");
		abort();
		
		// *************************************************************
		// * Ignore the following signals:
		// * 		SIGHUP:  hangup signal.
		// * 		SIGQUIT: terminal quit signal
		// *		default: All other signals
		// *************************************************************
		case SIGHUP:
		case SIGQUIT:
#endif
		
		default:
		break;
		}
	signal(signo, SignalManager::defaultHandler);
	}

