#include "copyright.h" #include <stdio.h> #include <ctype.h> #include <signal.h> #include <sys/wait.h> #include <time.h> #include <stdarg.h> #include "db.h" #include "config.h" #include "interface.h" #include "match.h" #include "externs.h" /* declarations */ static const char *dumpfile = 0; static int epoch = 0; static int alarm_triggered = 0; static int alarm_block = 0; static void fork_and_dump(void); void dump_database(void); void do_dump(dbref player) { if(Wizard(player)) { alarm_triggered = 1; notify(player, "Dumping..."); } else { notify(player, "Sorry, you are in a no dumping zone."); } } void do_shutdown(dbref player) { if(Wizard(player)) { writelog("SHUTDOWN: by %s\n", unparse_object(player, player)); fflush(stderr); shutdown_flag = 1; } else { notify(player, "Your delusions of grandeur have been duly noted."); } } /* should be void, but it's defined as int */ static int alarm_handler(void) { alarm_triggered = 1; if(!alarm_block) { fork_and_dump(); } return 0; } static void dump_database_internal(void) { char tmpfile[2048]; FILE *f; sprintf(tmpfile, "%s.#%d#", dumpfile, epoch - 1); unlink(tmpfile); /* nuke our predecessor */ sprintf(tmpfile, "%s.#%d#", dumpfile, epoch); if((f = fopen(tmpfile, "w")) != NULL) { db_write(f); fclose(f); if(rename(tmpfile, dumpfile) < 0) perror(tmpfile); } else { perror(tmpfile); } } void panic(const char *message) { char panicfile[2048]; FILE *f; int i; writelog("PANIC: %s\n", message); /* turn off signals */ for(i = 0; i < NSIG; i++) { signal(i, SIG_IGN); } /* shut down interface */ emergency_shutdown(); /* dump panic file */ sprintf(panicfile, "%s.PANIC", dumpfile); if((f = fopen(panicfile, "w")) == NULL) { perror("CANNOT OPEN PANIC FILE, YOU LOSE:"); #ifndef NODUMPCORE signal(SIGILL, SIG_DFL); abort(); #endif NODUMPCORE _exit(135); } else { writelog("DUMPING: %s\n", panicfile); db_write(f); fclose(f); writelog("DUMPING: %s (done)\n", panicfile); #ifndef NODUMPCORE signal(SIGILL, SIG_DFL); abort(); #endif NODUMPCORE _exit(136); } } void writelog(const char *fmt, ...) { va_list list; struct tm *tm; long t; char buffer[2048]; va_start(list, fmt); vsprintf(buffer, fmt, list); t = time(NULL); tm = localtime(&t); fprintf(stderr, "%d/%02d %02d:%02d:%02d %s", tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, buffer); fflush(stderr); va_end(list); } void dump_database(void) { epoch++; writelog("DUMPING: %s.#%d#\n", dumpfile, epoch); dump_database_internal(); writelog("DUMPING: %s.#%d# (done)\n", dumpfile, epoch); } static void fork_and_dump(void) { int child; epoch++; writelog("CHECKPOINTING: %s.#%d#\n", dumpfile, epoch); #ifdef USE_VFORK child = vfork(); #else /* USE_VFORK */ child = fork(); #endif /* USE_VFORK */ if(child == 0) { /* in the child */ close(0); /* get that file descriptor back */ dump_database_internal(); _exit(0); /* !!! */ } else if(child < 0) { perror("fork_and_dump: fork()"); } /* in the parent */ /* reset alarm */ alarm_triggered = 0; alarm(DUMP_INTERVAL); } static int reaper(void) { union wait stat; while(wait3(&stat, WNOHANG, 0) > 0); return 0; } int init_game(const char *infile, const char *outfile) { FILE *f; if((f = fopen(infile, "r")) == NULL) return -1; /* ok, read it in */ writelog("LOADING: %s\n", infile); if(db_read(f) < 0) return -1; writelog("LOADING: %s (done)\n", infile); /* everything ok */ fclose(f); /* initialize random number generator */ srandom(getpid()); /* set up dumper */ if(dumpfile) free((void *) dumpfile); dumpfile = alloc_string(outfile); signal(SIGALRM, (void (*)) alarm_handler); signal(SIGHUP, (void (*)) alarm_handler); signal(SIGCHLD, (void (*)) reaper); alarm_triggered = 0; alarm(DUMP_INTERVAL); return 0; } /* use this only in process_command */ #define Matched(string) { if(!string_prefix((string), command)) goto bad; } void process_command(dbref player, char *command) { char *arg1; char *arg2; char *q; /* utility */ char *p; /* utility */ char *index(char *, char); if(command == 0) abort(); /* robustify player */ if(player < 0 || player >= db_top || Typeof(player) != TYPE_PLAYER) { writelog("process_command: bad player %d\n", player); return; } #ifdef LOG_COMMANDS writelog("COMMAND from %s(%d) in %s(%d): %s\n", db[player].name, player, db[db[player].location].name, db[player].location, command); #endif /* LOG_COMMANDS */ /* eat leading whitespace */ while(*command && isspace(*command)) command++; /* eat extra white space */ q = p = command; while(*p) { /* scan over word */ while(*p && !isspace(*p)) *q++ = *p++; /* smash spaces */ while(*p && isspace(*++p)); if(*p) *q++ = ' '; /* add a space to separate next word */ } /* terminate */ *q = '\0'; /* block dump to prevent db inconsistencies from showing up */ alarm_block = 1; /* check for single-character commands */ if(*command == SAY_TOKEN) { do_say(player, command+1, NULL); } else if(*command == POSE_TOKEN) { do_pose(player, command+1, NULL); } else if(can_move(player, command)) { /* command is an exact match for an exit */ do_move(player, command); } else { /* parse arguments */ /* find arg1 */ /* move over command word */ for(arg1 = command; *arg1 && !isspace(*arg1); arg1++); /* truncate command */ if(*arg1) *arg1++ = '\0'; /* move over spaces */ while(*arg1 && isspace(*arg1)) arg1++; /* find end of arg1, start of arg2 */ for(arg2 = arg1; *arg2 && *arg2 != ARG_DELIMITER; arg2++); /* truncate arg1 */ for(p = arg2 - 1; p >= arg1 && isspace(*p); p--) *p = '\0'; /* go past delimiter if present */ if(*arg2) *arg2++ = '\0'; while(*arg2 && isspace(*arg2)) arg2++; switch(command[0]) { case '@': switch(command[1]) { case 'b': case 'B': switch (command[2]) { case 'o': case 'O': switch (command[3]) { case 'o': case 'O': Matched("@boot"); do_boot(player, arg1); break; case 'b': case 'B': if(string_compare(command, "@bobble")) goto bad; do_bobble(player, arg1, arg2); break; default: goto bad; } break; default: goto bad; } break; case 'c': case 'C': /* chown, create */ switch(command[2]) { case 'h': case 'H': Matched("@chown"); do_chown(player, arg1, arg2); break; #ifdef RECYCLE case 'o': case 'O': Matched("@count"); do_count(player, arg1); break; #endif RECYCLE case 'r': case 'R': Matched("@create"); do_create(player, arg1, atol(arg2)); break; default: goto bad; } break; case 'd': case 'D': /* describe, dig, or dump */ switch(command[2]) { case 'e': case 'E': Matched("@describe"); do_describe(player, arg1, arg2); break; case 'i': case 'I': Matched("@dig"); do_dig(player, arg1); break; case 'u': case 'U': Matched("@dump"); do_dump(player); break; default: goto bad; } break; case 'f': /* fail, find, or force */ switch(command[2]) { case 'a': case 'A': Matched("@fail"); do_fail(player, arg1, arg2); break; case 'i': case 'I': Matched("@find"); do_find(player, arg1); break; case 'o': case 'O': Matched("@force"); do_force(player, arg1, arg2); break; default: goto bad; } break; case 'l': case 'L': /* lock or link */ switch(command[2]) { case 'i': case 'I': Matched("@link"); do_link(player, arg1, arg2); break; case 'o': case 'O': Matched("@lock"); do_lock(player, arg1, arg2); break; default: goto bad; } break; case 'm': case 'M': Matched("@mass_teleport"); do_mass_teleport(player, arg1); break; case 'n': case 'N': /* @name or @newpassword */ switch(command[2]) { case 'a': case 'A': Matched("@name"); do_name(player, arg1, arg2); break; case 'e': if(strcmp(command, "@newpassword")) goto bad; do_newpassword(player, arg1, arg2); break; default: goto bad; } break; case 'o': case 'O': switch(command[2]) { case 'f': case 'F': Matched("@ofail"); do_ofail(player, arg1, arg2); break; case 'p': case 'P': Matched("@open"); do_open(player, arg1, arg2); break; case 's': case 'S': Matched("@osuccess"); do_osuccess(player, arg1, arg2); break; case 'w': case 'W': Matched("@owned"); do_owned(player, arg1); break; default: goto bad; } break; case 'p': case 'P': switch(command[2]) { case 'a': case 'A': Matched("@password"); do_password(player, arg1, arg2); break; #ifdef REGISTRATION case 'c': case 'C': Matched("@pcreate"); do_pcreate(player, arg1, arg2); break; #endif REGISTRATION default: goto bad; } break; #ifdef RECYCLE case 'r': case 'R': /* Recycle */ Matched("@recycle"); do_recycle(player, arg1); break; #endif RECYCLE case 's': case 'S': /* set, shutdown, success */ switch(command[2]) { case 'e': case 'E': Matched("@set"); do_set(player, arg1, arg2); break; case 'h': if(strcmp(command, "@shutdown")) goto bad; do_shutdown(player); break; case 't': case 'T': Matched("@stats"); do_stats(player, arg1); break; case 'u': case 'U': Matched("@success"); do_success(player, arg1, arg2); break; default: goto bad; } break; case 't': case 'T': switch(command[2]) { case 'e': case 'E': Matched("@teleport"); do_teleport(player, arg1, arg2); break; case 'o': case 'O': if(string_compare(command, "@toad")) goto bad; do_bobble(player, arg1, arg2); break; default: goto bad; } break; case 'u': case 'U': if(string_prefix(command, "@unli")) { Matched("@unlink"); do_unlink(player, arg1); } else if(string_prefix(command, "@unlo")) { Matched("@unlock"); do_unlock(player, arg1); } else if(string_prefix(command, "@unb")) { Matched("@unbobble"); do_unbobble(player, arg1, arg2); } else if(string_prefix(command, "@unt")) { Matched("@untoad"); do_unbobble(player, arg1, arg2); } else { goto bad; } break; case 'w': if(strcmp(command, "@wall")) goto bad; do_wall(player, arg1, arg2); break; default: goto bad; } break; case 'd': case 'D': Matched("drop"); do_drop(player, arg1); break; case 'e': case 'E': if (command[1] == 'x' || command[1] == 'X') { Matched("examine"); do_examine(player, arg1); break; } else { goto bad; } case 'g': case 'G': /* get, give, go, or gripe */ switch(command[1]) { case 'e': case 'E': Matched("get"); do_get(player, arg1); break; case 'i': case 'I': Matched("give"); do_give(player, arg1, atol(arg2)); break; case 'o': case 'O': Matched("goto"); do_move(player, arg1); break; case 'r': case 'R': Matched("gripe"); do_gripe(player, arg1, arg2); break; default: goto bad; } break; case 'h': case 'H': Matched("help"); do_help(player); break; case 'i': case 'I': Matched("inventory"); do_inventory(player); break; case 'k': case 'K': Matched("kill"); do_kill(player, arg1, atol(arg2)); break; case 'l': case 'L': Matched("look"); do_look_at(player, arg1); break; case 'm': case 'M': Matched("move"); do_move(player, arg1); break; case 'n': case 'N': /* news */ if(string_compare(command, "news")) goto bad; do_news(player); break; case 'p': case 'P': Matched("page"); do_page(player, arg1, arg2); break; case 'r': case 'R': switch(command[1]) { case 'e': case 'E': Matched("read"); /* undocumented alias for look at */ do_look_at(player, arg1); break; case 'o': case 'O': Matched("rob"); do_rob(player, arg1); break; default: goto bad; } break; case 's': case 'S': /* say, "score" */ switch(command[1]) { case 'a': case 'A': Matched("say"); do_say(player, arg1, arg2); break; case 'c': case 'C': Matched("score"); do_score(player); break; default: goto bad; } break; case 't': case 'T': switch(command[1]) { case 'a': case 'A': Matched("take"); do_get(player, arg1); break; case 'h': case 'H': Matched("throw"); do_drop(player, arg1); break; default: goto bad; } break; case 'w': case 'W': Matched("whisper"); do_whisper(player, arg1, arg2); break; default: bad: notify(player, "Huh? (Type \"help\" for help.)"); #ifdef LOG_FAILED_COMMANDS if(!controls(player, db[player].location)) { writelog("HUH from %s(%d) in %s(%d)[%s]: %s %s\n", db[player].name, player, db[db[player].location].name, db[player].location, db[db[db[player].location].owner].name, command, reconstruct_message(arg1, arg2)); } #endif /* LOG_FAILED_COMMANDS */ break; } } /* unblock alarms */ alarm_block = 0; if(alarm_triggered) { fork_and_dump(); } } #undef Matched