dbm/
misc/
old-docs/
/* 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