/
teeny/db/
teeny/dbm/
teeny/doc/
teeny/includes/
#include <stdio.h>
#include <sys/wait.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <sys/timeb.h>

#include "teeny.h"

/*
Copyright(C) 1990, Andrew Molitor, All Rights Reserved.
This software may be freely used, modified, and redistributed,
as long as this copyright message is left intact, and this
software is not used to develop any commercial product, or used
in any product that is provided on a pay-for-use basis.

No warranties whatsoever. This is not guaranteed to compile, nor,
in the event that it does compile to binaries, are these binaries
guaranteed to perform any function whatsoever.
*/

/*
	Mainline of TeenyMUD. Gets it going, and provides console service.
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 TEXT_DUMP_FILE "teeny.text"

#define USAGE \
"usage: teeny -C <cachesize> -[ls] -i <indexfile> -c <chunkfile> -t <textdb>"

/*
	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
	
*/

void turn_on_console();
void dump_finished();

time_t time();
long atol();


/*
	A couple globals to determine overall state of the MUD.
*/

int console_on;
char *chunk,*indexfl;  /* Names of the chunkfile and indexfile */

/* 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;
	time_t tm;
	long cachesize;
	int initialdb;
	char *textdb;
	int flags;

	/* Snarf args. */

	cachesize = DEFAULT_CACHE;
	chunk = DEFAULT_CHUNK;
	indexfl = DEFAULT_INDEX;
	initialdb = NONE;
	for(i = 1 ; i < argc ; i++){
		if(*(argv[i]) == '-'){
			switch((argv[i])[1]){
			case 'c':
				i++;
				if(i < argc)
					chunk = argv[i];
				else {
					puts(USAGE);
					exit(1);
				}
				break;
			case 'C':
				i++;
				if(i < argc)
					cachesize = atol(argv[i]) * 1024;
				else {
					puts(USAGE);
					exit(1);
				}
				break;
			case 'i':
				i++;
				if(i < argc)
					indexfl = argv[i];
				else {
					puts(USAGE);
					exit(1);
				}
				break;
			case 'l':
				if(initialdb != NONE){
					puts("use only one of t, s, l flags");
					puts(USAGE);
					exit(1);
				}
				initialdb = EXISTING;
				break;
			case 't':
				if(initialdb != NONE){
					puts("use only one of t, s, l flags");
					puts(USAGE);
					exit(1);
				}

				i++;
				if(i < argc)
					textdb = argv[i];
				else {
					puts(USAGE);
					exit(1);
				}
				initialdb = TEXT;
				break;
			case 's':
				if(initialdb != NONE){
					puts("use only one of t, s, l flags");
					puts(USAGE);
					exit(1);
				}
				initialdb = STARTUP;
				break;

			default:
				puts(USAGE);
				exit(0);
			}
		} else {
			puts(USAGE);
			exit(0);
		}
	}
	if(initialdb == NONE){
		puts("Please specify a database.");
		puts(USAGE);
		exit(1);
	}
	/* Catch some signals */

	(void)signal(SIGINT,turn_on_console);
	(void)signal(SIGUSR1,dump_finished);

	initialize_cache(cachesize);

	/* Set up dump and timeslice timers */

	init_timers();

	/* Set up the DB. */

	srandom((int)(time(&tm)));
	open_chunkfile(chunk);

	switch(initialdb){
	case TEXT:
		if(text_load(textdb) == -1){
			fatal("Initial load","Load of text DB failed.");
		}
		break;
	case STARTUP:
		/* Start up a modest blank DB */

		initialize_db(256,256);
		(void)create_obj(2); /* Room Zero. */
		(void)create_obj(0); /* Wizard */
		(void)set_str_elt(0,NAME,"Limbo");
		(void)set_str_elt(1,NAME,"Wizard foo");
		(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 | WIZARD);
		(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:
		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);
	console_on = 0;

	while(mudstat() != STOPPED && ioloop() == 0){
		if(console_on){
			do_console();
		}
		iosync();
	}

	notify_wall("Going down -- Bye!!\n");
	iosync();

	iowrap();	/* This also deanimates players */

	/* Wait for any dumps in progress to terminate */

	(void)wait((union wait *)0);

	/* Dump the DB */

	
	cache_flush();
	if(write_descriptors(indexfl) == -1){
		warning("Final db dump","write_descriptors() failed.");
	}
	puts("TeenyMUD done.");
	exit(0);
}

/*
	MUD status getter and setter.
*/

static int mud_status;

mudstat()
{
	return(mud_status);
}

set_mudstat(i)
{
	mud_status = i;
}

/*
	Implements the console. It supports a few simple and
to-be-used-with-caution commands. When the console is running the MUD is
completely frozen, this is deliberate.

*/

#define BAD_CONS_CMD "Bad command. Use b,d,g,s,t,w."

do_console()
{
	char cmd[80],*p;
	int num;

	while(1){
		write(1,"\nconsole>",9);
		(void)gets(cmd);
		p = cmd;
		while(isspace(*p)) p++;

		switch(*p){
		case 'd':	/* dump */
			dump_db();
			break;
		case 'b':
			p++;
			while(isspace(*p) && *p) p++;
			if(isdigit(*p)){
				num = atoi(p);
				iobdisconnect(num);
				write(1,"Booted.\n",8);
			} else {
				write(1,"Number, please.\n",16);
			}
			break;
		case 'w':	/* WHO */
			console_wholist();
			break;
		case 'g':	/* Go. */
			console_on = 0;
			return;
			break;
		case 's':	/* Shutdown. */
			console_on = 0;
			set_mudstat(STOPPED);
			return;
			break;
		case 't':	/* Text dump */
			p++;
			while(isspace(*p) && *p) p++;
			if(*p){
				text_dump(p);
			} else {
				text_dump(TEXT_DUMP_FILE);
			}
			break;
		default:
			write(1,BAD_CONS_CMD,sizeof(BAD_CONS_CMD));
			break;
		}
	}
}


/*
	Turns the console ON. This WILL freeze the MUD solid.
*/

void turn_on_console()
{
	console_on = 1;
}

/*
	Resets the MUD status when a dump completes.
*/

void dump_finished()
{
	(void)wait((union wait *)0);  /* Wait for it to exit(). Thanks chup! */
	set_mudstat(RUNNING);
}

/*
	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,0755)) == -1){
			warning("dump_db","could not open indexfile for read");
			goto dropthru;
		}
		if((out = open("old.index",O_WRONLY | O_CREAT ,0755)) == -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.*/

		if((in = open(chunk,O_RDONLY,0755)) == -1){
			warning("dump_db","could not open chunkfile for read");
			goto dropthru;
		}
		if((out = open("old.chunks",O_WRONLY | O_CREAT ,0755)) == -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");
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);
	}
}