/* @@@HEAD@@@ // */ #define _main_ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <time.h> #include "config.h" #include "defs.h" #include "y.tab.h" #include "codegen.h" #include "opcodes.h" #include "cdc_types.h" #include "object.h" #include "data.h" #include "ident.h" #include "cdc_string.h" #include "match.h" #include "cache.h" #include "sig.h" #include "db.h" #include "util.h" #include "io.h" #include "log.h" #include "dump.h" #include "execute.h" #include "token.h" #include "modules.h" internal void initialize(int argc, char **argv); internal void main_loop(void); internal void usage (char * name); /* // -------------------------------------------------------------------- // // Rather obvious, no? // */ int main(int argc, char **argv) { initialize(argc, argv); main_loop(); /* Sync the cache, flush output buffers, and exit normally. */ cache_sync(); db_close(); flush_output(); return 0; } /* // -------------------------------------------------------------------- // // Initialization // */ #define nextarg() { \ argv++; \ argc--; \ if (!argc) { \ usage(name); \ fprintf(stderr, "** No followup argument to -%s.\n", opt); \ exit(0); \ } \ } #define addarg(__str) { \ arg.type = STRING; \ str = string_from_chars(__str, strlen(__str)); \ arg.u.str = str; \ string_discard(str); \ args = list_add(args, &arg); \ } \ internal void initialize(int argc, char **argv) { FILE * fp; object_t * obj; list_t * parents, * args; int use_textdump, force_text_dump = 0; string_t * str; data_t arg, * d; char * opt, * name, * basedir = NULL; name = *argv; argv++; argc--; /* Ditch stdin, so we can reuse the file descriptor */ fclose(stdin); /* Initialize internal tables and variables. */ init_codegen(); init_ident(); init_op_table(); init_match(); init_util(); init_sig(); init_execute(); init_scratch_file(); init_token(); init_modules(argc, argv); init_defs(); /* db argument list */ args = list_new(0); /* parse arguments */ while (argc) { opt = *argv; if (*opt == '-' && strlen(opt) == 2) { opt++; switch (*opt) { case 'b': nextarg(); strcpy(c_dir_binary, *argv); break; case 'r': nextarg(); strcpy(c_dir_root, *argv); break; case 't': nextarg(); strcpy(c_dir_textdump, *argv); break; case 'e': nextarg(); strcpy(c_dir_bin, *argv); break; case 'f': force_text_dump++; break; case 'h': usage(name); exit(0); break; default: addarg(*argv); } } else { if (basedir == NULL) basedir = *argv; else addarg(*argv); } argv++; argc--; } if (basedir == NULL) basedir = "."; /* Switch into database directory. */ if (chdir(basedir) == F_FAILURE) { usage(name); fprintf(stderr, "** Couldn't change to base directory \"%s\".\n", basedir); exit(1); } /* people like to know what is up */ fprintf(stderr, "Initializing database..."); /* Initialize database and network modules. */ init_cache(); #if 1 init_binary_db(); #else use_textdump = init_db(0); #endif /* 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 ColdC 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); d = list_empty_spaces(parents, 1); d->type = DBREF; d->u.dbref = ROOT_DBREF; obj = object_new(SYSTEM_DBREF, parents); list_discard(parents); } cache_discard(obj); #if DISABLED /* Read a text dump if there was no existing binary database. */ if (use_textdump) { write_err("Reading from textdump..."); fp = fopen("textdump", "r"); if (!fp) { fail_to_start("Couldn't open text dump file."); } else { text_dump_read(fp); fclose(fp); } } #endif printf("Sending Startup Message.\n"); /* Send a startup message to the system object. */ arg.type = LIST; arg.u.list = args; task(NULL, SYSTEM_DBREF, startup_id, 1, &arg); list_discard(args); } /* // -------------------------------------------------------------------- // // The core of the interpreter, while this is looping it is interpreting // */ internal void main_loop(void) { int seconds = 0; time_t next_heartbeat = 0, last_heartbeat = 0, t = 0; while (running) { /* delete any defunct connection or server records */ 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) { next_heartbeat = (last_heartbeat - (last_heartbeat % heartbeat_freq) ) + heartbeat_freq; time(&t); seconds = (t >= next_heartbeat) ? 0 : next_heartbeat - t; seconds = (paused ? 0 : seconds); /* fprintf(stderr, "seconds: %d\n", seconds); */ } /* wait seconds for something to happen */ handle_io_event_wait(seconds); /* input */ handle_connection_input(); /* handle new or pending connections */ handle_new_and_pending_connections(); /* do heartbeat? */ if (heartbeat_freq != -1) { time(&t); /* yep */ if (t >= next_heartbeat) { /* call heartbeat on $sys */ last_heartbeat = t; task(NULL, SYSTEM_DBREF, heartbeat_id, 0); /* clenup the cache */ cache_cleanup(); } } /* output */ handle_connection_output(); /* complete paused tasks */ if (paused) run_paused_tasks(); } } internal void usage (char * name) { fprintf(stderr, "\n\ Usage: %s [options] [base dir]\n\n\ base directory will default to \".\" if unspecified.\n\n\ Options which the driver does not understand are passed onto\n\ the database with $sys.startup().\n\n\ Options:\n\n\ -b <binary> binary db directory name, default: \"binary\"\n\ -r <root> root file directory name, default: \"root\"\n\ -t <textdump> textdump filename, default: \"textdump\"\n\ -e <bindir> executables directory name, default: \"root/bin\"\n\ -f force loading from a textdump\n\ \n", name); } #undef _main_