// ------------------------------------------------------------------
//
// Root Object
//
// We define some base object handling methods here
// (initialization/de-initialization etc)
//
// object variable 'child' defines whether we are a core object,
// or a spawned child object.
//
// ------------------------------------------------------------------
object $root;
var child = 0;
var index = 0;
// ----------------------------------------------
// initialization methods
private method .initialize(): nooverride {
var ancestors, ancestor, pos, len, method, name;
ancestors = ancestors();
len = ancestors.length();
for pos in [0 .. len - 1] {
ancestor = ancestors[len - pos];
name = substr(tostr(ancestor), 2);
if (!name)
continue;
method = tosym("init_" + tostr(name));
catch ~methodnf {
if (find_method(method) != ancestor) {
throw(~perm, "Initialization method for " + ancestor
+ " in wrong place("
+ find_method(method) + ")");
}
.(method)();
}
}
child = 1;
};
private method .uninitialize(): nooverride {
var ancestor, p, name, method;
for ancestor in (ancestors()) {
name = substr(tostr(ancestor), 2);
if (!name)
continue;
method = tosym("uninit_" + name);
catch ~methodnf {
if ((> find_method(method) <) != ancestor) {
throw(~perm, "Uninitialization method for " + ancestor
+ " in wrong place (" + find_method(method) + ")");
}
(> .(method)() <);
}
}
};
// ----------------------------------------------
// create/destroy objects
public method .spawn(): nooverride {
arg [name];
var base, obj;
if (child)
throw(~perm, "Attempt to spawn a non-core object (" + .objname() + ")");
obj = (> create([this()]) <);
catch any {
(> obj.initialize() <);
} with handler {
$sys.log(traceback().tb_to_text());
if (!(| obj.destroy() |))
throw(~ack, "Unable to destroy aborted attempt", traceback());
rethrow(error());
}
if (name && type(name[1]) == 'symbol)
name = name[1];
else
name = .next_objname();
(| obj.set_objname(name) |);
return obj;
};
public method .set_core() {
child = 0;
};
// children (specifically $slate) can override this to return a more
// random yet unique objname
public method .next_objname() {
index = index + 1;
return tosym(tostr((| objname() |) || objnum()) + "_" + index);
};
public method .destroy() {
if (!child)
throw(~perm, "Attempt to destroy a core object.");
catch any {
(| .uninitialize() |);
(> destroy() <);
}
};
// ----------------------------------------------
// thunk the objname handling functions
public method .set_objname(): nooverride {
arg name;
(> set_objname(name) <);
};
public method .objname(): nooverride {
arg [args];
var name;
name = (| objname() |);
if (args)
return name;
if (name)
return "$" + tostr(name);
return toliteral(this());
};
// ----------------------------------------------
// root initialization method
root method .init_root() {
child = 1;
};
// ----------------------------------------------
// so others can find out what children we have
public method .children() {
return children();
};
public method .parents() {
return parents();
};
// ------------------------------------------------------------------
//
// System Object
//
// System methods, 'global' values (primarily for the web)
//
// ------------------------------------------------------------------
object $sys;
var http_codes = #[[200, "Ok"],\
[201, "Created"],\
[202, "Accepted"],\
[203, "Provisional Information"],\
[204, "No Content"],\
[300, "Multiple Choices"],\
[301, "Moved Permanently"],\
[302, "Moved Temporarily"],\
[303, "Method"],\
[304, "Not Modified"],\
[400, "Bad Request"],\
[401, "Unauthorized"],\
[402, "Payment Required"],\
[403, "Forbidden"],\
[404, "Not Found"],\
[405, "Method Not Allowed"],\
[406, "None Acceptable"],\
[407, "Proxy Authentication Required"],\
[408, "Request Timeout"],\
[409, "Conflict"],\
[410, "Gone"],\
[500, "Internal Server Error"],\
[501, "Not Implemented"],\
[502, "Bad Gateway"],\
[503, "Service Unavailable"],\
[504, "Gateway Timeout"]];
var page_head = "";
var page_tail = "";
var primary_server = "";
var sleeping = [];
var server_name = "";
public method .log() {
arg text;
var l;
if (type(text) == 'list) {
for l in (text)
.log(l);
} else {
dblog("[" + $time.format("%d %h %y %H:%M") + "] " + text);
}
};
driver method .startup() {
arg args;
var obj, v;
catch any {
.read_config();
if ("-d" in args) {
.log("** System variables:");
for v in (variables())
.log("** " + v + " = " + toliteral(get_var(v)));
}
// cleanup old connections
for obj in ($http_connection.children() +
((| $login_connection.children() |) || []))
(| obj.destroy() |);
// start web daemon
(> $http_connection.startup( (| toint(args[1]) |) || 2000) <);
// give us a heartbeat every 1 second (sleep needs it this fast)
set_heartbeat(1);
} with handler {
(| .log("** Error Encountered upon startup:") |);
(| .log(traceback().tb_to_text()) |);
shutdown();
}
};
// see if we need to wake any sleeping objects
driver method .heartbeat() {
var task, sleepers;
sleepers = sleeping;
sleeping = [];
while (sleepers) {
task = sleepers[1];
sleeping = delete(sleepers, 1);
resume(task);
}
};
public method .sleep() {
sleeping = setadd(sleeping, task_id());
suspend();
};
driver method .signal() {
arg signal;
};
//
// this point on deals with the configuration file
//
public method .set_value() {
arg value;
var list, tag, reg, out, sub;
list = match_template("*=*", value);
if (!list)
throw(~invalid, "Invalid configuration, must be 'tag=value'");
value = list[2];
tag = tosym(lowercase(list[1].strip().trim()));
(> set_var(tag, value) <);
};
public method .page_head() {
arg [args];
var out, title;
title = [@args, server_name][1];
out = "<head><title>" + title + "</title></head>\n";
if (listlen(args) > 1)
return [out + page_head];
return [out + page_head];
};
public method .page_tail() {
arg [args];
if (args)
return [page_tail];
return page_tail;
};
public method .server_name() {
return server_name;
};
public method .topics() {
return topics;
};
public method .db_location() {
return db_location;
};
public method .sql_server() {
return sql_server;
};
public method .primary_server() {
return primary_server;
};
public method .catalog_id() {
return catalog_id;
};
public method .response() {
arg code, message;
var name, x;
if (!(name = (| http_codes[code] |)))
return .response(500, "We had a booboo! Invalid code: " + code);
return "<head><title>" + code + " " + name +
"</title></head>\n<body>\n<h1 align=center>" + code + " " + name +
"</h1>\n<hr>" + message + .page_tail();
};
public method .read_config() {
var line, count, more, tag, value, name, i;
catch ~file, ~directory {
(> fopen("config") <);
} with handler {
shutdown();
throw(~shutdown, "Unable to open 'config' file in root db filesystem.");
}
atomic(1);
catch any {
while ((line = (| fread() |)) != ~eof) {
count++;
line = line.trim();
if (!line || line[1] == "#")
continue;
if (!more) {
i = ":" in line;
if (!i)
throw(~invalid, strfmt("Line %s invalid (%25e)", line));
tag = substr(line, 1, i - 1);
value = substr(line, i + 1).trim();
} else {
if (!strcmp(line, more)) {
more = 0;
value += "\n";
} else {
value = value + "\n" + line;
continue;
}
}
if (match_begin(value, "<<")) {
more = substr(value, 3).trim();
value = "";
continue;
}
name = lowercase(strsed(tag, "-", "_", "g"));
name = (| tosym(name) |);
if (!name)
throw(~invtag, "Invalid tag '" + tag + "'");
catch any
set_var(name, value);
with { dblog("name: " + name);
throw(~invtag, "Invalid tag '" + tag + "'"); }
refresh();
}
}
atomic(0);
fclose();
};