/* * signal.c -- Curently included into interface.c * * Seperates the signal handlers and such into a seperate file * for maintainability. * * Broken off from interface.c, and restructured for POSIX * compatible systems by Peter A. Torkelson, aka WhiteFire. */ #ifdef SOLARIS # ifndef _POSIX_SOURCE # define _POSIX_SOURCE /* Solaris needs this */ # endif #endif #include "config.h" #include "interface.h" #include "externs.h" #include "version.h" #ifndef WIN32 #include <signal.h> #include <sys/wait.h> /* * SunOS can't include signal.h and sys/signal.h, stupid broken OS. */ #if defined(HAVE_SYS_SIGNAL_H) && !defined(SUN_OS) # include <sys/signal.h> #endif #if defined(ULTRIX) || defined(_POSIX_VERSION) #undef RETSIGTYPE #define RETSIGTYPE void #endif /* * Function prototypes */ void set_signals(void); RETSIGTYPE bailout(int); RETSIGTYPE sig_dump_status(int i); RETSIGTYPE sig_shutdown(int i); RETSIGTYPE sig_reap(int i); #ifdef _POSIX_VERSION void our_signal(int signo, void (*sighandler) (int)); #else # define our_signal(s,f) signal((s),(f)) #endif /* * our_signal(signo, sighandler) * * signo - Signal #, see defines in signal.h * sighandler - The handler function desired. * * Calls sigaction() to set a signal, if we are posix. */ #ifdef _POSIX_VERSION void our_signal(int signo, void (*sighandler) (int)) { struct sigaction act, oact; act.sa_handler = sighandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* Restart long system calls if a signal is caught. */ #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif /* Make it so */ sigaction(signo, &act, &oact); } #endif /* _POSIX_VERSION */ void set_dumper_signals() { our_signal(SIGPIPE, SIG_IGN); /* Ignore Blocked Pipe */ our_signal(SIGHUP, SIG_IGN); /* Ignore Terminal Hangup */ our_signal(SIGCHLD, SIG_IGN); /* Ignore Child termination */ our_signal(SIGFPE, SIG_IGN); /* Ignore FP exceptions */ our_signal(SIGUSR1, SIG_IGN); /* Ignore SIGUSR1 */ our_signal(SIGUSR2, SIG_IGN); /* Ignore SIGUSR2 */ our_signal(SIGINT, SIG_DFL); /* Take Interrupt signal and die! */ our_signal(SIGTERM, SIG_DFL); /* Take Terminate signal and die! */ our_signal(SIGSEGV, SIG_DFL); /* Take Segfault and die! */ #ifdef SIGTRAP our_signal(SIGTRAP, SIG_DFL); #endif #ifdef SIGIOT our_signal(SIGIOT, SIG_DFL); #endif #ifdef SIGEMT our_signal(SIGEMT, SIG_DFL); #endif #ifdef SIGBUS our_signal(SIGBUS, SIG_DFL); #endif #ifdef SIGSYS our_signal(SIGSYS, SIG_DFL); #endif #ifdef SIGXCPU our_signal(SIGXCPU, SIG_IGN); /* CPU usage limit exceeded */ #endif #ifdef SIGXFSZ our_signal(SIGXFSZ, SIG_IGN); /* Exceeded file size limit */ #endif #ifdef SIGVTALRM our_signal(SIGVTALRM, SIG_DFL); #endif } /* * set_signals() * set_sigs_intern(bail) * * Traps a bunch of signals and reroutes them to various * handlers. Mostly bailout. * * If called from bailout, then reset all to default. * * Called from main() and bailout() */ #define SET_BAIL (bail ? SIG_DFL : bailout) #define SET_IGN (bail ? SIG_DFL : SIG_IGN) static void set_sigs_intern(int bail) { /* we don't care about SIGPIPE, we notice it in select() and write() */ our_signal(SIGPIPE, SET_IGN); /* didn't manage to lose that control tty, did we? Ignore it anyway. */ our_signal(SIGHUP, SET_IGN); /* resolver's exited. Better clean up the mess our child leaves */ our_signal(SIGCHLD, bail ? SIG_DFL : sig_reap); /* standard termination signals */ our_signal(SIGINT, SET_BAIL); our_signal(SIGTERM, SET_BAIL); /* catch these because we might as well */ /* our_signal(SIGQUIT, SET_BAIL); */ #ifdef SIGTRAP our_signal(SIGTRAP, SET_IGN); #endif #ifdef SIGIOT our_signal(SIGIOT, SET_BAIL); #endif #ifdef SIGEMT our_signal(SIGEMT, SET_BAIL); #endif #ifdef SIGBUS our_signal(SIGBUS, SET_BAIL); #endif #ifdef SIGSYS our_signal(SIGSYS, SET_BAIL); #endif our_signal(SIGFPE, SET_BAIL); our_signal(SIGSEGV, SET_BAIL); our_signal(SIGTERM, bail ? SET_BAIL : sig_shutdown); #ifdef SIGXCPU our_signal(SIGXCPU, SET_BAIL); #endif #ifdef SIGXFSZ our_signal(SIGXFSZ, SET_BAIL); #endif #ifdef SIGVTALRM our_signal(SIGVTALRM, SET_BAIL); #endif our_signal(SIGUSR2, SET_BAIL); /* status dumper (predates "WHO" command) */ our_signal(SIGUSR1, bail ? SIG_DFL : sig_dump_status); } void set_signals(void) { set_sigs_intern(FALSE); } /* * Signal handlers */ /* * BAIL! */ RETSIGTYPE bailout(int sig) { char message[1024]; /* turn off signals */ set_sigs_intern(TRUE); snprintf(message, sizeof(message), "BAILOUT: caught signal %d", sig); panic(message); exit(7); #if !defined(SYSV) && !defined(_POSIX_VERSION) && !defined(ULTRIX) return 0; #endif } /* * Spew WHO to file */ RETSIGTYPE sig_dump_status(int i) { dump_status(); #if !defined(SYSV) && !defined(_POSIX_VERSION) && !defined(ULTRIX) return 0; #endif } /* * Gracefully shut the server down. */ RETSIGTYPE sig_shutdown(int i) { log_status("SHUTDOWN: via SIGNAL\n"); shutdown_flag = 1; restart_flag = 0; #if !defined(SYSV) && !defined(_POSIX_VERSION) && !defined(ULTRIX) return 0; #endif } /* * Clean out Zombie Resolver Process. */ #if !defined(SYSV) && !defined(_POSIX_VERSION) && !defined(ULTRIX) #define RETSIGVAL 0 #else #define RETSIGVAL #endif RETSIGTYPE sig_reap(int i) { /* If DISKBASE is not defined, then there are two types of * children that can die. First is the nameservice resolver. * Second is the database dumper. If resolver exits, we should * note it in the log -- at least give the admin the option of * knowing about it, and dealing with it as necessary. */ /* The fix for SSL connections getting closed when databases were * saved with DISKBASE disabled required closing all sockets * when the server fork()ed. This made it impossible for that * process to spit out the "save done" message. However, because * that process dies as soon as it finishes dumping the database, * can detect that the child died, and broadcast the "save done" * message anyway. */ int status = 0; int reapedpid = 0; reapedpid = waitpid(-1, &status, WNOHANG); if(!reapedpid) { #ifdef DETACH log2file(LOG_ERR_FILE,"SIG_CHILD signal handler called with no pid!"); #else fprintf(stderr, "SIG_CHILD signal handler called with no pid!\n"); #endif } else { if (reapedpid == global_resolver_pid) { log_status("resolver exited with status %d\n", status); if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { /* If the resolver exited with an error, respawn it. */ spawn_resolver(); } else if (WIFSIGNALED(status)) { /* If the resolver exited due to a signal, respawn it. */ spawn_resolver(); } #ifndef DISKBASE } else if(reapedpid == global_dumper_pid) { int warnflag = 0; log_status("forked DB dump task exited with status %d\n", status); if (WIFSIGNALED(status)) { warnflag = 1; } else if (WIFEXITED(status)) { /* In case NOCOREDUMP is defined, check for panic()s exit codes. */ int statres = WEXITSTATUS(status); if (statres == 135 || statres == 136) { warnflag = 1; } } if (warnflag) { wall_wizards("# WARNING: The forked DB save process crashed while saving the database."); wall_wizards("# This is probably due to memory corruption, which can crash this server."); wall_wizards("# Unless you have a REALLY good unix programmer around who can try to fix"); wall_wizards("# this process live with a debugger, you should try to restart this Muck"); wall_wizards("# as soon as possible, and accept the data lost since the previous DB save."); } global_dumpdone = 1; global_dumper_pid = 0; #endif } else { fprintf(stderr, "unknown child process (pid %d) exited with status %d\n", reapedpid, status); } } return RETSIGVAL; } #else /* WIN32 */ #include <wincon.h> #include <windows.h> #include <signal.h> #define VK_C 0x43 #define CONTROL_KEY (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED) void sig_reap(int i) {} void sig_shutdown(int i) {} void sig_dumpstatus(int i) {} void set_sigs_intern(int bail) {} void set_signals() {} void bailout(int sig) { char message[1024]; snprintf(message, sizeof(message), "BAILOUT: caught signal %d", sig); panic(message); exit(7); } BOOL WINAPI HandleConsole(DWORD mesg) { switch(mesg) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: break; case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: shutdown_flag = 1; restart_flag = 0; log_status("SHUTDOWN: via SIGNAL\n"); break; default: return false; } return true; } void set_console() { HANDLE InputHandle; SetConsoleCtrlHandler(HandleConsole, true); SetConsoleTitle(VERSION); } #endif /* WIN32 */