/* game.c */
#include "copyrite.h"
#include "config.h"
#include <ctype.h>
#include <fcntl.h>
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#include <signal.h>
#ifdef I_SYS_WAIT
#include <sys/wait.h>
#endif
#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#ifdef WIN32
#include <process.h>
void Win32MUSH_setup _((void));
#endif
#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif
#ifdef HAS_GETRUSAGE
#include <sys/resource.h>
#endif
#ifdef I_STDLIB
#include <stdlib.h>
#endif
#ifdef I_UNISTD
#include <unistd.h>
#endif
#include <stdio.h>
#include "conf.h"
#include "mushdb.h"
#include "game.h"
#include "externs.h"
#include "intrface.h"
#include "match.h"
#include "globals.h"
#ifdef USE_MAILER
#include "extmail.h"
#endif
#ifdef CHAT_SYSTEM
#include "extchat.h"
#endif
#ifdef MEM_CHECK
#include "memcheck.h"
#endif
#include "getpgsiz.h"
#include "parse.h"
#include "access.h"
#include "version.h"
#include "command.h"
#ifdef hpux
#include <sys/syscall.h>
#define getrusage(x,p) syscall(SYS_GETRUSAGE,x,p)
#endif /* fix to HP-UX getrusage() braindamage */
#include "confmagic.h"
#ifdef HAS_WAITPID
#define WAIT_TYPE int
#else
#ifdef UNION_WAIT
#define WAIT_TYPE union wait
#else
#define WAIT_TYPE int
#endif
#endif
/* declarations */
char dumpfile[200];
time_t start_time; /* MUSH start time */
extern time_t mudtime; /* MUSH current time */
static int epoch = 0;
int reserved;
int depth = 0; /* excessive recursion prevention */
extern int invok_counter; /* function recursion prevention */
extern dbref cplr;
extern char ccom[];
int paranoid_dump = 0; /* if paranoid, scan before dumping */
int paranoid_checkpt = 0; /* write out an okay message every x objs */
static void dump_database_internal _((void));
static FILE *db_open _((const char *filename));
static FILE *db_open_write _((const char *filename));
static void db_close _((FILE * f));
Signal_t reapear _((int));
#ifdef CHAT_SYSTEM
int parse_chat _((dbref player, char *command));
#endif
void do_readcache _((dbref player));
void set_interp _((dbref player, dbref cause, char const *obj,
char const *attrib, char const *val, int from_port));
int test_set _((dbref player, dbref cause, char const *command,
char *arg1, char *arg2, int from_port));
char **argv_hack _((dbref player, dbref cause, char const *arg,
char *fargs[], int eflags));
int check_alias _((const char *command, const char *list));
int list_check _((dbref thing, dbref player, char type,
char end, char *str, int just_match));
int alias_list_check _((dbref thing, const char *command,
const char *type));
int loc_alias_check _((dbref loc, const char *command,
const char *type));
void do_poor _((dbref player, char *arg1));
void do_writelog _((dbref player, char *str, int ltype));
void bind_and_queue _((dbref player, dbref cause,
char *action, char *arg));
void do_scan _((dbref player, char *command, int flag));
void do_list _((dbref player, char *arg));
void do_dolist _((dbref player, char *list, char *command,
dbref cause, int flag, int notify_flag));
void do_uptime _((dbref player));
char *make_new_epoch_file _((const char *basename, int the_epoch));
void fork_and_dump _((int forking));
void dest_info _((dbref thing, dbref tt));
#ifdef HAS_GETRUSAGE
void rusage_stats _((void));
#endif
void do_restart _((void));
extern void charge_action _((dbref player, dbref thing, const char *awhat));
extern int filter_found _((dbref thing, const char *msg, int flag));
extern void local_startup _((void));
extern void do_reboot _((dbref player, int flag));
extern void local_dump_database _((void));
extern dbref first_free; /* head of free object list, destroy.c */
dbref orator = NOTHING;
/*
* used to allocate storage for temporary stuff, cleared before command
* execution
*/
void
do_dump(player, num, flag)
dbref player;
char *num;
int flag;
{
/* flag: 0 = normal, 1 = paranoid, 2 = debug */
time_t tt;
if (Wizard(player)) {
if (options.daytime) {
notify(player, "Sorry, CPU intensive commands are currently disabled.");
return;
}
tt = time((time_t *) 0);
#ifdef ALWAYS_PARANOID
if (1) {
#else
if (flag) {
#endif
/* want to do a scan before dumping each object */
paranoid_dump = flag;
if (num && *num) {
/* checkpoint interval given */
paranoid_checkpt = atoi(num);
if ((paranoid_checkpt < 1) || (paranoid_checkpt >= db_top)) {
notify(player, "Permission denied. Invalid checkpoint interval.");
paranoid_dump = 0;
return;
}
} else {
/* use a default interval */
paranoid_checkpt = db_top / 5;
if (paranoid_checkpt < 1)
paranoid_checkpt = 1;
}
if (flag == 1) {
notify(player, tprintf("Paranoid dumping, checkpoint interval %d.",
paranoid_checkpt));
fprintf(checklog_fp,
"*** PARANOID DUMP *** done by %s(#%d),\n",
Name(player), player);
} else {
notify(player, tprintf("Debug dumping, checkpoint interval %d.",
paranoid_checkpt));
fprintf(checklog_fp,
"*** DEBUG DUMP *** done by %s(#%d),\n",
Name(player), player);
}
fprintf(checklog_fp, "\tcheckpoint interval %d, at %s",
paranoid_checkpt, ctime(&tt));
} else {
/* normal dump */
paranoid_dump = 0; /* just to be safe */
notify(player, "Dumping...");
fprintf(checklog_fp, "** DUMP ** done by %s(#%d) at %s",
db[player].name, player, ctime(&tt));
}
fflush(checklog_fp);
fork_and_dump(1);
paranoid_dump = 0;
} else {
notify(player, "Sorry, you are in a no dumping zone.");
}
}
/* print out stuff into error file */
void
report()
{
if (GoodObject(cplr))
do_rawlog(LT_TRACE, "TRACE: Cmd:%s\tdepth:%d\tby #%d at #%d", ccom,
depth, cplr, Location(cplr));
else
do_rawlog(LT_TRACE, "TRACE: Cmd:%s\tdepth:%d\tby #%d", ccom,
depth, cplr);
fflush(tracelog_fp);
}
#ifdef HAS_GETRUSAGE
void
rusage_stats()
{
struct rusage usage;
int pid;
#ifndef hpux
int psize;
#endif
pid = getpid();
#ifndef hpux
psize = getpagesize();
#endif
getrusage(RUSAGE_SELF, &usage);
fprintf(stderr, "\nProcess statistics:\n");
fprintf(stderr, "Time used: %10ld user %10ld sys\n",
usage.ru_utime.tv_sec, usage.ru_stime.tv_sec);
#ifndef hpux
fprintf(stderr, "Max res mem: %10ld pages %10ld bytes\n",
usage.ru_maxrss, (usage.ru_maxrss * psize));
#else
fprintf(stderr, "Max res mem: %10 pages\n", usage.ru_maxrss);
#endif /* hpux */
fprintf(stderr, "Integral mem:%10ld shared %10ld private %10ld stack\n",
usage.ru_ixrss, usage.ru_idrss, usage.ru_isrss);
fprintf(stderr, "Page faults: %10ld hard %10ld soft %10ld swapouts\n",
usage.ru_majflt, usage.ru_minflt, usage.ru_nswap);
fprintf(stderr, "Disk I/O: %10ld reads %10ld writes\n",
usage.ru_inblock, usage.ru_oublock);
fprintf(stderr, "Network I/O: %10ld in %10ld out\n",
usage.ru_msgrcv, usage.ru_msgsnd);
fprintf(stderr, "Context swi: %10ld vol %10ld forced\n",
usage.ru_nvcsw, usage.ru_nivcsw);
fprintf(stderr, "Signals: %10ld\n", usage.ru_nsignals);
}
#endif /* HAS_GETRUSAGE */
void
do_shutdown(player, flag)
dbref player;
int flag; /* -1 = panic shutdown, 0 = normal, 1 = paranoid */
{
if (flag == -1 && !God(player)) {
notify(player, "It takes a God to make me panic.");
return;
}
if (Wizard(player)) {
flag_broadcast(0, 0, "GAME: Shutdown by %s", db[player].name);
do_log(LT_ERR, player, NOTHING, "SHUTDOWN by %s\n",
real_unparse(player, player, 1));
/* This will create a file used to check if a restart should occur */
#ifdef AUTORESTART
system("touch NORESTART");
#endif
if (flag == -1) {
panic("@shutdown/panic");
} else {
if (flag == 1) {
paranoid_checkpt = db_top / 5;
if (paranoid_checkpt < 1)
paranoid_checkpt = 1;
paranoid_dump = 1;
}
shutdown_flag = 1;
}
} else {
notify(player, "Your delusions of grandeur have been duly noted.");
}
}
static void
dump_database_internal()
{
char realdumpfile[2048];
char realtmpfl[2048];
char tmpfl[2048];
FILE *f;
local_dump_database();
#ifdef ALWAYS_PARANOID
paranoid_checkpt = db_top / 5;
if (paranoid_checkpt < 1)
paranoid_checkpt = 1;
#endif
sprintf(realdumpfile, "%s%s", dumpfile, options.compresssuff);
strcpy(tmpfl, make_new_epoch_file(dumpfile, epoch));
sprintf(realtmpfl, "%s%s", tmpfl, options.compresssuff);
if ((f = db_open_write(tmpfl)) != NULL) {
switch (paranoid_dump) {
case 0:
#ifdef ALWAYS_PARANOID
db_paranoid_write(f, 0);
#else
db_write(f);
#endif
break;
case 1:
db_paranoid_write(f, 0);
break;
case 2:
db_paranoid_write(f, 1);
break;
}
db_close(f);
#ifdef WIN32
/* Win32 systems can't rename over an existing file, so unlink first */
unlink(realdumpfile);
#endif
if (rename(realtmpfl, realdumpfile) < 0)
perror(realtmpfl);
} else
perror(realtmpfl);
#ifdef USE_MAILER
sprintf(realdumpfile, "%s%s", options.mail_db, options.compresssuff);
strcpy(tmpfl, make_new_epoch_file(options.mail_db, epoch));
sprintf(realtmpfl, "%s%s", tmpfl, options.compresssuff);
if (mdb_top >= 0) {
if ((f = db_open_write(tmpfl)) != NULL) {
dump_mail(f);
db_close(f);
#ifdef WIN32
unlink(realdumpfile);
#endif
if (rename(realtmpfl, realdumpfile) < 0)
perror(realtmpfl);
} else
perror(realtmpfl);
}
#endif /* USE_MAILER */
#ifdef ALLOW_RPAGE
strcpy(tmpfl, make_new_epoch_file("rpage.db.Z", epoch));
if ((f = db_open_write(tmpfl)) != NULL) {
dump_server_database(f);
db_close(f);
#ifdef WIN32
unlink("rpage.db.Z");
#endif
if (rename(tmpfl, "rpage.db.Z") < 0)
perror(tmpfl);
} else
perror(tmpfl);
#endif /* ALLOW_RPAGE */
#ifdef CHAT_SYSTEM
sprintf(realdumpfile, "%s%s", options.chatdb, options.compresssuff);
strcpy(tmpfl, make_new_epoch_file(options.chatdb, epoch));
sprintf(realtmpfl, "%s%s", tmpfl, options.compresssuff);
if ((f = db_open_write(tmpfl)) != NULL) {
save_chatdb(f);
db_close(f);
#ifdef WIN32
unlink(realdumpfile);
#endif
if (rename(realtmpfl, realdumpfile) < 0)
perror(realtmpfl);
} else
perror(realtmpfl);
#endif
}
void
panic(message)
const char *message;
{
const char *panicfile = options.crash_db;
FILE *f;
int i;
fprintf(stderr, "PANIC: %s\n", message);
report();
flag_broadcast(0, 0, "EMERGENCY SHUTDOWN: %s", message);
/* turn off signals */
for (i = 0; i < NSIG; i++) {
signal(i, SIG_IGN);
}
/* shut down interface */
emergency_shutdown();
/* dump panic file */
if ((f = fopen(panicfile, "w")) == NULL) {
perror("CANNOT OPEN PANIC FILE, YOU LOSE");
_exit(135);
} else {
fprintf(stderr, "DUMPING: %s\n", panicfile);
db_write(f);
fclose(f);
fprintf(stderr, "DUMPING: %s (done)\n", panicfile);
_exit(136);
}
}
void
dump_database()
{
epoch++;
fprintf(stderr, "DUMPING: %s.#%d#\n", dumpfile, epoch);
dump_database_internal();
fprintf(stderr, "DUMPING: %s.#%d# (done)\n", dumpfile, epoch);
}
#ifndef WIN32
Signal_t reaper _((int sig));
Signal_t
reaper(sig)
int sig;
{
WAIT_TYPE my_stat;
#ifdef HAS_WAITPID
while (waitpid(-1, &my_stat, WNOHANG) > 0) ;
#else
while (wait3(&my_stat, WNOHANG, 0) > 0) ;
#endif
#ifndef HAS_SIGACTION
/* If we have sigaction, the signals are reliable */
#ifndef SIGNALS_KEPT
signal(SIGCLD, (Sigfunc) reaper); /* do this last or it'll loop */
#endif
#endif
#ifndef VOIDSIG
return 0;
#endif
}
#endif /* !WIN32 */
void
fork_and_dump(forking)
int forking;
{
int child, nofork;
epoch++;
fprintf(checklog_fp, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
fflush(checklog_fp);
if (NO_FORK)
nofork = 1;
else
nofork = !forking || (paranoid_dump == 2); /* Don't fork for dump/debug */
#ifdef WIN32
nofork = 1;
#endif
if (!nofork) {
#ifndef WIN32
child = fork();
if (child < 0) {
/* Oops, fork failed. Let's do a nofork dump */
do_log(LT_ERR, 0, 0, "fork_and_dump: fork() failed! Dumping nofork instead.");
if (DUMP_NOFORK_MESSAGE && *DUMP_NOFORK_MESSAGE)
flag_broadcast(0, 0, DUMP_NOFORK_MESSAGE);
child = 0;
nofork = 1;
}
#ifdef HAS_SETPRIORITY
else {
/* Lower the priority of the child to make parent more responsive */
#ifdef HAS_GETPRIORITY
setpriority(PRIO_PROCESS, child, getpriority(PRIO_PROCESS, child) + 4);
#else
setpriority(PRIO_PROCESS, child, 8);
#endif
}
#endif
#endif
} else {
if (DUMP_NOFORK_MESSAGE && *DUMP_NOFORK_MESSAGE)
flag_broadcast(0, 0, DUMP_NOFORK_MESSAGE);
child = 0;
}
if (nofork || (!nofork && child == 0)) {
/* in the child */
#ifndef WIN32
close(reserved); /* get that file descriptor back */
#endif
#ifdef CONCENTRATOR
signal(SIGCLD, SIG_DFL);
#endif /* CONCENTRATOR */
dump_database_internal();
if (!nofork) {
_exit(0); /* !!! */
} else {
#ifndef WIN32
reserved = open("/dev/null", O_RDWR);
#endif
#ifdef CONCENTRATOR
signal(SIGCLD, (void *) reaper);
#endif /* CONCENTRATOR */
if (DUMP_NOFORK_COMPLETE && *DUMP_NOFORK_COMPLETE)
flag_broadcast(0, 0, DUMP_NOFORK_COMPLETE);
}
}
}
void
do_restart()
{
dbref thing;
ATTR *s;
char *r;
char buf[SBUF_LEN];
int j;
/* Do stuff that needs to be done for players only: add stuff to the
* alias table, and refund money from queued commands at shutdown.
*/
for (thing = 0; thing < db_top; thing++) {
if (Typeof(thing) == TYPE_PLAYER) {
if ((s = atr_get_noparent(thing, "ALIAS")) != NULL) {
strcpy(buf, uncompress(s->value));
add_player(thing, buf);
}
if ((s = atr_get_noparent(thing, "QUEUE")) != NULL) {
giveto(thing, QUEUE_COST * atoi(uncompress(s->value)));
atr_clr(thing, "QUEUE", GOD);
}
if ((s = atr_get_noparent(thing, "SEMAPHORE")) != NULL) {
atr_clr(thing, "SEMAPHORE", GOD);
}
}
}
/* Once we load all that, then we can trigger the startups and
* begin queueing commands. Also, let's make sure that we get
* rid of null names.
*/
for (j = 0; j < 10; j++) {
wnxt[j] = NULL;
rnxt[j] = NULL;
}
for (thing = 0; thing < db_top; thing++) {
if (Name(thing) == NULL) {
if (Destroyed(thing))
SET(Name(thing), "Garbage");
else {
do_log(LT_ERR, NOTHING, NOTHING, "Null name on object #%d",
thing);
SET(Name(thing), "XXXX");
}
}
if (!Destroyed(thing) &&
(Flags(thing) & STARTUP) && !(Flags(thing) & HALT)) {
s = atr_get_noparent(thing, "STARTUP");
if (!s)
continue; /* just in case */
r = safe_uncompress(s->value);
parse_que(thing, r, thing);
free((Malloc_t) r);
}
}
}
int
init_game(conf)
const char *conf;
{
FILE *f;
int a;
const char *infile, *outfile;
#ifdef USE_MAILER
const char *mailfile;
#endif
depth = 0;
for (a = 0; a < 10; a++) {
wenv[a] = NULL;
renv[a][0] = '\0';
wnxt[a] = NULL;
rnxt[a] = NULL;
}
/* set MUSH start time */
start_time = time((time_t *) 0);
fprintf(stderr, "%s\n", VERSION);
fprintf(stderr, "MUSH restarted, PID %d, at %s\n",
(int) getpid(), ctime(&start_time));
/* initialize all the hash tables: flags, functions, and attributes. */
init_flag_hashtab();
init_func_hashtab();
init_aname_hashtab();
command_init_preconfig();
config_file_startup(conf);
command_init_postconfig();
#ifdef WIN32
Win32MUSH_setup(); /* create index files, copy databases etc. */
#endif
infile = restarting ? options.output_db : options.input_db;
outfile = options.output_db;
#ifdef USE_MAILER
mailfile = options.mail_db;
#endif
/* read small text files into cache */
fcache_init();
f = db_open(infile);
/* ok, read it in */
fprintf(stderr, "ANALYZING: %s\n", infile);
if (init_compress(f) < 0) {
fprintf(stderr, "ERROR LOADING\n");
return -1;
}
fprintf(stderr, "ANALYZING: %s (done)\n", infile);
/* everything ok */
db_close(f);
f = db_open(infile);
if (!f)
return -1;
/* ok, read it in */
fprintf(stderr, "LOADING: %s\n", infile);
if (db_read(f) < 0) {
fprintf(stderr, "ERROR LOADING\n");
return -1;
}
fprintf(stderr, "LOADING: %s (done)\n", infile);
/* everything ok */
db_close(f);
/* complain about bad config options */
if (!GoodObject(PLAYER_START) || (Typeof(PLAYER_START) != TYPE_ROOM))
fprintf(stderr, "WARNING: Player_start (#%d) is NOT a room.\n",
PLAYER_START);
if (DO_GLOBALS &&
(!GoodObject(MASTER_ROOM) || (Typeof(MASTER_ROOM) != TYPE_ROOM)))
fprintf(stderr, "WARNING: Master room (#%d) is NOT a room.\n",
MASTER_ROOM);
#ifdef USE_MAILER
/* read mail database */
f = db_open(mailfile);
/* okay, read it in */
if (f == NULL) {
mail_init();
} else {
fprintf(stderr, "LOADING: %s\n", mailfile);
load_mail(f);
fprintf(stderr, "LOADING: %s (done)\n", mailfile);
db_close(f);
}
#endif /* USE_MAILER */
#ifdef CHAT_SYSTEM
init_chatdb();
f = db_open(options.chatdb);
if (f) {
fprintf(stderr, "LOADING: %s\n", options.chatdb);
if (load_chatdb(f)) {
fprintf(stderr, "LOADING: %s (done)\n", options.chatdb);
db_close(f);
} else {
fprintf(stderr, "ERROR LOADING %s\n", options.chatdb);
db_close(f);
return -1;
}
}
#endif
/* now do access file stuff */
read_access_file();
/* now do the rpage stuff */
#ifdef ALLOW_RPAGE
rpage_init();
#endif /* ALLOW_RPAGE */
/* initialize random number generator */
srandom(getpid());
/* set up dumper */
strcpy(dumpfile, outfile);
init_timer();
#ifndef WIN32
signal(SIGCLD, (void *) reaper);
#endif
/* Call Local Startup */
local_startup();
/* everything else ok. Restart all objects. */
do_restart();
return 0;
}
void
do_readcache(player)
dbref player;
{
if (!Wizard(player)) {
notify(player, "Permission denied.");
return;
}
fcache_load(player);
}
#ifdef CHAT_SYSTEM
int
parse_chat(player, command)
dbref player;
char *command;
{
/* function hacks up something of the form "+<channel> <message>",
* finding the two args, and passes it to do_chat
*/
char *arg1;
char *arg2;
char tbuf1[MAX_COMMAND_LEN];
char *s;
strncpy(tbuf1, command, MAX_COMMAND_LEN - 1); /* don't hack it up */
tbuf1[MAX_COMMAND_LEN - 1] = '\0';
s = tbuf1;
arg1 = s;
while (*s && !isspace(*s))
s++;
if (*s) {
*s++ = '\0';
while (*s && isspace(*s))
s++;
}
arg2 = s;
return do_chat_by_name(player, arg1, arg2);
}
#endif /* CHAT_SYSTEM */
#define list_match(x) list_check(x, player, '$', ':', cptr, 0)
#define cmd_match(x) atr_comm_match(x, player, '$', ':', cptr, 0);
void
process_command(player, command, cause, from_port)
dbref player;
char *command;
dbref cause;
int from_port; /* 1 if this is direct input from a port
* (i.e. typed directly by a player).
* attrib sets don't get parsed then.
*/
{
int a;
char *p; /* utility */
char unp[BUFFER_LEN]; /* unparsed command */
/* general form command arg0=arg1,arg2...arg10 */
int gagged = 0;
char temp[BUFFER_LEN]; /* utility */
int i; /* utility */
char *cptr;
depth = 0;
if (!command) {
do_log(LT_ERR, NOTHING, NOTHING, "ERROR: No command!!!");
return;
}
/* robustify player */
if (!GoodObject(player)) {
do_log(LT_ERR, NOTHING, NOTHING, "process_command bad player #%d",
player);
return;
}
gagged = IS(db[player].owner, TYPE_PLAYER, PLAYER_GAGGED);
/* Access the player */
Access(player);
/* Destroyed objects shouldn't execute commands */
if (Destroyed(player)) {
/* No message - nobody to tell, and it's too easy to do to log. */
return;
}
/* Halted objects can't execute commands */
if ((Typeof(player) != TYPE_PLAYER) && (Flags(player) & HALT)) {
notify(Owner(player),
tprintf("Attempt to execute command by halted object #%d",
player));
return;
}
/* Players and things should not have invalid locations. This check
* must be done _after_ the destroyed-object check.
*/
if ((!GoodObject(Location(player)) ||
(Destroyed(Location(player)))) &&
Mobile(player)) {
notify(Owner(player),
tprintf("Invalid location on command execution: %s(#%d)",
Name(player), player));
do_log(LT_ERR, NOTHING, NOTHING,
"Command attempted by %s(#%d) in invalid location #%d.",
Name(player), player, Location(player));
moveto(player, PLAYER_START); /* move it someplace valid */
}
/* The following check is removed due to a security hole it causes!
* 'If player is an exit or room execute command as owner'
*/
/* if ((Typeof(player) == TYPE_ROOM) || (Typeof(player) == TYPE_EXIT))
* player = db[player].owner; */
orator = player;
if (options.log_commands || Suspect(player))
do_log(LT_CMD, player, 0, "%s", command);
if (Flags(player) & VERBOSE)
raw_notify(Owner(player), tprintf("#%d] %s", player, command));
/* eat leading whitespace */
while (*command && isspace(*command))
command++;
/* eat trailing whitespace */
p = command + strlen(command) - 1;
while (isspace(*p) && (p >= command))
p--;
*++p = '\0';
/* ignore null commands that aren't from players */
if ((!command || !*command) && !from_port)
return;
/* important home checking comes first! */
if (strcmp(command, "home") == 0) {
if (Typeof(player) == TYPE_EXIT || Typeof(player) == TYPE_ROOM)
return;
#ifdef FIXED_FLAG
if (Fixed(Owner(player)))
notify(player, "You can't do that IC!");
else
#endif
do_move(player, command, 0);
return;
}
strcpy(unp, command);
cptr = command_parse(player, cause, command, from_port);
if (cptr) {
a = 0;
if (!gagged && Mobile(player)) {
/* if the "player" is an exit or room, no need to do these checks */
/* try matching enter aliases */
if (Location(player) != NOTHING &&
(i = alias_list_check(db[Location(player)].contents,
cptr, "EALIAS")) != -1) {
sprintf(temp, "#%d", i);
do_enter(player, temp, 1);
goto done;
}
/* if that didn't work, try matching leave aliases */
if ((Typeof(Location(player)) != TYPE_ROOM) &&
(loc_alias_check(Location(player), cptr, "LALIAS"))) {
do_leave(player);
goto done;
}
/* try matching user defined functions before chopping */
/* try objects in the player's location, the location itself,
* and objects in the player's inventory.
*/
if (Location(player) != NOTHING) {
a += list_match(Contents(Location(player)));
if (Location(player) != player)
a += cmd_match(Location(player));
}
if (Location(player) != player)
a += list_match(Contents(player));
/* now do check on zones */
if ((!a) && (Zone(Location(player)) != NOTHING)) {
if (DO_GLOBALS && (Typeof(Zone(Location(player))) == TYPE_ROOM)) {
/* zone of player's location is a zone master room */
if (Location(player) != Zone(player)) {
/* check zone master room exits */
if (remote_exit(player, cptr)) {
if (!Mobile(player))
goto done;
else {
do_move(player, cptr, 2);
goto done;
}
} else
/* check commands in the zone master room if no exits
* can match more than one $command in zone master room
*/
a += list_match(Contents(Zone(Location(player))));
} /* end of zone master room check */
} else
/* try matching commands on area zone object if GLOBALS
* aren't in use or zone object isn't a room
*/
if ((!a) && (Zone(Location(player)) != NOTHING))
a += cmd_match(Zone(Location(player)));
} /* end of matching on zone of player's location */
/* if nothing matched with zone master room/zone object, try
* matching zone commands on the player's personal zone
*/
if ((!a) && (Zone(player) != NOTHING) &&
(Zone(Location(player)) != Zone(player))) {
a += cmd_match(Zone(player));
}
/* end of zone stuff */
if (DO_GLOBALS) {
/* check global exits only if no other commands are matched */
if ((!a) && (Location(player) != MASTER_ROOM)) {
if (global_exit(player, cptr)) {
if (!Mobile(player))
goto done;
else {
do_move(player, cptr, 1);
goto done;
}
} else
/* global user-defined commands checked if all else fails.
* May match more than one command in the master room.
*/
a += list_match(Contents(MASTER_ROOM));
}
/* end of master room check */
}
} /* end of special checks */
if (!a) {
notify(player, "Huh? (Type \"help\" for help.)");
if (options.log_huhs)
do_log(LT_HUH, player, 0, "%s", unp);
}
}
/* command has been executed. Free up memory. */
done:
;
}
/* now undef everything that needs to be */
#undef list_match
#undef cmd_match
int
check_alias(command, list)
const char *command;
const char *list;
{
/* check if a string matches part of a semi-colon separated list */
const char *p;
while (*list) {
for (p = command; (*p && DOWNCASE(*p) == DOWNCASE(*list)
&& *list != EXIT_DELIMITER);
p++, list++) ;
if (*p == '\0') {
while (isspace(*list))
list++;
if (*list == '\0' || *list == EXIT_DELIMITER)
return 1; /* word matched */
}
/* didn't match. check next word in list */
while (*list && *list++ != EXIT_DELIMITER) ;
while (isspace(*list))
list++;
}
/* reached the end of the list without matching anything */
return 0;
}
/* match a list of things */
#ifdef CAN_NEWSTYLE
int
list_check(dbref thing, dbref player, char type, char end, char *str,
int just_match)
#else
int
list_check(thing, player, type, end, str, just_match)
dbref thing, player;
char type, end;
char *str;
int just_match;
#endif
{
int match = 0;
while (thing != NOTHING) {
if (atr_comm_match(thing, player, type, end, str, just_match))
match = 1;
thing = db[thing].next;
}
return (match);
}
int
alias_list_check(thing, command, type)
dbref thing;
const char *command;
const char *type;
{
ATTR *a;
char alias[BUFFER_LEN];
while (thing != NOTHING) {
a = atr_get_noparent(thing, type);
if (a) {
strcpy(alias, uncompress(a->value));
if (check_alias(command, alias) != 0)
return thing; /* matched an alias */
}
thing = db[thing].next;
}
return -1;
}
int
loc_alias_check(loc, command, type)
dbref loc;
const char *command;
const char *type;
{
ATTR *a;
char alias[BUFFER_LEN];
a = atr_get_noparent(loc, type);
if (a) {
strcpy(alias, uncompress(a->value));
return (check_alias(command, alias));
} else
return 0;
}
int
Hearer(thing)
dbref thing;
{
ALIST *ptr;
if (IS(thing, TYPE_PLAYER, PLAYER_CONNECT) || Puppet(thing))
return (1);
for (ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
#ifndef VISIBLE_EMPTY_ATTRS
if (!*AL_STR(ptr))
continue;
#endif
if (!strcmp(AL_NAME(ptr), "LISTEN"))
return 1;
}
return (0);
}
int
Commer(thing)
dbref thing;
{
ALIST *ptr;
for (ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
#ifndef VISIBLE_EMPTY_ATTRS
if (!*AL_STR(ptr))
continue;
#endif
if (AL_FLAGS(ptr) & AF_COMMAND && !(AL_FLAGS(ptr) & AF_NOPROG))
return (1);
}
return (0);
}
int
Listener(thing)
dbref thing;
{
ALIST *ptr;
if (IS(thing, TYPE_THING, THING_LISTEN) || IS(thing, TYPE_ROOM, ROOM_LISTEN))
return (1);
for (ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
#ifndef VISIBLE_EMPTY_ATTRS
if (!*AL_STR(ptr))
continue;
#endif
if (AL_FLAGS(ptr) & AF_LISTEN && !(AL_FLAGS(ptr) & AF_NOPROG))
return (1);
}
return (0);
}
void
do_poor(player, arg1)
dbref player;
char *arg1;
{
int amt = atoi(arg1);
dbref a;
if (!God(player)) {
notify(player, "Only God can cause financial ruin.");
return;
}
for (a = 0; a < db_top; a++)
if (Typeof(a) == TYPE_PLAYER)
s_Pennies(a, amt);
notify(player,
tprintf("The money supply of all players has been reset to %d %s.",
amt, MONIES));
do_log(LT_WIZ, player, NOTHING,
"** POOR done ** Money supply reset to %d %s.",
amt, MONIES);
fflush(wizlog_fp);
}
void
do_writelog(player, str, ltype)
dbref player;
char *str;
int ltype;
{
if (!Wizard(player)) {
notify(player, "Permission denied.");
return;
}
do_rawlog(ltype, "LOG: %s(#%d%s): %s", Name(player), player,
unparse_flags(player, GOD), str);
notify(player, "Logged.");
}
/* Bind occurences of '##' in "action" to "arg", then run "action" */
void
bind_and_queue(player, cause, action, arg)
dbref player;
dbref cause;
char *action;
char *arg;
{
char *repl, *command;
repl = replace_string("##", arg, action);
command = strip_braces(repl);
if (repl)
free(repl);
#ifdef MEM_CHECK
del_check("replace_string.buff");
#endif
parse_que(player, command, cause);
if (command)
free(command);
#ifdef MEM_CHECK
del_check("strip_braces.buff");
#endif
}
void
do_scan(player, command, flag)
dbref player;
char *command;
int flag;
{
/* scan for possible matches of user-def'ed commands */
#define ScanFind(p,x) \
(Can_Examine(p,x) && \
((num = atr_comm_match(x, p, '$', ':', command, 1)) != 0))
dbref thing;
int num;
if (!GoodObject(Location(player))) {
notify(player, "Sorry, you are in an invalid location.");
return;
}
if (!command || !*command) {
notify(player, "What command do you want to scan for?");
return;
}
if (flag & CHECK_NEIGHBORS) {
notify(player, "Matches on contents of this room:");
DOLIST(thing, db[Location(player)].contents) {
if (ScanFind(player, thing))
notify(player,
tprintf("%s [%d]", unparse_object(player, thing), num));
}
}
if (flag & CHECK_HERE) {
if (ScanFind(player, Location(player)))
notify(player, tprintf("Matched here: %s [%d]",
unparse_object(player, Location(player)), num));
}
if (flag & CHECK_INVENTORY) {
notify(player, "Matches on carried objects:");
DOLIST(thing, db[player].contents) {
if (ScanFind(player, thing))
notify(player, tprintf("%s [%d]",
unparse_object(player, thing), num));
}
}
if (flag & CHECK_SELF) {
if (ScanFind(player, player))
notify(player, tprintf("Matched self: %s [%d]",
unparse_object(player, player), num));
}
if (flag & CHECK_ZONE) {
/* zone checks */
if (Zone(Location(player)) != NOTHING) {
if (Typeof(Zone(Location(player))) == TYPE_ROOM) {
/* zone of player's location is a zone master room */
if (Location(player) != Zone(player)) {
notify(player, "Matches on zone master room of location:");
DOLIST(thing, db[Zone(Location(player))].contents) {
if (ScanFind(player, thing))
notify(player, tprintf("%s [%d]",
unparse_object(player, thing), num));
}
}
} else {
/* regular zone object */
if (ScanFind(player, Zone(Location(player))))
notify(player,
tprintf("Matched zone of location: %s [%d]",
unparse_object(player, Zone(Location(player))), num));
}
}
if ((Zone(player) != NOTHING) &&
(Zone(player) != Zone(Location(player)))) {
/* check the player's personal zone */
if (ScanFind(player, Zone(player)))
notify(player, tprintf("Matched personal zone: %s [%d]",
unparse_object(player, Zone(player)), num));
}
}
if (DO_GLOBALS && (flag & CHECK_GLOBAL) &&
(Location(player) != MASTER_ROOM) &&
(Zone(Location(player)) != MASTER_ROOM) &&
(Zone(player) != MASTER_ROOM)) {
/* try Master Room stuff */
notify(player, "Matches on objects in the Master Room:");
DOLIST(thing, db[MASTER_ROOM].contents) {
if (ScanFind(player, thing))
notify(player, tprintf("%s [%d]",
unparse_object(player, thing), num));
}
}
}
void
do_dolist(player, list, command, cause, flag, notify_flag)
dbref player;
char *list, *command;
dbref cause;
int flag; /* 0 for @dolist, 1 for @map */
int notify_flag; /* execute '@notify me' at end? */
{
char *curr, *objstring;
char outbuf[BUFFER_LEN];
char *ebuf1, *ebuf2, *bp;
int place;
char placestr[10];
int j;
if (!command || !*command) {
notify(player, "What do you want to do with the list?");
if (notify_flag)
parse_que(player, "@notify me", cause);
return;
}
/* set up environment for any spawned commands */
for (j = 0; j < 10; j++) {
wnxt[j] = wenv[j];
rnxt[j] = renv[j];
}
bp = outbuf;
curr = list;
place = 0;
while (curr && *curr) {
while (*curr == ' ')
curr++;
if (*curr) {
place++;
sprintf(placestr, "%d", place);
objstring = ebuf1 = curr;
process_expression(objstring, &ebuf1, (char const **) &curr,
player, cause, cause,
PE_NOTHING, PT_SPACE, NULL);
if (*curr == ' ')
*curr++ = '\0';
if (!flag) {
/* @dolist, queue command */
ebuf1 = replace_string("#@", placestr, command);
bind_and_queue(player, cause, ebuf1, objstring);
#ifdef MEM_CHECK
del_check("replace_string.buff");
#endif
free(ebuf1);
} else {
/* it's @map, add to the output list */
if (bp != outbuf)
safe_chr(' ', outbuf, &bp);
ebuf1 = replace_string("##", objstring, command);
ebuf2 = replace_string("#@", placestr, ebuf1);
free(ebuf1);
#ifdef MEM_CHECK
del_check("replace_string.buff");
#endif
ebuf1 = ebuf2;
process_expression(outbuf, &bp, (char const **) &ebuf1,
player, cause, cause,
PE_DEFAULT, PT_DEFAULT, NULL);
free(ebuf2);
#ifdef MEM_CHECK
del_check("replace_string.buff");
#endif
}
}
}
*bp = '\0';
if (flag) {
/* if we're doing a @map, copy the list to an attribute */
atr_add(player, "MAPLIST", outbuf, GOD, NOTHING);
notify(player, "Function mapped onto list.");
}
if (notify_flag) {
/* Execute a '@notify me' so the object knows we're done
* with the list execution. We don't execute nfy_que()
* directly, since we want the command to be queued
* _after_ the list has executed.
*/
parse_que(player, "@notify me", cause);
}
}
void
do_uptime(player)
dbref player;
{
char tbuf1[BUFFER_LEN];
char *p;
#ifdef HAS_UPTIME
FILE *fp;
char c;
int i;
#endif
int pid;
#ifndef hpux
int psize;
#endif
int sec, min;
#ifdef HAS_GETRUSAGE
struct rusage usage;
#endif /* HAS_GETRUSAGE */
/* calculate time until next dump */
min = (options.dump_counter - mudtime) / 60;
sec = (options.dump_counter - mudtime) % 60;
sprintf(tbuf1, "Up since: %s", ctime(&start_time));
if ((p = strchr(tbuf1, '\n')))
*p = '\0';
notify(player, tbuf1);
sprintf(tbuf1, "Time now: %s", ctime(&mudtime));
if ((p = strchr(tbuf1, '\n')))
*p = '\0';
notify(player, tbuf1);
if (!Wizard(player)) {
notify(player,
tprintf("Time until next database save: %d minutes %d seconds.",
min, sec));
return;
}
#ifdef HAS_UPTIME
fp = popen(UPTIME_PATH, "r");
/* just in case the system is screwy */
if (fp == NULL) {
notify(player, "Error -- cannot execute uptime.");
fprintf(stderr, "** ERROR ** popen for @uptime returned NULL.");
return;
}
/* print system uptime */
for (i = 0; (c = getc(fp)) != '\n'; i++)
tbuf1[i] = c;
tbuf1[i] = '\0';
pclose(fp);
notify(player, tbuf1);
#endif
/* do process stats */
pid = getpid();
#ifndef hpux
#ifdef WIN32
psize = 1024; /* NJG: a guess! */
#else
psize = getpagesize();
#endif
notify(player, tprintf("\nProcess ID: %10d %10d bytes per page",
pid, psize));
#else
notify(player, tprintf("\nProcess ID: %10d", pid));
#endif /* hpux */
#ifdef HAS_GETRUSAGE
getrusage(RUSAGE_SELF, &usage);
notify(player, tprintf("Time used: %10d user %10d sys",
usage.ru_utime.tv_sec, usage.ru_stime.tv_sec));
#ifndef hpux
notify(player, tprintf("Max res mem: %10d pages %10d bytes",
usage.ru_maxrss, (usage.ru_maxrss * psize)));
#else
notify(player, tprintf("Max res mem: %10 dpages", usage.ru_maxrss));
#endif /* hpux */
notify(player, tprintf("Integral mem:%10d shared %10d private %10d stack",
usage.ru_ixrss, usage.ru_idrss, usage.ru_isrss));
notify(player, tprintf("Page faults: %10d hard %10d soft %10d swapouts",
usage.ru_majflt, usage.ru_minflt, usage.ru_nswap));
notify(player, tprintf("Disk I/O: %10d reads %10d writes",
usage.ru_inblock, usage.ru_oublock));
notify(player, tprintf("Network I/O: %10d in %10d out",
usage.ru_msgrcv, usage.ru_msgsnd));
notify(player, tprintf("Context swi: %10d vol %10d forced",
usage.ru_nvcsw, usage.ru_nivcsw));
notify(player, tprintf("Signals: %10d", usage.ru_nsignals));
#endif /* HAS_GETRUSAGE */
notify(player, tprintf("The head of the object free list is #%d.",
first_free));
notify(player,
tprintf("Time until next database save: %d minutes %d seconds.",
min, sec));
}
/* Open a db file, which may be compressed, and return a file pointer */
static FILE *
db_open(filename)
const char *filename;
{
FILE *f;
#ifndef WIN32
if (options.uncompressprog && *options.uncompressprog) {
/* We do this because on some machines (SGI Irix, for example),
* the popen will not return NULL if the mailfile isn't there.
*/
f = fopen(tprintf("%s%s", filename, options.compresssuff), "r");
if (f) {
fclose(f);
f = popen(tprintf("%s < %s%s", options.uncompressprog, filename, options.compresssuff), "r");
}
} else
#endif
{
f = fopen(filename, "r");
}
return f;
}
/* Open a file or pipe (if compressing) for writing */
static FILE *
db_open_write(filename)
const char *filename;
{
FILE *f;
#ifndef WIN32
if (options.compressprog && *options.compressprog) {
f = popen(tprintf("%s >%s%s", options.compressprog, filename, options.compresssuff), "w");
} else
#endif
{
f = fopen(filename, "w");
}
return f;
}
/* Close a db file, which may really be a pipe */
static void
db_close(f)
FILE *f;
{
#ifndef WIN32
if (options.compressprog && *options.compressprog) {
pclose(f);
} else
#endif
{
fclose(f);
}
}
void
do_list(player, arg)
dbref player;
char *arg;
{
if (!arg || !*arg)
notify(player, "I don't understand what you want to @list.");
else if (string_prefix("commands", arg))
do_list_commands(player);
else if (string_prefix("functions", arg))
do_list_functions(player);
else if (string_prefix("motd", arg))
do_motd(player, 3, "");
else if (string_prefix("attribs", arg))
do_list_attribs(player);
else
notify(player, "I don't understand what you want to @list.");
}
char *
make_new_epoch_file(basename, the_epoch)
const char *basename;
int the_epoch;
{
static char result[BUFFER_LEN]; /* STATIC! */
/* Unlink the last the_epoch and create a new one */
sprintf(result, "%s.#%d#", basename, the_epoch - 1);
unlink(result);
sprintf(result, "%s.#%d#", basename, the_epoch);
return result;
}