home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 35 Internet
/
35-Internet.zip
/
tinymush.zip
/
TinyMush
/
os2src
/
game.c
< prev
next >
Wrap
Text File
|
1999-08-28
|
36KB
|
1,458 lines
/* game.c -- initialization stuff */
#include "autoconf.h"
#include "copyright.h"
#ifndef lint
static char *RCSid = "$Id: game.c,v 1.9 1995/03/21 00:00:27 ambar Exp $";
USE(RCSid);
#endif
#include "interface.h"
#include "file_c.h"
#include "match.h"
#include "flags.h"
#include "attrs.h"
#include "rwho_clilib.h"
#ifdef OS2
#include <process.h>
#endif
extern void NDECL(init_attrtab);
extern void NDECL(init_cmdtab);
extern void NDECL(cf_init);
extern void NDECL(pcache_init);
extern int FDECL(cf_read, (char *fn));
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 NDECL(init_timer);
extern void FDECL(raw_notify, (dbref, const char *));
extern void NDECL(do_second);
extern void FDECL(do_dbck, (dbref, dbref, int));
void FDECL(fork_and_dump, (int));
void NDECL(dump_database);
void NDECL(pcache_sync);
extern void NDECL(dump_restart_db);
static void FDECL(dump_database_internal, (int));
static void NDECL(init_rlimit);
int reserved;
/*
* used to allocate storage for temporary stuff, cleared before command
* execution
*/
void
do_dump(player, cause, key)
dbref player, cause;
int key;
{
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(pattern, str, args, nargs)
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) {
free(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 */
}
free(re);
return 1;
}
/* ----------------------------------------------------------------------
* atr_match: Check attribute list for wild card matches and queue them.
*/
static int
atr_match1(thing, parent, player, type, str, raw_str, check_exclude,
hash_insert)
dbref thing, parent, player;
char type, *str, *raw_str;
int check_exclude, hash_insert;
{
dbref aowner;
int match, attr, aflags, i;
char *buff, *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;
buff = alloc_lbuf("atr_match1");
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) ||
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,
((aflags & AF_NOPARSE) ? raw_str : str),
args, 10)) ||
(wild_match(buff + 1,
((aflags & AF_NOPARSE) ? raw_str : str),
args, 10, 0))) {
match = 1;
wait_que(thing, player, 0, NOTHING, s, args, 10,
mudstate.global_regs);
for (i = 0; i < 10; i++) {
if (args[i])
free_lbuf(args[i]);
}
}
}
atr_pop();
free_lbuf(buff);
return (match);
}
int
atr_match(thing, player, type, str, raw_str, check_parents)
dbref thing, player;
char type, *str, *raw_str;
int check_parents;
{
int match, lev, result, exclude, insert;
dbref parent;
/* If thing is halted, or it doesn't have a COMMANDS flag and we're
* we're doing a $-match, don't check it.
*/
if (((type == AMATCH_CMD) &&
!Has_Commands(thing) && mudconf.req_cmds_flag) || 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, raw_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, raw_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(object, player, filter, msg)
dbref object, player;
int filter;
const char *msg;
{
int aflags;
dbref aowner;
char *buf, *nbuf, *cp, *dp, *preserve[MAX_GLOBAL_REGS];
buf = atr_pget(object, filter, &aowner, &aflags);
if (!*buf) {
free_lbuf(buf);
return (1);
}
save_global_regs("check_filter_save", preserve);
dp = nbuf = exec(object, player, EV_FIGNORE | EV_EVAL | EV_TOP, buf,
(char **) NULL, 0);
free_lbuf(buf);
restore_global_regs("check_filter_restore", preserve);
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(object, player, prefix, msg, dflt)
dbref object, player;
int prefix;
const char *msg, *dflt;
{
int aflags;
dbref aowner;
char *buf, *nbuf, *cp, *preserve[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);
nbuf = exec(object, player, EV_FIGNORE | EV_EVAL | EV_TOP, buf,
(char **) NULL, 0);
free_lbuf(buf);
restore_global_regs("add_prefix_restore", preserve);
buf = nbuf;
cp = &buf[strlen(buf)];
}
if (cp != buf)
safe_str((char *) " ", buf, &cp);
safe_str((char *) msg, buf, &cp);
return (buf);
}
static char *
dflt_from_msg(sender, sendloc)
dbref sender, 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);
return tbuff;
}
#ifdef PUEBLO_SUPPORT
/* 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(src, dest, destp)
const char *src;
char *dest;
char **destp;
{
static char new_buf[LBUF_SIZE * 5];
const char *msg_orig;
char *mp_escaped;
char *temp, *bufp;
if (destp == 0) {
temp = dest;
destp = &temp;
}
for (*new_buf = '\0', bufp = new_buf, msg_orig = src;
msg_orig && *msg_orig;
msg_orig++) {
switch (*msg_orig) {
case '<':
*bufp = '\0';
strcat(new_buf, (char *) "<");
bufp += 4;
break;
case '>':
*bufp = '\0';
strcat(new_buf, (char *) ">");
bufp += 4;
break;
case '&':
*bufp = '\0';
strcat(new_buf, (char *) "&");
bufp += 5;
break;
case '\"':
*bufp = '\0';
strcat(new_buf, (char *) """);
bufp += 6;
break;
default:
*bufp++ = *msg_orig;
break;
}
}
*bufp = '\0';
safe_str(new_buf, dest, destp);
destp = '\0';
return;
}
#endif /* PUEBLO_SUPPORT */
void
notify_check(target, sender, msg, key)
dbref target, sender;
int key;
const char *msg;
{
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;
/* 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);
*tbuff = '(';
tbuff[1] = '#';
ltos(&tbuff[2], sender);
safe_str(tbuff, msg_ns, &mp);
safe_chr(')', 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) {
strcpy(tbuff, (const char *) "<-(#");
ltos(&tbuff[4], mudstate.curr_enactor);
safe_str(tbuff, msg_ns, &mp);
safe_chr(')', msg_ns, &mp);
}
safe_str((char *) "] ", msg_ns, &mp);
free_sbuf(tbuff);
}
safe_long_str((char *) msg, msg_ns, &mp);
} 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:
#ifndef PUEBLO_SUPPORT
if (key & MSG_ME)
raw_notify(target, msg_ns);
#else
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);
}
}
}
#endif /* ! PUEBLO_SUPPORT */
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_check.puppet");
safe_str(Name(target), tbuff, &tp);
safe_str((char *) "> ", tbuff, &tp);
safe_str(msg_ns, tbuff, &tp);
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_match(tp, (char *) msg, args, 10, 0)) {
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
*/
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 < nargs; 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, (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);
}
}
if (Bouncer(target))
pass_listen = 1;
/* 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) {
#ifdef PUEBLO_SUPPORT
notify_check(obj, sender, buff,
MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | (key & MSG_HTML));
#else
notify_check(obj, sender, buff,
MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE);
#endif /* PUEBLO_SUPPORT */
}
}
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(loc, player, exception, msg)
dbref loc, player, exception;
const char *msg;
{
dbref first;
if (loc != exception)
notify_check(loc, player, msg,
(MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A));
DOLIST(first, Contents(loc)) {
if (first != exception) {
notify_check(first, player, msg,
(MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE));
}
}
}
void
notify_except2(loc, player, exc1, exc2, msg)
dbref loc, player, exc1, 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));
}
}
}
void
do_shutdown(player, cause, key, message)
dbref player, cause;
int key;
char *message;
{
int fd;
if (key & SHUTDN_COREDUMP) {
if (player != NOTHING) {
raw_broadcast(0, "Game: Aborted by %s", Name(Owner(player)));
STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN")
log_text((char *) "Abort and coredump by ");
log_name(player);
ENDLOG
}
/* Don't bother to even shut down the network or dump. Die. Die now. */
abort();
}
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 = tf_open(mudconf.status_file, O_RDWR | O_CREAT | O_TRUNC);
(void) write(fd, message, strlen(message));
(void) write(fd, (char *) "\n", 1);
tf_close(fd);
/* 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(1);
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;
return;
}
static void
dump_database_internal(dump_type)
int dump_type;
{
char tmpfile[256], prevfile[256];
FILE *f;
if (dump_type == 1) {
/* Panic dump! */
unlink(mudconf.crashdb);
f = tf_fopen(mudconf.crashdb, O_WRONLY | O_CREAT | O_TRUNC);
if (f != NULL) {
db_write(f, F_MUSH, OUTPUT_VERSION | OUTPUT_FLAGS);
tf_fclose(f);
} else {
log_perror("DMP", "FAIL", "Opening crash file",
mudconf.crashdb);
}
return;
}
if (dump_type == 2) {
/* Dump for restart. First, try to write a flatfile. */
f = tf_fopen(mudconf.indb, O_WRONLY | O_CREAT | O_TRUNC);
if (f != NULL) {
db_write(f, F_MUSH, OUTPUT_VERSION | OUTPUT_FLAGS);
tf_fclose(f);
} else {
log_perror("DMP", "FAIL", "Opening restart file",
mudconf.indb);
}
}
sprintf(prevfile, "%s.prev", mudconf.outdb);
#ifdef VMS
sprintf(tmpfile, "%s.-%d-", mudconf.outdb, mudstate.epoch - 1);
unlink(tmpfile); /* nuke our predecessor */
sprintf(tmpfile, "%s.-%d-", mudconf.outdb, mudstate.epoch);
#else
sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch - 1);
unlink(tmpfile); /* nuke our predecessor */
sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch);
#endif /* VMS */
f = tf_fopen(tmpfile, O_WRONLY | O_CREAT | O_TRUNC);
if (f) {
db_write(f, F_MUSH, OUTPUT_VERSION | OUTPUT_FLAGS);
tf_fclose(f);
#ifdef OS2
unlink(prevfile); /* OS/2 won't rename over an existing file */
#endif /* OS2 */
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);
}
}
void
NDECL(dump_database)
{
char
*buff;
mudstate.epoch++;
mudstate.dumping = 1;
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(0);
STARTLOG(LOG_DBSAVES, "DMP", "DONE")
log_text((char *) "Dump complete: ");
log_text(buff);
ENDLOG
free_mbuf(buff);
mudstate.dumping = 0;
}
void
fork_and_dump(key)
int key;
{
int child;
char *buff;
if (*mudconf.dump_msg)
raw_broadcast(0, "%s", mudconf.dump_msg);
mudstate.epoch++;
mudstate.dumping = 1;
buff = alloc_mbuf("fork_and_dump");
#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", "CHKPT")
if (!key || (key & DUMP_TEXT)) {
log_text((char *) "SYNCing");
if (!key || (key & DUMP_STRUCT))
log_text((char *) " and ");
}
if (!key || (key & DUMP_STRUCT)) {
log_text((char *) "Checkpointing: ");
log_text(buff);
}
ENDLOG
free_mbuf(buff);
al_store(); /* Save cached modified attribute list */
if (!key || (key & DUMP_TEXT))
pcache_sync();
SYNC;
if (!key || (key & DUMP_STRUCT)) {
#ifndef VMS
if (mudconf.fork_dump) {
if (mudconf.fork_vfork) {
child = vfork();
} else {
child = fork();
}
} else {
child = 0;
}
#else
child = 0;
#endif /* VMS */
if (child == 0) {
dump_database_internal(0);
#ifndef VMS
if (mudconf.fork_dump)
_exit(0);
#endif /* VMS */
} else if (child < 0) {
log_perror("DMP", "FORK", NULL, "fork()");
}
}
if (!mudconf.fork_dump)
mudstate.dumping = 0;
if (*mudconf.postdump_msg)
raw_broadcast(0, "%s", mudconf.postdump_msg);
}
static int
NDECL(load_game)
{
FILE *f = NULL;
char infile[256];
int db_format, db_version, db_flags;
strcpy(infile, mudconf.indb);
if ((f = tf_fopen(mudconf.indb, O_RDONLY)) == 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
return -1;
}
STARTLOG(LOG_STARTUP, "INI", "LOAD")
log_text((char *) "Load complete.");
ENDLOG
/* everything ok */
tf_fclose(f);
return (0);
}
/* ----------------------------------------------------------------------
* do_restart: Routines to gracefully restart.
*/
void do_restart(player, cause, key)
dbref player, cause;
int key;
{
char indb[PBUF_SIZE], outdb[PBUF_SIZE];
int stat;
if (mudstate.dumping) {
notify(player, "Dumping. Please try again later.");
return;
}
raw_broadcast(0, "Game: Restart by %s, please wait.",
Name(Owner(player)));
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT")
log_text((char *)"Restart by ");
log_name(player);
ENDLOG
dump_database_internal(2);
SYNC;
CLOSE;
alarm(0);
dump_restart_db();
execl(mudconf.exec_path, mudconf.exec_path, mudconf.config_file,
(char *) NULL);
}
/* ----------------------------------------------------------------------
* Misc. utilities.
*/
/* match a list of things */
int
list_check(thing, player, type, str, raw_str, check_parent, stop_status)
dbref thing, player;
char type, *str, *raw_str;
int check_parent;
int *stop_status;
{
int match;
match = 0;
while (thing != NOTHING) {
if ((thing != player) &&
(atr_match(thing, player, type, str, raw_str, check_parent) > 0)) {
match = 1;
if (Stop_Match(thing)) {
*stop_status = 1;
return match;
}
}
thing = Next(thing);
}
return match;
}
int
Hearer(thing)
dbref thing;
{
char *as, *buff, *s;
dbref aowner;
int attr, aflags;
ATTR *ap;
if (Connected(thing) || Puppet(thing))
return 1;
if (mudstate.inpipe && (thing == mudstate.poutobj))
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_rwho(player, cause, key)
dbref player, cause;
int key;
{
#ifdef RWHO_IN_USE
if (key == RWHO_START) {
if (!mudstate.rwho_on) {
rwhocli_setup(mudconf.rwho_host,
mudconf.rwho_info_port,
mudconf.rwho_pass, mudconf.mud_name,
mudstate.short_ver);
rwho_update();
if (!Quiet(player))
notify(player, "RWHO transmission started.");
mudstate.rwho_on = 1;
} else {
notify(player, "RWHO transmission already on.");
}
} else if (key == RWHO_STOP) {
if (mudstate.rwho_on) {
rwhocli_shutdown();
if (!Quiet(player))
notify(player, "RWHO transmission stopped.");
mudstate.rwho_on = 0;
} else {
notify(player, "RWHO transmission already off.");
}
} else {
notify(player, "Illegal combination of switches.");
}
#else
notify(player, "RWHO support has not been compiled in to the server.");
#endif
}
void
do_readcache(player, cause, key)
dbref player, 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;
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);
cache_reset(0);
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);
cache_reset(0);
}
}
}
free_lbuf(fp);
free_lbuf(tstr);
}
#ifndef VMS
void
#endif /* VMS */
main(argc, argv)
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);
}
/*
fclose(stdin);
fclose(stdout);
*/
tf_init();
#ifdef IN_MEM_COMPRESSION
init_string_compress();
#endif
mindb = 0; /* Are we creating a new db? */
time(&mudstate.start_time);
mudstate.restart_time = mudstate.start_time;
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();
init_rlimit();
init_cmdtab();
init_logout_cmdtab();
init_flagtab();
init_functab();
init_attrtab();
init_version();
hashinit(&mudstate.player_htab, 511);
nhashinit(&mudstate.fwdlist_htab, 57);
nhashinit(&mudstate.objstack_htab, 57);
nhashinit(&mudstate.parent_htab, 57);
nhashinit(&mudstate.desc_htab, 37);
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);
}
/* Note that if either the relative path to the executable or the
* relative path to the conf file exceeds the buffer size, that
* You Have A Problem -- @restart will not work for you.
*/
strncpy(mudconf.exec_path, argv[0], PBUF_SIZE - 1);
mudconf.exec_path[PBUF_SIZE - 1] = '\0';
fcache_init();
helpindex_init();
if (mindb)
unlink(mudconf.gdbm);
if (init_gdbm_db(mudconf.gdbm) < 0) {
STARTLOG(LOG_ALWAYS, "INI", "LOAD")
log_text((char *) "Couldn't load text database: ");
log_text(mudconf.gdbm);
ENDLOG
exit(2);
}
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);
}
srandom(getpid());
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.logout_cmd_htab);
hashreset(&mudstate.func_htab);
hashreset(&mudstate.flags_htab);
hashreset(&mudstate.attr_name_htab);
nhashreset(&mudstate.attr_num_htab);
hashreset(&mudstate.player_htab);
nhashreset(&mudstate.fwdlist_htab);
nhashreset(&mudstate.objstack_htab);
hashreset(&mudstate.news_htab);
hashreset(&mudstate.help_htab);
hashreset(&mudstate.wizhelp_htab);
nhashreset(&mudstate.desc_htab);
for (mindb = 0; mindb < MAX_GLOBAL_REGS; mindb++)
mudstate.global_regs[mindb] = alloc_lbuf("main.global_reg");
mudstate.now = time(NULL);
process_preload();
STARTLOG(LOG_STARTUP, "INI", "LOAD")
log_text((char *) "Startup processing complete.");
ENDLOG
load_restart_db();
if (mudconf.rwho_transmit)
do_rwho(NOTHING, NOTHING, RWHO_START);
/* go do it */
mudstate.now = time(NULL);
init_timer();
shovechars(mudconf.port);
close_sockets(0, (char *) "Going down - Bye");
dump_database();
CLOSE;
exit(0);
}
static void
NDECL(init_rlimit)
{
#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);
#else
#if defined(_SEQUENT_) && defined(NUMFDS_LIMIT)
setdtablesize(NUMFDS_LIMIT);
#endif /* Sequent and unlimiting #define'd */
#endif /* HAVE_SETRLIMIT */
}