/* main.c: The main program. */ #define _POSIX_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <time.h> #include "x.tab.h" #include "codegen.h" #include "opcodes.h" #include "object.h" #include "match.h" #include "cache.h" #include "sig.h" #include "db.h" #include "util.h" #include "io.h" #include "data.h" #include "log.h" #include "dump.h" #include "execute.h" #include "ident.h" #include "cmstring.h" #include "token.h" #include "config.h" int running = 1; long heartbeat_freq = -1; time_t last_heartbeat; static void initialize(int argc, char **argv); static void main_loop(void); int main(int argc, char **argv) { initialize(argc, argv); main_loop(); /* We get this far after a C-- shutdown(). Sync the cache, flush output * buffers, and exit normally. */ cache_sync(); db_close(); flush_output(); return 0; } static void initialize(int argc, char **argv) { FILE *fp; Object *obj; List *parents, *args; int i, use_text_dump; String *str; Data d; /* Ditch stdin and stdout, so we can reuse the file descriptors. */ fclose(stdin); fclose(stdout); /* Initialize internal tables and variables. */ init_codegen(); init_op_table(); init_match(); init_util(); init_sig(); init_execute(); init_ident(); init_scratch_file(); init_token(); /* Make sure we have enough arguments. */ if (argc < 2) { fprintf(stderr, "Usage: %s <database> <db args>\n", argv[0]); exit(1); } /* Switch into database direectory. */ if (chdir(argv[1]) == -1) { fprintf(stderr, "Couldn't change to directory %s.\n", argv[1]); exit(1); } /* Build argument list from arguments. */ args = list_new(argc); for (i = 0; i < argc; i++) { args->el[i].type = STRING; str = string_from_chars(argv[i], strlen(argv[i])); substr_set_to_full_string(&args->el[i].u.substr, str); } /* Initialize database and network modules. */ init_cache(); use_text_dump = init_db(); /* Order of operations note: it might seem like we'd want to read the text * dump (if we're going to) before making sure there's a root and system * object. However, this way is correct, since the textdump reader can * evaluate arbitrary C-- code and thus should start with a consistent * database. */ /* Make sure there is a root object. */ obj = cache_retrieve(ROOT_DBREF); if (!obj) { parents = list_new(0); obj = object_new(ROOT_DBREF, parents); list_discard(parents); } cache_discard(obj); /* Make sure there is a system object. */ obj = cache_retrieve(SYSTEM_DBREF); if (!obj) { parents = list_new(1); parents->el[0].type = DBREF; parents->el[0].u.dbref = ROOT_DBREF; obj = object_new(SYSTEM_DBREF, parents); list_discard(parents); } cache_discard(obj); /* Read a text dump if there was no existing binary database. */ if (use_text_dump) { fp = fopen("textdump", "r"); if (!fp) { fail_to_start("Couldn't open text dump file."); } else { text_dump_read(fp); fclose(fp); } } /* Send a startup message to the system object. */ d.type = LIST; sublist_set_to_full_list(&d.u.sublist, args); task(NULL, SYSTEM_DBREF, startup_id, 1, &d); list_discard(args); } static void main_loop(void) { int seconds; time_t next_heartbeat = 0, t; while (running) { /* Delete any defunct connection or server records. This sends a * "disconnect"* message to the system object for each connection done * away with. */ flush_defunct(); /* Sanity check: make sure there are no objects in active chains. */ cache_sanity_check(); /* Find number of seconds before next heartbeat. */ if (heartbeat_freq == -1) { seconds = -1; } else { next_heartbeat = (last_heartbeat - (last_heartbeat % heartbeat_freq) ) + heartbeat_freq; time(&t); seconds = (t >= next_heartbeat) ? 0 : next_heartbeat - t; } /* Handle any I/O events waiting. */ handle_io_events(seconds); if (heartbeat_freq != -1) { time(&t); if (t >= next_heartbeat) { last_heartbeat = t; task(NULL, SYSTEM_DBREF, heartbeat_id, 0); } } } }