// ------------------------------------------------------------------ // // 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(); };