/
Sapphire/bin/
Sapphire/db/
Sapphire/db/OLC_rooms/
Sapphire/db/abi/
Sapphire/db/em_src/
Sapphire/db/helps/
Sapphire/db/helps/emman/ifunc/
Sapphire/db/npcs/Tatt/
Sapphire/db/objects/Tatt/
Sapphire/db/q_data/
Sapphire/db/rooms/Tatt/
Sapphire/doc/
Sapphire/doc/em/
Sapphire/etc/
Sapphire/src/abic/
Sapphire/src/areacon/
Sapphire/src/client/
Sapphire/src/embc/
Sapphire/src/emi/
Sapphire/src/emi/test/
Sapphire/src/include/
Sapphire/src/sapphire/em/
Sapphire/src/tcon/
/*
 * Copyright (C) 1995-1997 Christopher D. Granz
 *
 * This header may not be removed.
 *
 * Refer to the file "License" included in this package for further
 * information and before using any of the following.
 */

/*
 * This file contains all the signal handling routines.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <errno.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "sapphire.h"
#include "emerald.h"


/*
 * Structures
 */
struct signal_handler
{
    int                                                         iSignal;
    void ( *pHandler )                ( int, int, struct sigcontext * );
};


/*
 * Prototypes
 */
int              sigaltstack               ( const struct sigaltstack *,
                                                 struct sigaltstack * );
static void      basic_handler                  ( int, char *, char * );


/*
 * Globals
 */
bool                                                          bIsPrompt;

static jmp_buf                                           jbCheckAddress;
static bool                                            bCheckingAddress;


/*
 * Tables
 */
static struct signal_handler                        shSignalHandlers[] =
{
    { SIGHUP,  SIG_IGN          },
    { SIGINT,  &sigint_handler  },
    { SIGPIPE, SIG_IGN          },
    { SIGILL,  &sigill_handler  },
    { SIGBUS,  &sigbus_handler  },
    { SIGSEGV, &sigsegv_handler },
    { SIGSYS,  &sigsys_handler  },
    { SIGTERM, &sigterm_handler },
    { SIGPROF, &sigprof_handler },
    { 0,       NULL             }
};


/*
 * Functions
 */
void init_signal_handlers( void )
{
    struct sigaltstack sSignalStack;
    struct sigaction sSignalAction;
    int i;

    sSignalStack.ss_base         = alloc_mem( SIGSTKSZ );
    sSignalStack.ss_size         = SIGSTKSZ;
    sSignalStack.ss_flags        = 0;

#ifndef _sysLinux
    if ( sigaltstack( &sSignalStack, NULL ) < 0 )
        sap_fatal( "Signal stack: %s.", strerror( errno ) );
#endif

    for ( i = 0; shSignalHandlers[i].iSignal != 0; i++ )
    {
        sSignalAction.sa_handler = shSignalHandlers[i].pHandler;
        sSignalAction.sa_mask    = 0;
#ifndef _sysLinux
        sSignalAction.sa_flags   = SA_ONSTACK;
#endif

        sigaction( shSignalHandlers[i].iSignal, &sSignalAction, NULL );
    }
}


static void basic_handler( int iSignal, char *pMsg1, char *pMsg2 )
{
    pid_t pPid;

    if ( pLogFile != stdout )
        fprintf( stderr, pMsg1 );

    lprintf( pMsg1 );

    /*
     * Fork and then force the default handler for the signal
     * to be called by the child.  Thus, we can core dump
     * (or anything else) and still reboot.
     */
retry:
    if ( ( pPid = fork( ) ) < 0 )
    {
        if ( errno == EINTR )
            goto retry;

        sap_fatal( "Fork process: %s.", strerror( errno ) );
    }
    else if ( pPid == 0 )
    {
        struct sigaction sSignalAction;

        sSignalAction.sa_handler = SIG_DFL;
        sigemptyset( &sSignalAction.sa_mask );
        sSignalAction.sa_flags   = 0;

        if ( sTelnetControl > -1 )
            close( sTelnetControl );

        if ( sBinaryControl > -1 )
            close( sBinaryControl );

        if ( sMUDCommControl > -1 )
            close( sMUDCommControl );

        sigaction( iSignal, &sSignalAction, NULL );
        kill( getpid( ), iSignal ); /* Force the default handler. */
    }
    else
    {
        MUD_COMM_DATA *pMUDComm, *pMUDCommNext;
        TERM_DATA *pTerm, *pTermNext;
        char *pArg[]  = { pProgramName, pBootFilename, NULL, NULL };
        int i;

        do
        {
            errno     = 0;
            wait( &i );
        }
        while ( errno == EINTR );

        for ( pMUDComm = pMUDCommList; pMUDComm; pMUDComm = pMUDCommNext )
        {
            pMUDCommNext = pMUDComm->pNext;
            close_mudcomm_connection( &pMUDComm );
        }

        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTermNext )
        {
            pTermNext = pTerm->pNext;
            write_string_buffer( pTerm, pMsg2 );
            close_connection( &pTerm );
        }

        if ( sTelnetControl > -1 )
            close( sTelnetControl );

        if ( sBinaryControl > -1 )
            close( sBinaryControl );

        if ( sMUDCommControl > -1 )
            close( sMUDCommControl );

        if ( pLogFile != stdout )
            fclose( pLogFile );
        else
            pArg[2]   = "-l";

        sleep( 15 );

        do
        {
            errno     = 0;
            execv( pProgramName, pArg );
        }
        while ( errno == EINTR );

        if ( errno != 0 )
            exit( 1 );
    }
}


