# include <kernel/kernel.h> # include <kernel/objreg.h> # include <kernel/rsrc.h> # include <kernel/access.h> # include <kernel/user.h> # include <status.h> # include <type.h> # include <trace.h> # define TLSVAR2 ::call_trace()[1][TRACE_FIRSTARG][1] # define LONG_TIME (365 * 24 * 60 * 60) # define CHECKARG(arg, n, func) if (!(arg)) badarg((n), (func)) /* * NAME: badarg() * DESCRIPTION: called when an argument check failed */ private void badarg(int n, string func) { error("Bad argument " + n + " for function " + func); } private object prev, next; /* previous and next in linked list */ private string creator, owner; /* creator and owner of this object */ private mapping resources; /* resources associated with this object */ private mapping events; /* events for this object */ nomask void _F_prev(object obj) { if (previous_program() == OBJREGD) prev = obj; } nomask void _F_next(object obj) { if (previous_program() == OBJREGD) next = obj; } nomask object _Q_prev() { if (previous_program() == OBJREGD) return prev; } nomask object _Q_next() { if (previous_program() == OBJREGD) return next; } /* * NAME: query_owner() * DESCRIPTION: query the owner of an object */ nomask string query_owner() { return owner; } /* * NAME: _F_rsrc_incr() * DESCRIPTION: increase/decrease a resource associated with this object */ nomask void _F_rsrc_incr(string rsrc, int incr) { if (previous_program() == RSRCOBJ) { if (!resources) { resources = ([ rsrc : incr ]); } else if (!resources[rsrc]) { resources[rsrc] = incr; } else if (!(resources[rsrc] += incr)) { resources[rsrc] = nil; } } } void create(varargs int clone) { } /* default high-level create function */ /* * NAME: _F_create() * DESCRIPTION: kernel creator function */ nomask void _F_create() { if (!prev) { string oname; # ifdef CREATOR string cname; # endif object driver; int clone, number; rlimits (-1; -1) { /* * set creator and owner */ oname = object_name(this_object()); driver = ::find_object(DRIVER); creator = driver->creator(oname); clone = !!sscanf(oname, "%s#%d", oname, number); if (clone) { owner = TLSVAR2; } else { owner = creator; } if (number >= 0) { /* * register object */ if (oname != BINARY_CONN && oname != TELNET_CONN && oname != OBJREGD) { ::find_object(OBJREGD)->link(this_object(), owner); if (clone) { driver->clone(this_object(), owner); } } } else { /* * new non-persistent object */ prev = driver; } # ifdef CREATOR cname = function_object(CREATOR, this_object()); if (cname && sscanf(cname, USR_DIR + "/System/%*s") != 0) { /* extra initialisation function */ if (call_other(this_object(), CREATOR, clone)) { return; } } # endif } /* call higher-level creator function */ if (sscanf(oname, "%*s" + CLONABLE_SUBDIR) == 0 && sscanf(oname, "%*s" + LIGHTWEIGHT_SUBDIR) == 0) { create(); } else { create(clone); } } } /* * NAME: _F_destruct() * DESCRIPTION: prepare object for being destructed */ nomask void _F_destruct() { if (previous_program() == AUTO) { object rsrcd; int i, j; rsrcd = ::find_object(RSRCD); if (events) { object **evtlist, *objlist, obj; /* * decrease resources of other objects subscribed to events */ evtlist = map_values(events); i = sizeof(evtlist); while (--i >= 0) { j = sizeof(objlist = evtlist[i] - ({ nil })); while (--j >= 0) { obj = objlist[j]; if (obj != this_object()) { rsrcd->rsrc_incr(obj->query_owner(), "events", obj, -1); } } } } /* * remove callouts */ rsrcd->remove_callouts(this_object(), owner, (resources && resources["callouts"]) ? resources["callouts"] : 0); if (resources) { string *names; int *values; /* * decrease resources associated with object */ names = map_indices(resources); values = map_values(resources); i = sizeof(names); while (--i >= 0) { rsrcd->rsrc_incr(owner, names[i], this_object(), -values[i]); } } if (next) { ::find_object(OBJREGD)->unlink(this_object(), owner); if (sscanf(object_name(this_object()), "%*s#") != 0) { /* * non-clones are handled by driver->remove_program() */ rsrcd->rsrc_incr(owner, "objects", nil, -1); } } } } /* * NAME: find_object() * DESCRIPTION: find an object */ static object find_object(string path) { CHECKARG(path, 1, "find_object"); if (!this_object()) { return nil; } path = ::find_object(DRIVER)->normalize_path(path, object_name(this_object()) + "/..", creator); if (sscanf(path, "%*s" + INHERITABLE_SUBDIR) != 0) { /* * It is not possible to find a lib object by name, or to call a * function in it. */ return nil; } return ::find_object(path); } /* * NAME: destruct_object() * DESCRIPTION: destruct an object */ static int destruct_object(mixed obj) { object driver; string oname, oowner; int lib; /* check and translate argument */ driver = ::find_object(DRIVER); if (typeof(obj) == T_STRING) { if (!this_object()) { return FALSE; } obj = ::find_object(driver->normalize_path(obj, object_name(this_object()) + "/..", creator)); if (!obj) { return FALSE; } } else { CHECKARG(typeof(obj) == T_OBJECT, 1, "destruct_object"); if (!this_object()) { return FALSE; } } /* * check privileges */ oname = object_name(obj); if (sscanf(oname, "%s#%d", oname, lib) != 0 && lib < 0) { error("Cannot destruct non-persistent object"); } lib = sscanf(oname, "%*s" + INHERITABLE_SUBDIR); oowner = (lib) ? driver->creator(oname) : obj->query_owner(); if ((sscanf(oname, "/kernel/%*s") != 0 && !lib && !KERNEL()) || (creator != "System" && owner != oowner)) { error("Cannot destruct object: not owner"); } rlimits (-1; -1) { if (!lib) { if (oname != BINARY_CONN && oname != TELNET_CONN) { driver->destruct(obj, oowner); } obj->_F_destruct(); } else { driver->destruct_lib(object_name(obj), oowner); } ::destruct_object(obj); } return TRUE; } /* * NAME: compile_object() * DESCRIPTION: compile a master object */ static object compile_object(string path, varargs string source) { string oname, uid; object driver, rsrcd, obj; int *rsrc, lib, new, stack, ticks; CHECKARG(path, 1, "compile_object"); if (!this_object()) { error("Permission denied"); } /* * check access */ oname = object_name(this_object()); driver = ::find_object(DRIVER); path = driver->normalize_path(path, oname + "/..", creator); lib = sscanf(path, "%*s" + INHERITABLE_SUBDIR); uid = driver->creator(path); if (uid && creator != "System" && !::find_object(ACCESSD)->access(oname, path, (sscanf(path, "/kernel/%*s") == 0 && lib && !source) ? READ_ACCESS : WRITE_ACCESS)) { error("Access denied"); } /* * check resource usage */ rsrcd = ::find_object(RSRCD); rsrc = rsrcd->rsrc_get(uid, "objects"); if (rsrc[RSRC_USAGE] >= rsrc[RSRC_MAX] && rsrc[RSRC_MAX] >= 0) { error("Too many objects"); } /* * do the compiling */ new = !::find_object(path); stack = ::status()[ST_STACKDEPTH]; ticks = ::status()[ST_TICKS]; rlimits (-1; -1) { catch { if (new && !lib) { if ((stack >= 0 && stack - 2 < rsrcd->rsrc_get(uid, "create stack")[RSRC_MAX]) || (ticks >= 0 && ticks < rsrcd->rsrc_get(uid, "create ticks")[RSRC_MAX])) { error("Insufficient stack or ticks to create object"); } } driver->compiling(path); if (source) { obj = ::compile_object(path, source); } else { obj = ::compile_object(path); } if (new) { rsrcd->rsrc_incr(uid, "objects", nil, 1, TRUE); } if (lib) { driver->compile_lib(path, uid, source); } else { driver->compile(obj, uid, source); } } : { driver->compile_failed(path, uid); rlimits (stack; ticks) { error(TLSVAR2); } } } if (new && !lib) { call_other(obj, "???"); /* initialize & register */ } return (lib) ? nil : obj; } /* * NAME: clone_object() * DESCRIPTION: clone an object */ static object clone_object(string path, varargs string uid) { string oname; object rsrcd, obj; int *rsrc, stack, ticks; CHECKARG(path, 1, "clone_object"); if (uid) { CHECKARG(creator == "System", 1, "clone_object"); } else { uid = owner; } if (!this_object()) { error("Permission denied"); } /* * check access */ oname = object_name(this_object()); path = ::find_object(DRIVER)->normalize_path(path, oname + "/..", creator); if ((sscanf(path, "/kernel/%*s") != 0 && !KERNEL()) || (creator != "System" && !::find_object(ACCESSD)->access(oname, path, READ_ACCESS))) { /* * kernel objects can only be cloned by kernel objects, and cloning * in general requires read access */ error("Access denied"); } /* * check if object can be cloned */ if (!owner || !(obj=::find_object(path)) || sscanf(path, "%*s" + CLONABLE_SUBDIR) == 0 || sscanf(path, "%*s" + LIGHTWEIGHT_SUBDIR) != 0 || sscanf(path, "%*s" + INHERITABLE_SUBDIR) != 0) { /* * no owner for clone, master object not compiled, or not path of * clonable */ error("Cannot clone " + path); } /* * check resource usage */ rsrcd = ::find_object(RSRCD); if (path != BINARY_CONN && path != TELNET_CONN && path != RSRCOBJ) { rsrc = rsrcd->rsrc_get(uid, "objects"); if (rsrc[RSRC_USAGE] >= rsrc[RSRC_MAX] && rsrc[RSRC_MAX] >= 0) { error("Too many objects"); } } if (::status()[ST_NOBJECTS] == ::status()[ST_OTABSIZE]) { error("Too many objects"); } /* * do the cloning */ stack = ::status()[ST_STACKDEPTH]; ticks = ::status()[ST_TICKS]; catch { rlimits (-1; -1) { if ((stack >= 0 && stack - 2 < rsrcd->rsrc_get(uid, "create stack")[RSRC_MAX]) || (ticks >= 0 && ticks < rsrcd->rsrc_get(uid, "create ticks")[RSRC_MAX])) { error("Insufficient stack or ticks to create object"); } if (path != BINARY_CONN && path != TELNET_CONN && path != RSRCOBJ) { rsrcd->rsrc_incr(uid, "objects", nil, 1, TRUE); } TLSVAR2 = uid; } } : error(TLSVAR2); return ::clone_object(obj); } /* * NAME: new_object() * DESCRIPTION: create a new non-persistent object */ static object new_object(mixed obj, varargs string uid) { string str; object rsrcd; int new, stack, ticks; switch (typeof(obj)) { case T_STRING: str = object_name(this_object()); str = ::find_object(DRIVER)->normalize_path(obj, str + "/..", creator); obj = ::find_object(str); new = TRUE; break; case T_OBJECT: str = object_name(obj); if (sscanf(str, "%*s#-1") == 0) { error("new_object() requires non-persistent object argument"); } new = FALSE; break; default: error("Bad argument 1 for function new_object"); } if (uid) { CHECKARG(new && creator == "System", 1, "new_object"); } else { uid = owner; } if (!this_object()) { error("Permission denied"); } /* * create the object */ if (new) { /* * check if object can be created */ if (!obj || sscanf(str, "%*s" + LIGHTWEIGHT_SUBDIR) == 0 || sscanf(str, "%*s" + CLONABLE_SUBDIR) != 0 || sscanf(str, "%*s" + INHERITABLE_SUBDIR) != 0) { /* * master object not compiled, or not path of non-persistent object */ error("Cannot create new instance of " + str); } rsrcd = ::find_object(RSRCD); stack = ::status()[ST_STACKDEPTH]; ticks = ::status()[ST_TICKS]; catch { rlimits (-1; -1) { if ((stack >= 0 && stack - 2 < rsrcd->rsrc_get(uid, "create stack")[RSRC_MAX]) || (ticks >= 0 && ticks < rsrcd->rsrc_get(uid, "create ticks")[RSRC_MAX])) { error("Insufficient stack or ticks to create object"); } TLSVAR2 = uid; } } : error(TLSVAR2); } return ::new_object(obj); } /* * NAME: call_trace() * DESCRIPTION: call trace */ static mixed **call_trace() { mixed **trace, *call; int i; object driver; trace = ::call_trace(); if (creator != "System") { driver = ::find_object(DRIVER); for (i = sizeof(trace) - 1; --i >= 0; ) { if (sizeof(call = trace[i]) > TRACE_FIRSTARG && creator != driver->creator(call[TRACE_PROGNAME])) { /* remove arguments */ trace[i] = call[.. TRACE_FIRSTARG - 1]; } } } return trace; } /* * NAME: status() * DESCRIPTION: get information about an object */ static mixed *status(varargs mixed obj) { object driver; string oname; mixed *status, **callouts, *co; int i; if (!this_object()) { return nil; } if (!obj) { mixed *precompiled; status = ::status(); if (status[ST_STACKDEPTH] >= 0) { status[ST_STACKDEPTH]++; } precompiled = status[ST_PRECOMPILED]; if (precompiled) { for (i = sizeof(precompiled); --i >= 0; ) { precompiled[i] = object_name(precompiled[i]); } } return status; } /* * check arguments */ driver = ::find_object(DRIVER); if (typeof(obj) == T_STRING) { /* get corresponding object */ obj = ::find_object(driver->normalize_path(obj, object_name(this_object()) + "/..", creator)); if (!obj) { return nil; } } CHECKARG(typeof(obj) == T_OBJECT, 1, "status"); status = ::status(obj); callouts = status[O_CALLOUTS]; if (callouts && (i=sizeof(callouts)) != 0) { oname = object_name(obj); if (sscanf(oname, "/kernel/%*s") != 0) { /* can't see callouts in kernel objects */ status[O_CALLOUTS] = ({ }); } else if (obj != this_object() && creator != "System" && (!owner || owner != obj->query_owner())) { /* remove arguments from callouts */ do { --i; co = callouts[i]; callouts[i] = ({ co[CO_HANDLE], co[CO_FIRSTXARG], (co[CO_FIRSTXARG + 1]) ? 0 : co[CO_DELAY] }); } while (i != 0); } else { do { --i; co = callouts[i]; callouts[i] = ({ co[CO_HANDLE], co[CO_FIRSTXARG], (co[CO_FIRSTXARG + 1]) ? 0 : co[CO_DELAY] }) + co[CO_FIRSTXARG + 2]; } while (i != 0); } } return status; } /* * NAME: this_user() * DESCRIPTION: return the user object and not a connection object */ static object this_user() { object user; user = ::this_user(); while (user && user <- LIB_CONN) { user = user->query_user(); } return user; } /* * NAME: users() * DESCRIPTION: return an array with the current user objects */ static object *users() { if (!this_object()) { return nil; } else if (object_name(this_object()) == USERD) { /* connection objects */ return ::users(); } else { return ::find_object(USERD)->query_users(); } } /* * NAME: swapout() * DESCRIPTION: swap out all objects */ static void swapout() { if (creator != "System") { error("Permission denied"); } ::swapout(); } /* * NAME: dump_state() * DESCRIPTION: create state dump */ static void dump_state() { if (creator != "System" || !this_object()) { error("Permission denied"); } rlimits (-1; -1) { ::find_object(DRIVER)->prepare_reboot(); ::dump_state(); } } /* * NAME: shutdown() * DESCRIPTION: shutdown the system */ static void shutdown() { if (creator != "System" || !this_object()) { error("Permission denied"); } rlimits (-1; -1) { ::find_object(DRIVER)->message("System halted.\n"); ::shutdown(); } } /* * NAME: call_touch() * DESCRIPTION: arrange to be warned when a function is called in an object */ static void call_touch(object obj) { if (creator != "System") { error("Permission denied"); } ::call_touch(obj); } /* * NAME: _F_call_limited() * DESCRIPTION: call a function with limited stack depth and ticks */ private mixed _F_call_limited(mixed arg1, mixed *args) { object rsrcd; int stack, ticks; string function; mixed tls, *limits, result; rsrcd = ::find_object(RSRCD); function = arg1; stack = ::status()[ST_STACKDEPTH]; ticks = ::status()[ST_TICKS]; rlimits (-1; -1) { tls = ::call_trace()[1][TRACE_FIRSTARG]; if (tls == arg1) { tls = arg1 = allocate(::find_object(DRIVER)->query_tls_size()); } limits = tls[0] = rsrcd->call_limits(tls[0], owner, stack, ticks); } rlimits (limits[LIM_MAXSTACK]; limits[LIM_MAXTICKS]) { result = call_other(this_object(), function, args...); ticks = ::status()[ST_TICKS]; rlimits (-1; -1) { rsrcd->update_ticks(limits, ticks); tls[0] = limits[LIM_NEXT]; return result; } } } /* * NAME: call_limited() * DESCRIPTION: call a function with the current object owner's resource limits */ static mixed call_limited(string function, mixed args...) { CHECKARG(function, 1, "call_limited"); if (!this_object()) { return nil; } CHECKARG(function_object(function, this_object()) != AUTO || function == "create", 1, "call_limited"); return _F_call_limited(function, args); } /* * NAME: call_out() * DESCRIPTION: start a callout */ static int call_out(string function, mixed delay, mixed args...) { int handle; string oname; CHECKARG(function, 1, "call_out"); handle = typeof(delay); CHECKARG(handle == T_INT || handle == T_FLOAT, 2, "call_out"); if (!this_object()) { return 0; } CHECKARG(function_object(function, this_object()) != AUTO || function == "create", 1, "call_out"); oname = object_name(this_object()); if (sscanf(oname, "%*s#-1") != 0) { error("Callout in non-persistent object"); } /* * add callout */ if (sscanf(oname, "/kernel/%*s/rsrc") != 0) { /* direct callouts for resource management objects */ return ::call_out(function, delay, args...); } catch { rlimits (-1; -1) { handle = ::call_out("_F_callout", delay, function, FALSE, args); if (::find_object(RSRCD)->rsrc_incr(owner, "callouts", this_object(), 1)) { return handle; } ::remove_call_out(handle); error("Too many callouts"); } } : error(::call_trace()[1][TRACE_FIRSTARG][1]); } /* * NAME: remove_call_out() * DESCRIPTION: remove a callout */ static mixed remove_call_out(int handle) { rlimits (-1; -1) { mixed delay; if (!next && prev) { error("No callouts in non-persistent object"); } if ((delay=::remove_call_out(handle)) != -1 && ::find_object(RSRCD)->remove_callout(this_object(), owner, handle)) { return 0; } return delay; } } /* * NAME: _F_callout() * DESCRIPTION: callout gate */ nomask void _F_callout(string function, int suspended, mixed *args) { if (!previous_program()) { if (!suspended && !::find_object(RSRCD)->suspended(this_object(), owner)) { _F_call_limited(function, args); } else { int handle; handle = ::call_out("_F_callout", LONG_TIME, function, TRUE, args); if (!suspended) { ::find_object(RSRCD)->suspend(this_object(), owner, handle); } } } } /* * NAME: _F_release() * DESCRIPTION: release a suspended callout */ nomask void _F_release(mixed handle) { if (previous_program() == RSRCD) { int i; mixed **callouts; callouts = ::status(this_object())[O_CALLOUTS]; ::remove_call_out(handle); for (i = sizeof(callouts); callouts[--i][CO_HANDLE] != handle; ) ; handle = allocate(::find_object(DRIVER)->query_tls_size()); _F_call_limited(callouts[i][CO_FIRSTXARG], callouts[i][CO_FIRSTXARG + 2]); } } /* * NAME: add_event() * DESCRIPTION: add a new event type */ static void add_event(string name) { CHECKARG(name, 1, "add_event"); if (!next && prev) { error("Cannot add event in non-persistent object"); } if (!events) { events = ([ ]); } if (!events[name]) { events[name] = ({ }); } } /* * NAME: remove_event() * DESCRIPTION: remove an event type */ static void remove_event(string name) { object *objlist, rsrcd; int i; CHECKARG(name, 1, "remove_event"); if (events && (objlist=events[name])) { rsrcd = ::find_object(RSRCD); i = sizeof(objlist -= ({ nil })); rlimits (-1; -1) { while (--i >= 0) { rsrcd->rsrc_incr(objlist[i]->query_owner(), "events", objlist[i], -1); } events[name] = nil; } } else { error("No such event"); } } /* * NAME: query_events() * DESCRIPTION: return a list of existing events */ static string *query_events() { if (events) { return map_indices(events); } else { return ({ }); } } /* * NAME: _F_subscribe_event() * DESCRIPTION: subscribe to an event */ nomask void _F_subscribe_event(object obj, string oowner, string name, int subscribe) { if (previous_program() == AUTO) { object *objlist, rsrcd; if (!events || !(objlist=events[name])) { error("No such event"); } rsrcd = ::find_object(RSRCD); if (subscribe) { if (sizeof(objlist & ({ obj })) != 0) { error("Already subscribed to event"); } objlist = objlist - ({ nil }) + ({ obj }); catch { rlimits (-1; -1) { if (!rsrcd->rsrc_incr(oowner, "events", obj, 1)) { error("Too many events"); } events[name] = objlist; } } : error(TLSVAR2); } else { if (sizeof(objlist & ({ obj })) == 0) { error("Not subscribed to event"); } rlimits (-1; -1) { rsrcd->rsrc_incr(oowner, "events", obj, -1); events[name] -= ({ nil, obj }); } } } } /* * NAME: subscribe_event() * DESCRIPTION: subscribe to an event */ static void subscribe_event(object obj, string name) { CHECKARG(obj, 1, "subscribe_event"); CHECKARG(name, 2, "subscribe_event"); if (!next || !obj->allow_subscribe(this_object(), name) || !obj) { error("Cannot subscribe to event"); } obj->_F_subscribe_event(this_object(), owner, name, TRUE); } /* * NAME: unsubscribe_event() * DESCRIPTION: unsubscribe from an event */ static void unsubscribe_event(object obj, string name) { CHECKARG(obj, 1, "unsubscribe_event"); CHECKARG(name, 2, "unsubscribe_event"); obj->_F_subscribe_event(this_object(), owner, name, FALSE); } /* * NAME: query_subscribed_event() * DESCRIPTION: return a list of objects subscribed to an event */ static object *query_subscribed_event(string name) { object *objlist; int sz; CHECKARG(name, 1, "query_subscribed_event"); if (!events || !(objlist=events[name])) { error("No such event"); } sz = sizeof(objlist); objlist -= ({ nil }); if (sz != sizeof(objlist)) { events[name] = objlist[..]; } return objlist; } /* * NAME: _F_start_event() * DESCRIPTION: start an event in this object */ nomask void _F_start_event(string name, mixed *args) { if (previous_program() == AUTO) { catch { rlimits (-1; -1) { int handle; handle = ::call_out("_F_callout", 0, name, FALSE, args); if (!::find_object(RSRCD)->rsrc_incr(owner, "callouts", this_object(), 1)) { ::remove_call_out(handle); error("Too many callouts"); } } } : error(::call_trace()[1][TRACE_FIRSTARG][1]); } } /* * NAME: event() * DESCRIPTION: broadcast an event */ static void event(string name, mixed args...) { object *objlist; string *names; int i, sz; CHECKARG(name, 1, "event"); if (!events || !(objlist=events[name])) { error("No such event"); } name = "evt_" + name; args = ({ this_object() }) + args; sz = sizeof(objlist); objlist -= ({ nil }); if (sz != sizeof(objlist)) { events[name] = objlist; sz = sizeof(objlist); } for (i = 0; i < sz; i++) { objlist[i]->_F_start_event(name, args); } } /* * NAME: event_except() * DESCRIPTION: broadcast an event, except to certain objects */ static void event_except(string name, object *exclude, mixed args...) { object *objlist; string *names; int i, sz; CHECKARG(name, 1, "event"); if (!events || !(objlist=events[name])) { error("No such event"); } name = "evt_" + name; args = ({ this_object() }) + args; sz = sizeof(objlist); objlist -= ({ nil }); if (sz != sizeof(objlist)) { events[name] = objlist; } for (i = 0, sz = sizeof(objlist -= exclude); i < sz; i++) { objlist[i]->_F_start_event(name, args); } } /* * NAME: read_file() * DESCRIPTION: read a string from a file */ static string read_file(string path, varargs int offset, int size) { string oname; CHECKARG(path, 1, "read_file"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); path = ::find_object(DRIVER)->normalize_path(path, oname + "/..", creator); if (creator != "System" && !::find_object(ACCESSD)->access(oname, path, READ_ACCESS)) { error("Access denied"); } return ::read_file(path, offset, size); } /* * NAME: write_file() * DESCRIPTION: write a string to a file */ static int write_file(string path, string str, varargs int offset) { string oname, fcreator; object driver, rsrcd; int size, result, *rsrc; CHECKARG(path, 1, "write_file"); CHECKARG(str, 2, "write_file"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); driver = ::find_object(DRIVER); path = driver->normalize_path(path, oname + "/..", creator); if (sscanf(path, "/kernel/%*s") != 0 || sscanf(path, "/include/kernel/%*s") != 0 || (creator != "System" && !::find_object(ACCESSD)->access(oname, path, WRITE_ACCESS))) { error("Access denied"); } fcreator = driver->creator(path); rsrcd = ::find_object(RSRCD); rsrc = rsrcd->rsrc_get(fcreator, "filequota"); if (creator != "System" && rsrc[RSRC_USAGE] >= rsrc[RSRC_MAX] && rsrc[RSRC_MAX] >= 0) { error("File quota exceeded"); } size = driver->file_size(path); catch { rlimits (-1; -1) { result = ::write_file(path, str, offset); if (result != 0 && (size=driver->file_size(path) - size) != 0) { rsrcd->rsrc_incr(fcreator, "filequota", nil, size, TRUE); } } } : error(TLSVAR2); return result; } /* * NAME: remove_file() * DESCRIPTION: remove a file */ static int remove_file(string path) { string oname; object driver; int size, result; CHECKARG(path, 1, "remove_file"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); driver = ::find_object(DRIVER); path = driver->normalize_path(path, oname + "/..", creator); if (sscanf(path, "/kernel/%*s") != 0 || sscanf(path, "/include/kernel/%*s") != 0 || (creator != "System" && !::find_object(ACCESSD)->access(oname, path, WRITE_ACCESS))) { error("Access denied"); } size = driver->file_size(path); catch { rlimits (-1; -1) { result = ::remove_file(path); if (result != 0 && size != 0) { ::find_object(RSRCD)->rsrc_incr(driver->creator(path), "filequota", nil, -size); } } } : error(TLSVAR2); return result; } /* * NAME: rename_file() * DESCRIPTION: rename a file */ static int rename_file(string from, string to) { string oname, fcreator, tcreator; object driver, accessd, rsrcd; int size, result, *rsrc; CHECKARG(from, 1, "rename_file"); CHECKARG(to, 2, "rename_file"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); driver = ::find_object(DRIVER); from = driver->normalize_path(from, oname + "/..", creator); to = driver->normalize_path(to, oname + "/..", creator); accessd = ::find_object(ACCESSD); if (sscanf(from + "/", "/kernel/%*s") != 0 || sscanf(to, "/kernel/%*s") != 0 || sscanf(from + "/", "/include/kernel/%*s") != 0 || from == "/include" || sscanf(to, "/include/kernel/%*s") != 0 || (creator != "System" && (!accessd->access(oname, from, WRITE_ACCESS) || !accessd->access(oname, to, WRITE_ACCESS)))) { error("Access denied"); } fcreator = driver->creator(from); tcreator = driver->creator(to); size = driver->file_size(from, TRUE); rsrcd = ::find_object(RSRCD); rsrc = rsrcd->rsrc_get(tcreator, "filequota"); if (size != 0 && fcreator != tcreator && creator != "System" && rsrc[RSRC_USAGE] >= rsrc[RSRC_MAX] && rsrc[RSRC_MAX] >= 0) { error("File quota exceeded"); } catch { rlimits (-1; -1) { result = ::rename_file(from, to); if (result != 0 && fcreator != tcreator) { rsrcd->rsrc_incr(tcreator, "filequota", nil, size, TRUE); rsrcd->rsrc_incr(fcreator, "filequota", nil, -size); } } } : error(TLSVAR2); return result; } /* * NAME: get_dir() * DESCRIPTION: get a directory listing */ static mixed **get_dir(string path) { string oname, *names, dir; mixed **list, *olist; int i, sz; CHECKARG(path, 1, "get_dir"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); path = ::find_object(DRIVER)->normalize_path(path, oname + "/..", creator); if (creator != "System" && !::find_object(ACCESSD)->access(oname, path, READ_ACCESS)) { error("Access denied"); } list = ::get_dir(path); names = explode(path, "/"); dir = implode(names[.. sizeof(names) - 2], "/"); names = list[0]; olist = allocate(sz = sizeof(names)); if (sscanf(path, "%*s" + INHERITABLE_SUBDIR) != 0) { /* lib objects */ for (i = sz; --i >= 0; ) { path = dir + "/" + names[i]; if ((sz=strlen(path)) >= 2 && path[sz - 2 ..] == ".c" && ::find_object(path[.. sz - 3])) { olist[i] = TRUE; } } } else { /* ordinary objects */ for (i = sz; --i >= 0; ) { object obj; path = dir + "/" + names[i]; if ((sz=strlen(path)) >= 2 && path[sz - 2 ..] == ".c" && (obj=::find_object(path[.. sz - 3]))) { olist[i] = obj; } } } return list + ({ olist }); } /* * NAME: file_info() * DESCRIPTION: get info for a single file */ static mixed *file_info(string path) { string name, *files; mixed *info; int i, sz; object obj; CHECKARG(path, 1, "file_info"); if (!this_object()) { error("Permission denied"); } name = object_name(this_object()); path = ::find_object(DRIVER)->normalize_path(path, name + "/..", creator); if (creator != "System" && !::find_object(ACCESSD)->access(name, path, READ_ACCESS)) { error("Access denied"); } info = ::get_dir(path); if (path == "/") { name = "."; } else { files = explode(path, "/"); name = files[sizeof(files) - 1]; } files = info[0]; sz = sizeof(files); if (sz <= 1) { if (sz == 0 || files[0] != name) { return nil; /* file does not exist */ } } else { /* name is a pattern: find in file list */ for (i = 0; name != files[i]; ) { if (++i == sz) { return nil; /* file does not exist */ } } } info = ({ info[1][i], info[2][i], nil }); if ((sz=strlen(path)) >= 2 && path[sz - 2 ..] == ".c" && (obj=::find_object(path[.. sz - 3]))) { info[2] = (sscanf(path, "%*s" + INHERITABLE_SUBDIR) != 0) ? TRUE : obj; } return info; } /* * NAME: make_dir() * DESCRIPTION: create a directory */ static int make_dir(string path) { string oname, fcreator; object driver, rsrcd; int result, *rsrc; CHECKARG(path, 1, "make_dir"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); driver = ::find_object(DRIVER); path = driver->normalize_path(path, oname + "/..", creator); if (sscanf(path, "/kernel/%*s") != 0 || sscanf(path, "/include/kernel/%*s") != 0 || (creator != "System" && !::find_object(ACCESSD)->access(oname, path, WRITE_ACCESS))) { error("Access denied"); } fcreator = driver->creator(path + "/"); rsrcd = ::find_object(RSRCD); rsrc = rsrcd->rsrc_get(fcreator, "filequota"); if (creator != "System" && rsrc[RSRC_USAGE] >= rsrc[RSRC_MAX] && rsrc[RSRC_MAX] >= 0) { error("File quota exceeded"); } catch { rlimits (-1; -1) { result = ::make_dir(path); if (result != 0) { rsrcd->rsrc_incr(fcreator, "filequota", nil, 1, TRUE); } } } : error(TLSVAR2); return result; } /* * NAME: remove_dir() * DESCRIPTION: remove a directory */ static int remove_dir(string path) { string oname; object driver; int result; CHECKARG(path, 1, "remove_dir"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); driver = ::find_object(DRIVER); path = driver->normalize_path(path, oname + "/..", creator); if (sscanf(path, "/kernel/%*s") != 0 || sscanf(path, "/include/kernel/%*s") != 0 || (creator != "System" && !::find_object(ACCESSD)->access(oname, path, WRITE_ACCESS))) { error("Access denied"); } catch { rlimits (-1; -1) { result = ::remove_dir(path); if (result != 0) { ::find_object(RSRCD)->rsrc_incr(driver->creator(path + "/"), "filequota", nil, -1); } } } : error(TLSVAR2); return result; } /* * NAME: restore_object() * DESCRIPTION: restore the state of an object */ static int restore_object(string path) { string oname; CHECKARG(path, 1, "restore_object"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); path = ::find_object(DRIVER)->normalize_path(path, oname + "/..", creator); if (creator != "System" && !::find_object(ACCESSD)->access(oname, path, READ_ACCESS)) { error("Access denied"); } return ::restore_object(path); } /* * NAME: save_object() * DESCRIPTION: save the state of an object */ static void save_object(string path) { string oname, fcreator; object driver, rsrcd; int size, *rsrc; CHECKARG(path, 1, "save_object"); if (!this_object()) { error("Permission denied"); } oname = object_name(this_object()); driver = ::find_object(DRIVER); path = driver->normalize_path(path, oname + "/..", creator); if ((sscanf(path, "/kernel/%*s") != 0 && sscanf(oname, "/kernel/%*s") == 0) || sscanf(path, "/include/kernel/%*s") != 0 || (creator != "System" && !::find_object(ACCESSD)->access(oname, path, WRITE_ACCESS))) { error("Access denied"); } fcreator = driver->creator(path); rsrcd = ::find_object(RSRCD); rsrc = rsrcd->rsrc_get(fcreator, "filequota"); if (creator != "System" && rsrc[RSRC_USAGE] >= rsrc[RSRC_MAX] && rsrc[RSRC_MAX] >= 0) { error("File quota exceeded"); } size = driver->file_size(path); catch { rlimits (-1; -1) { ::save_object(path); if ((size=driver->file_size(path) - size) != 0) { rsrcd->rsrc_incr(fcreator, "filequota", nil, size, TRUE); } } } : error(TLSVAR2); } /* * NAME: editor() * DESCRIPTION: pass a command to the editor */ static string editor(varargs string cmd) { object rsrcd, driver; string result; mixed *info; if (creator != "System" || !this_object() || !next) { error("Permission denied"); } catch { rlimits (-1; -1) { rsrcd = ::find_object(RSRCD); if (!query_editor(this_object())) { if (!rsrcd->rsrc_incr(owner, "editors", this_object(), 1)) { error("Too many editors"); } ::find_object(OBJREGD)->add_editor(this_object()); } driver = ::find_object(DRIVER); TLSVAR2 = nil; result = (cmd) ? ::editor(cmd) : ::editor(); info = TLSVAR2; if (!query_editor(this_object())) { rsrcd->rsrc_incr(owner, "editors", this_object(), -1); ::find_object(OBJREGD)->remove_editor(this_object()); } if (info) { rsrcd->rsrc_incr(driver->creator(info[0]), "filequota", nil, driver->file_size(info[0]) - info[1], TRUE); } } } : error(TLSVAR2); return result; }