/* * NAME: driver.c * DESCRIPTION: driver/mudlib interface */ # define DEBUG 0 inherit "/std/auto"; # undef DGD_NET /* define iff the DGD networking package is installed */ # include <objects.h> # include <moo/config.h> # include <moo/verb.h> # include <dgd/status.h> # include <dgd/type.h> # define VERS_DGD ("DGD version " + status()[ST_VERSION]) # define VERS_LPMOO(x) ("LPMOO version " + (x)) # define panic(x) do { write(x); shutdown(); return; } while (0) int swap_interval; /* seconds between full swapouts */ int swap_co_handle; /* call_out() handle */ int flags; /* status flags */ int dump_time; /* time of last state dump */ object global; /* the global object */ int ticks_second; /* how many DGD ticks in one second */ # ifdef DGD_NET mapping ports; /* current open ports */ # endif # define F_DGD_NET 0x01 /* DGD networking installed? */ # define F_BACKDOOR 0x02 /* enabled backdoor eval? */ # define F_BOOTSTRAP 0x04 /* bootstrapping? */ /* * NAME: write() * DESCRIPTION: output a message */ static void write(string msg) { send_message("[" + ctime(time())[4 .. 15] + "] ** " + msg + "\n"); } /* * NAME: create() * DESCRIPTION: called when object is created */ static void create(void) { write(VERS_DGD); global = load_object(GLOBAL); } /* * NAME: notify() * DESCRIPTION: proxy-user / debugging output */ void notify(string msg) { send_message(msg + "\n"); } /* * NAME: log() * DESCRIPTION: write a log message */ void log(string msg) { write(msg); } /* * NAME: checkpoint() * DESCRIPTION: write a binary state dump */ void checkpoint(void) { write("Writing state dump"); dump_time = time(); dump_state(); } # ifdef DGD_NET /* * NAME: close_ports() * DESCRIPTION: destroy all port objects */ static void close_ports(void) { if (ports) { object *objs; int i; objs = map_values(ports); for (i = sizeof(objs); i--; ) if (objs[i]) destruct_object(objs[i]); } ports = ([ ]); } # endif /* * NAME: do_shutdown() * DESCRIPTION: bring the mud down */ void do_shutdown(void) { # ifdef DGD_NET close_ports(); # endif checkpoint(); write("Shutting down"); shutdown(); } /* * NAME: initialize() * DESCRIPTION: called once at startup */ static void initialize(void) { object config; if (catch(config = load_object(CONFIG))) panic("Error loading MOO configuration file; please check"); write(VERS_LPMOO(config->query(CF_LPMOO_VERSION))); flags |= F_BOOTSTRAP; DBLOADER->main(config->query(CF_BOOTSTRAP_FILE)); } /* * NAME: bootstrap_finished() * DESCRIPTION: called when bootstrap has completed */ void bootstrap_finished(int success) { destruct_object(find_object(DBLOADER)); write(success ? "Bootstrap finished" : "Bootstrap failed"); do_shutdown(); } /* * NAME: do_swap() * DESCRIPTION: clean up memory */ static void do_swap(void) { swap_co_handle = call_out("do_swap", swap_interval); swapout(); } /* * NAME: query_net() * DESCRIPTION: return true iff networking is enabled */ int query_net(void) { return flags & F_DGD_NET; } /* * NAME: query_ports() * DESCRIPTION: return an array of current open ports */ int *query_ports(void) { # ifdef DGD_NET return CONFIG->query(CF_MPORT_LISTENING) ? map_indices(ports) : ({ }); # else return ({ }); # endif } /* * NAME: path_ed_read() * DESCRIPTION: handle an editor read path */ string path_ed_read(string path) { return path; } /* * NAME: path_ed_write() * DESCRIPTION: handle an editor write path */ string path_ed_write(string path) { return path; } /* * NAME: path_object() * DESCRIPTION: translate an object path */ string path_object(string path) { return path; } /* * NAME: path_inherit() * DESCRIPTION: translate an inherit path */ string path_inherit(string file, string path) { return path; } /* * NAME: path_include(string file, string path) * DESCRIPTION: translate an include path */ string path_include(string file, string path) { return path[0] == '/' ? path : file + "/../" + path; } /* * NAME: compile_object() * DESCRIPTION: used for virtual objects */ static object compile_object(string file) { return MOOOBJ_NAMEP(file) ? global->fetch_object() : 0; } /* * NAME: recompile() * DESCRIPTION: used to recompile objects */ static void recompile(object obj) { if (! sscanf(object_name(obj), "/lib/%*s")) destruct_object(obj); } # ifdef DGD_NET /* * NAME: connect() * DESCRIPTION: called to initiate an outbound TCP/IP connection */ void connect(object obj, string ipaddr, int port, string protocol) { ::connect(obj, ipaddr, port, protocol); } /* * NAME: listen() * DESCRIPTION: open a port and attach it to an object */ int listen(object listener, int port, string protocol) { object obj; if (ports[port] != 0) return E_INVARG; obj = clone_object(LISTENER); obj->configure(protocol == "telnet" ? TELNET : BINARY, listener); if (catch(open_port(obj, port, protocol))) { destruct_object(obj); return E_QUOTA; } ports[port] = obj; write("Listening on port " + (string) port + " for " + (protocol == "tcp" ? "binary" : protocol) + " connections"); return E_NONE; } /* * NAME: unlisten() * DESCRIPTION: close a port */ int unlisten(int port) { object obj; obj = ports[port]; if (obj == 0) return E_INVARG; destruct_object(obj); ports[port] = 0; write("Stopped listening on port " + (string) port); return E_NONE; } # endif /* * NAME: calibrate_ticks() * DESCRIPTION: determine how many DGD ticks in one second */ private void calibrate_ticks(void) { int timer; timer = time(); while (timer == time()) ; ticks_second = get_exec_cost(); timer = time() + 2; while (timer > time()) ; ticks_second -= get_exec_cost(); ticks_second /= 2; } /* * NAME: get_ticks_second() * DESCRIPTION: return the number of calculated DGD ticks per second */ int get_ticks_second(void) { return ticks_second; } /* * NAME: restored() * DESCRIPTION: re-initialize the system after a restore */ static void restored(void) { object config; flags = 0; write(VERS_DGD); write("State restored from dump file (" + ctime(dump_time) +")"); if (config = find_object(CONFIG)) destruct_object(config); if (catch(config = load_object(CONFIG))) panic("Error loading MOO configuration file; please check"); write(VERS_LPMOO(config->query(CF_LPMOO_VERSION))); remove_call_out(swap_co_handle); if (swap_interval = config->query(CF_SWAP_INTERVAL)) { swap_co_handle = call_out("do_swap", swap_interval); write("Swapping out every " + (swap_interval / 60) + " minutes"); } calibrate_ticks(); write("Calibrated " + (string) ticks_second + " DGD ticks per second"); if (config->query(CF_EVAL_BACKDOOR)) { flags |= F_BACKDOOR; write("Enabled built-in eval backdoor for first connection"); } # ifdef DGD_NET flags |= F_DGD_NET; if (config->query(CF_OUTBOUND_NET)) write("Outbound networking enabled"); close_ports(); if (config->query(CF_MPORT_LISTENING)) write("Multiple-port listening enabled"); else { if (listen(MOOOBJ(0), config->query(CF_TELNET_PORT), "telnet") != E_NONE) write("Unable to open telnet port"); if (listen(MOOOBJ(0), config->query(CF_BINARY_PORT), "tcp") != E_NONE) write("Unable to open binary port"); } # else write("Built-in networking only"); # endif catch(global->restart(dump_time)); write("Multi-user mode"); } /* * NAME: init_connection() * DESCRIPTION: prepare a connection object */ object init_connection(object conn, object listener, int port) { if (flags & F_BOOTSTRAP) conn->set_bootstrap(1); if (flags & F_BACKDOOR) { flags &= ~F_BACKDOOR; conn->set_backdoor(1); } conn->set_listener(listener); conn->set_port_number(port); return conn; } /* * NAME: telnet_connect() * DESCRIPTION: called when a user connects; return a player object */ object telnet_connect(void) { return init_connection(clone_object(TELNET), MOOOBJ(0), 0); } /* * NAME: binary_connect() * DESCRIPTION: handle binary connections */ object binary_connect(void) { return init_connection(clone_object(BINARY), MOOOBJ(0), 0); } /* * NAME: compile_log() * DESCRIPTION: (don't) return the name of a compile time error log */ static string compile_log(string file) { return 0; } /* * NAME: log_error() * DESCRIPTION: log a runtime error */ static void log_error(string error, int caught) { mixed **trace, *frame; int i, sz, line; string obj, prog, func; object user; user = this_user(); if (! DEBUG && error[0 .. 1] == "K!") /* killed or suspended */ { if (user) user->finish(); return; } if (! DEBUG && caught) return; sz = sizeof(trace = call_trace()) - 1; /* locate a MOO call frame */ for (i = sz - 1, frame = trace[i]; i-- && (sizeof(frame) < 7 || typeof(frame[6]) != T_ARRAY || sizeof(frame[6]) != I_LINK + 1); frame = trace[i]); if (i >= 0 && ! caught) { mixed *info; int do_traceback; info = frame[6]; if (info[I_VERBOBJ]) info[I_LINENO] = info[I_VERBOBJ]->get_lineno(); else info[I_LINENO] = -1; global->remove_refs(info); if (error[0 .. 1] == "E!") { error = error[2 ..]; do_traceback = 1; } else if (strlen(error) > 11 && error[0 .. 11] == "Maximum exec") { error = "Task ran out of ticks"; do_traceback = 1; } else if (error == "Array too large") { error = "List too long"; do_traceback = 1; } else if (strlen(error) > 16 && error[0 .. 16] == "Mapping too large") { error = "Table too large"; do_traceback = 1; } else if (error == "Too many callouts") { error = "Too many forked tasks"; do_traceback = 1; } else if (error == "String too long") do_traceback = 1; if (do_traceback) { global->traceback(info, error); if (user) user->finish(); return; } } if ((flags & F_BOOTSTRAP) && strlen(error) > 11 && error[0 .. 11] == "Maximum exec") return; write(error + (caught ? " (caught)" : "")); for (i = 0; i < sz; ++i) { string str; int len; frame = trace[i]; obj = frame[0]; prog = frame[1]; func = frame[2]; line = frame[3]; if (line == 0) str = " "; else { str = " " + line; str = str[strlen(str) - 4 ..]; } str += " " + func + " "; len = strlen(func); if (len < 17) str += " "[len ..]; str += prog; if (prog != obj) { len = strlen(prog); if (len < strlen(obj) && prog == obj[.. len - 1]) str += " (" + obj[len ..] + ")"; else str += " (" + obj + ")"; } send_message(str + "\n"); } if (flags & F_BOOTSTRAP) bootstrap_finished(0); if (user && ! caught) { user->notify("** Sorry, an internal error occurred: " + error); user->notify("** Object: " + obj + ", program: " + prog + (line == 0 ? ", function \"" + func + "\"" : ", line " + line)); user->finish(); } }