/* main.c */
#include "copyright.h"
#include "config.h"
#include <stdio.h>
#ifndef NO_WAIT_H
#include <sys/wait.h>
#endif /* NO_WAIT_H */
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#ifndef NO_UIO_H
#include <sys/uio.h>
#endif /* NO_UIO_H */
#include <fcntl.h>
#include <sys/timeb.h>
#include <netdb.h>
#include "teeny.h"
#include "rwho.h"
/*
* Mainline of TeenyMUD. Also does some junk like dumping the DB. This is
* the module that binds all the rest of the stuff together into a MUD.
*/
#define USAGE \
"usage: %s\n\t-C <cachesize>\n\t-P <port>\n\tOne of: -l, -s, or -t <textdb>\n\t-i <indexfile>\n\t-c <chunkfile>\n\tRwho options: -R\t(disables rwho)\n\t-S <server>\n\t-W <password>\n"
/*
* Flags: C -- set cache size in 1K chunks l -- load an existing database if
* present t -- load in a text formatted database s -- start up the minimal
* db c -- specify a chunkfile i -- specify an indexfile P -- specify the
* port number to use
*
*/
void dump_finished();
void signal_shutdown();
/* SMALLOC */
/* extern void dump_malloc_data(); */
long atol();
/*
* A couple globals to determine overall state of the MUD.
*/
int mud_port;
char *chunk, *indexfl;/* Names of the chunkfile and indexfile */
int max_logins = 0;
#ifdef SUPPORT_RWHO
int rwho_enabled = 1;
char *rwho_server = RWHO_SERVER;
char *rwho_pword = RWHO_PWORD;
#endif
/* These are just to specify the DB to load/build */
#define NONE -1
#define STARTUP 0
#define TEXT 1
#define EXISTING 2
main(argc, argv)
int argc;
char *argv[];
{
int i;
long cachesize;
int initialdb;
char *textdb;
int flags;
/* Snarf args. */
cachesize = DEFAULT_CACHE;
chunk = DEFAULT_CHUNK;
indexfl = DEFAULT_INDEX;
initialdb = NONE;
mud_port = DEFAULT_PORT;
for (i = 1; i < argc; i++) {
if (*(argv[i]) == '-') {
switch ((argv[i])[1]) {
case 'c':
i++;
if (i < argc)
chunk = argv[i];
else {
printf(USAGE, argv[0]);
exit(1);
}
break;
case 'C':
i++;
if (i < argc)
cachesize = atol(argv[i]) * 1024;
else {
printf(USAGE, argv[0]);
exit(1);
}
break;
case 'P':
i++;
if (i < argc) {
mud_port = atol(argv[i]);
} else {
printf(USAGE, argv[0]);
exit(1);
}
break;
case 'i':
i++;
if (i < argc)
indexfl = argv[i];
else {
printf(USAGE, argv[0]);
exit(1);
}
break;
case 'l':
if (initialdb != NONE) {
puts("Use only one of t, s, l flags");
printf(USAGE, argv[0]);
exit(1);
}
initialdb = EXISTING;
break;
case 't':
if (initialdb != NONE) {
puts("Use only one of t, s, l flags");
printf(USAGE, argv[0]);
exit(1);
}
i++;
if (i < argc)
textdb = argv[i];
else {
printf(USAGE, argv[0]);
exit(1);
}
initialdb = TEXT;
break;
case 's':
if (initialdb != NONE) {
puts("Use only one of t, s, l flags");
printf(USAGE, argv[0]);
exit(1);
}
initialdb = STARTUP;
break;
#ifdef SUPPORT_RWHO
case 'R':
rwho_enabled = 0;
break;
case 'S':
i++;
if (i < argc)
rwho_server = argv[i];
else {
printf(USAGE, argv[0]);
exit(1);
}
break;
case 'W':
i++;
if (i < argc)
rwho_pword = argv[i];
else {
printf(USAGE, argv[0]);
exit(1);
}
break;
#endif
default:
printf(USAGE, argv[0]);
exit(0);
}
} else {
printf(USAGE, argv[0]);
exit(0);
}
}
if (initialdb == NONE) {
puts("Please specify a database.");
printf(USAGE, argv[0]);
exit(1);
}
/* Catch some signals */
#ifndef SYSV_SIGNALS
(void) signal(SIGUSR1, dump_finished);
(void) signal(SIGINT, signal_shutdown);
(void) signal(SIGTERM, signal_shutdown);
/* (void) signal(SIGPIPE, dump_malloc_data); /* SMALLOC */
(void) signal(SIGPIPE, SIG_IGN);
#else
reset_signals();
#endif /* SYSV_SIGNALS */
initialize_cache(cachesize);
/* Set up dump and timeslice timers */
init_timers();
/* Set up the DB. */
#ifdef OLD_RAND
srand((int) getpid());
#else
srandom((int) getpid());
#endif /* OLD_RAND */
switch (initialdb) {
case TEXT:
init_chunkfile(chunk);
if (text_load(textdb) == -1) {
fatal("Initial load", "Load of text DB failed.");
}
break;
case STARTUP:
/* Start up a modest blank DB */
init_chunkfile(chunk);
initialize_db(256);
(void) create_obj(TYP_ROOM);/* Room Zero. */
(void) create_obj(TYP_PLAYER); /* Wizard */
(void) set_str_elt(0, NAME, "Limbo");
(void) set_str_elt(1, NAME, "Wizard");
(void) set_str_elt(1, PASSWORD, crypt("potrzebie", CRYPT_KEY));
(void) set_int_elt(1, NEXT, -1);
(void) set_int_elt(1, LOC, 0);
(void) get_int_elt(1, FLAGS, &flags);
(void) set_int_elt(1, FLAGS, flags | GOD);
(void) set_int_elt(0, CONTENTS, 1);
(void) set_int_elt(1, HOME, 0);
(void) set_int_elt(0, OWNER, 1);
(void) set_int_elt(1, OWNER, 1);
break;
case EXISTING:
open_chunkfile(chunk);
read_descriptors(indexfl);
break;
}
/* Set up the net layer, and go to it. */
if (ioinit() == 1) { /* Eeek! */
fatal("Starting I/O system", "ioinit failed. Ditching.");
}
set_mudstat(RUNNING);
while (mudstat() != STOPPED && ioloop() == 0) {
iosync();
}
notify_wall(SHUTDOWN_MSG);
#ifdef SUPPORT_RWHO
if (rwho_enabled)
rwhocli_shutdown();
#endif
iosync();
iowrap(); /* This also deanimates players */
/* Wait for any dumps in progress to terminate */
(void) wait((union wait *) 0);
/* Dump the DB */
cache_flush();
close_chunkfile();
if (write_descriptors(indexfl) == -1) {
warning("Final db dump", "write_descriptors() failed.");
}
log_status("TeenyMUD done.\n");
return (0);
}
/*
* MUD status getter and setter.
*/
static int mud_status;
mudstat()
{
return (mud_status);
}
set_mudstat(i)
{
mud_status = i;
}
/*
* Resets the MUD status when a dump completes.
*/
void dump_finished()
{
(void) wait((union wait *) 0);
set_mudstat(RUNNING);
#ifdef SYSV_SIGNALS
reset_signals();
#endif /* SYSV_SIGNALS */
}
/*
* Dumps the database. Amazing, huh?
*/
dump_db()
{
int in, out;
char work[BUFFSIZ];
int inrv, outrv, rv;
/* We damned well *better* not have any dumps still in progress. */
/* This should wait until the last dump is done, on the off */
/* chance that there is one still in progress. */
(void) wait((union wait *) 0);
cache_flush();
/* Now the disk database is in sync with the descriptor table */
/* We fork(), write this back out, and copy away the written out DB */
set_mudstat(DUMPING); /* To prevent unlocking of the cache */
rv = fork();
if (rv == 0) { /* We're in the child */
if (write_descriptors(indexfl) == -1) {
warning("dump_db", "write_descriptors() failed");
goto dropthru;
}
/* Copy the index file. */
if ((in = open(indexfl, O_RDONLY, 0600)) == -1) {
warning("dump_db", "could not open indexfile for read");
goto dropthru;
}
if ((out = open(BACKUP_INDEX, O_WRONLY | O_CREAT, 0600)) == -1) {
warning("dump_db", "could not open indexfile for write");
goto dropthru;
}
while ((inrv = read(in, work, 512)) > 0) {
outrv = write(out, work, inrv);
}
(void) close(in);
(void) close(out);
if (inrv == -1)
warning("dump_db", "error reading indexfile");
if (outrv == -1)
warning("dump_db", "error writing indexfile");
/* Copy the chunk file. */
/* i DO NOT like this */
/* close_chunkfile(); */
if ((in = open(chunk, O_RDONLY, 0600)) == -1) {
warning("dump_db", "could not open chunkfile for read");
goto dropthru;
}
if ((out = open(BACKUP_CHUNK, O_WRONLY | O_CREAT, 0600)) == -1) {
warning("dump_db", "could not open chunkfile for write");
goto dropthru;
}
while ((inrv = read(in, work, 512)) > 0) {
outrv = write(out, work, inrv);
}
(void) close(in);
(void) close(out);
if (inrv == -1)
warning("dump_db", "error reading chunkfile");
if (outrv == -1)
warning("dump_db", "error writing chunkfile");
/* open_chunkfile(chunk); */
dropthru:
/* We're done. Signal the parent */
(void) kill(getppid(), SIGUSR1);
_exit(0);
} else if (rv == -1) { /* The fork() failed! */
warning("dump_db", "could not fork() to finish dump");
set_mudstat(RUNNING);
}
}
#ifdef SYSV_SIGNALS
reset_signals()
{
#ifdef USE_SMALLOC
extern void sigpipe();
(void) signal(SIGPIPE, sigpipe);
#endif
(void) signal(SIGUSR1, dump_finished);
(void) signal(SIGINT, SIG_IGN);
(void) signal(SIGHUP, SIG_IGN);
(void) signal(SIGTERM, signal_shutdown);
}
#endif /* SYSV_SIGNALS */
void signal_shutdown()
{
log_status("Caught a shutdown signal... Closing down.\n");
set_mudstat(STOPPED);
}
#ifdef USE_SMALLOC
void sigpipe()
{
dump_malloc_data();
reset_signals();
}
#endif