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