/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
object $sys: $root;

var $root created_on = 796268969;
var $root defined_settings = #[["loggers", #[['get, ['loggers]], ['set, ['set_loggers]], ['parse, ['parse_itemlist, 'parse_object]], ['format, ['format_itemlist]]]], ["deny-hosts", #[['get, ['deny_hosts]], ['set, ['set_deny_hosts]], ['parse, ['parse_itemlist, 'parse_deny_host]], ['format, ['format_itemlist]]]], ["deny-users", #[['get, ['deny_users]], ['set, ['set_deny_users]], ['parse, ['parse_itemlist, 'parse_deny_user]], ['format, ['format_itemlist]]]], ["backup-interval", #[['get, ['get_backup_interval]], ['set, ['set_backup_interval]], ['parse, ['parse_backup_interval]], ['format, ['fmt_backup_interval]]]], ["validate-email-addresses", #[['parse, ['is_boolean]], ['format, ['format_boolean]], ['get, ['validate_email_addresses]], ['set, ['set_validate_email_addresses]]]], ["initial-quota", #[['get, ['get_initial_quota]], ['set, ['set_initial_quota]], ['parse, ['parse_initial_quota]], ['format, ['fmt_initial_quota]]]], ["new-user-class", #[['get, ['get_new_user_class]], ['set, ['set_new_user_class]], ['parse, ['parse_new_user_class]], ['format, ['format_object]]]], ["anonymous-user-class", #[['get, ['get_anon_user_class]], ['set, ['set_anon_user_class]], ['parse, ['parse_anon_user_class]], ['format, ['format_object]]]], ["startup-objects", #[['get, ['get_startup_objects]], ['set, ['set_startup_objects]], ['parse, ['parse_itemlist, 'parse_startup_object]], ['format, ['format_itemlist]]]], ["writable-core", #[['get, ['writable_core]], ['set, ['set_writable_core]], ['parse, ['is_boolean]], ['format, ['format_boolean]]]]];
var $root flags = ['methods, 'code, 'core, 'variables];
var $root inited = 1;
var $root managed = [$sys];
var $root manager = $sys;
var $root quota_exempt = 1;
var $sys admins = [];
var $sys agents = [$root, $daemon];
var $sys backup = #[['interval, 3600], ['last, 0], ['next, 0]];
var $sys bindings = #[['atomic, $sys], ['create, $sys], ['backup, $sys], ['shutdown, $sys], ['set_heartbeat, $sys], ['cancel, $scheduler], ['task_info, $scheduler], ['execute, $sys], ['bind_function, $sys], ['unbind_function, $sys], ['bind_port, $daemon], ['unbind_port, $daemon], ['open_connection, $connection], ['reassign_connection, $daemon], ['fopen, $file], ['fstat, $file], ['fchmod, $file], ['fmkdir, $file], ['frmdir, $file], ['files, $file], ['fremove, $file], ['frename, $file], ['fclose, $file], ['fseek, $file], ['feof, $file], ['fwrite, $file], ['fread, $file], ['fflush, $file], ['chparents, $root], ['destroy, $root], ['dblog, $sys], ['add_var, $root], ['del_var, $root], ['variables, $root], ['list_method, $root], ['add_method, $root], ['del_method, $root], ['method_bytecode, $root], ['methods, $root], ['rename_method, $root], ['set_method_access, $root], ['set_method_flags, $root], ['data, $root], ['del_objname, $root], ['set_objname, $root], ['suspend, $scheduler], ['resume, $scheduler], ['set_user, $user], ['config, $sys]];
var $sys core_version = "3.0a9.02";
var $sys deny_hosts = [];
var $sys deny_users = [];
var $sys loggers = [$daemon, $user, $connection, $dns];
var $sys starting = #[['quota, 76800], ['new_user_class, $admin], ['anonymous_user_class, $guest]];
var $sys startup = #[['objects, [$login_daemon, $http_daemon, $smtp_daemon, $world, $dns, $lag_watcher]], ['heartbeat_interval, 2]];
var $sys system = [$sys, $root];
var $sys touched = 0;
var $sys validate_email_addresses = 0;
var $sys writable_core = 0;

