/
teeny/db/
teeny/dbm/
teeny/docs/
teeny/includes/
teeny/misc/
teeny/news/
teeny/text/
/* 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"

/*
 * 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 -C <cachesize> -P <Port> -[ls] -i <indexfile> -c <chunkfile> -t <textdb>\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            clean_up();

long            atol();

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

int             mud_port;

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

extern char	poll[36];	/* For the @doing command */

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

  strcpy(poll, "Doing");

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

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

  (void) signal(SIGUSR1, dump_finished);
  (void) signal(SIGINT, clean_up);
  (void) signal(SIGTERM, clean_up);
#ifdef _AIX
  (void) signal(SIGDANGER, clean_up);
#endif				/* AIX only */

#ifdef SYSV_SIGNALS
  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 */
  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);

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

  notify_wall(SHUTDOWN_MSG);

  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;
}

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

/*
 * Attempts the shut the mud down nicely. 
 */
void
clean_up(i)
{
  set_mudstat(STOPPED);
  log_command("SHUTDOWN By signal: %d\n", i);
}

/*
 * 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(BACKUP_INDEX, O_WRONLY | O_CREAT | O_TRUNC, 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(BACKUP_CHUNK, O_WRONLY | O_CREAT | O_TRUNC, 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);
  }
}

#ifdef SYSV_SIGNALS
reset_signals()
{
  (void) signal(SIGUSR1, dump_finished);
}

#endif				/* SYSV_SIGNALS */