atd/area/
atd/build/
atd/clans/
atd/log/
atd/player/store/
atd/site/
atd/src/bin/
#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