/*
 * Handles when the user types the interupt character (^C).
 */
void sigint_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    struct sigaction sSignalAction;

    sSignalAction.sa_handler = &sigint_handler_2;
    sSignalAction.sa_mask    = 0;
    sSignalAction.sa_flags   = 0;
    sigaction( SIGINT, &sSignalAction, NULL );

    fprintf( stderr, "\n[Main Control Menu]\n"
      "\n"
      "  1) Exit menu\n"
      "  2) Shutdown\n"
      "\n"
      "  3) Show connected sockets\n"
      "  4) Show character creation code\n"
      "\n"
      "  5) Enter shell (`%s')\n"
#ifdef DEBUG
      "  6) Core dump\n"
#endif
      , pShellProgramName );

    fprintf( stderr, "\n%s> ", pMUDName );
    bIsPrompt = TRUE;
}


void sigint_handler_2( int iSignal, int iCode, struct sigcontext *pSC )
{
    TERM_DATA *pTerm;

    for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
    {
        write_string_buffer( pTerm, "\n\rServer shutdown.\n\r" );
        tflush( pTerm );
    }

    putc( '\n', stderr );

    if ( pLogFile != stdout )
        fprintf( stderr, "Server shutdown.\n" );

    lprintf( "Server shutdown." );

#ifdef DEBUG
    abort( );
#else
    exit( 0 );
#endif
}


/*
 * Handles illegal instructions.
 */
void sigill_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    basic_handler( iSignal, "\nIllegal instruction; will core dump.\n"
      "Forcing reboot.\n", "\n\rThere has been an internal error.\n\r"
      "I must attemp to reboot.\n\r" );
}


/*
 * Handles segmentation faults.
 */
void sigsegv_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    if ( bCheckingAddress == TRUE )
        longjmp( jbCheckAddress, 1 );
    else
        basic_handler( iSignal, "\nSegmentation fault; will core dump.\n"
          "Forcing reboot.\n", "\n\rThere has been a memory failure.\n\r"
          "I must attemp to reboot.\n\r" );
}


/*
 * Handles bus errors.
 */
void sigbus_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    basic_handler( iSignal, "\nBus error; will core dump.\n"
      "Forcing reboot.\n", "\n\rThere has been an internal error.\n\r"
      "I must attemp to reboot.\n\r" );
}


/*
 * Handles invalid system calls.
 */
void sigsys_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    basic_handler( iSignal, "\nInvalid system call; will core dump.\n"
      "Forcing reboot.\n", "\n\rThere has been an internal error.\n\r"
      "I must attemp to reboot.\n\r" );
}


/*
 * Handles software termination signals (usaully caused by `kill'.)
 */
void sigterm_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    TERM_DATA *pTerm;

    for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
    {
        write_string_buffer( pTerm, "\n\rServer shutdown.\n\r" );
        tflush( pTerm );
    }

    if ( pLogFile != stdout )
        fprintf( stderr, "Server shutdown.\n" );

    lprintf( "Server shutdown." );

    exit( 0 );
}


void sigprof_handler( int iSignal, int iCode, struct sigcontext *pSC )
{
    em_abort( );
}


bool valid_address( void *p )
{
    char cBuf[2];
    char *p2             = (char *) p;

    if ( setjmp( jbCheckAddress ) == 1 )
    {
        bCheckingAddress = FALSE;
        return ( FALSE );
    }

    bCheckingAddress     = TRUE;
    sprintf( cBuf, "%c", *p2 ); /* Try dereferencing it. */

    return ( TRUE );
}


/*
 * End of signal.c
 */