MudOSa4DGD/
MudOSa4DGD/bin/
MudOSa4DGD/data/
MudOSa4DGD/doc/
MudOSa4DGD/doc/driver/
MudOSa4DGD/doc/efun/bitstrings/
MudOSa4DGD/doc/efun/command/
MudOSa4DGD/doc/efun/communication/
MudOSa4DGD/doc/efun/heart_beat/
MudOSa4DGD/doc/efun/interactive/
MudOSa4DGD/doc/efun/inventory/
MudOSa4DGD/doc/efun/living/
MudOSa4DGD/doc/efun/mappings/
MudOSa4DGD/doc/efun/strings/
MudOSa4DGD/doc/efun/uid/
MudOSa4DGD/doc/funs/
MudOSa4DGD/doc/language/
MudOSa4DGD/mudlib/dgd/doc/
MudOSa4DGD/mudlib/dgd/lib/include/dgd/
MudOSa4DGD/mudlib/dgd/lib/std/
MudOSa4DGD/mudlib/dgd/lib/sys/
MudOSa4DGD/mudlib/dgd/log/
MudOSa4DGD/mudlib/log/
MudOSa4DGD/mudlib/std/include/
MudOSa4DGD/mudlib/std/obj/
/*
 * 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;
}