/
2.0.4beta/doc/
2.0.4beta/gnu/
2.0.4beta/sha/
/* main.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif			/* HAVE_SYS_WAIT_H */
#include <sys/ioctl.h>
#include <sys/file.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#ifdef HAVE_SETRLIMIT
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif			/* HAVE_SYS_TIME_H */
#include <sys/resource.h>
#endif				/* HAVE_SETRLIMIT */
#if !defined(HAVE_SETRLIMIT) || !defined(HAVE_SYS_TIME_H) || \
	defined(TIME_WITH_SYS_TIME)
#include <time.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif			/* HAVE_UNISTD_H */

#ifndef SIGUSR1
/*
 * Sigh.  I tried building on an ancient 4.2BSD system that lacks a SIGUSR1
 * define.  It appears to support the full 32 signals, though, and the last
 * few appear to be free to use.  This might break on other systems, but
 * I hope to God there aren't any others that don't have SIGUSR1!
 */
#ifndef NSIG
 #error You need to figure out how to replace SIGUSR1.  Sorry.
#else
#define SIGUSR1	(NSIG-1)		/* XXX */
#endif
#endif			/* no SIGUSR1 */

#include "conf.h"
#include "teeny.h"
#include "ptable.h"
#include "sha/sha_wrap.h"
#include "gnu/getopt.h"
#include "externs.h"

static void print_version _ANSI_ARGS_((void));
static void print_help _ANSI_ARGS_((void));
static void ty_detach _ANSI_ARGS_((void));
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
static void rlimit_init _ANSI_ARGS_((void));
#endif				/* HAVE_RLIMIT */
static void signals_init _ANSI_ARGS_((void));

extern int errno;

static int dump_pid;

static char *progname;

static struct option const getopt_options[] =
{
  { "detach", no_argument, 0, 'D' },
  { "configuration", required_argument, 0, 'c' },
  { "force", no_argument, 0, 'f' },
  { "help", no_argument, 0, 'h' },
  { "version", no_argument, 0, 'v' },
  { NULL, 0, 0, '\0' }
};

extern const char teenymud_version[];

static void print_version()
{
  fprintf(stderr, "This is TeenyMUD version %s.\n", teenymud_version);
}

static void print_help()
{
  print_version();

  fputs("Choose from the following:\n", stderr);
  fputs("-D, --detach\t\tdetach server from controlling terminal\n", stderr);
  fputs("-c, --configuration\tspecify configuration file\n", stderr);
  fputs("-f, --force\t\tforce boot\n", stderr);
  fputs("-h, --help\t\tdisplay this information\n", stderr);
  fputs("-v, --version\t\tdisplay program version information\n", stderr);
}

int main(argc, argv)
    int argc;
    char **argv;
{
  char *config = (char *)NULL;
  int detach = 0;
  int force = 0;
  int ch;

  progname = ty_basename(argv[0]);

  opterr = 0;
  while ((ch = getopt_long(argc, argv, "c:fDhv", getopt_options,
  			   (int *) 0)) != -1) {
    switch (ch) {
    case 'h':
      print_help();
      exit(0);
    case 'v':
      print_version();
      exit(0);
    case 'D':			/* detach */
      detach++;
      break;
    case 'c':			/* config file */
      config = optarg;
      break;
    case 'f':			/* force a boot */
      force++;
      break;
    default:
      fprintf(stderr, "%s: bad option -- %c\n", progname, ch);
      fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
      	      progname, progname);
      exit(0);
    }
  }
  if (config == (char *)NULL) {
    fprintf(stderr, "%s: You must specify a configuration file.\n", progname);
    fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
    	    progname, progname);
    exit(0);
  }

  mudstat.status = MUD_COLD;
  time(&mudstat.now);

  /* this prints it's own error messages. */
  if((conf_init(config) == -1) && !force)
    exit(-1);

  /* first things first. */
  if(chdir(mudconf.chdir_path) != 0) {
    if(!force) {
      fprintf(stderr, "%s: chdir [%s] failed: %s\n", progname, 
	      mudconf.chdir_path, strerror(errno));
      exit(0);
    } else
      logfile(LOG_ERROR, "chdir() failed: %s, continuing...\n",
	      strerror(errno));
  }

  /* do various initializations */
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
  rlimit_init();
#endif				/* HAVE_RLIMIT */
  ty_malloc_init();
  signals_init();
  cache_init();
  interface_init();
  help_init();
  prims_init();
  srandom((int) getpid());
#ifdef HAVE_TZSET
  tzset();
