// game.cpp // // $Id: game.cpp,v 1.20 2000/09/20 19:22:31 sdennis Exp $ // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include <sys/stat.h> #include <signal.h> #include "mudconf.h" #include "file_c.h" #include "db.h" #include "interface.h" #include "match.h" #include "flags.h" #include "powers.h" #include "attrs.h" #include "alloc.h" #include "slave.h" #include "comsys.h" #include "vattr.h" #ifdef RADIX_COMPRESSION #include "radix.h" #endif extern void NDECL(init_attrtab); extern void NDECL(init_cmdtab); extern void NDECL(cf_init); extern void NDECL(pcache_init); extern int cf_read(void); extern void NDECL(init_functab); extern void FDECL(close_sockets, (int emergency, char *message)); extern void NDECL(init_version); extern void NDECL(init_logout_cmdtab); extern void FDECL(raw_notify, (dbref, const char *)); extern void FDECL(do_dbck, (dbref, dbref, int)); extern void boot_slave(dbref, dbref, int); void FDECL(fork_and_dump, (int)); void NDECL(dump_database); void NDECL(pcache_sync); #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) static void NDECL(init_rlimit); #endif int reserved; #ifdef WIN32 extern CRITICAL_SECTION csDescriptorList; // for thread synchronisation #else // WIN32 #ifdef CONCENTRATE int conc_pid = 0; #endif #endif // WIN32 #ifdef MEMORY_BASED int corrupt = 0; #endif // used to allocate storage for temporary stuff, cleared before command // execution // void do_dump(dbref player, dbref cause, int key) { #if !defined(VMS) && !defined(WIN32) if (mudstate.dumping) { notify(player, "Dumping in progress. Try again later."); return; } #endif // !VMS && !WIN32 notify(player, "Dumping..."); fork_and_dump(key); } /* * print out stuff into error file */ void NDECL(report) { 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 } } /* ---------------------------------------------------------------------- * regexp_match: Load a regular expression match and insert it into * registers. */ int regexp_match(char *pattern, char *str, char *args[], int nargs) { regexp *re; int got_match; 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 ((re = regcomp(pattern)) == NULL) { /* * 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); if (!got_match) { MEMFREE(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) && (re->startp[i]) && (re->endp[i]); i++) { len = re->endp[i] - re->startp[i]; args[i] = alloc_lbuf("regexp_match"); strncpy(args[i], re->startp[i], len); args[i][len] = '\0'; /* strncpy() does not null-terminate */ } MEMFREE(re); return 1; } /* * ---------------------------------------------------------------------- * * atr_match: 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; /* * See if we can do it. Silently fail if we can't. */ if (!could_doit(player, parent, A_LUSE)) return -1; match = 0; atr_push(); 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) || hashfindLEN(&(ap->number), sizeof(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) { hashaddLEN(&(ap->number), sizeof(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]); } } } atr_pop(); 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; hashflush(&mudstate.parent_htab); 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; } /* * --------------------------------------------------------------------------- * * notify_check: 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; char *preserve[MAX_GLOBAL_REGS]; int preserve_len[MAX_GLOBAL_REGS]; buf = atr_pget(object, filter, &aowner, &aflags); if (!*buf) { free_lbuf(buf); return (1); } save_global_regs("check_filter_save", preserve, preserve_len); nbuf = dp = alloc_lbuf("check_filter"); str = buf; TinyExec(nbuf, &dp, 0, object, player, EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **)NULL, 0); *dp = '\0'; dp = nbuf; free_lbuf(buf); restore_global_regs("check_filter_restore", preserve, preserve_len); do { cp = parse_to(&dp, ',', EV_STRIP_CURLY); 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; char *preserve[MAX_GLOBAL_REGS]; int preserve_len[MAX_GLOBAL_REGS]; buf = atr_pget(object, prefix, &aowner, &aflags); if (!*buf) { cp = buf; safe_str((char *)dflt, buf, &cp); } else { save_global_regs("add_prefix_save", preserve, preserve_len); nbuf = bp = alloc_lbuf("add_prefix"); str = buf; TinyExec(nbuf, &bp, 0, object, player, EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **)NULL, 0); *bp = '\0'; free_lbuf(buf); restore_global_regs("add_prefix_restore", preserve, preserve_len); 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_check.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; } /* Do HTML escaping, converting < to <, etc. 'dest' needs to be * allocated & freed by the caller. * * If you're using this to append to a string, you can pass in the * safe_{str|chr} (char **) so we can just do the append directly, * saving you an alloc_lbuf()...free_lbuf(). If you want us to append * from the start of 'dest', just pass in a 0 for 'destp'. * * Returns 0 if the copy succeeded, 1 if it failed. */ int html_escape(const char *src, char *dest, char **destp) { const char *msg_orig; char *temp; int ret = 0; if (destp == 0) { temp = dest; destp = &temp; } for (msg_orig = src; msg_orig && *msg_orig && !ret; msg_orig++) { char *p = *destp; switch (*msg_orig) { case '<': safe_str("<", dest, destp); break; case '>': safe_str(">", dest, destp); break; case '&': safe_str("&", dest, destp); break; case '\"': safe_str(""", dest, destp); break; default: safe_chr(*msg_orig, dest, destp); break; } // For <>&\, this may cause an extra loop around before it figures out that we are // out of buffer, but no harm is done in this, and the common case is a single character. // if (p == *destp) { ret = 1; } } **destp = 0; return ret; } void notify_check(dbref target, dbref sender, const char *msg, int key) { char *msg_ns, *mp, *tbuff, *tp, *buff; 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; #ifdef WOD_REALMS if ((key & MSG_OOC) == 0) { if ((key & MSG_SAYPOSE) != 0) { if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(target, sender, ACTION_IS_TALKING)) { return; } } else { if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(target, sender, ACTION_IS_MOVING)) { return; } } } #endif /* * 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_check"); if (Nospoof(target) && (target != sender) && (target != mudstate.curr_enactor) && (target != mudstate.curr_player)) { /* * 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_check.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_HTML) { raw_notify_html(target, msg_ns); } else { if (Html(target)) { char *msg_ns_escaped; msg_ns_escaped = alloc_lbuf("notify_check_escape"); html_escape(msg_ns, msg_ns_escaped, 0); raw_notify(target, msg_ns_escaped); free_lbuf(msg_ns_escaped); } else { raw_notify(target, msg_ns); } } } if (!mudconf.player_listen) { check_listens = 0; } // FALLTHROUGH case TYPE_THING: case TYPE_ROOM: // If we're in a pipe, objects can receive raw_notify if // they're not a player. (players were already notified // above. // if (mudstate.inpipe && !isPlayer(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_check.puppet"); safe_str(Name(target), tbuff, &tp); safe_str((char *)"> ", tbuff, &tp); safe_str(msg_ns, tbuff, &tp); *tp = '\0'; raw_notify(Owner(target), tbuff); 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--) { // Nothing // ; } pass_listen = 1; } free_lbuf(tp); } // If we matched the @listen or are monitoring, check the // USE lock. // 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. // if ( (key & MSG_ME) && pass_uselock && (sender != target) && 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_check(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_check(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_check(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 (obj != target) { notify_check(obj, sender, buff, MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | key & MSG_HTML); } } 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_check(obj, sender, buff, MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE); } } 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_check(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, int key) { dbref first; if (loc != exception) { notify_check(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A | key)); } DOLIST(first, Contents(loc)) { if (first != exception) { notify_check(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | key)); } } } void notify_except2(dbref loc, dbref player, dbref exc1, dbref exc2, const char *msg) { dbref first; if ((loc != exc1) && (loc != exc2)) { notify_check(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_check(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE)); } } } /* ---------------------------------------------------------------------- * Reporting of CPU information. */ static void report_timecheck ( dbref player, int yes_screen, int yes_log, int yes_clear ) { int thing, obj_counted; CLinearTimeDelta ltdPeriod, ltdTotal; CLinearTimeAbsolute ltaNow; ltaNow.GetUTC(); ltdPeriod = ltaNow - mudstate.cpu_count_from; if (! (yes_log && (LOG_TIMEUSE & mudconf.log_options) != 0)) { yes_log = 0; STARTLOG(LOG_ALWAYS, "WIZ", "TIMECHECK"); log_name(player); log_text((char *) " checks object time use over "); log_number(ltdPeriod.ReturnSeconds()); log_text((char *) " seconds\n"); ENDLOG; } else { start_log("OBJ", "CPU"); log_name(player); log_text((char *) " checks object time use over "); log_number(ltdPeriod.ReturnSeconds()); log_text((char *) " seconds\n"); } obj_counted = 0; ltdTotal.Set100ns(0); // Step through the db. Care only about the ones that are nonzero. // DO_WHOLE_DB(thing) { CLinearTimeDelta <d = db[thing].cpu_time_used; if (ltd.Return100ns()) { ltdTotal += ltd; long used_msecs = ltd.ReturnMilliseconds(); obj_counted++; if (yes_log) { Log.printf("#%d\t%ld\n", thing, used_msecs); } if (yes_screen) { raw_notify(player, tprintf("#%d\t%ld", thing, used_msecs)); } if (yes_clear) { ltd.Set100ns(0); } } } if (yes_screen) { raw_notify(player, tprintf("Counted %d objects using %ld msecs over %d seconds.", obj_counted, ltdTotal.ReturnMilliseconds(), ltdPeriod.ReturnSeconds())); } if (yes_log) { Log.printf("Counted %d objects using %ld msecs over %d seconds.", obj_counted, ltdTotal.ReturnMilliseconds(), ltdPeriod.ReturnSeconds()); end_log(); } if (yes_clear) { mudstate.cpu_count_from = ltaNow; } } void do_timecheck(dbref player, dbref cause, int key) { int yes_screen, yes_log, yes_clear; yes_screen = yes_log = yes_clear = 0; if (key == 0) { // No switches, default to printing to screen and clearing counters. // yes_screen = 1; yes_clear = 1; } else { if (key & TIMECHK_RESET) yes_clear = 1; if (key & TIMECHK_SCREEN) yes_screen = 1; if (key & TIMECHK_LOG) yes_log = 1; } report_timecheck(player, yes_screen, yes_log, yes_clear); } void do_shutdown(dbref player, dbref cause, int key, char *message) { int fd; 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 fd = open(mudconf.status_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600); if (fd != -1) { (void)write(fd, message, strlen(message)); (void)write(fd, (char *)"\n", 1); DebugTotalFiles++; if (close(fd) == 0) { DebugTotalFiles--; } } // 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(); SYNC; CLOSE; STARTLOG(LOG_ALWAYS, "DMP", "PANIC"); log_text((char *)"Panic dump: "); log_text(mudconf.crashdb); ENDLOG; dump_database_internal(DUMP_I_PANIC); 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; } #ifndef STANDALONE // There are several types of dumps: // // Type 0 - Normal mudstate.dumping controlled // Type 1 - Panic uncontrolled but only one of these happening at a time. // Type 2 - Restart mudstate.dumping controlled. // Type 3 - FLAT mudstate.dumping controlled. // Type 4 - signal uncontrolled and if we fault twice, the game ends -- // see check_panicking. // // When changing this function and to keep forking dumps safe, keep in mind // that the following combinations can be occuring at the same time. Don't // touch each other's files. // // Type 0 and 2 are allowed to touch each other's files. Type 1 and 4 should not // touch files used in Type 0 or Type 2. // typedef struct { char **ppszOutputBase; char szOutputSuffix[14]; BOOL bUseTemporary; int fType; char *pszErrorMessage; } DUMP_PROCEDURE; DUMP_PROCEDURE DumpProcedures[NUM_DUMP_TYPES] = { { 0, "" , FALSE, 0, "" }, // 0 -- Handled specially. { &mudconf.crashdb, "" , FALSE, UNLOAD_VERSION | UNLOAD_OUTFLAGS, "Opening crash file" }, // 1 { &mudconf.indb, "" , TRUE, OUTPUT_VERSION | OUTPUT_FLAGS, "Opening outputfile" }, // 2 { &mudconf.outdb, ".FLAT" , FALSE, UNLOAD_VERSION | UNLOAD_OUTFLAGS, "Opening flatfile" }, // 3 { &mudconf.indb, ".KILLED", FALSE, UNLOAD_VERSION | UNLOAD_OUTFLAGS, "Opening killed file"} // 4 }; void dump_database_internal(int dump_type) { char tmpfile[SIZEOF_PATHNAME+32]; char outfn[SIZEOF_PATHNAME+32]; char prevfile[SIZEOF_PATHNAME+32]; FILE *f; if (dump_type < 0 || dump_type >= NUM_DUMP_TYPES) { return; } BOOL bPotentialConflicts = FALSE; #if !defined(VMS) && !defined(WIN32) // If we are already dumping for some reason, and suddenly get a type 1 or // type 4 dump, basically don't touch mail and comsys files. The other // dump will take care of them as well as can be expected for now, and if // we try to, we'll just step on them. // if ( mudstate.dumping && ( dump_type == DUMP_I_PANIC || dump_type == DUMP_I_SIGNAL)) { bPotentialConflicts = TRUE; } #endif // !VMS && !WIN32 if (dump_type > 0) { DUMP_PROCEDURE *dp = &DumpProcedures[dump_type]; sprintf(outfn, "%s%s", *(dp->ppszOutputBase), dp->szOutputSuffix); if (dp->bUseTemporary) { #ifdef VMS sprintf(tmpfile, "%s.-%d-", outfn, mudstate.epoch); #else sprintf(tmpfile, "%s.#%d#", outfn, mudstate.epoch); #endif RemoveFile(tmpfile); f = fopen(tmpfile, "wb"); } else { RemoveFile(outfn); f = fopen(outfn, "wb"); } if (f) { DebugTotalFiles++; setvbuf(f, NULL, _IOFBF, 16384); db_write(f, F_MUX, dp->fType); if (fclose(f) == 0) { DebugTotalFiles--; } if (dp->bUseTemporary) { ReplaceFile(tmpfile, outfn); } } else { log_perror("DMP", "FAIL", dp->pszErrorMessage, outfn); } if (!bPotentialConflicts) { if (mudconf.have_mailer) { f = fopen(mudconf.mail_db, "wb"); if (f) { DebugTotalFiles++; dump_mail(f); if (fclose(f) == 0) { DebugTotalFiles--; } } } if (mudconf.have_comsys || mudconf.have_macros) { save_comsys(mudconf.comsys_db); } } return; } // Nuke our predecessor // sprintf(prevfile, "%s.prev", mudconf.outdb); sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch - 1); RemoveFile(tmpfile); sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch); if (mudconf.compress_db) { sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch - 1); RemoveFile(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), "wb"); if (f) { DebugTotalFiles++; setvbuf(f, NULL, _IOFBF, 16384); db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS); if (pclose(f) != -1) { DebugTotalFiles--; } ReplaceFile(mudconf.outdb, prevfile); if (ReplaceFile(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, "wb"); if (f) { DebugTotalFiles++; setvbuf(f, NULL, _IOFBF, 16384); db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS); if (fclose(f) == 0) { DebugTotalFiles--; } ReplaceFile(mudconf.outdb, prevfile); if (ReplaceFile(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) { f = fopen(mudconf.mail_db, "wb"); if (f) { DebugTotalFiles++; dump_mail(f); if (fclose(f) == 0) { DebugTotalFiles--; } } } if (mudconf.have_comsys || mudconf.have_macros) { save_comsys(mudconf.comsys_db); } } #endif void NDECL(dump_database) { char *buff; mudstate.epoch++; #if !defined(VMS) && !defined(WIN32) if (mudstate.dumping) { STARTLOG(LOG_DBSAVES, "DMP", "DUMP"); log_text("Waiting on previously-forked child before dumping... "); ENDLOG; while (mudstate.dumping) { // We have a forked dump in progress, so we will wait until the // child exits. // sleep(1); } } mudstate.dumping = 1; #endif buff = alloc_mbuf("dump_database"); #ifdef VMS sprintf(buff, "%s.-%d-", mudconf.outdb, mudstate.epoch); #else sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); #endif // VMS STARTLOG(LOG_DBSAVES, "DMP", "DUMP"); log_text((char *)"Dumping: "); log_text(buff); ENDLOG; pcache_sync(); SYNC; dump_database_internal(DUMP_I_NORMAL); STARTLOG(LOG_DBSAVES, "DMP", "DONE") log_text((char *)"Dump complete: "); log_text(buff); ENDLOG; free_mbuf(buff); #if !defined(VMS) && !defined(WIN32) // This doesn't matter. We are about the stop the game. However, // leave it in. // mudstate.dumping = 0; #endif } void fork_and_dump(int key) { char *buff; #if !defined(VMS) && !defined(WIN32) // fork_and_dump is never called with mudstate.dumping == 1, but we'll // ensure assertion now. // if (mudstate.dumping) return; mudstate.dumping = 1; #endif // If no options were given, then it means DUMP_TEXT+DUMP_STRUCT. // if (key == 0) { key = DUMP_TEXT+DUMP_STRUCT; } if (*mudconf.dump_msg) { raw_broadcast(0, "%s", mudconf.dump_msg); } check_mail_expiration(); buff = alloc_lbuf("fork_and_dump"); if (key & (DUMP_TEXT|DUMP_STRUCT)) { STARTLOG(LOG_DBSAVES, "DMP", "CHKPT"); if (key & DUMP_TEXT) { log_text("SYNCing"); if (key & DUMP_STRUCT) { log_text(" and "); } } if (key & DUMP_STRUCT) { mudstate.epoch++; #ifndef VMS sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); #else sprintf(buff, "%s.-%d-", mudconf.outdb, mudstate.epoch); #endif // VMS log_text("Checkpointing: "); log_text(buff); } ENDLOG; } if (key & DUMP_FLATFILE) { STARTLOG(LOG_DBSAVES, "DMP", "FLAT"); log_text("Creating flatfile: "); sprintf(buff, "%s.FLAT", mudconf.outdb); log_text(buff); ENDLOG; } free_lbuf(buff); #ifndef MEMORY_BASED // Save cached modified attribute list // al_store(); #endif // MEMORY_BASED if (key & DUMP_TEXT) { pcache_sync(); } SYNC; int child = 0; BOOL bChildExists = FALSE; if (key & (DUMP_STRUCT|DUMP_FLATFILE)) { #if !defined(VMS) && !defined(WIN32) if (mudconf.fork_dump) { if (mudconf.fork_vfork) { child = vfork(); } else { child = fork(); } } #endif // VMS if (child == 0) { if (key & DUMP_STRUCT) { dump_database_internal(DUMP_I_NORMAL); } if (key & DUMP_FLATFILE) { dump_database_internal(DUMP_I_FLAT); } #if !defined(VMS) && !defined(WIN32) if (mudconf.fork_dump) { _exit(0); } #endif // VMS } else if (child < 0) { log_perror("DMP", "FORK", NULL, "fork()"); } else { bChildExists = TRUE; } } #if !defined(VMS) && !defined(WIN32) if (!bChildExists) { // We have the ability to fork children, but we are not configured to // use it; or, we tried to fork a child and failed; or, we didn't // need to dump the structure or a flatfile. // mudstate.dumping = 0; } #endif if (*mudconf.postdump_msg) { raw_broadcast(0, "%s", mudconf.postdump_msg); } } #define LOAD_GAME_SUCCESS 0 #define LOAD_GAME_NO_INPUT_DB (-1) #define LOAD_GAME_CANNOT_OPEN (-2) #define LOAD_GAME_LOADING_PROBLEM (-3) #ifdef MEMORY_BASED static int load_game(void) #else static int load_game(int ccPageFile) #endif { FILE *f = NULL; char infile[SIZEOF_PATHNAME+8]; struct stat statbuf; int db_format, db_version, db_flags; int compressed = 0; #ifndef VMS if (mudconf.compress_db) { StringCopy(infile, mudconf.indb); strcat(infile, ".gz"); if (stat(infile, &statbuf) == 0) { f = popen(tprintf(" %s < %s", mudconf.uncompress, infile), "rb"); if (f != NULL) { DebugTotalFiles++; compressed = 1; } } } #endif // VMS if (compressed == 0) { StringCopy(infile, mudconf.indb); if (stat(infile, &statbuf) != 0) { // Indicate that we couldn't load because the input db didn't // exist. // return LOAD_GAME_NO_INPUT_DB; } if ((f = fopen(infile, "rb")) == NULL) { return LOAD_GAME_CANNOT_OPEN; } DebugTotalFiles++; setvbuf(f, NULL, _IOFBF, 16384); } // 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) { // Everything is not ok. // if (compressed) { if (pclose(f) != -1) { DebugTotalFiles--; } } else { if (fclose(f) == 0) { DebugTotalFiles--; } } f = 0; STARTLOG(LOG_ALWAYS, "INI", "FATAL") log_text((char *)"Error loading "); log_text(infile); ENDLOG return LOAD_GAME_LOADING_PROBLEM; } // Everything is ok. // if (compressed) { if (pclose(f) != -1) { DebugTotalFiles--; } } else { if (fclose(f) == 0) { DebugTotalFiles--; } } f = 0; #ifndef MEMORY_BASED if (db_flags & V_GDBM) { // It loaded an output file. // if (ccPageFile == HF_OPEN_STATUS_NEW) { STARTLOG(LOG_STARTUP, "INI", "LOAD"); log_text((char *)"Attributes are not present in either the input file or the attribute database."); ENDLOG; } } else { // It loaded a flatfile. // if (ccPageFile == HF_OPEN_STATUS_OLD) { STARTLOG(LOG_STARTUP, "INI", "LOAD"); log_text((char *)"Attributes present in both the input file and the attribute database."); ENDLOG; } } #endif if (mudconf.have_comsys || mudconf.have_macros) { load_comsys(mudconf.comsys_db); } if (mudconf.have_mailer) { f = fopen(mudconf.mail_db, "rb"); if (f) { DebugTotalFiles++; setvbuf(f, NULL, _IOFBF, 16384); Log.printf("LOADING: %s\n", mudconf.mail_db); load_mail(f); Log.printf("LOADING: %s (done)\n", mudconf.mail_db); if (fclose(f) == 0) { DebugTotalFiles--; } f = 0; } } STARTLOG(LOG_STARTUP, "INI", "LOAD"); log_text((char *)"Load complete."); ENDLOG; return LOAD_GAME_SUCCESS; } /* * 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; atr_push(); for (attr = atr_head(thing, &as); attr; attr = atr_next(&as)) { if (attr == A_LISTEN) { if (buff) free_lbuf(buff); atr_pop(); 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); atr_pop(); return 1; } } } if (buff) free_lbuf(buff); atr_pop(); return 0; } void do_readcache(dbref player, dbref cause, int key) { helpindex_load(player); fcache_load(player); } static void NDECL(process_preload) { 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; scheduler.RunTasks(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. // scheduler.RunTasks(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); } long DebugTotalFiles = 3; long DebugTotalSockets = 0; #ifdef WIN32 long DebugTotalThreads = 1; long DebugTotalSemaphores = 0; #endif // WIN32 #ifdef WIN32 // workaround till we have a getopt for windows #undef USE_GETOPT // ugly but easy to remove :) -- carsten #endif #ifdef USE_GETOPT extern char *optarg; #endif int DCL_CDECL main(int argc, char *argv[]) { int bMinDB = FALSE; int bSyntaxError = FALSE; char *conffile = NULL; #ifndef USE_GETOPT int iConfig = 0; // DEFAULT #else int ch; #endif #ifndef USE_GETOPT switch (argc) { case 1: // Use default config file. // break; case 2: // Use the specified config file. // if (strcmp(argv[1], "-s") == 0) { bMinDB = TRUE; } else { iConfig = 1; // argv[1]; } break; case 3: if (strcmp(argv[1], "-s") == 0) { // First parameter is "-s", second paramter is the config file. // bMinDB = TRUE; iConfig = 2; // argv[2]; } else if (strcmp(argv[2], "-s") == 0) { bMinDB = TRUE; iConfig = 1; // argv[1]; } else { bSyntaxError = TRUE; } break; default: // Syntax error. // bSyntaxError = TRUE; } if (iConfig) { conffile = argv[iConfig]; } #else while ((ch = getopt(argc, argv, "?sc:")) != -1) { switch (ch) { case 's': bMinDB = TRUE; break; case 'c': conffile = optarg; break; case ':': default : // Syntax error. // bSyntaxError = TRUE; } } #endif if (bSyntaxError) { printf("Usage: %s [-s] [config-file]\n", argv[0]); return 1; } SeedRandomNumberGenerator(); TIME_Initialize(); game_pid = getpid(); #ifdef WIN32 // Find which version of Windows we are using - Completion ports do // not work with Windows 95/98 OSVERSIONINFO VersionInformation; VersionInformation.dwOSVersionInfoSize = sizeof (VersionInformation); GetVersionEx(&VersionInformation); platform = VersionInformation.dwPlatformId; hGameProcess = GetCurrentProcess(); if (platform == VER_PLATFORM_WIN32_NT) { Log.WriteString("Running under Windows NT\n"); // Get a handle to the kernel32 DLL // HINSTANCE hInstKernel32 = LoadLibrary("kernel32"); if (!hInstKernel32) { Log.WriteString("LoadLibrary of kernel32 for a CancelIo entry point failed. Cannot continue.\n"); return 1; } // Find the entry point for CancelIO so we can use it. This is done dynamically because Windows 95/98 doesn't // have a CancelIO entry point. If it were done at load time, it would always fail on Windows 95/98...even // though we don't use it or depend on it in that case. // fpCancelIo = (FCANCELIO *)GetProcAddress(hInstKernel32, "CancelIo"); if (fpCancelIo == NULL) { Log.WriteString("GetProcAddress of _CancelIo failed. Cannot continue.\n"); return 1; } fpGetProcessTimes = (FGETPROCESSTIMES *)GetProcAddress(hInstKernel32, "GetProcessTimes"); if (fpGetProcessTimes == NULL) { Log.WriteString("GetProcAddress of GetProcessTimes failed. Cannot continue.\n"); return 1; } } else { Log.WriteString("Running under Windows 95/98\n"); } if (QueryPerformanceFrequency((LARGE_INTEGER *)&QP_D)) { bQueryPerformanceAvailable = TRUE; QP_A = FACTOR_100NS_PER_SECOND/QP_D; QP_B = FACTOR_100NS_PER_SECOND%QP_D; QP_C = QP_D/2; } // Initialize WinSock. // WORD wVersionRequested = MAKEWORD(2,2); WSADATA wsaData; if (WSAStartup(wVersionRequested, &wsaData) != 0) { Log.WriteString("ERROR: Could not initialize WinSock.\n"); return 101; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { // We can't run on this version of WinSock. // Log.printf("INFO: We requested WinSock v2.2, but only WinSock v%d.%d was available.\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); //WSACleanup(); //return 102; } #endif // WIN32 #ifdef RADIX_COMPRESSION init_string_compress(); #endif // RADIX_COMPRESSION #ifdef MEMORY_BASED // Database isn't corrupted. // corrupt = 0; #endif mudstate.start_time.GetLocal(); mudstate.cpu_count_from.GetUTC(); pool_init(POOL_LBUF, LBUF_SIZE); pool_init(POOL_MBUF, MBUF_SIZE); pool_init(POOL_SBUF, SBUF_SIZE); pool_init(POOL_BOOL, sizeof(struct boolexp)); pool_init(POOL_DESC, sizeof(DESC)); pool_init(POOL_QENTRY, sizeof(BQUE)); tcache_init(); pcache_init(); cf_init(); #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) init_rlimit(); #endif init_cmdtab(); init_logout_cmdtab(); init_flagtab(); init_powertab(); init_functab(); init_attrtab(); init_version(); if (conffile) { mudconf.config_file = StringClone(conffile); } else { mudconf.config_file = StringClone(CONF_FILE); } cf_read(); fcache_init(); helpindex_init(); #ifdef MEMORY_BASED db_free(); #else // MEMORY_BASED if (bMinDB) { RemoveFile(mudconf.game_dir); RemoveFile(mudconf.game_pag); } int ccPageFile = init_dbfile(mudconf.game_dir, mudconf.game_pag); if (HF_OPEN_STATUS_ERROR == ccPageFile) { STARTLOG(LOG_ALWAYS, "INI", "LOAD"); log_text((char *)"Couldn't load text database: "); log_text(mudconf.game_dir); log_text(mudconf.game_pag); ENDLOG; return 2; } #endif // MEMORY_BASED mudstate.record_players = 0; if (bMinDB) { db_make_minimal(); } else { #ifdef MEMORY_BASED int ccInFile = load_game(); #else int ccInFile = load_game(ccPageFile); #endif if (LOAD_GAME_NO_INPUT_DB == ccInFile) { // The input file didn't exist. // #ifndef MEMORY_BASED if (HF_OPEN_STATUS_NEW == ccPageFile) { // Since the .db file didn't exist, and the .pag/.dir files // were newly created, just create a minimal DB. // #endif // !MEMORY_BASED db_make_minimal(); ccInFile = LOAD_GAME_SUCCESS; #ifndef MEMORY_BASED } #endif // !MEMORY_BASED } if (ccInFile != LOAD_GAME_SUCCESS) { STARTLOG(LOG_ALWAYS, "INI", "LOAD") log_text((char *)"Couldn't load: "); log_text(mudconf.indb); ENDLOG return 2; } } set_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); hashreset(&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); hashreset(&mudstate.fwdlist_htab); hashreset(&mudstate.news_htab); hashreset(&mudstate.help_htab); hashreset(&mudstate.wizhelp_htab); hashreset(&mudstate.plushelp_htab); hashreset(&mudstate.staffhelp_htab); hashreset(&mudstate.wiznews_htab); hashreset(&mudstate.desc_htab); for (int i = 0; i < MAX_GLOBAL_REGS; i++) { mudstate.global_regs[i] = alloc_lbuf("main.global_reg"); mudstate.glob_reg_len[i] = 0; } // If master room does not exist in the DB, clear // the master room in the configuration. // if (mudconf.master_room != NOTHING) { // A master room was specified in the config. // if (mudconf.master_room < 0 || mudstate.db_top <= mudconf.master_room) { // The specified master room outside the range of valid DBrefs // mudconf.master_room = NOTHING; } } process_preload(); #ifndef WIN32 load_restart_db(); if (!mudstate.restarting) #endif // WIN32 { if (fclose(stdout) == 0) { DebugTotalFiles--; } if (fclose(stdin) == 0) { DebugTotalFiles--; } } boot_slave(0, 0, 0); #if defined(CONCENTRATE) && !defined(VMS) && !defined(WIN32) if (!mudstate.restarting) { // Start up the port concentrator. // conc_pid = fork(); if (conc_pid < 0) { perror("fork"); exit(-1); } if (conc_pid == 0) { char mudp[32], inetp[32]; // Add port argument to concentrator. // Tiny_ltoa(mudconf.port, mudp); Tiny_ltoa(mudconf.conc_port, inetp); execl("./bin/conc", "concentrator", inetp, mudp, "1", 0); } STARTLOG(LOG_ALWAYS, "CNC", "STRT"); log_text("Concentrating ports... "); log_text(tprintf("Main: %d Conc: %d", mudconf.port, mudconf.conc_port)); ENDLOG; } #endif // CONCENTRATE && !WIN32 // go do it. // init_timer(); #ifdef WIN32 if (platform == VER_PLATFORM_WIN32_NT) { process_output = process_outputNT; shovecharsNT(mudconf.port); } else { process_output = process_output9x; shovechars9x(mudconf.port); } #else // !WIN32 shovechars(mudconf.port); #endif // WIN32 close_sockets(0, (char *)"Going down - Bye"); dump_database(); CLOSE; // Go ahead and explicitly free the memory for these things so // that it's easy to spot unintentionally memory leaks. // helpindex_clean(&mudstate.staffhelp_htab); helpindex_clean(&mudstate.plushelp_htab); helpindex_clean(&mudstate.wiznews_htab); helpindex_clean(&mudstate.news_htab); helpindex_clean(&mudstate.help_htab); helpindex_clean(&mudstate.wizhelp_htab); db_free(); #ifdef WIN32 // critical section not needed any more if (platform == VER_PLATFORM_WIN32_NT) { DeleteCriticalSection(&csDescriptorList); } WSACleanup(); #endif return 0; } #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) static void NDECL(init_rlimit) { 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