#include <time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/times.h> /* for deadlock safety */
#include "merc.h"
void db_dump args ( ( void ) );
extern volatile int alarm_threshold;
extern volatile int last_checkpoint;
void shutdown_wgrace args( ( void ) );
#if 1
/* Core dumps aren't created when the signal handler is used.
* Thus as a debug tool these are removed.
*/
#if defined( DEBUG )
void init_signals()
{
signal( SIGPIPE, SIG_IGN );
}
#else
volatile sig_atomic_t fatal_error_in_process = 0;
/* Copyover note:
* Whenever a program is executed from exec (as in the case of copyover)
* all signals from the previous execution of the mud return to their
* SIG_DEF (default) signal handler, since copyover starts the mud
* process all over again, init_signals will get called and properly
* reset the signal handler.
*/
/** Function: signal_handler
* Descr : Our signal handler, to shutdown gracefully (if possible)
* : upon reciept of a signal signifying we should shutdown.
* Returns : void
* Syntax : (n/a)
* Written : v1.0 11/98
* Author : Gary McNickle <gary@dharvest.com>
*/
void signal_handler( int sig )
{
/* Since our 'shutdown_wgrace' is not reentrant, and may (likely) access
* non-volatile global variables, be damn sure it's not called twice.
*/
if( fatal_error_in_process )
raise( sig );
switch( sig )
{
case SIGBUS:
case SIGTERM:
case SIGABRT:
case SIGSEGV:
{
fatal_error_in_process = 1; /* Yes, this IS a fatal error */
/* Log signal to bug log file */
bugf( "Caught deadly signal: %s.",
(sig == SIGBUS) ? "SIGBUS" : (sig == SIGTERM) ? "SIGTERM"
: (sig == SIGABRT) ? "SIGABRT" : (sig == SIGXCPU) ? "SIGXCPU"
: "SIGSEGV" );
shutdown_wgrace(); /* Attempt a graceful shutdown */
raise( sig ); /* set return status of process */
break;
}
break;
}
}
/** Function: init_signals
* Descr : Initialize signals that we trap, setting up a handler for them.
* Returns : void
* Syntax : void
* Written : v1.0 11/98
* Author : Gary McNickle <gary@dharvest.com>
* Note : By default, signals sent to sighandler are blocked until the
* : handler returns, but other signals are not blocked. We need
* : to block any other signal we recieve that we normally trap,
* : until we are done trying to be graceful...
*/
void init_signals()
{
struct sigaction sigact;
sigset_t mask;
signal( SIGPIPE, SIG_IGN );
/* NOTE: We inherit any current signal actions by default.
* Don't install a signal handler for any ignored signals.
*/
sigaction( SIGBUS, NULL, &sigact );
if( sigact.sa_handler != SIG_IGN )
{
sigact.sa_handler = signal_handler;
sigemptyset( &mask );
/* block these signals to the handler while it's running */
sigaddset( &mask, SIGTERM );
sigaddset( &mask, SIGABRT );
sigaddset( &mask, SIGSEGV );
sigaddset( &mask, SIGPIPE );
sigaddset( &mask, SIGXCPU );
sigprocmask( SIG_BLOCK, &mask, NULL); /*JLR*/
sigact.sa_mask = mask;
sigact.sa_flags = SA_RESETHAND;
sigaction( SIGBUS, &sigact, NULL );
}
else
log_string( "Init: Signal SIGBUS ignored." );
sigaction( SIGTERM, NULL, &sigact );
if( sigact.sa_handler != SIG_IGN )
{
sigact.sa_handler = signal_handler;
sigemptyset( &mask );
/* block these signals to the handler while it's running */
sigaddset( &mask, SIGBUS );
sigaddset( &mask, SIGABRT );
sigaddset( &mask, SIGSEGV );
sigaddset( &mask, SIGPIPE );
sigaddset( &mask, SIGXCPU );
sigprocmask( SIG_BLOCK, &mask, NULL); /*JLR*/
sigact.sa_mask = mask;
sigact.sa_flags = SA_RESETHAND;
sigaction( SIGTERM, &sigact, NULL );
}
else
log_string( "Init: Signal SIGTERM ignored." );
sigaction( SIGABRT, NULL, &sigact );
if( sigact.sa_handler != SIG_IGN )
{
sigact.sa_handler = signal_handler;
sigemptyset( &mask );
/* block these signals to the handler while it's running */
sigaddset( &mask, SIGBUS );
sigaddset( &mask, SIGTERM );
sigaddset( &mask, SIGSEGV );
sigaddset( &mask, SIGPIPE );
sigaddset( &mask, SIGXCPU );
sigprocmask( SIG_BLOCK, &mask, NULL); /*JLR*/
sigact.sa_mask = mask;
sigact.sa_flags = SA_RESETHAND;
sigaction ( SIGABRT, &sigact, NULL );
}
else
log_string( "Init: Signal SIGABRT ignored." );
sigaction ( SIGSEGV, NULL, &sigact );
if( sigact.sa_handler != SIG_IGN )
{
sigact.sa_handler = signal_handler;
sigemptyset( &mask );
/* block these signals to the handler while it's running */
sigaddset( &mask, SIGBUS );
sigaddset( &mask, SIGTERM );
sigaddset( &mask, SIGABRT );
sigaddset( &mask, SIGPIPE );
sigaddset( &mask, SIGXCPU );
sigprocmask( SIG_BLOCK, &mask, NULL); /*JLR*/
sigact.sa_mask = mask;
sigact.sa_flags = SA_RESETHAND;
sigaction ( SIGSEGV, &sigact, NULL );
}
else
log_string("Init: Signal SIGSEGV ignored.");
sigaction( SIGXCPU, NULL, &sigact );
if( sigact.sa_handler != SIG_IGN )
{
sigact.sa_handler = signal_handler;
sigemptyset( &mask );
/* block these signals to the handler while it's running */
sigaddset( &mask, SIGBUS );
sigaddset( &mask, SIGTERM );
sigaddset( &mask, SIGABRT );
sigaddset( &mask, SIGSEGV );
sigaddset( &mask, SIGPIPE );
sigprocmask( SIG_BLOCK, &mask, NULL ); /*JLR*/
sigact.sa_mask = mask;
sigact.sa_flags = SA_RESETHAND;
sigaction( SIGXCPU, &sigact, NULL );
}
else
log_string( "Init: Signal SIGXCPU ignored." );
log_string( "Init: Signals initialized." );
}
/** Function: shutdown_wgrace
* Descr : Upon receipt of a fatal signal, attempt a graceful shutdown.
* Returns : void
* Syntax : void
* Written : v1.0 11/98
* Author : Gary McNickle <gary@dharvest.com>
*/
void shutdown_wgrace()
{
DESCRIPTOR_DATA *d,*d_next;
db_dump( );
/* Notify players of impending crash, and save all pfiles */
for ( d = descriptor_list; d != NULL; d = d_next )
{
if( d->character )
{
if( d->connected == CON_PLAYING )
send_to_char( "\n\r-*- System Crash... Saving game data. -*-\n\r",
d->character);
do_save ( d->character, "" );
}
d_next = d->next;
close_socket( d );
}
}
#endif
/**************************************************************************
* CPU time safety checker.
* This is based on code by Erwin S. Andreasen.
* An alarm is set to call alarm_handler every ALARM_FREQUENCY seconds.
* At this time, if the CPU usage exceeds the threshold value:
* alarm_threshold, then the MUD will halt.
* When booting, the threshold value is much larger.
*/
/* Returns the current amount of time used. */
int get_user_time( void )
{
struct tms tx;
times( &tx );
return (int)( tx.tms_utime * 10 / CLK_TCK );
}
/*
* This signal handler is called when the user timer triggers. Note that for
* debugging purposes the exit is not called. This allows me to find where
* the code is stuck in the debugger.
*/
void alarm_handler( int signo )
{
int usage_now = get_user_time();
/* Has there gone alarm_threshold CPU seconds without alarm_update? */
if( usage_now - last_checkpoint > alarm_threshold )
{
bug( "User CPU time limit exceeded.",0);
signal( signo, SIG_DFL );
#if !defined( DEBUG )
exit( 1 );
#endif
}
last_checkpoint = usage_now;
}
/*
* Install handler for the virtual timer.
*/
void install_alarm( void )
{
struct sigaction sigact;
struct itimerval itimer;
last_checkpoint = get_user_time();
/*
* Install the signal handler.
*/
sigact.sa_handler = alarm_handler;
sigact.sa_flags = SA_RESTART; /* Restart interrupted system calls */
sigemptyset( &sigact.sa_mask );
if( sigaction( SIGVTALRM, &sigact, NULL ) < 0 )
{
perror( "init_alarm_handler:sigaction" );
exit( 1 );
}
/*
* Start the timer,
* the handler is called every ALARM_FREQUENCY CPU seconds.
*/
itimer.it_interval.tv_usec = 0;
itimer.it_interval.tv_sec = ALARM_FREQUENCY;
itimer.it_value.tv_usec = 0;
itimer.it_value.tv_sec = ALARM_FREQUENCY;
/* start the timer - in that many CPU seconds, alarm_handler will be called */
if( setitimer( ITIMER_VIRTUAL, &itimer, NULL ) < 0)
{
perror( "reset_itimer:setitimer" );
exit( 1 );
}
log_string( "Init: The alarm has been set." );
}
/*
* This is called after boot_db to change the allowed amount of CPU time.
*/
void update_alarm( void )
{
int now_time = get_user_time();
alarm_threshold = ALARM_RUNNING;
logf( NULL, "Init: Boot took %d.%d user CPU seconds.",
( now_time - last_checkpoint ) / 10,
( now_time - last_checkpoint ) % 10 );
last_checkpoint = now_time;
}
#else
void init_signals()
{
}
#endif