/* * game.c */ #include "copyright.h" #include "config.h" #include <sys/stat.h> #include <signal.h> #include <event.h> #include <regex.h> #include "mudconf.h" #include "config.h" #include "file_c.h" #include "db.h" #include "interface.h" #include "match.h" #include "externs.h" #include "flags.h" #include "powers.h" #include "attrs.h" #include "alloc.h" #include "vattr.h" #include "commac.h" #ifdef SQL_SUPPORT #include "sqlchild.h" #endif #ifndef NEXT #endif #ifdef HAVE_IEEEFP_H #include <ieeefp.h> #endif #ifdef HAVE_SYS_UCONTEXT_H #include <sys/ucontext.h> #endif #define NSUBEXP 10 extern void init_attrtab(void); extern void init_cmdtab(void); extern void init_mactab(void); extern void init_chantab(void); extern void cf_init(void); extern void pcache_init(void); extern int cf_read(char *fn); extern void init_functab(void); extern void close_sockets(int emergency, char *message); extern void init_version(void); extern void init_logout_cmdtab(void); extern void init_timer(void); extern void raw_notify(dbref, const char *); extern void do_second(void); extern void do_dbck(dbref, dbref, int); #ifdef ARBITRARY_LOGFILES void logcache_init(); void logcache_destruct(); #endif #ifdef HUDINFO_SUPPORT extern void init_hudinfo(void); #endif void fork_and_dump(int); void dump_database(void); void do_dump_optimize(dbref, dbref, int); void pcache_sync(void); void dump_database_internal(int); static void init_rlimit(void); int reserved; extern int corrupt; /* * used to allocate storage for temporary stuff, cleared before command * execution */ void do_dump(dbref player, dbref cause, int key) { notify(player, "Dumping..."); /* * DUMP_OPTIMIZE takes advantage of a feature of GDBM to compress * unused space in the database, and will not be very useful * except sparingly, perhaps done every month or so. */ if(key & DUMP_OPTIMIZE) do_dump_optimize(player, cause, key); else fork_and_dump(key); } void do_dump_optimize(dbref player, dbref cause, int key) { raw_notify(player, "Database is memory based."); } /** * print out stuff into error file */ void report(void) { STARTLOG(LOG_BUGS, "BUG", "INFO") { log_text((char *) "Command: '"); log_text(mudstate.debug_cmd); log_text((char *) "'"); ENDLOG; } if(Good_obj(mudstate.curr_player)) { STARTLOG(LOG_BUGS, "BUG", "INFO") { log_text((char *) "Player: "); log_name_and_loc(mudstate.curr_player); if((mudstate.curr_enactor != mudstate.curr_player) && Good_obj(mudstate.curr_enactor)) { log_text((char *) " Enactor: "); log_name_and_loc(mudstate.curr_enactor); } ENDLOG; }} } /* * Load a regular expression match and insert it into * registers. */ int regexp_match(char *pattern, char *str, char *args[], int nargs) { regex_t re; int got_match; regmatch_t pmatch[NSUBEXP]; int i, len; /* * Load the regexp pattern. This allocates memory which must be * later freed. A free() of the regexp does free all structures * under it. */ if(regcomp(&re, pattern, REG_EXTENDED) != 0) { /* * This is a matching error. We have an error message in * regexp_errbuf that we can ignore, since we're doing * command-matching. */ return 0; } /* * Now we try to match the pattern. The relevant fields will * automatically be filled in by this. */ got_match = (regexec(&re, str, NSUBEXP, pmatch, 0) == 0); if(!got_match) { regfree(&re); return 0; } /* * Now we fill in our args vector. Note that in regexp matching, * 0 is the entire string matched, and the parenthesized strings * go from 1 to 9. We DO PRESERVE THIS PARADIGM, for consistency * with other languages. */ for(i = 0; i < nargs; i++) { args[i] = NULL; } /* Convenient: nargs and NSUBEXP are the same. * We are also guaranteed that our buffer is going to be LBUF_SIZE * so we can copy without fear. */ for(i = 0; (i < NSUBEXP) && (pmatch[i].rm_so != -1) && (pmatch[i].rm_eo != -1); i++) { len = pmatch[i].rm_eo - pmatch[i].rm_so; args[i] = alloc_lbuf("regexp_match"); memset(args[i], 0, LBUF_SIZE); strncpy(args[i], str + pmatch[i].rm_so, len); args[i][len] = '\0'; /* strncpy() does not null-terminate */ } regfree(&re); return 1; } /** * Check attribute list for wild card matches and queue them. */ static int atr_match1(dbref thing, dbref parent, dbref player, char type, char *str, int check_exclude, int hash_insert) { dbref aowner; int match, attr, aflags, i; char buff[LBUF_SIZE], *s, *as; char *args[10]; ATTR *ap; memset(args, 0, sizeof(args)); /* * See if we can do it. Silently fail if we can't. */ if(!could_doit(player, parent, A_LUSE)) return -1; match = 0; for(attr = atr_head(parent, &as); attr; attr = atr_next(&as)) { ap = atr_num(attr); /* * Never check NOPROG attributes. */ if(!ap || (ap->flags & AF_NOPROG)) continue; /* * If we aren't the bottom level check if we saw this attr * * * * * before. Also exclude it if the attribute type is * * * PRIVATE. */ if(check_exclude && ((ap->flags & AF_PRIVATE) || nhashfind(ap->number, &mudstate.parent_htab))) { continue; } atr_get_str(buff, parent, attr, &aowner, &aflags); /* * Skip if private and on a parent */ if(check_exclude && (aflags & AF_PRIVATE)) { continue; } /* * If we aren't the top level remember this attr so we * * * * exclude * it from now on. */ if(hash_insert) nhashadd(ap->number, (int *) &attr, &mudstate.parent_htab); /* * Check for the leadin character after excluding the attrib * * * * * This lets non-command attribs on the child block * * * * commands * on the parent. */ if((buff[0] != type) || (aflags & AF_NOPROG)) continue; /* * decode it: search for first un escaped : */ for(s = buff + 1; *s && (*s != ':'); s++); if(!*s) continue; *s++ = 0; if(((aflags & AF_REGEXP) && regexp_match(buff + 1, str, args, 10)) || wild(buff + 1, str, args, 10)) { match = 1; wait_que(thing, player, 0, NOTHING, 0, s, args, 10, mudstate.global_regs); for(i = 0; i < 10; i++) { if(args[i]) free_lbuf(args[i]); } } } return (match); } int atr_match(dbref thing, dbref player, char type, char *str, int check_parents) { int match, lev, result, exclude, insert; dbref parent; /* * If thing is halted, don't check anything */ if(Halted(thing)) return 0; /* * If not checking parents, just check the thing */ match = 0; if(!check_parents) return atr_match1(thing, thing, player, type, str, 0, 0); /* * Check parents, ignoring halted objects */ exclude = 0; insert = 1; nhashflush(&mudstate.parent_htab, 0); ITER_PARENTS(thing, parent, lev) { if(!Good_obj(Parent(parent))) insert = 0; result = atr_match1(thing, parent, player, type, str, exclude, insert); if(result > 0) { match = 1; } else if(result < 0) { return match; } exclude = 1; } return match; } /** * Notifies the object #target of the message msg, and * optionally notify the contents, neighbors, and location also. */ int check_filter(dbref object, dbref player, int filter, const char *msg) { int aflags; dbref aowner; char *buf, *nbuf, *cp, *dp, *str; buf = atr_pget(object, filter, &aowner, &aflags); if(!*buf) { free_lbuf(buf); return (1); } nbuf = dp = alloc_lbuf("check_filter"); str = buf; exec(nbuf, &dp, 0, object, player, EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **) NULL, 0); *dp = '\0'; dp = nbuf; free_lbuf(buf); do { cp = parse_to(&dp, ',', EV_STRIP); if(quick_wild(cp, (char *) msg)) { free_lbuf(nbuf); return (0); } } while (dp != NULL); free_lbuf(nbuf); return (1); } static char *add_prefix(dbref object, dbref player, int prefix, const char *msg, const char *dflt) { int aflags; dbref aowner; char *buf, *nbuf, *cp, *bp, *str; buf = atr_pget(object, prefix, &aowner, &aflags); if(!*buf) { cp = buf; safe_str((char *) dflt, buf, &cp); } else { nbuf = bp = alloc_lbuf("add_prefix"); str = buf; exec(nbuf, &bp, 0, object, player, EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **) NULL, 0); *bp = '\0'; free_lbuf(buf); buf = nbuf; cp = &buf[strlen(buf)]; } if(cp != buf) safe_str((char *) " ", buf, &cp); safe_str((char *) msg, buf, &cp); *cp = '\0'; return (buf); } static char *dflt_from_msg(dbref sender, dbref sendloc) { char *tp, *tbuff; tp = tbuff = alloc_lbuf("notify_checked.fwdlist"); safe_str((char *) "From ", tbuff, &tp); if(Good_obj(sendloc)) safe_str(Name(sendloc), tbuff, &tp); else safe_str(Name(sender), tbuff, &tp); safe_chr(',', tbuff, &tp); *tp = '\0'; return tbuff; } char *colorize(dbref player, char *from); void notify_checked(dbref target, dbref sender, const char *msg, int key) { char *msg_ns, *mp, *tbuff, *tp, *buff, *colbuf = NULL; char *args[10]; dbref aowner, targetloc, recip, obj; int i, nargs, aflags, has_neighbors, pass_listen; int check_listens, pass_uselock, is_audible; FWDLIST *fp; /* * If speaker is invalid or message is empty, just exit */ if(!Good_obj(target) || !msg || !*msg) return; /* * Enforce a recursion limit */ mudstate.ntfy_nest_lev++; if(mudstate.ntfy_nest_lev >= mudconf.ntfy_nest_lim) { mudstate.ntfy_nest_lev--; return; } /* * If we want NOSPOOF output, generate it. It is only needed if * we are sending the message to the target object */ if(key & MSG_ME) { mp = msg_ns = alloc_lbuf("notify_checked"); if(Nospoof(target) && (target != sender) && (target != mudstate.curr_enactor) && (target != mudstate.curr_player && Good_obj(sender))) { /* * I'd really like to use tprintf here but I can't * because the caller may have. * notify(target, tprintf(...)) is quite common * in the code. */ tbuff = alloc_sbuf("notify_checked.nospoof"); safe_chr('[', msg_ns, &mp); safe_str(Name(sender), msg_ns, &mp); sprintf(tbuff, "(#%d)", sender); safe_str(tbuff, msg_ns, &mp); if(sender != Owner(sender)) { safe_chr('{', msg_ns, &mp); safe_str(Name(Owner(sender)), msg_ns, &mp); safe_chr('}', msg_ns, &mp); } if(sender != mudstate.curr_enactor) { sprintf(tbuff, "<-(#%d)", mudstate.curr_enactor); safe_str(tbuff, msg_ns, &mp); } safe_str((char *) "] ", msg_ns, &mp); free_sbuf(tbuff); } safe_str((char *) msg, msg_ns, &mp); *mp = '\0'; } else { msg_ns = NULL; } /* * msg contains the raw message, msg_ns contains the NOSPOOFed msg */ check_listens = Halted(target) ? 0 : 1; switch (Typeof(target)) { case TYPE_PLAYER: if(key & MSG_ME) { if(key & MSG_COLORIZE) colbuf = colorize(target, msg_ns); raw_notify(target, colbuf ? colbuf : msg_ns); } if(colbuf) free_lbuf(colbuf); if(!mudconf.player_listen) check_listens = 0; case TYPE_THING: case TYPE_ROOM: /* If we're in a pipe, objects can receive raw_notify * if they're not a player and connected (if we didn't * do this, they'd be notified twice! */ if(mudstate.inpipe && (!isPlayer(target) || (isPlayer(target) && !Connected(target)))) { raw_notify(target, msg_ns); } /* * Forward puppet message if it is for me */ has_neighbors = Has_location(target); targetloc = where_is(target); is_audible = Audible(target); if((key & MSG_ME) && Puppet(target) && (target != Owner(target)) && ((key & MSG_PUP_ALWAYS) || ((targetloc != Location(Owner(target))) && (targetloc != Owner(target))))) { tp = tbuff = alloc_lbuf("notify_checked.puppet"); safe_str(Name(target), tbuff, &tp); safe_str((char *) "> ", tbuff, &tp); if(key & MSG_COLORIZE) colbuf = colorize(Owner(target), msg_ns); safe_str(colbuf ? colbuf : msg_ns, tbuff, &tp); *tp = '\0'; raw_notify(Owner(target), tbuff); if(colbuf) free_lbuf(colbuf); free_lbuf(tbuff); } /* * Check for @Listen match if it will be useful */ pass_listen = 0; nargs = 0; if(check_listens && (key & (MSG_ME | MSG_INV_L)) && H_Listen(target)) { tp = atr_get(target, A_LISTEN, &aowner, &aflags); if(*tp && wild(tp, (char *) msg, args, 10)) { for(nargs = 10; nargs && (!args[nargs - 1] || !(*args[nargs - 1])); nargs--); pass_listen = 1; } free_lbuf(tp); } /* * If we matched the @listen or are monitoring, check the * * * USE lock */ if(sender < 0) sender = GOD; pass_uselock = 0; if((key & MSG_ME) && check_listens && (pass_listen || Monitor(target))) pass_uselock = could_doit(sender, target, A_LUSE); /* * Process AxHEAR if we pass LISTEN, USElock and it's for me */ if((key & MSG_ME) && pass_listen && pass_uselock) { if(sender != target) did_it(sender, target, 0, NULL, 0, NULL, A_AHEAR, args, nargs); else did_it(sender, target, 0, NULL, 0, NULL, A_AMHEAR, args, nargs); did_it(sender, target, 0, NULL, 0, NULL, A_AAHEAR, args, nargs); } /* * Get rid of match arguments. We don't need them anymore */ if(pass_listen) { for(i = 0; i < 10; i++) if(args[i] != NULL) free_lbuf(args[i]); } /* * Process ^-listens if for me, MONITOR, and we pass USElock */ /* * \todo Eventually come up with a cleaner method for making sure * the sender isn't the same as the target. */ if((key & MSG_ME) && (sender != target || Staff(target)) && pass_uselock && Monitor(target)) { (void) atr_match(target, sender, AMATCH_LISTEN, (char *) msg, 0); } /* * Deliver message to forwardlist members */ if((key & MSG_FWDLIST) && Audible(target) && check_filter(target, sender, A_FILTER, msg)) { tbuff = dflt_from_msg(sender, target); buff = add_prefix(target, sender, A_PREFIX, msg, tbuff); free_lbuf(tbuff); fp = fwdlist_get(target); if(fp) { for(i = 0; i < fp->count; i++) { recip = fp->data[i]; if(!Good_obj(recip) || (recip == target)) continue; notify_checked(recip, sender, buff, (MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE)); } } free_lbuf(buff); } /* * Deliver message through audible exits */ if(key & MSG_INV_EXITS) { DOLIST(obj, Exits(target)) { recip = Location(obj); if(Audible(obj) && ((recip != target) && check_filter(obj, sender, A_FILTER, msg))) { buff = add_prefix(obj, target, A_PREFIX, msg, "From a distance,"); notify_checked(recip, sender, buff, MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE); free_lbuf(buff); } } } /* * Deliver message through neighboring audible exits */ if(has_neighbors && ((key & MSG_NBR_EXITS) || ((key & MSG_NBR_EXITS_A) && is_audible))) { /* * If from inside, we have to add the prefix string * * * * * * of * the container. */ if(key & MSG_S_INSIDE) { tbuff = dflt_from_msg(sender, target); buff = add_prefix(target, sender, A_PREFIX, msg, tbuff); free_lbuf(tbuff); } else { buff = (char *) msg; } DOLIST(obj, Exits(Location(target))) { recip = Location(obj); if(Good_obj(recip) && Audible(obj) && (recip != targetloc) && (recip != target) && check_filter(obj, sender, A_FILTER, msg)) { tbuff = add_prefix(obj, target, A_PREFIX, buff, "From a distance,"); notify_checked(recip, sender, tbuff, MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE); free_lbuf(tbuff); } } if(key & MSG_S_INSIDE) { free_lbuf(buff); } } /* * Deliver message to contents */ if(((key & MSG_INV) || ((key & MSG_INV_L) && pass_listen)) && (check_filter(target, sender, A_INFILTER, msg))) { /* * Don't prefix the message if we were given the * * * * * MSG_NOPREFIX key. */ if(key & MSG_S_OUTSIDE) { buff = add_prefix(target, sender, A_INPREFIX, msg, ""); } else { buff = (char *) msg; } DOLIST(obj, Contents(target)) { if(Slave(obj) && (key & MSG_NO_SLAVE)) continue; if(obj != target) { notify_checked(obj, sender, buff, MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE ); } } if(key & MSG_S_OUTSIDE) free_lbuf(buff); } /* * Deliver message to neighbors */ if(has_neighbors && ((key & MSG_NBR) || ((key & MSG_NBR_A) && is_audible && check_filter(target, sender, A_FILTER, msg)))) { if(key & MSG_S_INSIDE) { tbuff = dflt_from_msg(sender, target); buff = add_prefix(target, sender, A_PREFIX, msg, ""); free_lbuf(tbuff); } else { buff = (char *) msg; } DOLIST(obj, Contents(targetloc)) { if((obj != target) && (obj != targetloc)) { notify_checked(obj, sender, buff, MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | (key & MSG_COLORIZE)); } } if(key & MSG_S_INSIDE) { free_lbuf(buff); } } /* * Deliver message to container */ if(has_neighbors && ((key & MSG_LOC) || ((key & MSG_LOC_A) && is_audible && check_filter(target, sender, A_FILTER, msg)))) { if(key & MSG_S_INSIDE) { tbuff = dflt_from_msg(sender, target); buff = add_prefix(target, sender, A_PREFIX, msg, tbuff); free_lbuf(tbuff); } else { buff = (char *) msg; } notify_checked(targetloc, sender, buff, MSG_ME | MSG_F_UP | MSG_S_INSIDE); if(key & MSG_S_INSIDE) { free_lbuf(buff); } } } if(msg_ns) free_lbuf(msg_ns); mudstate.ntfy_nest_lev--; } void notify_except(dbref loc, dbref player, dbref exception, const char *msg) { dbref first; if(loc != exception) notify_checked(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A)); DOLIST(first, Contents(loc)) { if(exception == NOSLAVE) if(Slave(first)) continue; if(first != exception) notify_checked(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | (exception == NOSLAVE ? MSG_NO_SLAVE : 0))); } } void notify_except2(dbref loc, dbref player, dbref exc1, dbref exc2, const char *msg) { dbref first; if((loc != exc1) && (loc != exc2)) notify_checked(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A)); DOLIST(first, Contents(loc)) { if(first != exc1 && first != exc2) { notify_checked(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE)); } } } void do_shutdown(dbref player, dbref cause, int key, char *message) { FILE *fs; ResetSpecialObjects(); if(player != NOTHING) { raw_broadcast(0, "Game: Shutdown by %s", Name(Owner(player))); STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN") { log_text((char *) "Shutdown by "); log_name(player); ENDLOG; } } else { raw_broadcast(0, "Game: Fatal Error: %s", message); STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN") { log_text((char *) "Fatal error: "); log_text(message); ENDLOG; } } STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN") { log_text((char *) "Shutdown status: "); log_text(message); ENDLOG; } fs = fopen(mudconf.status_file, "w"); fprintf(fs, "%s\n", message); fclose(fs); /* * Do we perform a normal or an emergency shutdown? Normal shutdown * * * * * is handled by exiting the main loop in shovechars, * emergency * * * * shutdown is done here. */ if(key & SHUTDN_PANIC) { /* * Close down the network interface */ emergency_shutdown(); /* * Close the attribute text db and dump the header db */ pcache_sync(); STARTLOG(LOG_ALWAYS, "DMP", "PANIC") { log_text((char *) "Panic dump: "); log_text(mudconf.crashdb); ENDLOG; } dump_database_internal(DUMP_CRASHED); STARTLOG(LOG_ALWAYS, "DMP", "DONE") { log_text((char *) "Panic dump complete: "); log_text(mudconf.crashdb); ENDLOG; } } /* * Set up for normal shutdown */ mudstate.shutdown_flag = 1; event_loopexit(NULL); return; } void dump_database_internal(int dump_type) { char tmpfile[256], outfn[256], prevfile[256]; FILE *f; #ifdef USE_PYTHON runPythonHook("save"); #endif if(dump_type == DUMP_CRASHED) { unlink(mudconf.crashdb); f = fopen(mudconf.crashdb, "w"); if(f != NULL) { db_write(f, F_MUX, UNLOAD_VERSION | UNLOAD_OUTFLAGS); fclose(f); } else { log_perror("DMP", "FAIL", "Opening crash file", mudconf.crashdb); } if(mudconf.have_mailer) if((f = fopen(mudconf.mail_db, "w"))) { dump_mail(f); fclose(f); } if(mudconf.have_comsys || mudconf.have_macros) save_comsys_and_macros(mudconf.commac_db); SaveSpecialObjects(DUMP_CRASHED); return; } if(dump_type == DUMP_RESTART) { f = fopen(mudconf.indb, "w"); if(f != NULL) { /* Write a flatfile */ db_write(f, F_MUX, UNLOAD_VERSION | UNLOAD_OUTFLAGS); fclose(f); } else { log_perror("DMP", "FAIL", "Opening restart file", mudconf.indb); } if(mudconf.have_mailer) if((f = fopen(mudconf.mail_db, "w"))) { dump_mail(f); fclose(f); } if(mudconf.have_comsys || mudconf.have_macros) save_comsys_and_macros(mudconf.commac_db); if(mudconf.have_specials) SaveSpecialObjects(DUMP_RESTART); return; } if(dump_type == DUMP_KILLED) { sprintf(tmpfile, "%s.KILLED", mudconf.indb); f = fopen(tmpfile, "w"); if(f != NULL) { /* Write a flatfile */ db_write(f, F_MUX, UNLOAD_VERSION | UNLOAD_OUTFLAGS); fclose(f); } else { log_perror("DMP", "FAIL", "Opening killed file", mudconf.indb); } if(mudconf.have_mailer) if((f = fopen(mudconf.mail_db, "w"))) { dump_mail(f); fclose(f); } if(mudconf.have_comsys || mudconf.have_macros) save_comsys_and_macros(mudconf.commac_db); if(mudconf.have_specials) SaveSpecialObjects(DUMP_KILLED); return; } sprintf(prevfile, "%s.prev", mudconf.outdb); sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch - 1); unlink(tmpfile); /* * nuke our predecessor */ sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch); if(mudconf.compress_db) { sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch - 1); unlink(tmpfile); sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch); StringCopy(outfn, mudconf.outdb); strcat(outfn, ".gz"); f = popen(tprintf("%s > %s", mudconf.compress, tmpfile), "w"); if(f) { db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS); pclose(f); rename(mudconf.outdb, prevfile); if(rename(tmpfile, outfn) < 0) log_perror("SAV", "FAIL", "Renaming output file to DB file", tmpfile); } else { log_perror("SAV", "FAIL", "Opening", tmpfile); } } else { f = fopen(tmpfile, "w"); if(f) { db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS); fclose(f); rename(mudconf.outdb, prevfile); if(rename(tmpfile, mudconf.outdb) < 0) log_perror("SAV", "FAIL", "Renaming output file to DB file", tmpfile); } else { log_perror("SAV", "FAIL", "Opening", tmpfile); } } if(mudconf.have_mailer) if((f = fopen(mudconf.mail_db, "w"))) { dump_mail(f); fclose(f); } if(mudconf.have_comsys || mudconf.have_macros) save_comsys_and_macros(mudconf.commac_db); if(mudconf.have_specials) SaveSpecialObjects(DUMP_NORMAL); } void dump_database(void) { char *buff; mudstate.epoch++; mudstate.dumping = 1; buff = alloc_mbuf("dump_database"); sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); STARTLOG(LOG_DBSAVES, "DMP", "DUMP") { log_text((char *) "Dumping: "); log_text(buff); ENDLOG; } pcache_sync(); dump_database_internal(DUMP_NORMAL); STARTLOG(LOG_DBSAVES, "DMP", "DONE") { log_text((char *) "Dump complete: "); log_text(buff); ENDLOG; } free_mbuf(buff); mudstate.dumping = 0; } void fork_and_dump(int key) { char *buff; if(*mudconf.dump_msg) raw_broadcast(0, "%s", mudconf.dump_msg); check_mail_expiration(); mudstate.epoch++; mudstate.dumping = 1; buff = alloc_mbuf("fork_and_dump"); sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); log_error(LOG_DBSAVES, "DMP", "CHKPT", "Saving database: %s", buff); pcache_sync(); if(!key || (key & DUMP_STRUCT)) { if(!fork()) { dprintk("child database write process starting."); unbind_signals(); dump_database_internal(DUMP_NORMAL); dprintk("child database write process finished."); exit(0); } } mudstate.dumping = 0; if(*mudconf.postdump_msg) raw_broadcast(0, "%s", mudconf.postdump_msg); } static int load_game(void) { FILE *f; int compressed; char infile[256]; struct stat statbuf; int db_format, db_version, db_flags; f = NULL; compressed = 0; if(mudconf.compress_db) { StringCopy(infile, mudconf.indb); strcat(infile, ".gz"); if(stat(infile, &statbuf) == 0) { if((f = popen(tprintf(" %s < %s", mudconf.uncompress, infile), "r")) != NULL) compressed = 1; } } if(compressed == 0) { StringCopy(infile, mudconf.indb); if((f = fopen(mudconf.indb, "r")) == NULL) return -1; } /* * ok, read it in */ STARTLOG(LOG_STARTUP, "INI", "LOAD") { log_text((char *) "Loading: "); log_text(infile); ENDLOG; }; if(db_read(f, &db_format, &db_version, &db_flags) < 0) { STARTLOG(LOG_ALWAYS, "INI", "FATAL") { log_text((char *) "Error loading "); log_text(infile); ENDLOG; } if(compressed) pclose(f); else fclose(f); return -1; } if(compressed) pclose(f); else fclose(f); if(mudconf.have_comsys || mudconf.have_macros) load_comsys_and_macros(mudconf.commac_db); /* Load the mecha stuff.. */ if(mudconf.have_specials) LoadSpecialObjects(); if(mudconf.have_mailer) if((f = fopen(mudconf.mail_db, "r"))) { load_mail(f); fclose(f); } STARTLOG(LOG_STARTUP, "INI", "LOAD") { log_text((char *) "Load complete."); ENDLOG; } /* * everything ok */ return (0); } /** * match a list of things, using the no_command flag */ int list_check(dbref thing, dbref player, char type, char *str, int check_parent) { int match, limit; match = 0; limit = mudstate.db_top; while (thing != NOTHING) { if((thing != player) && (!(No_Command(thing)))) { if(atr_match(thing, player, type, str, check_parent) > 0) match = 1; } thing = Next(thing); if(--limit < 0) return match; } return match; } int Hearer(dbref thing) { char *as, *buff, *s; dbref aowner; int attr, aflags; ATTR *ap; if(mudstate.inpipe && (thing == mudstate.poutobj)) return 1; if(Connected(thing) || Puppet(thing)) return 1; if(Monitor(thing)) buff = alloc_lbuf("Hearer"); else buff = NULL; for(attr = atr_head(thing, &as); attr; attr = atr_next(&as)) { if(attr == A_LISTEN) { if(buff) free_lbuf(buff); return 1; } if(Monitor(thing)) { ap = atr_num(attr); if(!ap || (ap->flags & AF_NOPROG)) continue; atr_get_str(buff, thing, attr, &aowner, &aflags); /* * Make sure we can execute it */ if((buff[0] != AMATCH_LISTEN) || (aflags & AF_NOPROG)) continue; /* * Make sure there's a : in it */ for(s = buff + 1; *s && (*s != ':'); s++); if(s) { free_lbuf(buff); return 1; } } } if(buff) free_lbuf(buff); return 0; } void do_readcache(dbref player, dbref cause, int key) { helpindex_load(player); fcache_load(player); } static void process_preload(void) { dbref thing, parent, aowner; int aflags, lev, i; char *tstr; FWDLIST *fp; fp = (FWDLIST *) alloc_lbuf("process_preload.fwdlist"); tstr = alloc_lbuf("process_preload.string"); i = 0; DO_WHOLE_DB(thing) { /* * Ignore GOING objects */ if(Going(thing)) continue; do_top(10); /* * Look for a STARTUP attribute in parents */ ITER_PARENTS(thing, parent, lev) { if(Flags(thing) & HAS_STARTUP) { did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_STARTUP, (char **) NULL, 0); /* * Process queue entries as we add them */ do_second(); do_top(10); break; } } /* * Look for a FORWARDLIST attribute */ if(H_Fwdlist(thing)) { (void) atr_get_str(tstr, thing, A_FORWARDLIST, &aowner, &aflags); if(*tstr) { fwdlist_load(fp, GOD, tstr); if(fp->count > 0) fwdlist_set(thing, fp); } } } free_lbuf(fp); free_lbuf(tstr); } int main(int argc, char *argv[]) { int mindb; if((argc > 2) && (!strcmp(argv[1], "-s") && (argc > 3))) { fprintf(stderr, "Usage: %s [-s] [config-file]\n", argv[0]); exit(1); } event_init(); #if defined(HAVE_IEEEFP_H) && defined(HAVE_SYS_UCONTEXT_H) /* * Inhibit IEEE fp exception on overflow */ fpsetmask(fpgetmask() & ~FP_X_OFL); #endif mindb = 0; /* Are we creating a new db? */ corrupt = 0; /* Database isn't corrupted. */ memset(&mudstate, 0, sizeof(mudstate)); time(&mudstate.start_time); time(&mudstate.restart_time); mudstate.executable_path = strdup(argv[0]); mudstate.db_top = -1; tcache_init(); pcache_init(); cf_init(); init_rlimit(); init_cmdtab(); init_mactab(); init_chantab(); init_logout_cmdtab(); init_flagtab(); init_powertab(); init_functab(); init_attrtab(); init_version(); #ifdef HUDINFO_SUPPORT init_hudinfo(); #endif hashinit(&mudstate.player_htab, 250 * HASH_FACTOR); nhashinit(&mudstate.mail_htab, 50 * HASH_FACTOR); nhashinit(&mudstate.fwdlist_htab, 25 * HASH_FACTOR); nhashinit(&mudstate.parent_htab, 5 * HASH_FACTOR); mudstate.desctree = rb_init(desc_cmp, NULL); vattr_init(); if(argc > 1 && !strcmp(argv[1], "-s")) { mindb = 1; if(argc == 3) cf_read(argv[2]); else cf_read((char *) CONF_FILE); } else if(argc == 2) { cf_read(argv[1]); } else { cf_read((char *) CONF_FILE); } fcache_init(); helpindex_init(); db_free(); mudstate.record_players = 0; if(mindb) db_make_minimal(); else if(load_game() < 0) { STARTLOG(LOG_ALWAYS, "INI", "LOAD") { log_text((char *) "Couldn't load: "); log_text(mudconf.indb); ENDLOG; } exit(2); } #ifdef USE_PYTHON MUXPy_Init(); runPythonHook("load"); #endif /* initialize random.. */ srandom(getpid()); /* set singnals.. */ bind_signals(); /* * Do a consistency check and set up the freelist */ do_dbck(NOTHING, NOTHING, 0); /* * Reset all the hash stats */ hashreset(&mudstate.command_htab); hashreset(&mudstate.macro_htab); hashreset(&mudstate.channel_htab); nhashreset(&mudstate.mail_htab); hashreset(&mudstate.logout_cmd_htab); hashreset(&mudstate.func_htab); hashreset(&mudstate.flags_htab); hashreset(&mudstate.attr_name_htab); hashreset(&mudstate.player_htab); nhashreset(&mudstate.fwdlist_htab); hashreset(&mudstate.news_htab); hashreset(&mudstate.help_htab); hashreset(&mudstate.wizhelp_htab); hashreset(&mudstate.plushelp_htab); hashreset(&mudstate.wiznews_htab); for(mindb = 0; mindb < MAX_GLOBAL_REGS; mindb++) { mudstate.global_regs[mindb] = alloc_lbuf("main.global_reg"); memset(mudstate.global_regs[mindb], 0, LBUF_SIZE); } mudstate.now = time(NULL); process_preload(); dnschild_init(); if(!load_restart_db_xdr()) { load_restart_db(); } #ifdef SQL_SUPPORT sqlchild_init(); #endif #ifdef ARBITRARY_LOGFILES logcache_init(); #endif #ifdef MCHECK mtrace(); #endif /* * go do it */ mudstate.now = time(NULL); init_timer(); shovechars(mudconf.port); #ifdef MCHECK muntrace(); #endif close_sockets(0, (char *) "Going down - Bye"); dump_database(); #ifdef ARBITRARY_LOGFILES logcache_destruct(); #endif #ifdef SQL_SUPPORT sqlchild_destruct(); #endif exit(0); } static void init_rlimit(void) { #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit *rlp; rlp = (struct rlimit *) alloc_lbuf("rlimit"); if(getrlimit(RLIMIT_NOFILE, rlp)) { log_perror("RLM", "FAIL", NULL, "getrlimit()"); free_lbuf(rlp); return; } rlp->rlim_cur = rlp->rlim_max; if(setrlimit(RLIMIT_NOFILE, rlp)) log_perror("RLM", "FAIL", NULL, "setrlimit()"); free_lbuf(rlp); #endif /* * HAVE_SETRLIMIT */ }