/** * \file timer.c * * \brief Timed events in PennMUSH. * * */ #include "copyrite.h" #include "config.h" #include <stdio.h> #include <ctype.h> #include <fcntl.h> #include <string.h> #include <signal.h> #include <stdlib.h> #ifdef I_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif #ifdef WIN32 #include <windows.h> #endif #ifdef I_UNISTD #include <unistd.h> #endif #include "conf.h" #include "externs.h" #include "dbdefs.h" #include "lock.h" #include "extmail.h" #include "match.h" #include "flags.h" #include "access.h" #include "log.h" #include "game.h" #include "help.h" #include "parse.h" #include "confmagic.h" static sig_atomic_t hup_triggered = 0; static sig_atomic_t usr1_triggered = 0; extern void inactivity_check(void); extern void reopen_logs(void); static void migrate_stuff(int amount); #ifndef WIN32 void hup_handler(int); void usr1_handler(int); #endif void dispatch(void); #ifndef WIN32 /** Handler for HUP signal. * Do the minimal work here - set a global variable and reload the handler. * \param x unused. */ void hup_handler(int x __attribute__ ((__unused__))) { hup_triggered = 1; reload_sig_handler(SIGHUP, hup_handler); } /** Handler for USR1 signal. * Do the minimal work here - set a global variable and reload the handler. * \param x unused. */ void usr1_handler(int x __attribute__ ((__unused__))) { usr1_triggered = 1; reload_sig_handler(SIGUSR1, usr1_handler); } #endif /* WIN32 */ /** Set up signal handlers. */ void init_timer(void) { #ifndef WIN32 install_sig_handler(SIGHUP, hup_handler); install_sig_handler(SIGUSR1, usr1_handler); #endif #ifndef PROFILING #ifdef HAS_ITIMER install_sig_handler(SIGPROF, signal_cpu_limit); #endif #endif } /** Migrate some number of chunks. * The requested amount is only a guideline; the actual amount * migrated will be more or less due to always migrating all the * attributes, locks, and mail on any given object together. * \param amount the suggested number of attributes to migrate. */ static void migrate_stuff(int amount) { static int start_obj = 0; static chunk_reference_t **refs = NULL; static int refs_size = 0; int end_obj; int actual; ATTR *aptr; lock_list *lptr; MAIL *mp; if (db_top == 0) return; end_obj = start_obj; actual = 0; do { for (aptr = List(end_obj); aptr; aptr = AL_NEXT(aptr)) if (aptr->data != NULL_CHUNK_REFERENCE) actual++; for (lptr = Locks(end_obj); lptr; lptr = L_NEXT(lptr)) if (L_KEY(lptr) != NULL_CHUNK_REFERENCE) actual++; if (IsPlayer(end_obj)) { for (mp = find_exact_starting_point(end_obj); mp; mp = mp->next) if (mp->msgid != NULL_CHUNK_REFERENCE) actual++; } end_obj = (end_obj + 1) % db_top; } while (actual < amount && end_obj != start_obj); if (actual == 0) return; if (!refs || actual > refs_size) { if (refs) mush_free((Malloc_t) refs, "migration reference array"); refs = (chunk_reference_t **) mush_malloc(actual * sizeof(chunk_reference_t *), "migration reference array"); refs_size = actual; if (!refs) mush_panic("Could not allocate migration reference array"); } #ifdef DEBUG_MIGRATE do_rawlog(LT_TRACE, "Migrate asked %d, actual objects #%d to #%d for %d", amount, start_obj, (end_obj + db_top - 1) % db_top, actual); #endif actual = 0; do { for (aptr = List(start_obj); aptr; aptr = AL_NEXT(aptr)) if (aptr->data != NULL_CHUNK_REFERENCE) { refs[actual] = &(aptr->data); actual++; } for (lptr = Locks(start_obj); lptr; lptr = L_NEXT(lptr)) if (L_KEY(lptr) != NULL_CHUNK_REFERENCE) { refs[actual] = &(lptr->key); actual++; } if (IsPlayer(start_obj)) { for (mp = find_exact_starting_point(start_obj); mp; mp = mp->next) if (mp->msgid != NULL_CHUNK_REFERENCE) { refs[actual] = &(mp->msgid); actual++; } } start_obj = (start_obj + 1) % db_top; } while (start_obj != end_obj); chunk_migration(actual, refs); } /** Handle events that may need handling. * This routine is polled from bsd.c. At any call, it can handle * the HUP and USR1 signals. At calls that are 'on the second', * it goes on to perform regular every-second processing and to * check whether it's time to do other periodic processes like * purge, dump, or inactivity checks. */ void dispatch(void) { static int idle_counter = 0; /* A HUP reloads configuration and reopens logs */ if (hup_triggered) { do_rawlog(LT_ERR, T("SIGHUP received: reloading .txt and .cnf files")); config_file_startup(NULL, 0); config_file_startup(NULL, 1); fcache_load(NOTHING); help_reindex(NOTHING); read_access_file(); reopen_logs(); hup_triggered = 0; } /* A USR1 does a shutdown/reboot */ if (usr1_triggered) { do_reboot(NOTHING, 0); /* We don't return from this */ usr1_triggered = 0; /* But just in case */ } if (!globals.on_second) return; globals.on_second = 0; mudtime = time(NULL); do_second(); migrate_stuff(CHUNK_MIGRATE_AMOUNT); if (options.purge_counter <= mudtime) { /* Free list reconstruction */ options.purge_counter = options.purge_interval + mudtime; global_eval_context.cplr = NOTHING; strcpy(global_eval_context.ccom, "purge"); purge(); } if (options.dbck_counter <= mudtime) { /* Database consistency check */ options.dbck_counter = options.dbck_interval + mudtime; global_eval_context.cplr = NOTHING; strcpy(global_eval_context.ccom, "dbck"); dbck(); } if (idle_counter <= mudtime) { /* Inactivity check */ idle_counter = 60 + mudtime; inactivity_check(); } /* Database dump routines */ if (options.dump_counter <= mudtime) { log_mem_check(); options.dump_counter = options.dump_interval + mudtime; strcpy(global_eval_context.ccom, "dump"); fork_and_dump(1); flag_broadcast(0, "ON-VACATION", "%s", T ("Your ON-VACATION flag is set! If you're back, clear it.")); } else if (NO_FORK && (options.dump_counter - 60 == mudtime) && *options.dump_warning_1min) { flag_broadcast(0, 0, "%s", options.dump_warning_1min); } else if (NO_FORK && (options.dump_counter - 300 == mudtime) && *options.dump_warning_5min) { flag_broadcast(0, 0, "%s", options.dump_warning_5min); } if (options.warn_interval && (options.warn_counter <= mudtime)) { options.warn_counter = options.warn_interval + mudtime; strcpy(global_eval_context.ccom, "warnings"); run_topology(); } local_timer(); } sig_atomic_t cpu_time_limit_hit = 0; /** Was the cpu time limit hit? */ int cpu_limit_warning_sent = 0; /** Have we issued a cpu limit warning? */ #ifndef PROFILING #if defined(HAS_ITIMER) /** Handler for PROF signal. * Do the minimal work here - set a global variable and reload the handler. * \param signo unused. */ void signal_cpu_limit(int signo __attribute__ ((__unused__))) { cpu_time_limit_hit = 1; reload_sig_handler(SIGPROF, signal_cpu_limit); } #elif defined(WIN32) #if _MSC_VER <= 1100 && !defined(UINT_PTR) #define UINT_PTR UINT #endif UINT_PTR timer_id; VOID CALLBACK win32_timer(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { cpu_time_limit_hit = 1; } #endif #endif int timer_set = 0; /**< Is a CPU timer set? */ /** Start the cpu timer (before running a command). */ void start_cpu_timer(void) { #ifndef PROFILING cpu_time_limit_hit = 0; cpu_limit_warning_sent = 0; timer_set = 1; #if defined(HAS_ITIMER) /* UNIX way */ { struct itimerval time_limit; if (options.queue_entry_cpu_time > 0) { ldiv_t t; /* Convert from milliseconds */ t = ldiv(options.queue_entry_cpu_time, 1000); time_limit.it_value.tv_sec = t.quot; time_limit.it_value.tv_usec = t.rem * 1000; time_limit.it_interval.tv_sec = 0; time_limit.it_interval.tv_usec = 0; if (setitimer(ITIMER_PROF, &time_limit, NULL)) { perror("setitimer"); timer_set = 0; } } else timer_set = 0; } #elif defined(WIN32) /* Windoze way */ if (options.queue_entry_cpu_time > 0) timer_id = SetTimer(NULL, 0, (unsigned) options.queue_entry_cpu_time, (TIMERPROC) win32_timer); else timer_set = 0; #endif #endif } /** Reset the cpu timer (after running a command). */ void reset_cpu_timer(void) { #ifndef PROFILING if (timer_set) { #if defined(HAS_ITIMER) struct itimerval time_limit, time_left; time_limit.it_value.tv_sec = 0; time_limit.it_value.tv_usec = 0; time_limit.it_interval.tv_sec = 0; time_limit.it_interval.tv_usec = 0; if (setitimer(ITIMER_PROF, &time_limit, &time_left)) perror("setitimer"); #elif defined(WIN32) KillTimer(NULL, timer_id); #endif } cpu_time_limit_hit = 0; cpu_limit_warning_sent = 0; timer_set = 0; #endif }