#endif				/* HAVE_TZSET */

  if (dbmfile_open(mudconf.dbm_file, 0) != 0)
    panic("main: couldn't open dbm file %s\n", mudconf.dbm_file);
  if (database_read(mudconf.db_file) != 0)
    panic("main: couldn't open database file %s\n", mudconf.db_file);
  ptable_init();

  /* let the world know we live! */
  fprintf(stdout, "TeenyMUD %s is Copyright(C) 1993, 1994, 1995 Jason Downs.\n",
  	  teenymud_version);
  fputs("TeenyMUD is free software and comes with absolutely no warranty.\n",
  	stdout);

  if(detach)
    ty_detach();

  fflush(stdout);

  /* Set out state, and enable logins. */
  mudstat.status = MUD_RUNNING;
  mudstat.logins = LOGINS_ENABLED;

  /* things to do before actually starting the server... */
  handle_startup();		/* queue up STARTUP attributes */

  /* run the server */
  tcp_loop();			/* this is what actually does it */

  mud_shutdown(0);
  return (0);
}

static void ty_detach()
{
  int pid;
#ifndef HAVE_SETSID
  int fd;
#endif			/* HAVE_SETSID */

  if ((pid = fork()) == -1)
    logfile(LOG_ERROR, "detach: couldn't fork() to detach the server.\n");
  else if (pid != 0) {
    printf("[Detaching process %d.]\n", pid);
    exit(0);
  }

  close(0);
  close(1);
#ifdef HAVE_SETSID
  if (setsid() == -1)
    panic("detach: setsid() failed, %s\n", strerror(errno));
#else
  if ((fd = open("/dev/tty", O_RDWR, 0600)) == -1)
    panic("detach: can't open /dev/tty, %s\n", strerror(errno));
  if (ioctl(fd, TIOCNOTTY, 0) == -1)
    panic("detach: ioctl() failed, %s\n", strerror(errno));
  close(fd);

#if defined(aiws)
  setpgrp();
#endif
#endif                          /* HAVE_SETSID */
}

void mud_shutdown(status)
    int status;
{
  char buff[128];
  FILE *fd;
  int wflags[FLAGS_LEN];

  /* just for you, xibo */
  if ((fd = fopen(mudconf.shtd_file, "r")) != (FILE *) NULL) {
    bzero((VOID *)wflags, sizeof(wflags));
    while (fgets(buff, 128, fd) != (char *) NULL) {
      remove_newline(buff);
      tcp_wall(wflags, buff, -1);
    }
    fclose(fd);
  }
  tcp_shutdown();

  /* Wait for any dumps in progress to terminate */
  waitpid(dump_pid, NULL, 0);

  /* Dump the DB */

  cache_flush();
  dbmfile_close();
  if (database_write(mudconf.db_file) == -1)
    logfile(LOG_ERROR, "mud_shutdown: final database dump failed.\n");

  logfile(LOG_STATUS, "%s done.\n", mudconf.name);

  exit(status);
}

/* Resets the MUD status when a dump completes. */
static RETSIGTYPE dump_finished()
{
  waitpid(dump_pid, NULL, 0);
  cache_unlock();
  mudstat.status = MUD_RUNNING;

#ifdef HAVE_SYSV_SIGNALS
  signals_init();
#endif				/* HAVE_SYSV_SIGNALS */
}