private method ._loop_for_core() {
    arg code;
    var obj;
    
    $root.add_method(code, '___coretmp);
    for obj in ($root.descendants()) {
        obj.___coretmp();
        pause();
    }
    $root.del_method('___coretmp);
};

public method ._status(): native;

public method .add_method() {
    arg code, name, @evalonly;
    var line;
    
    (> .perms(sender()) <);
    line = (("SYSTEM: ." + tostr(name)) + "() ") + (evalonly ? "EVAL" : "MODIFIED");
    line = (line + " by ") + (sender().namef('ref));
    .log(line);
    return (> pass(code, name, @evalonly) <);
};

public method .add_to_system() {
    arg obj;
    
    if (!(.is_admin(sender())))
        throw(~perm, "Sender is not an admin.");
    if (!((obj in admins) || (obj in agents)))
        throw(~perm, "Object is not an agent or admin.");
    system = system.union([obj]);
};

public method .admins() {
    return admins;
};

public method .agents() {
    return agents;
};

public method .atomic() {
    arg @args;
    
    (> .perms(sender(), 'system) <);
    return (> atomic(@args) <);
};

public method .backup() {
    return backup;
};

driver method .backup_done() {
    var elapsed;
    
    elapsed = time() - (backup['started]);
    backup = backup.del('started);
    catch any {
        $channel_ui._broadcast('System, "Backup completed, elapsed time " + ($time.elapsed(elapsed, 'long)));
        $channel_ui._broadcast('System, "Executing filesystem cleanup.. ");
        pause();
        pause();
        .execute("backup", []);
    }
};

private method .clean_database() {
    var obj, p, c, cmd, other;
    
    // cleanup some of $root's messiness
    for obj in ($root.descendants()) {
        (| obj.clean_root() |);
        refresh();
    }
    other = (((($command_cache.children()).setremove($user_interfaces)).mmap('descendants)).flatten()).compress();
    
    // purge all command caches
    for obj in ($has_commands.descendants()) {
        (| obj.purge_caches() |);
        refresh();
    }
    
    // check user info (move'em home etc)
    for obj in ($user.descendants()) {
        if ((| (obj.home()) != (obj.location()) |))
            (| obj.move_to(obj.home()) |);
        refresh();
    }
    
    // validate all locations and location content's
    for obj in ($physical.descendants()) {
        (| obj.validate_contents() |);
        if (obj.has_ancestor($located)) {
            if ((!valid(obj.location())) || (!(obj in ((obj.location()).contents()))))
                obj.move_to((| obj.home() |) || $lost_and_found);
        }
        refresh();
    }
};

root method .clear_definer_vars(): nooverride  {
    arg definer, objs;
    var code, v, a, meth, errs, d;
    
    code = [];
    for v in (definer.variables())
        code += [("(| clear_var('" + v) + ") |);"];
    meth = tosym("_root_tmp_" + time());
    if (!(definer.add_method(code, meth))) {
        for d in (objs) {
            pause();
            (| d.(meth)() |);
        }
        (| definer.del_method(meth) |);
    }
};

root method .core_sys(): nooverride  {
    deny_hosts = (deny_users = []);
    touched = 0;
    system = [$sys, $root];
    backup = #[['interval, 3600], ['last, 0], ['next, 0]];
    validate_email_addresses = 0;
    starting = #[['quota, 76800], ['new_user_class, $admin], ['anonymous_user_class, $guest]];
    startup = #[['objects, [$login_daemon, $http_daemon, $smtp_daemon, $world, $dns, $lag_watcher]], ['heartbeat_interval, 2]];
    agents = [$root, $daemon];
    admins = [];
    writable_core = 0;
};

private method .create() {
    arg parents, name, manager;
    var new;
    
    new = create(parents);
    catch any {
        new.set_objname(name);
        new.initialize();
        new.change_manager(manager);
    } with {
        // Failed to initialize the child; destroy it.
        (| new.destroy() |);
        rethrow(error());
    }
    return new;
};

public method .create_user() {
    arg name, password, email, @type;
    var user;
    
    if ((!(| .perms(caller(), $login_interface) |)) && (!(| .perms(sender(), 'system) |)))
        throw(~perm, "Caller and Sender are not allowed to call this method.");
    [(type ?= 'new_user_class)] = type;
    catch any {
        user = (starting[type]).spawn(name);
        user.set_name(name);
        if (type == 'new_user_class)
            user.set_password(password);
        user.change_manager(user);
        user.set_user_info("rl-email", $user_info, email);
    } with {
        // Failed to initialize the child; destroy it.
        (| user.destroy() |);
        rethrow(error());
    }
    return user;
};

public method .del_from_system() {
    arg obj;
    
    if (!(.is_admin(sender())))
        throw(~perm, "Sender is not an admin.");
    if (!((obj in admins) || (obj in agents)))
        throw(~perm, "Object is not an agent or admin.");
    system = system.setremove(obj);
};

public method .deny_hosts() {
    arg @args;
    
    return deny_hosts;
};

public method .deny_users() {
    arg @args;
    
    return deny_users;
};

public method .destroy_sender() {
    // potential problem spot, but sometimes its needed
    // add core definer items to the list, if you want them to call it
    (> .perms(caller(), $exit, $connection, $connection_interface) <);
    (> sender().destroy() <);
};

public method .dirty() {
    return touched > (backup['last]);
};

public method .do_backup() {
    arg @args;
    var line, who, how, name, dirty;
    
    (> .perms(sender(), 'system) <);
    dirty = .dirty();
    [(who ?= sender()), (how ?= 'request)] = args;
    if (!valid(who))
        who = sender();
    backup = backup.add('next, time() + (backup['interval]));
    backup = backup.add('last, time());
    if ((how == 'interval) && (!dirty))
        return;
    catch any {
        name = who.namef('ref);
        .log(("BACKUP (" + name) + ") ");
        line = (("Backup started at " + ($time.format("%r"))) + " by ") + name;
        $channel_ui._broadcast('System, line);
    }
    
    // double pause will hopefully let people know about it before it happens
    pause();
    pause();
    backup = backup.add('started, time());
    (> backup() <);
};

public method .do_shutdown() {
    arg time, why;
    var increments, line, name, mins;
    
    if ((!($sys.is_admin(sender()))) || (definer() != this()))
        throw(~perm, "Sender is not an admin.");
    increments = [600, 300, 180, 60];
    while (increments && (time < (increments[1])))
        increments = increments.delete(1);
    name = sender().namef('xref);
    .log(("*** SHUTDOWN called by " + name) + " ***");
    if (why) {
        why = ("*** REASON: " + why) + " ***";
        .log(why);
    }
    while (1) {
        if (!increments) {
            $channel_ui._broadcast('all, "*** SYSTEM SHUTDOWN ***");
            if (why)
                $channel_ui._broadcast('all, why);
            break;
        }
        line = "*** SYSTEM SHUTDOWN IN ";
        mins = (increments[1]) / 60;
        line = ((line + tostr(mins)) + " MINUTE") + ((mins == 1) ? "" : "S");
        line = ((line + " CALLED BY ") + name) + " ***";
        $channel_ui._broadcast('all, line);
        if (why)
            $channel_ui._broadcast('all, why);
        $scheduler.sleep(increments[1]);
        increments = increments.delete(1);
    }
    pause();
    pause();
    return .shutdown();
};

public method .execute() {
    arg script, args, @background;
    
    (> .perms(sender(), 'system) <);
    (> execute(script, args, @background) <);
};

private method .finish_core() {
    // cleanup heartbeat
    set_heartbeat(0);
    .add_method(TMP_HEARTBEAT_CODE, 'heartbeat);
    .del_var('TMP_HEARTBEAT_CODE);
    
    // ok, finish up
    catch any {
        dblog("** corifying remaining objects");
        $root.corify();
        dblog("** cleaning database..");
        .clean_database();
        dblog("** shutting down..");
        shutdown();
    } with {
        dblog("traceback: " + traceback());
    }
};

public method .fmt_backup_interval() {
    arg data;
    
    return $time.to_english(data);
};

public method .fmt_initial_quota() {
    arg data;
    
    return data.to_bytes();
};

public method .get_anon_user_class() {
    arg @args;
    
    return starting['anonymous_user_class];
};

public method .get_backup_interval() {
    arg @args;
    
    return backup['interval];
};

public method .get_initial_quota() {
    arg @args;
    
    return starting['quota];
};

public method .get_new_user_class() {
    arg @args;
    
    return starting['new_user_class];
};

public method .get_starting() {
    arg what;
    
    return starting[what];
};

public method .get_startup() {
    arg what;
    
    return startup[what];
};

public method .get_startup_objects() {
    arg @args;
    
    return startup['objects];
};

driver method .heartbeat() {
    if (sender() != 0)
        throw(~perm, "Sender is not the server.");
    (| $scheduler.pulse() |);
    if (time() > (backup['next]))
        .do_backup(this(), 'interval);
};

public method .host_denied() {
    arg remote;
    var t;
    
    for t in (deny_hosts) {
        if (match_begin(remote, t) != 0)
            return 1;
    }
    return 0;
};

private method .init_database() {
    var obj, p, c, cmd, other;
    
    // get back caches
    other = (((($command_cache.children()).setremove($user_interfaces)).mmap('descendants)).flatten()).compress();
    for obj in (other) {
        (| obj.rehash_caches() |);
        refresh();
    }
    
    // create location caches
    for obj in ((| $location.descendants() |) || []) {
        for p in (obj.contents()) {
            (| obj.add_object_to_remote_cache(p) |);
            refresh();
        }
        refresh();
    }
};

private method .initialize_core() {
    var obj;
    
    (| .clean_database() |);
    
    // reset child indices
    ._loop_for_core(["child_index = 0;"]);
};

public method .is_admin() {
    arg obj;
    
    return (obj == $sys) || (obj in admins);
};

public method .is_system() {
    arg obj;
    
    return obj in system;
};

public method .log() {
    arg text;
    var l;
    
    if ((!sender()) in loggers) {
        if ((!(| .perms(sender(), 'system) |)) && (!(| .perms(caller(), 'system) |)))
            throw(~perm, "Only system objects can log.");
    }
    if (type(text) == 'list) {
        for l in (text)
            .log(l);
    } else {
        dblog((("[" + ($time.format("%d %h %y %H:%M"))) + "] ") + text);
    }
};

public method .loggers() {
    arg @args;
    
    return loggers;
};

private method .make_core() {
    arg ver;
    var obj, d, o, top, x, admin, tmp, name;
    
    // core rooms should be +core, and cant be destroyed
    // traverse the list inverseley, less unseen heirarchial shuffling
    dblog("** Starting Core Extraction " + ctime());
    d = $root.descendants();
    top = listlen(d);
    core_version = ver;
    dblog(("** " + top) + " objects total, tidying up core objects...");
    
    // tidy up core objects first
    for obj in (d) {
        refresh();
        if (obj.has_flag('core)) {
            tmp++;
            catch any {
                obj.change_manager(obj);
                for o in (obj.writers('literal)) {
                    refresh();
                    obj.del_writer(o);
                }
            } with {
                dblog(("** TIDY " + obj) + " traceback: ");
                for o in ($parse_lib.traceback(traceback()))
                    dblog("**   " + o);
            }
        }
    }
    
    // nuke anything not core
    dblog(("** " + tmp) + " core objects, destroying non-core objects...");
    for x in [1 .. top] {
        refresh();
        obj = d[(top - x) + 1];
        if (!valid(obj)) {
            // dblog(".. Skipping invalid object " + obj);
            continue;
        }
        if (!(obj.has_flag('core))) {
            if (obj.children()) {
                dblog("** ABORTING: A NON CORE OBJECT HAS CORE CHILDREN: ");
                dblog((("** " + obj) + " => ") + (obj.children()));
                shutdown();
            }
            catch any {
                // dblog(".. Destroying " + obj);
                obj.destroy();
            } with {
                dblog(("** " + obj) + ".destroy() traceback: ");
                for o in ($parse_lib.traceback(traceback()))
                    dblog("**   " + o);
            }
        }
    }
    
    // shutdown this task so that references can have
    // a chance to clear out, on destroyed objects.
    dblog("** done, suspending for new task **");
    
    // do this the hard way, to be secure
    set_heartbeat(1);
    .add_var('TMP_HEARTBEAT_CODE, .list_method('heartbeat));
    .add_method([".finish_core();"], 'heartbeat);
};

public method .new_admin() {
    if (caller() != $admin)
        throw(~perm, "Caller is not $admin.");
    admins = setadd(admins, sender());
};

public method .next_objnum(): native;

public method .old_admin() {
    if (caller() != $admin)
        throw(~perm, "Caller is not $admin.");
    admins = setremove(admins, sender());
    system = setremove(system, sender());
};

public method .parse_anon_user_class() {
    arg value, @args;
    
    value = (> $object_lib.to_dbref(value) <);
    if (!(value.is($user)))
        throw(~perm, (value.namef('ref)) + " is not a $user object.");
    if (!(value.has_flag('command_cache)))
        throw(~perm, (value.namef('ref)) + " is not a command cache object.");
    return value;
};

public method .parse_backup_interval() {
    arg value, @args;
    
    value = (> $time.from_english(value) <);
    if (value < 300)
        throw(~perm, "You cannot set your backup interval to less than 5 minutes");
    return value;
};

public method .parse_deny_host() {
    arg value, @args;
    var parts;
    
    // DNS Hostnames are not used due to the extreme slowdown it would
    // cause to _ALL_ services
    if (match_regexp(value, "[^0-9.]"))
        throw(~type, "Invalid Internet Host IP address or subnet: " + value);
    parts = explode(value, ".");
    if (listlen(parts) < 2)
        throw(~type, "Do you really want to deny an entire class A network?");
    if (listlen(parts) > 4)
        throw(~type, "Invalid IP Address: " + (parts.join(".")));
    return parts.join(".");
};

public method .parse_deny_user() {
    arg value, @args;
    var obj;
    
    if (value && ((value[1]) == "$")) {
        obj = (> $object_lib.to_dbref(value) <);
        if (!(obj.is($user)))
            throw(~perm, (obj.namef('ref)) + " is not a user.");
    } else {
        obj = (> $user_db.search(value) <);
    }
    return obj;
};

public method .parse_initial_quota() {
    arg value, @args;
    
    value = (> value.to_bytes() <);
    if (value < 1024)
        throw(~perm, "You cannot set your initial quota to less than 1 kb");
    return value;
};

public method .parse_new_user_class() {
    arg value, @args;
    
    value = (> $object_lib.to_dbref(value) <);
    if (!(value.is($user)))
        throw(~perm, (value.namef('ref)) + " is not a $user object.");
    if (!(value.has_flag('command_cache)))
        throw(~perm, (value.namef('ref)) + " is not a command cache object.");
    return value;
};

public method .parse_startup_object() {
    arg value, action, @args;
    
    value = (> $object_lib.to_dbref(value) <);
    if (action == 'del) {
        if (!(value in (startup['objects])))
            throw(~failed, ("The object '" + value) + "' is not set, and thus cannot be removed");
        return value;
    }
    return value;
};

root method .sender_going_away() {
    admins = admins.setremove(sender());
    agents = agents.setremove(sender());
    system = system.setremove(sender());
};

public method .server_info() {
    arg what, @long;
    var tmp;
    
    switch (what) {
        case 'up_time:
            return time() - (startup['time]);
        case 'startup_time:
            return startup['time];
        case 'server_hostname:
            return $dns.hostname("");
        case 'server_ip:
            return $dns.ip("");
        case 'last_backup:
            return backup['last];
        case 'driver_version:
            tmp = .version();
            return (((((((long ? "Cold Genesis " : "") + (tmp[1])) + ".") + (tmp[2])) + "p") + (tmp[3])) + "-") + ((listlen(tmp) == 3) ? "NEED TO UPGRADE" : (tmp[4]));
        case 'core_version:
            return (long ? "ColdCore " : "") + core_version;
        default:
            throw(~unknown, "Unknown flag.");
    }
};

protected method .set_anon_user_class() {
    arg name, definer, value;
    
    starting = starting.add('anon_user_class, value);
};

protected method .set_backup_interval() {
    arg name, definer, value;
    
    backup = backup.add('interval, value);
};

protected method .set_deny_hosts() {
    arg name, definer, value;
    
    switch (value[1]) {
        case 'set:
            deny_hosts = value[2];
        case 'add:
            deny_hosts = setadd(deny_hosts, value[2]);
        case 'del:
            deny_hosts = setremove(deny_hosts, value[2]);
        default:
            throw(~type, "Unknown action: " + (value[1]));
    }
};

protected method .set_deny_users() {
    arg name, definer, value;
    
    switch (value[1]) {
        case 'set:
            deny_users = value[2];
        case 'add:
            deny_users = setadd(deny_users, value[2]);
        case 'del:
            deny_users = setremove(deny_users, value[2]);
        default:
            throw(~type, "Unknown action: " + (value[1]));
    }
};

protected method .set_initial_quota() {
    arg name, definer, value;
    
    starting = starting.add('quota, value);
};

protected method .set_loggers() {
    arg name, definer, value;
    
    switch (value[1]) {
        case 'set:
            loggers = value[2];
        case 'add:
            loggers = setadd(loggers, value[2]);
        case 'del:
            loggers = setremove(loggers, value[2]);
        default:
            throw(~type, "Unknown action: " + (value[1]));
    }
};

protected method .set_new_user_class() {
    arg name, definer, value;
    
    starting = starting.add('new_user_class, value);
};

public method .set_starting() {
    arg what, value;
    var valid;
    
    (> .perms(sender(), 'system) <);
    valid = starting.keys();
    if ((!what) in valid)
        throw(~type, "Key must be one of " + toliteral(valid));
    starting = starting.add(what, value);
};

public method .set_startup() {
    arg what, value;
    var valid;
    
    (> .perms(sender(), 'system) <);
    valid = startup.keys();
    if ((!what) in valid)
        throw(~type, "Key must be one of " + toliteral(valid));
    startup = startup.add(what, value);
};

protected method .set_startup_objects() {
    arg name, definer, value;
    
    switch (value[1]) {
        case 'set:
            startup = startup.add('objects, value[2]);
        case 'add:
            startup = startup.add('objects, setadd(startup['objects], value[2]));
        case 'del:
            startup = startup.add('objects, setremove(startup['objects], value[2]));
        default:
            throw(~type, "Unknown action: " + (value[1]));
    }
};

protected method .set_validate_email_addresses() {
    arg name, definer, value;
    
    validate_email_addresses = value;
};

protected method .set_writable_core() {
    arg name, definer, value;
    
    writable_core = value;
};

public method .shutdown() {
    var opt, str, obj;
    
    (> .perms(sender(), $sys) <);
    
    // tell startup objects that we are closing shop
    for obj in (startup['objects]) {
        .log(("Calling " + obj) + ".shutdown()");
        catch any
            (> obj.shutdown() <);
        with
            .log($parse_lib.traceback(traceback()));
    }
    touched = 0;
    return shutdown();
};

driver method .signal() {
    arg signal;
    var line;
    
    // the driver will send the following signals:
    //   HUP  -- driver will drop out of atomic mode and will flush i/o
    //   USR2 -- driver does nothing
    //   USR1 -- driver aborts all currently executing tasks, not suggested
    //   QUIT -- For both of these the driver will shutdown after current
    //   INT     tasks finish their current execution frame.  Note: do not
    //           preempt tasks at this point as they will not resume.
    line = ("** Received Signal: " + signal) + " **";
    (| $channel_ui._broadcast('System, line) |);
    if (sig in ['QUIT, 'INT]) {
        (| $channel_ui._broadcast('all, "******************************") |);
        (| $channel_ui._broadcast('all, "** IMMINENT SERVER SHUTDOWN **") |);
        (| $channel_ui._broadcast('all, "******************************") |);
    }
};

public method .spawn_selfmanager() {
    arg name, @writers;
    var obj, w;
    
    // Differs from .spawn_sender in that the object created manages itself.  
    // Add to this list as necessary:
    (> .perms(sender(), $mail_message) <);
    obj = (> .create([sender()], name, this()) <);
    for w in (writers)
        obj.add_writer(w);
    obj.change_manager(obj);
    return obj;
};

public method .spawn_sender() {
    arg suffix, manager;
    var namestr;
    
    (> .perms(caller(), $root, $sys) <);
    namestr = (tostr(sender().objname()) + "_") + suffix;
    return .create([sender()], tosym(namestr), manager);
};

driver method .startup() {
    arg args;
    var opt, str, obj, f, ver, firsttime, doinit;
    
    set_heartbeat(0);
    
    // clean db?
    if ("-clean" in args) {
        catch any {
            dblog("** Cleaning Database..");
            (> .clean_database() <);
            dblog("** Done.");
            if (dict_contains(startup, 'time))
                startup = dict_del(startup, 'time);
        } with {
            .log($parse_lib.traceback(traceback()));
        }
        doinit = "-init" in args;
    } else {
        doinit = ("-init" in args) || (!dict_contains(startup, 'time));
    }
    
    // init?
    if (doinit) {
        catch any {
            dblog("** Initializing Database..");
            (> .init_database() <);
            dblog("** Done.");
        } with {
            .log($parse_lib.traceback(traceback()));
        }
    }
    
    // done?
    if ("-quit" in args) {
        dblog("** Shutting down.");
        .shutdown();
        return;
    }
    
    // make core?
    if ((opt = find opt in (args) where (opt.match_begin("-makecore=")))) {
        ver = regexp(args[opt], "-MAKECORE=(.*)$")[1];
        dblog(("** Calling .make_core(\"" + ver) + "\")..");
        .make_core(ver);
        return;
    }
    
    // Standard startup..
    startup = startup.add('time, time());
    backup = backup.add('next, time() + (backup['interval]));
    catch any {
        // get back the heartbeat
        set_heartbeat(startup['heartbeat_interval]);
    
        // Bind functions for security
        for f in (bindings) {
            catch any
                bind_function(@f);
            with
                dblog((("** Unable to bind function " + (f[1])) + "() to ") + (f[2]));
        }
    
        // tell objects who should know, that we are online
        if (type(args) != 'list)
            args = [];
        for obj in (startup['objects]) {
            .log(("Calling " + obj) + ".startup()");
            catch any
                (> obj.startup(@args) <);
            with
                .log($parse_lib.traceback(traceback()));
        }
    } with {
        .log(("** Startup ERROR at " + ctime()) + ":");
        .log(toliteral(traceback()));
        .log($parse_lib.traceback(traceback(), -1, ""));
    }
};

public method .startup_dnsserv() {
    if (sender() != $dns)
        (> .perms(sender(), 'system) <);
    execute("dnsstartup", [], 1);
};

public method .status() {
    return (> ._status() <) + [backup['interval], backup['last], backup['next]];
};

public method .system() {
    return system;
};

public method .task_info() {
    arg @args;
    
    (> .perms(sender(), 'system) <);
    return (> task_info(@args) <);
};

public method .touch() {
    arg @nocorelock;
    
    if ((!nocorelock) && ((sender().has_flag('core)) && (!writable_core)))
        throw(~perm, sender() + " is a core object, and the core isn't writable.");
    touched = time();
};

public method .touched() {
    return touched;
};

public method .user_denied() {
    arg user;
    
    return user in deny_users;
};

public method .validate_email_addresses() {
    arg @args;
    
    return validate_email_addresses;
};

public method .version(): native;

public method .writable_core() {
    arg @args;
    
    return writable_core;
};

bind_native .status() ._status();