#include "copyright.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#ifdef __LCC__
#include <time.h>
#endif
#ifndef WIN32
#include <unistd.h>
#include <sys/resource.h>
#include <sys/wait.h>
#endif
#include "db.h"
#include "config.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
/* declarations */
static char *dumpfile = 0;
static int epoch = 0;
static int alarm_triggered = 0;
#ifndef WIN32
#define WIN32CLEANUP
static int alarm_block = 0;
#else
#define WIN32CLEANUP WSACleanup();
static time_t alarm_time = 0;
#endif
static void fork_and_dump (void);
void dump_database (void);
void do_dump (dbref player)
{
if (Wizard (player)) {
alarm_triggered = 1;
notify (player, "Dumping...");
} else {
notify (player, "Sorry, you are in a no dumping zone.");
}
}
void do_shutdown (dbref player)
{
if (Wizard (player)) {
fprintf (stderr, "SHUTDOWN: by %s(%d)\n", getname (player), player);
fflush (stderr);
shutdown_flag = 1;
} else {
notify (player, "Your delusions of grandeur have been duly noted.");
}
}
#ifndef WIN32
/* should be void, but it's defined as int */
static void alarm_handler (int sig)
{
alarm_triggered = 1;
if (!alarm_block) {
fork_and_dump ();
}
return;
}
#endif
static void dump_database_internal (void)
{
char tmpfile[2048];
FILE *f;
sprintf (tmpfile, "%s.#%d#", dumpfile, epoch - 1);
#ifdef _MSC_VER
remove (tmpfile); /* nuke our predecessor */
#else
unlink (tmpfile); /* nuke our predecessor */
#endif
sprintf (tmpfile, "%s.#%d#", dumpfile, epoch);
if ((f = fopen (tmpfile, "wb")) != NULL) {
db_write (f);
fclose (f);
#ifdef WIN32
#ifdef _MSC_VER
remove (dumpfile); /* nuke our predecessor */
#else
unlink (dumpfile); /* nuke our predecessor */
#endif
#endif
if (rename (tmpfile, dumpfile) < 0)
perror (dumpfile);
} else {
perror (tmpfile);
}
}
void panic (const char *message)
{
char panicfile[2048];
FILE *f;
int i;
fprintf (stderr, "PANIC: %s\n", message);
/* turn off signals */
for (i = 0; i < NSIG; i++) {
signal (i, SIG_IGN);
}
/* shut down interface */
emergency_shutdown ();
/* dump panic file */
sprintf (panicfile, "%s.PANIC", dumpfile);
if ((f = fopen (panicfile, "wb")) == NULL) {
perror ("CANNOT OPEN PANIC FILE, YOU LOSE:");
WIN32CLEANUP
_exit (135);
} else {
fprintf (stderr, "DUMPING: %s\n", panicfile);
db_write (f);
fclose (f);
fprintf (stderr, "DUMPING: %s (done)\n", panicfile);
WIN32CLEANUP
_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);
}
static void fork_and_dump ()
{
int child;
epoch++;
fprintf (stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
#ifndef WIN32
child = fork ();
if (child == 0) {
/* in the child */
close (0); /* get that file descriptor back */
#endif
dump_database_internal ();
#ifndef WIN32
_exit (0); /* !!! */
} else if (child < 0) {
perror ("fork_and_dump: fork()");
}
/* in the parent */
/* reset alarm */
#endif
alarm_triggered = 0;
#ifndef WIN32
alarm (DUMP_INTERVAL);
#else
time (&alarm_time);
alarm_time += DUMP_INTERVAL;
#endif
}
#ifndef WIN32
static void reaper (int sig)
{
// union wait stat;
int stat;
while (wait3 (&stat, WNOHANG, 0) > 0);
return;
}
#endif
int init_game (const char *infile, const char *outfile)
{
FILE *f;
if ((f = fopen (infile, "rb")) == NULL)
return -1;
/* ok, read it in */
fprintf (stderr, "LOADING: %s\n", infile);
if (db_read (f) < 0)
return -1;
fprintf (stderr, "LOADING: %s (done)\n", infile);
/* everything ok */
fclose (f);
#ifndef WIN32
/* initialize random number generator */
srandom (getpid ());
#endif
/* set up dumper */
if (dumpfile)
free (dumpfile);
dumpfile = alloc_string (outfile);
#ifndef WIN32
signal (SIGALRM, alarm_handler);
signal (SIGHUP, alarm_handler);
signal (SIGCHLD, reaper);
#endif
alarm_triggered = 0;
#ifndef WIN32
alarm (DUMP_INTERVAL);
#else
time (&alarm_time);
alarm_time += DUMP_INTERVAL;
#endif
return 0;
}
/* use this only in process_command */
#define Matched(string) { if(!string_prefix((string), command)) goto bad; }
void process_command (dbref player, char *command)
{
char *arg1;
char *arg2;
char *q; /* utility */
char *p; /* utility */
// char *index(char *, char);
if (command == 0)
abort ();
/* robustify player */
if (player < 0 || player >= db_top || Typeof (player) != TYPE_PLAYER) {
fprintf (stderr, "process_command: bad player %d\n", player);
return;
}
#ifdef LOG_COMMANDS
fprintf (stderr, "COMMAND from %s(%d) in %s(%d): %s\n",
getname (player), player,
getname (db[player].location), db[player].location, command);
#endif /* LOG_COMMANDS */
/* eat leading whitespace */
while (*command && isspace (*command))
command++;
/* eat extra white space */
q = p = command;
while (*p) {
/* scan over word */
while (*p && !isspace (*p))
*q++ = *p++;
/* smash spaces */
while (*p && isspace (*++p));
if (*p)
*q++ = ' '; /* add a space to separate next word */
}
/* terminate */
*q = '\0';
#ifndef WIN32
/* block dump to prevent db inconsistencies from showing up */
alarm_block = 1;
#endif
/* check for single-character commands */
if (*command == SAY_TOKEN) {
do_say (player, command + 1, NULL);
} else if (*command == POSE_TOKEN) {
do_pose (player, command + 1, NULL);
} else if (can_move (player, command)) {
/* command is an exact match for an exit */
do_move (player, command);
} else {
/* parse arguments */
/* find arg1 */
/* move over command word */
for (arg1 = command; *arg1 && !isspace (*arg1); arg1++);
/* truncate command */
if (*arg1)
*arg1++ = '\0';
/* move over spaces */
while (*arg1 && isspace (*arg1))
arg1++;
/* find end of arg1, start of arg2 */
for (arg2 = arg1; *arg2 && *arg2 != '='; arg2++);
/* truncate arg1 */
for (p = arg2 - 1; p >= arg1 && isspace (*p); p--)
*p = '\0';
/* go past '=' if present */
if (*arg2)
*arg2++ = '\0';
while (*arg2 && isspace (*arg2))
arg2++;
switch (command[0]) {
case '@':
switch (command[1]) {
case 'c':
case 'C':
/* chown, create */
switch (command[2]) {
case 'h':
case 'H':
Matched ("@chown");
do_chown (player, arg1, arg2);
break;
case 'r':
case 'R':
Matched ("@create");
do_create (player, arg1, atol (arg2));
break;
default:
goto bad;
}
break;
case 'd':
case 'D':
/* describe, dig, or dump */
switch (command[2]) {
case 'e':
case 'E':
Matched ("@describe");
do_describe (player, arg1, arg2);
break;
case 'i':
case 'I':
Matched ("@dig");
do_dig (player, arg1);
break;
case 'u':
case 'U':
Matched ("@dump");
do_dump (player);
break;
default:
goto bad;
}
break;
case 'f':
/* fail, find, or force */
switch (command[2]) {
case 'a':
case 'A':
Matched ("@fail");
do_fail (player, arg1, arg2);
break;
case 'i':
case 'I':
Matched ("@find");
do_find (player, arg1);
break;
#ifdef DO_FLUSH
case 'l':
case 'L':
if (string_compare (command, "@flush"))
goto bad;
do_flush (player);
break;
#endif /* DO_FLUSH */
case 'o':
case 'O':
Matched ("@force");
do_force (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'l':
case 'L':
/* lock or link */
switch (command[2]) {
case 'i':
case 'I':
Matched ("@link");
do_link (player, arg1, arg2);
break;
case 'o':
case 'O':
Matched ("@lock");
do_lock (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'n':
case 'N':
Matched ("@name");
do_name (player, arg1, arg2);
break;
case 'o':
case 'O':
switch (command[2]) {
case 'f':
case 'F':
Matched ("@ofail");
do_ofail (player, arg1, arg2);
break;
case 'p':
case 'P':
Matched ("@open");
do_open (player, arg1, arg2);
break;
case 's':
case 'S':
Matched ("@osuccess");
do_osuccess (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'p':
case 'P':
Matched ("@password");
do_password (player, arg1, arg2);
break;
case 's':
case 'S':
/* set, shutdown, success */
switch (command[2]) {
case 'e':
case 'E':
Matched ("@set");
do_set (player, arg1, arg2);
break;
case 'h':
if (strcmp (command, "@shutdown"))
goto bad;
do_shutdown (player);
break;
case 't':
case 'T':
Matched ("@stats");
do_stats (player, arg1);
break;
case 'u':
case 'U':
Matched ("@success");
do_success (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 't':
case 'T':
switch (command[2]) {
case 'e':
case 'E':
Matched ("@teleport");
do_teleport (player, arg1, arg2);
break;
case 'o':
if (string_compare (command, "@toad"))
goto bad;
do_toad (player, arg1);
break;
default:
goto bad;
}
break;
case 'u':
case 'U':
if (string_prefix (command, "@unli")) {
Matched ("@unlink");
do_unlink (player, arg1);
} else if (string_prefix (command, "@unlo")) {
Matched ("@unlock");
do_unlock (player, arg1);
} else {
goto bad;
}
break;
case 'w':
if (strcmp (command, "@wall"))
goto bad;
do_wall (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'd':
case 'D':
Matched ("drop");
do_drop (player, arg1);
break;
case 'e':
case 'E':
Matched ("examine");
do_examine (player, arg1);
break;
case 'g':
case 'G':
/* get, give, go, or gripe */
switch (command[1]) {
case 'e':
case 'E':
Matched ("get");
do_get (player, arg1);
break;
case 'i':
case 'I':
Matched ("give");
do_give (player, arg1, atol (arg2));
break;
case 'o':
case 'O':
Matched ("goto");
do_move (player, arg1);
break;
case 'r':
case 'R':
Matched ("gripe");
do_gripe (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'h':
case 'H':
Matched ("help");
do_help (player);
break;
case 'i':
case 'I':
Matched ("inventory");
do_inventory (player);
break;
case 'k':
case 'K':
Matched ("kill");
do_kill (player, arg1, atol (arg2));
break;
case 'l':
case 'L':
Matched ("look");
do_look_at (player, arg1);
break;
case 'm':
case 'M':
Matched ("move");
do_move (player, arg1);
break;
case 'n':
case 'N':
/* news */
if (string_compare (command, "news"))
goto bad;
do_news (player);
break;
case 'p':
case 'P':
Matched ("page");
do_page (player, arg1);
break;
case 'r':
case 'R':
switch (command[1]) {
case 'e':
case 'E':
Matched ("read"); /* undocumented alias for look at */
do_look_at (player, arg1);
break;
case 'o':
case 'O':
Matched ("rob");
do_rob (player, arg1);
break;
default:
goto bad;
}
break;
case 's':
case 'S':
/* say, "score" */
switch (command[1]) {
case 'a':
case 'A':
Matched ("say");
do_say (player, arg1, arg2);
break;
case 'c':
case 'C':
Matched ("score");
do_score (player);
break;
default:
goto bad;
}
break;
case 't':
case 'T':
switch (command[1]) {
case 'a':
case 'A':
Matched ("take");
do_get (player, arg1);
break;
case 'h':
case 'H':
Matched ("throw");
do_drop (player, arg1);
break;
default:
goto bad;
}
break;
default:
bad:
notify (player, "Huh? (Type \"help\" for help.)");
#ifdef LOG_FAILED_COMMANDS
if (!controls (player, db[player].location)) {
fprintf (stderr, "HUH from %s(%d) in %s(%d)[%s]: %s %s\n",
getname (player), player,
getname (db[player].location),
db[player].location,
getname (db[db[player].location].owner),
command, reconstruct_message (arg1, arg2));
}
#endif /* LOG_FAILED_COMMANDS */
break;
}
}
#ifndef WIN32
/* unblock alarms */
alarm_block = 0;
#else
if (time (0) > alarm_time)
alarm_triggered = 1;
#endif
if (alarm_triggered) {
fork_and_dump ();
}
}
#undef Matched