/* Dumps the database. Amazing, huh? */
void dump_db()
{
  static char *backdb = (char *)NULL;
#if defined(USE_BSDDBM) || defined(USE_GDBM)
  static char *backdbm = (char *)NULL;
#endif			/* USE_BSDDBM || USE_GDBM */
#ifdef USE_NDBM
  static char *dirname = (char *)NULL;
  static char *pagname = (char *)NULL;
  static char *backdir = (char *)NULL;
  static char *backpag = (char *)NULL;

  /* setup the dir and page file names */
  if (dirname == (char *)NULL) {
    dirname = (char *)ty_malloc(strlen(mudconf.dbm_file) + 5,
    				"dump_db.dirname");
    strcpy(dirname, mudconf.dbm_file);
    strcat(dirname, ".dir");
  }
  if (pagname == (char *)NULL) {
    pagname = (char *)ty_malloc(strlen(mudconf.dbm_file) + 5,
    				"dump_db.pagname");
    strcpy(pagname, mudconf.dbm_file);
    strcat(pagname, ".pag");
  }
#endif			/* USE_NDBM */

  /* set up the backup file names */
  if (backdb == (char *)NULL) {
    backdb = (char *)ty_malloc(strlen(mudconf.db_file) + 5, "dump_db.backdb");
    strcpy(backdb, mudconf.db_file);
    strcat(backdb, ".BAK");
  }
#if defined(USE_BSDDBM) || defined(USE_GDBM)
  if (backdbm == (char *)NULL) {
    backdbm = (char *)ty_malloc(strlen(mudconf.dbm_file) + 5,
    				"dump_db.backdbm");
    strcpy(backdbm, mudconf.dbm_file);
    strcat(backdbm, ".BAK");
  }
#endif			/* USE_BSDDBM || USE_GDBM */
#ifdef USE_NDBM
  if (backdir == (char *)NULL) {
    backdir = (char *)ty_malloc(strlen(dirname) + 5, "dump_db.backdir");
    strcpy(backdir, dirname);
    strcat(backdir, ".BAK");
  }
  if (backpag == (char *)NULL) {
    backpag = (char *)ty_malloc(strlen(pagname) + 5, "dump_db.backpag");
    strcpy(backpag, pagname);
    strcat(backpag, ".BAK");
  }
#endif			/* USE_NDBM */

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

  waitpid(dump_pid, NULL, 0);

  cache_flush();
  mudstat.status = MUD_DUMPING;
  cache_lock();

  if ((dump_pid = fork()) == 0) {	/* We're in the child */
    /* write out the descriptor file. */
    if (database_write(mudconf.db_file) == -1)
      logfile(LOG_ERROR,
	      "dump_db: database_write() failed, can't write db file.\n");
    /* back it up */
    copy_file(mudconf.db_file, backdb);

    /* back up the rest of the database */
#if defined(USE_BSDDBM) || defined(USE_GDBM)
    copy_file(mudconf.dbm_file, backdbm);
#endif			/* USE_BSDDBM || USE_GDBM */
#ifdef USE_NDBM
    copy_file(dirname, backdir);
    copy_file(pagname, backpag);
#endif			/* USE_NDBM */

    /* We're done. Signal the parent */

    kill(getppid(), SIGUSR1);
    _exit(0);

  } else if (dump_pid == -1) {	/* The fork() failed! */
    logfile(LOG_ERROR, "dump_db: could not fork(), %s\n", strerror(errno));
  }
}

static RETSIGTYPE signal_shutdown()
{
  logfile(LOG_STATUS, "Caught a shutdown signal... Closing down.\n");
  mud_shutdown(0);
}

static RETSIGTYPE signal_dump()
{
  if(mudstat.status != MUD_DUMPING) {
    logfile(LOG_STATUS, "Caught a dump signal... Dumping the database.\n");
    dump_db();
  } else
    logfile(LOG_STATUS, "Caught a dump signal, but already dumping.\n");
}

#ifndef NSIG
#define NSIG	32		/* XXX */
#endif

static void signals_init()
{
  register int snum;

  /* Reset all signals to ignore. */
  for(snum = 1; snum < NSIG; snum++)
      signal(snum, SIG_IGN);
  
  /* Set proper defaults for the rest. */
#ifdef SIGSEGV
  signal(SIGSEGV, SIG_DFL);
#endif
#ifdef SIGBUS
  signal(SIGBUS, SIG_DFL);
#endif
#ifdef SIGQUIT
  signal(SIGQUIT, SIG_DFL);
#endif
#ifdef SIGINT
  signal(SIGINT, signal_dump);
#endif
#ifdef SIGTERM
  signal(SIGTERM, signal_shutdown);
#endif
#ifdef SIGDANGER
  signal(SIGDANGER, signal_shutdown);
#endif
#ifdef SIGPWR
  signal(SIGPWR, signal_shutdown);
#endif
#ifdef SIGXFSZ
  signal(SIGXFSZ, signal_shutdown);
#endif
#ifdef SIGXCPU
  signal(SIGXCPU, signal_shutdown);
#endif
#ifdef SIGLOST
  signal(SIGLOST, signal_shutdown);
#endif

  /* SIGUSR1 and SIGUSR2 are required. */
  signal(SIGUSR1, dump_finished);
}

#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
static void rlimit_init()
{
  struct rlimit rlp;

  if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) {
    rlp.rlim_cur = rlp.rlim_max;
    if (setrlimit(RLIMIT_NOFILE, &rlp) != 0)
      logfile(LOG_ERROR, "rlimit_init: setrlimit failed, %s\n",
	      strerror(errno));
  } else
    logfile(LOG_ERROR, "rlimit_init: getrlimit failed, %s\n", strerror(errno));
}
#endif				/* HAVE_RLIMIT */