/*
* driver.c
*
* Interface to the driver for the DriverLib and vica versa.
*
* (C) Frank Schmidt, Jesus@NorseMUD
*
*/
#define __DRIVER
#include <driver.h>
/* inherit or include auto object? */
#if 1
inherit AUTO;
#else
/* include auto object, or start driver object from scratch? */
# if 1
# define _INCLUDED_AUTO_C
# include "auto.c"
# else
# endif
#endif
/* internal variables: */
private static object *users_array; /* array of cloned user objects */
private static int dump_co_handle; /* handle of dumping callout */
private static int swap_co_handle; /* handle of swapping callout */
private static int in_log_error; /* already in log_error() ? */
private int reboot_time; /* time when mud rebooted last time */
private int dump_time; /* time of last dump state */
private int dump_interval; /* interval between state dumps */
private int swap_time; /* time of last swap out */
private int swap_interval; /* interval between swappings */
private string driver_outputfile; /* outputfile for driver */
private int driver_echo; /* echo to stdout? */
private int driver_state; /* 0 = startup,
1 = up,
-1 = shutdown */
/* disallow direct send_message() in driver object */
int send_message(string msg) {
int ret;
if (DRIVER_PRIV()) {
/* echo to stdout? */
if (driver_echo)
ret=::send_message(msg);
if (driver_outputfile) {
/* write to output file */
dgd_write_file(driver_outputfile, msg);
}
}
else
illegal();
/* return number of bytes sent at end of sub-thread */
return ret;
}
/* output a message */
void send(mixed msg) {
if (DRIVER_PRIV()) {
#ifdef MUDOS_SPRINTF
/* allow any type */
if (!stringp(msg))
msg = sprintf("%O", msg);
#endif
send_message(msg + "\n");
}
else
illegal();
}
/* output a message with timestamp */
void notify(mixed msg) {
if (DRIVER_PRIV()) {
#ifdef MUDOS_SPRINTF
/* allow any type */
if (!stringp(msg))
msg = sprintf("%O", msg);
#endif
send(short_ctime(time()) + "> " + msg);
}
else
illegal();
}
/* output a debug message */
void debug(mixed msg) {
if (DRIVER_PRIV()) {
#ifdef MUDOS_SPRINTF
/* allow any type */
if (!stringp(msg))
msg = sprintf("%O", msg);
#endif
notify("DEBUG: " + msg);
}
else
illegal();
}
/* Write a log message */
void log(mixed msg) {
if (DRIVER_PRIV()) {
#ifdef MUDOS_SPRINTF
/* allow any type */
if (!stringp(msg))
msg = sprintf("%O", msg);
#endif
if (find_object(GLOBAL))
log_file(DRIVER_LOG, msg+"\n");
else
dgd_write_file(DRIVER_LOG, short_ctime(time())+" "+msg+"\n");
}
else
illegal();
}
/* catch tells to driver */
int __CATCH_TELL_DEF(string msg) {
if (DRIVER_PRIV()) {
send_message(msg);
return 1;
}
else
illegal();
}
/* get time of virtual reboot */
int query_reboot_time() {
if (DRIVER_PRIV())
return reboot_time;
else illegal();
}
/* return 0 in startup, 1 if up and -1 if shutting down */
int query_driver_state() {
if (DRIVER_PRIV()) {
return driver_state;
}
else
illegal();
}
/* Used to translate a path for call_other().
If 0 is returned, the object cannot be used. */
static object call_object(string path) {
object ob;
#ifdef DEFAULT_SRC_EXTENSION
/* accept objectnames ending with ".c" */
int i;
if ((i=strlen(path)) >= 2 && path[i-2..] == DEFAULT_SRC_EXTENSION)
path = path[0..i-3];
#endif
#ifdef PREVENT_MUDLIB_CALLS
/* disallow any call_other()s from mudlib to /dgd/ objects */
if (strcocmp(_DGD_DIR, path) && !strcocmp(_DGD_DIR, previous_program())) {
return 0;
}
#endif
if (ob=find_object(path))
return ob; /* found object */
return dgd_compile_object(path); /* had to compile it first */
}
/* Used to translate include path <path> upon compilation of the
object with filename <file>. If 0 is returned, the file <path>
cannot be included. */
static string path_include(string file, string path) {
/* disallow any including of /dgd/ files in the mudlib */
if (strcocmp(_DGD_DIR, path) && !strcocmp(_DGD_DIR, file))
return 0;
/* allow relative paths */
if (path[0] != '/')
return file + "/../" + path;
/* return path */
return path;
}
/* Used to translate inheritance path <path> upon compilation of
the object with filename <file>. If 0 is returned, the object
for <path> cannot be inherited. */
static object inherit_program(string file, string path) {
/* disallow any inheritance of /dgd/ files in the mudlib */
if (strcocmp(_DGD_DIR, path) && !strcocmp(_DGD_DIR, file))
return 0;
/* return program to inherit */
return call_object(path);
}
/* Return the path of a secondary error log upon the compilation of
an object from <file>. If 0 is returned, compile time errors are
only logged on stderr. */
static string compile_error(string file, int line, string error) {
RLIMITS_ON();
if (driver_state != 0 && find_object(MASTER)) {
/* let compile error through Master object */
catch(call_other(master(), __COMPILE_ERROR_FUNC, file, line, error));
}
else {
send("Internal compile error in "+file+" "+line+": "+error);
}
/* do not log the error here */
return 0;
RLIMITS_OFF();
}
/* Called when an error occurs. <caught> specifies whether or not
the error occurred in code run with catch(). runtime_error() is
called locked, so it will not run out of execution cost. */
static void runtime_error(string error, int caught, int ticks) {
RLIMITS_ON();
if (find_object(MASTER)) {
/* prevent recursion */
if (in_log_error++ <= 0) {
/* let runtime error through Master object */
catch(call_other(master(), __RUNTIME_ERROR_FUNC, error, caught, ticks));
}
else {
/* recursion! Error within runtime_error(). */
send("Error within runtime_error() in master object:\n"+error);
}
in_log_error = 0;
}
else {
send("Internal runtime error: "+error);
}
RLIMITS_OFF();
}
/* request at compilation if object is allowed to alter its resource-limits */
static int compile_rlimits(string file) {
if (strcocmp(_DGD_DIR, file))
return 1; /* /dgd/ objects may change rlimits unconditionally */
return 0;
}
/* runtime requests for change in rlimits, return 0 to disallow */
static int runtime_rlimits(object obj, int stack, int ticks) {
return 0;
}
/* Recompile a base program that is out of date? */
static void recompile(object ob) {
int ret;
/* Ask object if it wants to be recompiled */
if (catch(ret=call_other(ob, __QUERY_PREVENT_RECOMPILE_FUNC))) {
#ifndef AUTO_RECOMPILE_OBJECTS
return; /* default: no */
#endif
}
/* If it doesn't prevent it, destruct the master program */
if (!ret)
ob->__DESTROY_DEF();
}
/* last instance of a program is removed. Called from driver. */
static void remove_program(string program, int timestamp, int index) {
RLIMITS_ON();
RLIMITS_OFF();
}
/* Used to translate a path for an editor read command. If 0 is
returned, the file cannot be read.*/
static string path_read(string path) {
int ret;
object user, ply;
if (object_name(previous_object()) == EDITOR) {
/* EDITOR object with an interactive user */
if (!(user=previous_object()->query_user())) return 0;
if (!(ply=user->query_player())) return 0;
/* parse player path */
catch(path=call_other(master(), __PARSE_USER_PATH_FUNC, path, ply, "editor_read"));
}
else /* editor instance in an object */
ply = previous_object();
#ifdef MUDOS_USER_ID
catch(ret=call_other(master(), __VALID_READ_FUNC, path, geteuid(ply), "editor_read"));
#else
catch(ret=call_other(master(), __VALID_READ_FUNC, path, ply, "editor_read"));
#endif
/* return new path */
if (ret) return path;
}
/* Used to translate a path for an editor write command. If 0 is
returned, the file cannot be written to. */
static string path_write(string path) {
int ret;
object user, ply;
if (object_name(previous_object()) == EDITOR) {
/* EDITOR object with an interactive user */
if (!(user=previous_object()->query_user())) return 0;
if (!(ply=user->query_player())) return 0;
/* parse player path */
catch(path=call_other(master(), __PARSE_USER_PATH_FUNC, path, ply, "editor_write"));
}
else /* editor instance in an object */
ply = previous_object();
#ifdef MUDOS_USER_ID
catch(ret=call_other(master(), __VALID_WRITE_FUNC, path, geteuid(ply), "editor_write"));
#else
catch(ret=call_other(master(), __VALID_WRITE_FUNC, path, ply, "editor_write"));
#endif
/* return new path */
if (ret) return path;
}
/* Called when a user connects. Returns an user object */
static object telnet_connect() {
object user;
user = clone_object(USER);
/* update users array & clean-up */
users_array += ({ user });
users_array -= ({ 0 });
/* return new user object */
return user;
}
/* Called when a binary user connects. (Currently not in special use.) */
static object binary_connect() {
return telnet_connect();
}
/* Write a binary state dump */
void dump_all() {
if (DRIVER_PRIV()) {
dump_time = time();
dump_state();
log_file(DRIVER_DISK_LOG, "Writing state dump to disk.\n");
}
else
illegal();
}
/* dump-callout being called at dump_interval */
varargs void set_dump_interval(int i) {
if (!previous_object() || DRIVER_PRIV()) {
if (i > 0) {
if (dump_interval <= 0) {
log_file(DRIVER_DISK_LOG, "Periodical dumping turned ON.\n");
send("Periodical dumping turned ON.");
}
dump_interval = i;
if (driver_state > 0) /* only dump state if driver is up properly */
dump_all();
dump_co_handle = dgd_call_out("set_dump_interval", dump_interval, dump_interval);
}
else {
if (dump_interval > 0) {
log_file(DRIVER_DISK_LOG, "Periodical dumping turned OFF.\n");
send("Periodical dumping turned OFF.");
}
dump_interval = 0;
dump_co_handle = 0;
}
}
else
illegal();
}
/* Swap out all objects to free memory */
void swap_all() {
if (DRIVER_PRIV()) {
swap_time = time();
swapout();
log_file(DRIVER_DISK_LOG, "Swapping out to disk.\n");
}
else
illegal();
}
/* swap-callout being called at swap_interval */
varargs void set_swap_interval(int i) {
if (!previous_object() || DRIVER_PRIV()) {
if (i > 0) {
if (swap_interval <= 0) {
log_file(DRIVER_DISK_LOG, "Periodical swapping turned ON.\n");
send("Periodical swapping turned ON.");
}
swap_interval = i;
swap_all();
swap_co_handle = dgd_call_out("set_swap_interval", swap_interval, swap_interval);
}
else {
if (swap_interval > 0) {
log_file(DRIVER_DISK_LOG, "Periodical swapping turned OFF.\n");
send("Periodical swapping turned OFF.");
}
swap_interval = 0;
swap_co_handle = 0;
}
}
else
illegal();
}
/* Called when object is created */
static void __CREATE_DEF() {
int fatal;
string *preloads;
RLIMITS_ON();
if (object_name(this_object()) != DRIVER) {
/* ILLEGAL! */
illegal();
__DESTROY_DEF();
return;
}
#ifndef _INCLUDED_AUTO_C
::__CREATE_DEF();
#endif
#ifdef MUDOS_USER_ID
seteuid(getuid());
#endif
driver_state = 0;
fatal = 0;
/* reset user objects array */
users_array = ({ });
/* echo to stdout and write to output files */
#ifdef DRIVER_STDOUT_ECHO
driver_echo = 1;
#else
driver_echo = 0;
#endif
dgd_remove_file(DRIVER_OLD_STARTUP_LOG);
dgd_rename_file(DRIVER_STARTUP_LOG, DRIVER_OLD_STARTUP_LOG);
driver_outputfile = DRIVER_STARTUP_LOG;
send(fill_string("\\",70));
notify("Powering driver with "+DRIVERLIB_LONG_NAME+" v"+DRIVERLIB_VERSION);
notify("(C) Copyright Frank Schmidt 1998++");
send(fill_string("\\",3)+" DriverLib "+fill_string("\\",56));
/* preload some static stuff */
if (!find_object(GLOBAL)) {
send("Loading global.c...");
if (fatal |= (catch(compile_object(GLOBAL)) != 0))
send("Couldn't load global object.");
}
/* direct output to this_player() to driver */
if (!fatal) {
GLOBAL->set_this_player(this_object());
}
if (!fatal && !find_object(MASTER)) {
send("Loading master.c...");
if (fatal |= (catch(compile_object(MASTER)) != 0))
send("Couldn't load master object.");
}
if (!fatal && !find_object(USER)) {
send("Loading user.c...");
if (fatal |= (catch(compile_object(USER)) != 0))
send("Couldn't load user object.");
}
if (!fatal && !find_object(EDITOR)) {
send("Loading editor.c...");
if (fatal |= (catch(compile_object(EDITOR)) != 0))
send("Couldn't load editor object.");
}
if (!fatal && !find_object(SIMUL_EFUN)) {
send("Loading simul_efun.c...");
if (fatal |= (catch(compile_object(SIMUL_EFUN)) != 0))
send("Couldn't load simul_efun object.");
}
/* abort? */
if (fatal) {
send("Fatal error in startup. Driver shutting down.");
shutdown(1);
return;
}
/* state of driver in startup state */
log("Driver went up.");
/* Do preloading in Master object */
send(fill_string("-=",3)+" Master "+fill_string("-=",59));
fatal |= (catch(preloads = call_other(master(), __EPILOG_FUNC)) != 0);
if (!fatal && preloads && a_sizeof(preloads)) {
fatal |= (catch(map_array(preloads, __PRELOAD_FUNC, master())) != 0);
}
/* abort? */
if (fatal) {
send("Fatal error in startup. Driver shutting down.");
shutdown(1);
return;
}
/* The MUD is up! */
send(fill_string("-=",70));
send(DRIVER_SHORT_NAME+" v"+DRIVER_VERSION+" up and running.");
/* clean up this_player() */
GLOBAL->set_this_player(0);
/* set default swap- and dump intervals */
set_swap_interval(DRIVER_SWAP_INTERVAL);
set_dump_interval(DRIVER_DUMP_INTERVAL);
RLIMITS_OFF();
}
/* System started up after a reboot */
static void initialize() {
/* state of driver is now positive */
if (driver_state == 0) {
driver_state = 1;
reboot_time = time();
send("System rebooted.");
send(fill_string("\\",70));
}
/* set direction of output to our output file */
dgd_remove_file(DRIVER_OLD_OUTPUT_LOG);
dgd_rename_file(DRIVER_OUTPUT_LOG, DRIVER_OLD_OUTPUT_LOG);
driver_outputfile = DRIVER_OUTPUT_LOG;
}
/* System restarted up after a state dump */
static void restored() {
int i;
log("Driver restored a state dump from "+ctime(dump_time)+".");
send(fill_string("\\",70));
notify("Powering driver with "+DRIVERLIB_LONG_NAME+" v"+DRIVERLIB_VERSION);
notify("(C) Copyright Frank Schmidt 1998++");
send(fill_string("/",70));
#if 0
notify("Reinitializing driver.");
send(fill_string("\\",70));
#endif
send(DRIVER_SHORT_NAME+" v"+DRIVER_VERSION+" up and running.");
/* disconnect existing user objects */
if (i=a_sizeof(users_array-=({ 0 })) > 0) {
send("Disconnecting "+i+" users.");
while (--i >= 0)
users_array[i]->force_close();
}
/* clean/reset users array */
users_array -= ({ 0 });
send("Driver restored a state dump from "+ctime(dump_time)+".");
send(fill_string("/",70));
}
/* server process terminated from the outside. Called by driver. */
static void interrupt() {
log("Driver process received a kill signal.");
notify("Driver process received a kill signal.");
#ifdef DRIVER_KILL_SIGNAL_SHUTDOWNS
shutdown(-1);
#endif
}
/* driver is going to shutdown */
void do_shutdown(int exitcode) {
if (DRIVER_PRIV()) {
if (exitcode <= 0 && dump_interval > 0) {
/* dump latest copy of mud if exitcode <= 0 */
dump_all();
notify("Performed emergency dumpstate.");
}
/* state of driver is now negative */
log("Driver went down. (exitcode = "+(string)exitcode+")");
driver_state = -1;
send(fill_string("/",70));
if (exitcode != 0) /* abnormal termination? */
notify("Driver shutdown abnormally. "+
"(exitcode = "+(string)exitcode+")");
else /* exitcode 0: normal shutdown */
notify("Driver shutdown.");
send(fill_string("/",70));
}
else
illegal();
}
/* unmovable */
varargs int __MOVE_DEF(object dest) {
illegal();
}
/* destruction of driver object */
static void __DESTROY_DEF() {
if (DRIVER_PRIV()) {
#ifndef _INCLUDED_AUTO_C
::__DESTROY_DEF();
#endif
}
else
illegal();
}
/* prevent cloning of driver object! */
int __QUERY_PREVENT_CLONE_DEF() {
return 1;
}