/* 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);
}
}
}
}