/* game.c */
#include <stdio.h>
#ifdef WANT_ANSI
#ifdef __STDC__
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */
#include <string.h>
#include <ctype.h>
#ifndef VMS
#include <fcntl.h>
#else
#include <sys/fcntl.h>
#endif
#ifndef XENIX
#include <signal.h>
#ifndef VMS
#include <sys/wait.h>
#endif
#else
#include <sys/signal.h>
#endif /* xenix */
#include <sys/types.h>
#include <sys/stat.h>
#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "flags.h"
#include "rwho_clilib.h"
/* external prototypes */
#ifndef VMS
extern int fork(void);
extern int vfork(void);
#endif
extern void srandom(int seed);
extern void init_attrtab(void);
extern void init_cmdtab(void);
extern void cf_init(void);
extern int cf_read (const 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);
/* XXX Ansify these. */
extern void init_timer();
extern void raw_notify();
/* declarations */
int reserved;
void fork_and_dump(int key);
void dump_database();
/*
* used to allocate storage for temporary stuff, cleared before command
* execution
*/
void do_dump(dbref player, dbref cause, int key)
{
notify(player, "Dumping...");
fork_and_dump(key);
}
/* 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
}
}
/* ---------------------------------------------------------------------------
* notify_check: notifies the object #player of the message msg. The behavior
* on puppets is as follows: If (!check_puppet), then the owner of the puppet
* is informed. If (check_puppet), then the owner of the puppet is only
* informed if the owner is in a different room.
*/
void notify_check(dbref player, dbref enactor, const char *msg,
int check_puppet)
{
char *buff, *d, *tbuff;
char *in;
char *args[10];
dbref aowner;
int i, nargs, aflags, doit, doit_set;
if (!Good_obj(player))
return;
mudstate.ntfy_nest_lev++;
if (mudstate.ntfy_nest_lev >= mudconf.ntfy_nest_lim) {
mudstate.ntfy_nest_lev--;
return;
}
buff = alloc_lbuf("notify_check");
d = buff;
if ((Flags(Owner(player)) & NOSPOOF) &&
(player != enactor) &&
(player != mudstate.curr_enactor) &&
(player != mudstate.curr_player)) {
/* I'd really like to use tprintf here but I can't because
* the caller may have. notify(player, tprintf(...)) is
* quite common in the code.
*/
tbuff = alloc_sbuf("notify_check.nospoof");
safe_chr('[', buff, &d);
safe_str(Name(enactor), buff, &d);
sprintf(tbuff, "(#%d)", enactor);
safe_str(tbuff, buff, &d);
if (enactor != Owner(enactor)) {
safe_chr('{', buff, &d);
safe_str(Name(Owner(enactor)), buff, &d);
safe_chr('}', buff, &d);
}
if (enactor != mudstate.curr_enactor) {
sprintf(tbuff, "<-(#%d)", mudstate.curr_enactor);
safe_str(tbuff, buff, &d);
}
safe_str((char *)"] ", buff, &d);
free_sbuf(tbuff);
}
safe_str((char *)msg, buff, &d);
*d = '\0';
doit_set = 0;
doit = 0;
switch (Typeof(player)) {
case TYPE_PLAYER:
raw_notify(player, buff);
if (!mudconf.player_listen) break;
case TYPE_THING:
if ((Flags(player) & PUPPET) && (player != Owner(player)) &&
(!check_puppet ||
(Location(player) != Location(Owner(player))))) {
tbuff = alloc_lbuf("notify_check.puppet");
d = tbuff;
safe_str(Name(player), tbuff, &d);
safe_str((char *)"> ", tbuff, &d);
safe_str(buff, tbuff, &d);
*d = '\0';
raw_notify(Owner(player), tbuff);
free_lbuf(tbuff);
}
d = atr_pget(player, A_LISTEN, &aowner, &aflags);
if (*d && wild_match(d, (char *)msg, args, 10)) {
for (nargs=10;
nargs && (!args[nargs-1] || !(*args[nargs-1]));
nargs--) ;
/* We have a match. Process AxHEAR attributes only
* if the speaker passes the object's USE lock.
*/
doit = could_doit(enactor, player, A_LUSE);
doit_set = 1;
if (doit) {
if (enactor != player)
did_it(enactor, player, 0, NULL,
0, NULL, A_AHEAR,
args, nargs);
else
did_it(enactor, player, 0, NULL,
0, NULL, A_AMHEAR,
args, nargs);
did_it(enactor, player, 0, NULL, 0, NULL,
A_AAHEAR, args, nargs);
for (i=0; i<10; i++)
if (args[i]!=NULL) free_lbuf(args[i]);
/* Also pass the message on to the contents
* of the hearing object. Note: not telling
* player protects against two forms of
* recursion player doesn't tell itself (as
* container) or as contents using teleport it
* is possible to create a recursive loop but
* this will be terminated when the depth
* variable exceeds 30
*/
if (!member(enactor,Contents(player)) &&
check_filter(player, enactor, A_INFILTER, msg)) {
in = add_prefix(player, enactor, A_INPREFIX, msg, "");
notify_except(player, enactor, player, in, 0);
free_lbuf(in);
}
} else {
for (i=0; i<10; i++)
if (args[i]!=NULL) free_lbuf(args[i]);
}
}
free_lbuf(d);
if ((player != enactor) && Monitor(player)) {
if (!doit_set)
doit = could_doit(enactor, player, A_LUSE);
if (doit)
(void)atr_match(player, enactor,
AMATCH_LISTEN, (char *)msg, 0);
}
}
free_lbuf(buff);
mudstate.ntfy_nest_lev--;
}
void do_shutdown(dbref player, dbref cause, int extra, char *message)
{
STARTLOG(LOG_ALWAYS,"WIZ","SHTDN")
log_text((char *)"Shutdown by ");
log_name(player);
ENDLOG
STARTLOG(LOG_ALWAYS,"WIZ","SHTDN")
log_text((char *)"Shutdown status: ");
log_text(message);
ENDLOG
close(mudstate.reserved_fileid);
mudstate.reserved_fileid =
open(mudconf.status_file, O_RDWR|O_CREAT|O_TRUNC, 0);
(void)write(mudstate.reserved_fileid, message, strlen(message));
(void)write(mudstate.reserved_fileid, "\n", 1);
close(mudstate.reserved_fileid);
mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0);
mudstate.shutdown_flag = 1;
}
static void dump_database_internal()
{
char tmpfile[256], outfn[256];
FILE *f;
#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);
if(mudconf.compress_db) {
sprintf(tmpfile, "%s.#%d#.Z", mudconf.outdb, mudstate.epoch -1);
unlink (tmpfile);
sprintf(tmpfile, "%s.#%d#.Z", mudconf.outdb, mudstate.epoch);
strcpy(outfn, mudconf.outdb);
strcat(outfn, ".Z");
if ((f = popen(tprintf("%s > %s",mudconf.compress, tmpfile),"w"))!=NULL) {
db_write(f, F_MUSH, OUTPUT_VERSION|OUTPUT_FLAGS);
pclose(f);
if(rename(tmpfile, outfn) < 0)
perror(tmpfile);
return;
} else
perror(tmpfile);
}
#endif VMS
if ((f = fopen(tmpfile, "w")) != NULL) {
db_write(f, F_MUSH, OUTPUT_VERSION|OUTPUT_FLAGS);
fclose(f);
if (rename(tmpfile, mudconf.outdb) < 0)
perror(tmpfile);
} else
perror(tmpfile);
}
void panic(const char *message, int dump)
{
FILE *f;
int i;
STARTLOG(LOG_ALWAYS,"SYS","PANIC")
log_text((char *)message);
ENDLOG
report();
/* turn off signals */
for (i = 0; i < NSIG; i++) {
if(!dump)
signal(i, SIG_IGN);
}
/* shut down interface */
emergency_shutdown();
/* Close the gdbm db */
SYNC;
CLOSE;
/* dump panic file */
if ((f = fopen(mudconf.crashdb, "w")) == NULL) {
STARTLOG(LOG_ALWAYS,"DMP","FAIL")
perror(mudconf.crashdb);
ENDLOG
if(!dump)
_exit(135);
else
abort();
} else {
STARTLOG(LOG_ALWAYS,"DMP","PANIC")
log_text((char *)"Panic dump: ");
log_text(mudconf.crashdb);
ENDLOG
db_write(f, F_MUSH, OUTPUT_VERSION|OUTPUT_FLAGS);
fclose(f);
STARTLOG(LOG_ALWAYS,"DMP","DONE")
log_text((char *)"Dump complete: ");
log_text(mudconf.crashdb);
ENDLOG
if(!dump)
_exit(136);
else
abort();
}
}
void dump_database()
{
char *buff;
mudstate.epoch++;
buff=alloc_mbuf("dump_database");
#ifndef VMS
sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch);
#else
sprintf(buff, "%s.-%d-", mudconf.outdb, mudstate.epoch);
#endif VMS
sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch);
STARTLOG(LOG_DBSAVES,"DMP","DUMP")
log_text((char *)"Dumping: ");
log_text(buff);
ENDLOG
SYNC;
dump_database_internal();
STARTLOG(LOG_DBSAVES,"DMP","DONE")
log_text((char *)"Dump complete: ");
log_text(buff);
ENDLOG
free_mbuf(buff);
}
void fork_and_dump(int key)
{
int child;
char *buff;
if (*mudconf.dump_msg)
raw_broadcast(0, "%s", mudconf.dump_msg);
mudstate.epoch++;
buff=alloc_mbuf("fork_and_dump");
#ifndef 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))
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) {
close(mudstate.reserved_fileid);
dump_database_internal();
#ifndef VMS
if(mudconf.fork_dump)
_exit(0);
else
mudstate.reserved_fileid =
open(DEV_NULL, O_RDWR, 0);
#else
mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0);
#endif VMS
} else if (child < 0) {
perror("fork_and_dump: fork()");
}
}
}
static void reaper()
{
#ifndef XENIX
#ifndef VMS
union wait stat;
while (wait3(&stat, WNOHANG, 0) > 0);
#else
int status;
wait(&status);
#endif VMS
#else
int status;
wait(&status);
#endif XENIX
}
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;
#ifndef VMS
if(mudconf.compress_db) {
strcpy(infile, mudconf.indb);
strcat(infile, ".Z");
if(stat(infile, &statbuf) == 0) {
if ((f = popen(tprintf(" %s < %s",
mudconf.uncompress, infile), "r")) == NULL)
compressed = 1;
}
}
#endif VMS
if(compressed == 0) {
strcpy(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
return -1;
}
STARTLOG(LOG_STARTUP,"INI","LOAD")
log_text((char *)"Load complete.");
ENDLOG
/* everything ok */
#ifndef VMS
if(compressed)
pclose(f);
else
fclose(f);
#else
fclose(f);
#endif VMS
return (0);
}
/* match a list of things */
int list_check(dbref thing, dbref player, char type, char *str,
int check_parent)
{
int match = 0;
while (thing != NOTHING) {
if ((thing != player) &&
(atr_match(thing, player, type, str, check_parent) > 0))
match = 1;
thing = Next(thing);
}
return (match);
}
/*
* routine to check attribute list for wild card matches of certain type and
* queue them
*/
static int atr_match1(dbref thing, dbref parent, dbref player, char type,
char *str)
{
dbref aowner;
int match, attr, aflags;
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);
if (!ap || (ap->flags & AF_NOPROG))
continue;
atr_get_str(buff, parent, attr, &aowner, &aflags);
/* Make sure we can execute it */
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 (wild_match(buff + 1, str, args, 10)) {
match = 1;
wait_que(thing, player, RU_ARG1_COPY|RU_ARG2_TAKE,
0, NOTHING, s, args, 10);
}
}
atr_pop();
free_lbuf(buff);
return (match);
}
int atr_match(dbref thing, dbref player, char type, char *str,
int check_parents)
{
int match, lev, result;
dbref parent;
match = 0;
if (!check_parents)
return atr_match1(thing, thing, player, type, str);
for (lev=0, parent=thing;
(Good_obj(parent) && (lev < mudconf.parent_nest_lim));
parent=Parent(parent), lev++) {
result = atr_match1(thing, parent, player, type, str);
if (result > 0) {
match = 1;
} else if (result < 0) {
return -1;
}
}
return match;
}
int Hearer(dbref thing)
{
char *as, *buff, *s;
dbref aowner;
int attr, aflags;
ATTR *ap;
if (IS(thing, TYPE_PLAYER, PLAYER_CONNECT) || (Flags(thing) & PUPPET))
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;
}
#ifdef XENIX /* rename hack!!! */
rename(char *s1, char *s2)
{
char *buff;
buff=alloc_mbuf("rename");
sprintf(buff, "mv %s %s", s1, s2);
system(buff);
free_mbuf(buff);
}
#endif
void do_rwho (dbref player, dbref 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 (dbref player, dbref cause, int key)
{
helpindex_load(player);
fcache_load(player);
}
#ifndef VMS
void
#endif VMS
main(int argc, char *argv[])
{
int mindb, lev;
dbref thing, parent;
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);
mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0);
mindb = 0; /* Are we creating a new db? */
time(&mudstate.start_time);
pool_init(&mudstate.lbuf_pool, LBUF_SIZE);
pool_init(&mudstate.mbuf_pool, MBUF_SIZE);
pool_init(&mudstate.sbuf_pool, SBUF_SIZE);
pool_init(&mudstate.bool_pool, sizeof(struct boolexp));
pool_init(&mudstate.qentry_pool, sizeof(BQUE));
pool_init(&mudstate.desc_pool, sizeof(DESC));
cf_init();
init_cmdtab();
init_logout_cmdtab();
init_flagtab();
init_functab();
init_attrtab();
init_version();
hashinit(&mudstate.player_htab, 57);
if (argc > 1 && !strcmp(argv[1], "-s")) {
mindb = 1;
if(argc == 3)
cf_read(argv[2]);
else
cf_read(CONF_FILE);
} else if (argc == 2) {
cf_read(argv[1]);
} else {
cf_read(CONF_FILE);
}
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());
/* Do a consistency check and set up the freelist */
do_dbck(NOTHING, NOTHING, 0);
/* set up dumper and reaper */
init_timer();
#ifndef VMS
#ifndef XENIX
signal(SIGCHLD, reaper);
#else /* xenix */
signal(SIGCLD, reaper);
#endif
#endif VMS
/* Reset all the hash stats */
hashreset(&mudstate.command_htab);
hashreset(&mudstate.logout_cmd_htab);
hashreset(&mudstate.func_htab);
hashreset(&mudstate.p_flags_htab);
hashreset(&mudstate.t_flags_htab);
hashreset(&mudstate.r_flags_htab);
hashreset(&mudstate.e_flags_htab);
hashreset(&mudstate.attr_name_htab);
nhashreset(&mudstate.attr_num_htab);
hashreset(&mudstate.vattr_name_htab);
nhashreset(&mudstate.vattr_num_htab);
hashreset(&mudstate.player_htab);
hashreset(&mudstate.news_htab);
hashreset(&mudstate.help_htab);
hashreset(&mudstate.wizhelp_htab);
nhashreset(&mudstate.desc_htab);
/* Run everyone's STARTUP attributes. We need to check parents too */
DO_WHOLE_DB(thing) {
if (Flags(thing) & GOING)
continue;
for (lev=0, parent=thing;
(Good_obj(parent) && (lev < mudconf.parent_nest_lim));
parent=Parent(parent), lev++) {
if (Flags(thing) & STARTUP) {
did_it(Owner(thing), thing, 0, NULL, 0, NULL,
A_STARTUP, (char **)NULL, 0);
break;
}
}
}
set_signals();
if (mudconf.rwho_transmit)
do_rwho(NOTHING, NOTHING, RWHO_START);
/* go do it */
shovechars(mudconf.port);
close_sockets(0, (char *)"Going down - Bye");
dump_database();
CLOSE;
exit(0);
}