/* * 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, ®ular_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(); } }