btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * signal.c
 *
 * $Id $
 */

#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>

#include "debug.h"
#include "mudconf.h"
#include "externs.h"
#include "flags.h"

void signal_TERM(int, siginfo_t *, void *);
void signal_PIPE(int, siginfo_t *, void *);
void signal_USR1(int, siginfo_t *, void *);
void signal_SEGV(int, siginfo_t *, void *);
void signal_BUS(int, siginfo_t *, void *);

struct sigaction saTERM = {.sa_handler = NULL,.sa_sigaction = signal_TERM,
	.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_RESTART
};

struct sigaction saPIPE = {.sa_handler = NULL,.sa_sigaction = signal_PIPE,
	.sa_flags = SA_SIGINFO
};

struct sigaction saUSR1 = {.sa_handler = NULL,.sa_sigaction = signal_USR1,
	.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_RESTART 
};

struct sigaction saSEGV = {.sa_handler = NULL,.sa_sigaction = signal_SEGV,
	.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_RESTART
};

struct sigaction saBUS = {.sa_handler = NULL,.sa_sigaction = signal_BUS,
	.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_RESTART
};

stack_t sighandler_stack;  
stack_t regular_stack;

#define ALT_STACK_SIZE (0x40000)
#define ALT_STACK_ALIGN (0x1000)

void bind_signals()
{
    int error_code;
    dprintk("creating alternate signal stack.");
#ifdef HAVE_POSIX_MEMALIGN
    error_code = posix_memalign(&sighandler_stack.ss_sp, ALT_STACK_ALIGN,
            ALT_STACK_SIZE);
#else 
    sighandler_stack.ss_sp = malloc(ALT_STACK_SIZE);
    if(sighandler_stack.ss_sp != 0) error_code = 0;
#endif
    if(error_code == 0) {
        sighandler_stack.ss_size = ALT_STACK_SIZE;
        sighandler_stack.ss_flags = 0;
        memset(sighandler_stack.ss_sp, 0, ALT_STACK_SIZE);
        dperror(sigaltstack(&sighandler_stack, &regular_stack) <0);
        dprintk("Current stack at 0x%x with length 0x%x and flags 0x%x", 
                (unsigned int)regular_stack.ss_sp, regular_stack.ss_size, regular_stack.ss_flags);
        dprintk("Signal stack at 0x%x with length 0x%x and flags 0x%x", 
                (unsigned int)sighandler_stack.ss_sp, sighandler_stack.ss_size, sighandler_stack.ss_flags);
        saSEGV.sa_flags |= SA_ONSTACK;
        saBUS.sa_flags |= SA_ONSTACK;
    } else {
        dprintk("posix_memalign failed with %s", strerror(error_code));
        log_error(LOG_PROBLEMS, "SIG", "ERR", 
                "posix_memalign() failed with error %s, alternate stack not used.",
                strerror(error_code));
        log_error(LOG_PROBLEMS, "SIG", "ERR", 
                "running signal_handlers without sigaltstack() will corrupt your coredumps!");
        sighandler_stack.ss_sp = NULL;
    }
    dprintk("binding signals.");
    dperror(sigaction(SIGTERM, &saTERM, NULL) <0);
	sigaction(SIGPIPE, &saPIPE, NULL);
	sigaction(SIGUSR1, &saUSR1, NULL);
	sigaction(SIGSEGV, &saSEGV, NULL);
	sigaction(SIGBUS, &saBUS, NULL);
    signal(SIGCHLD, SIG_IGN);
    dprintk("done.");
}

void unbind_signals()
{
    dprintk("Unbinding signal handlers.");
	signal(SIGTERM, SIG_DFL);
	signal(SIGPIPE, SIG_DFL);
	signal(SIGUSR1, SIG_DFL);
	signal(SIGSEGV, SIG_DFL);
	signal(SIGBUS, SIG_DFL);
    signal(SIGCHLD, SIG_DFL);
    if(sighandler_stack.ss_sp != NULL) {
        void *temp_ptr;
        dprintk("Releasing signal handler stack.");
        sighandler_stack.ss_flags = SS_DISABLE;
        temp_ptr = sighandler_stack.ss_sp;
        sigaltstack(&sighandler_stack, NULL);
        free(temp_ptr);
        sighandler_stack.ss_sp = NULL;
    }
    dprintk("Finished.");
}

void signal_TERM(int signo, siginfo_t * siginfo, void *ucontext)
{
	dprintk("caught SIGTERM");
	do_shutdown(NOTHING, 0, SHUTDN_EXIT, "received SIGTERM from kernel.");
}

void signal_PIPE(int signo, siginfo_t * siginfo, void *ucontext)
{
    dprintk("caught SIGPIPE");
#ifdef HAVE_SIGINFO_T_SI_FD
	eradicate_broken_fd(siginfo->si_fd);
#else
	eradicate_broken_fd(-1);
#endif
}

void signal_USR1(int signo, siginfo_t * siginfo, void *ucontext)
{
    mux_release_socket();
    dprintk("caught SIGUSR1");
	do_restart(1, 1, 0);
}

void signal_SEGV(int signo, siginfo_t * siginfo, void *ucontext)
{
    dprintk("caught SIGSEGV");
	int child;
    mux_release_socket();
	if(!(child = fork())) {
		dump_restart_db();
		execl(mudstate.executable_path, mudstate.executable_path,
			  mudconf.config_file, NULL);
	} else {
		switch (siginfo->si_code) {
		case SEGV_MAPERR:
			raw_broadcast(0,
						  "Game: Invalid access of unamapped memory at %p. Restarting from Checkpoint.",
						  siginfo->si_addr);
			break;
		case SEGV_ACCERR:
			raw_broadcast(0,
						  "Game: Invalid access of protected memory at %p. Restarting from Checkpoint.",
						  siginfo->si_addr);
			break;
		default:
			raw_broadcast(0,
						  "Game: Unhandled SEGV at %p. Restarting from checkpoint.",
						  siginfo->si_addr);
			break;
		}
		dump_database_internal(DUMP_CRASHED);
		report();
	}
}

void signal_BUS(int signo, siginfo_t * siginfo, void *ucontext)
{
    dprintk("caught SIGBUS");
	int child;
    mux_release_socket();
	if(mudconf.sig_action != SA_EXIT && !(child = fork())) {
		dump_restart_db();
		execl(mudstate.executable_path, mudstate.executable_path,
			  mudconf.config_file, NULL);
	} else {
		switch (siginfo->si_code) {
		case BUS_ADRALN:
			raw_broadcast(0,
						  "Game: Invalid address alignment accessing %p. Restarting from Checkpoint.",
						  siginfo->si_addr);
			break;
		case BUS_ADRERR:
			raw_broadcast(0,
						  "Game: Invalid access of non-existent physical memory at %p. Restarting from Checkpoint.",
						  siginfo->si_addr);
			break;
		case BUS_OBJERR:
			raw_broadcast(0,
						  "Game: Invalid object specific hardware error access at %p. Restarting from Checkpoint.",
						  siginfo->si_addr);
			break;
		default:
			raw_broadcast(0,
						  "Game: Unhandled SEGV at %p. Restarting from checkpoint.",
						  siginfo->si_addr);
			break;
		}
		dump_database_internal(DUMP_CRASHED);
		report();
	}
}