1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: master.pike,v 1.10 2010/08/21 19:38:00 astra Exp $
24 #include <attributes.h>
27 #include <exception.h>
28 //! Run the server in a chroot environment
29 class master : public master{
36 #define LMESSAGE(s) if(llog) MESSAGE(s)
37 #define __DATABASE mConstants["_Database"]
38 #define MODULE_FILEPATH oServer->get_module("filepath:tree")
39 #define MODULE_SECURITY oServer->get_module("security")
42 #define MESSAGE_ERR(x) (oServer->get_module("log")->log_error(x))
44 //#define MOUNT_TRACE 1
46 object old_master;// = master();
47 object first, last, border;
51 private mapping alias = ([ ]);
53 #define debug_upgrade 0
54 #define debug_noncrit 0
57 private object oActiveUser;
58 private object oEffectiveUser;
59 private object oOldEffectiveUser;
60 private array oaPorts;
61 private array(program) paSockets;
62 private array oaUsers;
63 private mapping mConstants;
64 private mapping mErrors;
65 private object oServer;
66 private mapping mFunctions;//mapping of functions for each program
67 private int iCacheSize;
68 private int iMinCacheTime;
69 private int iLastSwap;
71 private mapping mPorts;
74 private Thread.Mutex cmd_mutex = Thread.Mutex();
75 private object oCmdLock;
88 LMESSAGE("New Master exchange !\n");
89 old_master = master();
90 object new_master = this_object();
92 foreach( indices(old_master), string varname ) {
93 catch { new_master[varname] = old_master[varname]; };
95 oActiveUser = thread_local();
96 oEffectiveUser = thread_local();
97 oOldEffectiveUser = thread_local();
105 mixed get_constant ( string constant_name )
107 if ( !mappingp( mConstants ) ) return 0;
108 return mConstants[constant_name];
112 string stupid_describe(mixed d, int l)
114 return sprintf("%O", d);
118 private void insert(object proxy)
121 proxy["oNext"] = first;
126 first["oPrev"] = proxy;
130 MESSAGE("Failed to insert proxy:\n"+sprintf("%O\n", err));
135 public void remove(object proxy)
138 first = proxy["oNext"];
140 proxy["oPrev"]["oNext"] = proxy["oNext"];
143 last = proxy["oPrev"];
145 proxy["oNext"]["oPrev"] = proxy["oPrev"];
150 public void front(object proxy)
152 if (proxy->check_swap()) {
155 if (!proxy["oNext"] && !proxy["oPrev"])
167 private void tail(object proxy)
172 if (!proxy["oNext"] && !proxy["oPrev"])
176 border = proxy["oPrev"];
185 public int swap(int max_swap_time, int external)
188 object oServer = mConstants["_Server"];
192 if (objectp(oServer)) {
193 iCacheSize = oServer->get_config("cachesize");
194 iMinCacheTime = oServer->get_config("cachetime");
195 if ( iCacheSize == 0 )
197 if (iMinCacheTime == 0)
202 // do only try to swap from time to time
203 if ( !external && (time() - iLastSwap) < iMinCacheTime )
208 if (iInMemory > (iCacheSize < 100 ? 100: iCacheSize))
212 function mtime = oServer->f_get_time_millis;
218 MESSAGE("Swapp/start with %O/%d projected/%d max visits/%d max time",
219 oDrop, (iInMemory-iCacheSize)/20, iInMemory / 5, max_swap_time);
221 while ( objectp(oDrop) && iInMemory > iCacheSize && visits < iInMemory &&
222 ((swappedOut < (iInMemory - iCacheSize)/20 && visits < iInMemory / 5) ||
223 (max_swap_time > 0 && mtime() - tt < max_swap_time)) )
225 while (objectp(oDrop) && oDrop->status() != PSTAT_SAVE_OK) {
226 visits+=(oDrop->status() == PSTAT_SAVE_PENDING);
227 oDrop = oDrop["oPrev"];
233 if ( oDrop->swap(iMinCacheTime) ) {
236 oDrop = oDrop["oPrev"];
239 oDrop = oDrop["oPrev"];
243 MESSAGE( "Swapping %d (%d in memory / %d objects cache size) in %d ms, "+
245 swappedOut, iInMemory, iCacheSize, (mtime()-tt), visits);
252 void got_loaded(object proxy)
254 if (proxy->check_swap()) {
258 // do not spend more than 10 ms in swapping / every x minutes (minCacheTime)
263 void got_dropped(object proxy)
269 int get_in_memory() {
277 void append(object proxy)
281 proxy["oPrev"]= last;
285 last["oNext"] = proxy;
290 array(array) p_list()
292 array(array) res = ({});
293 object proxy = first;
298 while (objectp(proxy))
301 fun=proxy->find_function("query_attribute");
306 errres = catch {name = fun(OBJ_NAME);};
308 name = errres[0][0..20];
315 ({ (string) proxy->get_object_id(),
316 ( (proxy->status()==1) ? " " +
317 describe_program(object_program(proxy->get_object()))
319 (stringp(name) ? name : "empty"),
320 PSTAT(proxy->status())
323 // MESSAGE("running through: object "+proxy->get_object_id());
324 proxy = proxy["oNext"];
326 // MESSAGE("List done ...");
330 array(program) dependents(program p)
334 array(program) ret = ({});
335 foreach (indices(programs), progName) {
336 prog = programs[progName];
337 if ( !programp(prog) )
339 array(program) inheritlist = Program.all_inherits(prog);
340 if ( search(inheritlist, p) >=0 ) {
348 array pnames(array(program) progs)
352 foreach (progs, prog) {
353 names += ({ describe_program(prog) });
359 * class ErrorContainer,
360 * it provides means to catch the messages sent from the pike binary to the
361 * compile_error from master.
362 * ErrorContainer.compile_error is called by compile_error
363 * if an Instance of ErrorContainer is set
365 * got_error and got_warning provide the messages sent to the ErrorContainer
373 string errors="", warnings="";
379 final mixed `[](mixed num) {
389 string get_warnings() {
393 void got_error(string file, int line, string err, int|void is_warning) {
394 if (file[..sizeof(d)-1] == d) {
395 file = file[sizeof(d)..];
399 sprintf("%s:%s\t%s\n", file, line ? (string) line : "-", err);
402 sprintf("%s:%s\t%s\n", file, line ? (string) line : "-", err);
405 // called from master()->compile_error
406 void compile_error(string file, int line, string err) {
407 got_error(file, line, "Error: " + err);
410 void compile_warning(string file, int line, string err) {
411 got_error(file, line, "Warning: " + err, 1);
416 if (sizeof(d) && (d[-1] != '/') && (d[-1] != '\\'))
422 object getErrorContainer()
424 return ErrorContainer();
428 * clear all broken compilations
430 void clear_compilation_failures()
432 foreach (indices (programs), string fname)
433 if (!programs[fname]) m_delete (programs, fname);
438 FATAL("Dumping proxies.... (%d in memory, %d swapped)", iInMemory, iSwapped);
439 mapping visited = ([ ]);
442 while ( objectp(o) && o->status ) {
445 werror("Circular dependency in master list: %O", o->get_object());
449 if (o->status()>PSTAT_DISK) {
450 werror("OBJ: %O\n", o);
453 werror("on disk: %O\n", o);
457 werror("----- Found %d proxies\n", i);
461 * upgrade a program and all its instances.
462 * @param program to update
463 * @return -1 Force needed
464 * @return -2 no program passed
465 * @return number of dropped objects
466 * @return error from compile (with backtrace)
468 int|string upgrade(program p, void|int force)
472 clear_compilation_failures();
473 return "Failed to find program";
476 if (p == programs["/kernel/proxy.pike"])
477 throw(({"Its impossible to upgrade a proxy object - You have to "+
478 "restart the server", backtrace()}));
480 clear_compilation_failures();
481 array(program) apDependents = dependents(p)+({ p });
482 string fname = search(programs, p);
484 if ( !stringp(fname) )
489 [type,id] = parse_URL_TYPE(fname);
490 if ( type != URLTYPE_DB && intp(id) && id > 0 )
491 fname = "/DB:#"+id+".pike";
495 ErrorContainer e = ErrorContainer();
497 m_delete(mErrors, fname);
498 set_inhibit_compile_errors(e);
500 tmp = compile_string(master_read_file(fname), fname);
502 set_inhibit_compile_errors(0);
504 if (err!=0) // testcompile otherwise don't drop !
506 clear_compilation_failures();
507 mErrors[fname]= e->get() /"\n";
508 FATAL("Error while upgrading: %O\n", e->get());
510 return "Failed to compile "+fname+"\n"+
515 // assume compilation is ok, or do we have to check all dependents ?
520 array aNeedDrop = ({ });
523 while ( objectp(o) && o->status && i < iInMemory )
526 if (o->status()>PSTAT_DISK) // if not in memory don't drop
528 if ( search(apDependents, object_program(o->get_object())) >= 0 )
530 if (!zero_type(o->check_upgrade) && o->check_upgrade())
540 foreach(aNeedDrop, object o)
542 if (functionp(o->upgrade))
549 mixed UpgradeErr = catch {
550 foreach(aNeedDrop, object o)
552 int dropped = o->drop();
554 foreach(apDependents, program prg) {
555 string pname = search(programs, prg);
556 if ( programp(alias[prg]) )
557 m_delete(programs, search(programs,alias[prg]));
559 m_delete(programs, pname);
563 FATAL("Error in upgrade!\n"+describe_backtrace(UpgradeErr));
565 return sizeof(aNeedDrop);
571 mConstants = all_constants();
575 register_server(object s)
577 if ( !objectp(oServer) )
588 register_user(object u)
592 if ( search(oaPorts, CALLER) == -1 )
593 THROW("Caller is not a port object !", E_ACCESS);
595 for ( i = sizeof(oaUsers) - 1; i >= 0; i-- ) {
596 if ( oaUsers[i] == u )
598 else if ( oaUsers[i]->is_closed() ) {
599 destruct(oaUsers[i]);
606 void unregister_user()
608 if ( !is_user(CALLER) )
609 error("Calling object is not a user !");
610 oaUsers -= ({ CALLER });
613 bool is_user(object u)
616 for ( i = sizeof(oaUsers) - 1; i >= 0; i-- ) {
617 if ( oaUsers[i] == u )
623 bool is_module(object m)
625 if ( objectp(oServer) )
626 return oServer->is_module(m);
637 int set_this_user(object obj)
642 oEffectiveUser->set(0);
643 oOldEffectiveUser->set(0);
644 if ( objectp(oCmdLock) )
645 destruct(oCmdLock); // unlocked again
650 if ( (!is_module(CALLER) && !is_user(CALLER)) ||
651 (objectp(obj) && !is_user(obj)) )
653 MESSAGE("failed to set active user...("+describe_object(obj)+")");
654 MESSAGE("CALLER: " + describe_object(CALLER));
655 foreach(oaUsers, object u) {
656 MESSAGE("User:"+describe_object(u));
658 error("Failed to set active user!\n");
663 if (catch(oCmdLock = cmd_mutex->lock()) ) {
664 FATAL("Failed to obtain lock - Backtrace - going on ...");
668 if ( objectp(obj) ) {
669 oActiveUser->set(obj); // use proxy
670 oEffectiveUser->set(0);
671 oOldEffectiveUser->set(0);
675 oEffectiveUser->set(0);
680 object seteuid(object user)
682 // now this is tricky
683 object caller = CALLER;
684 if (!objectp(caller))
685 error("Failed to seteuid , caller is unknown!");
686 if (is_socket(caller))
687 caller = caller->get_user_object();
689 object users = oServer->get_module("users");
691 if ( objectp(users) )
692 root = users->lookup("root");
694 if ( caller->get_creator() == root ||
695 caller->get_creator() == user ||
697 user == oOldEffectiveUser->get() )
699 oOldEffectiveUser->set(oEffectiveUser->get());
700 oEffectiveUser->set(user);
703 throw( ({ sprintf( "Failed to set effective user %O (caller: %O, creator: %O)!", user, caller, caller->get_creator() ), backtrace() }) );
708 if ( !objectp(oActiveUser) ) return 0;
710 object tu = oActiveUser->get();
713 return tu->get_user_object();
718 if ( !objectp(oActiveUser) ) return 0;
720 object tu = oActiveUser->get();
729 return oEffectiveUser->get();
733 register_port(object s)
735 if ( CALLER == oServer ) {
737 paSockets += ({ s->get_socket_program() });
747 object get_port(string name)
749 foreach(oaPorts, object port )
750 if ( objectp(port) && port->get_port_name() == name )
757 return copy_value(oaUsers);
763 * system_object(object obj)
767 * if ( obj == mConstants["_Security"] || obj == mConstants["_Database"] )
769 * if ( is_user(obj) )
771 * prg = object_program(obj);
772 * if ( prg == (program)"classes/object.pike" ||
773 * prg == (program)"classes/container.pike" ||
774 * prg == (program)"classes/exit.pike" ||
775 * prg == (program)"classes/room.pike" ||
776 * prg == (program)"classes/user.pike" ||
777 * prg == (program)"classes/group.pike" ||
778 * prg == (program)"proxy.pike" ||
779 * prg == (program)"/home/steam/pikeserver/kernel/steamsocket.pike" )
785 mixed parse_URL_TYPE(string f)
791 if ( sscanf(f, "/DB:%s", path) > 0 )
793 if (sscanf(path, "#%d.%s", id, ext))
796 return ({ URLTYPE_DB, id });
798 return ({ URLTYPE_DBO, id });
801 return ({ URLTYPE_DBFT, path });
803 else if ( sscanf(f, "steam:%s", path) > 0 ) {
804 return ({ URLTYPE_DBFT, path });
806 return ({URLTYPE_FS,0});
811 array(array) mount_points;
813 int mount(string source, string dest)
815 if ( objectp(oServer) && CALLER != oServer )
816 error("Unauthorized call to mount() !");
818 MESSAGE("Mounting %s on %s", source, dest);
820 // make sure we have proper prefixes
821 if (source[strlen(source)-1]!='/')
823 if (dest[strlen(dest)-1]!='/')
828 // insert them according to strlen
830 if (!arrayp(mount_points))
831 mount_points = ({ ({ source, dest }) });
835 while( i < sizeof(mount_points) &&
836 (strlen(mount_points[i][0])<strlen(source)))
841 mount_points= mount_points[..i-1] +
842 ({({ source, dest })}) +
847 void run_sandbox(string cdir, void|string user)
849 function change_root, switch_user;
851 if ( !stringp(cdir) )
855 change_root= System.chroot;
856 switch_user= System.setuid;
858 change_root = chroot;
859 switch_user = setuid;
862 change_root = chroot;
864 if ( change_root(cdir) ) {
866 MESSAGE("Running in chroot environment... (%s)\n", cdir);
867 object dir = Stdio.File("/etc","r");
869 error("Failed to find /etc directory - aborting !");
872 // try specified user:
873 if ( stringp(user) && user != "" ) {
874 foreach ( get_all_users(), array tmp_user_info ) {
875 if ( tmp_user_info[0] != user ) continue;
876 user_info = tmp_user_info;
880 // fallback on user nobody:
881 if ( !arrayp(user_info) ) {
883 foreach ( get_all_users(), array tmp_user_info ) {
884 if ( tmp_user_info[0] != user ) continue;
885 user_info = tmp_user_info;
889 if ( arrayp(user_info) && sizeof(user_info) > 3 ) {
890 if ( switch_user(user_info[2]) == 0 ) {
891 MESSAGE( "Switched to user %s [%O]\n", user, user_info[2] );
894 mount_points = ({ ({ "/", "/" }), ({ "/include", "/include" }) });
895 pike_include_path += ({ "/include" });
896 pike_module_path += ({ "/libraries" });
899 MESSAGE("change_root(%s) Failed !", getcwd());
904 string apply_mount_points(string orig)
910 if (!arrayp(mount_points))
913 if ( search(orig, "/DB:#") == 0 )
916 if (orig[0]!='/' && orig[0]!='#')
919 for (i=sizeof(mount_points);i--;)
920 if (search(orig, mount_points[i][0]) == 0)
922 res= mount_points[i][1]+orig[strlen(mount_points[i][0])..];
928 object master_file_stat(string x, void|int follow)
935 werror("[master_file_stat("+x+") ->");
937 [TypeURL, path] = parse_URL_TYPE(x);
941 x = apply_mount_points(x);
943 werror("fs("+x+")\n");
945 return ::master_file_stat(x);
949 werror(sprintf("db(%d)]\n",path));
951 p = __DATABASE->find_object(path);
954 if ( arrayp(s) && sizeof(s) > 6 )
955 return Stdio.Stat(s[..6]);
960 werror(sprintf("dbo(%d)]\n",path));
965 werror(sprintf("dbft(%s)]\n", path));
967 p = MODULE_FILEPATH->path_to_object(path);
970 if ( arrayp(s) && sizeof(s) > 6 )
971 return Stdio.Stat(s[..6]);
981 return copy_value(values(programs));
985 * Access the program pointer currently registered for programname
987 * @param string pname - the program to look up
988 * @return program - the associated program
991 program lookup_program(string pname)
993 return programs[pname];
996 program compile_string(string source,
997 void|string filename,
1001 void|int _show_if_constant_errors)
1003 if ( !stringp(source) )
1004 return compile_file(filename, handler, p, o);
1005 return ::compile_string(source, filename, handler, p, o, _show_if_constant_errors);
1009 program compile_file(string file,
1010 object|void handler,
1020 //LMESSAGE("compile_file("+file+")");
1021 [ TypeURL, path ] = parse_URL_TYPE(file);
1026 file = apply_mount_points(file);
1027 tmp = Stdio.File(file, "r");
1028 content = tmp->read();
1031 //return ::compile_file(file);
1033 return 0; // dump files not supported in database
1035 tmp = __DATABASE->find_object((int)path);
1036 content = tmp->get_source_code();
1039 tmp = MODULE_FILEPATH->path_to_object(path);
1040 if ( !objectp(tmp) || !functionp(tmp->get_source_code) ) {
1041 throw( ({sprintf("COMPILING %O: no get_source_code function in %O (path=%O)", file, tmp, path), backtrace() }));
1044 content = tmp->get_source_code();
1049 if ( !stringp(content) )
1052 FATAL("Warning: No content of file %O to compile...\n", file);
1054 // _loading = compile(cpp(content, file));
1055 if ( stringp(file) )
1056 m_delete(mErrors, file);
1057 _loading= compile(cpp(content,
1071 throw(({"Cant resolve filename\n", backtrace()}));
1074 program cast_to_program(string pname, string current_file, void|object handler)
1078 if ( (i=search(pname, "/DB:")) == 0 ) {
1079 if ( search(pname, ".pike") == 0 )
1081 p = lookup_program(pname);
1082 if ( programp(p) ) return p;
1083 return compile_file(pname);
1085 p = ::cast_to_program(pname, current_file);
1090 program low_findprog(string pname,
1092 object|void handler,
1095 //return ::low_findprog(apply_mount_points(pname), ext, handler, mkobj);
1096 return ::low_findprog(pname, ext, handler, mkobj);
1102 mixed resolv(string symbol, string filename, object handler)
1105 werror("[resolve("+symbol+","+filename+sprintf(",%O)\n",handler));
1107 mixed erg=::resolv(symbol, filename, handler);
1109 werror("[resolve returns:"+sprintf("%O\n",erg));
1114 string id_from_dbpath(string db_path)
1119 [type_URL, _path] = parse_URL_TYPE(db_path);
1120 if (type_URL == URLTYPE_DB)
1122 if (search(_path,"#")==0)
1127 p = MODULE_FILEPATH->path_to_object(db_path);
1129 return "#"+ p->get_object_id();
1136 mapping get_errors()
1141 array get_error(string file)
1144 return ({ file+"\n" }) + mErrors[file];
1149 void compile_error(string file, int line, string err)
1151 if ( !arrayp(mErrors[file]) )
1152 mErrors[file] = ({ });
1153 mErrors[file] += ({ sprintf("%s:%s\n", line?(string)line:"-",err) });
1154 ::compile_error(file, line, err);
1157 string handle_include(string f, string current_file, int local_include)
1164 tmp=current_file/"/";
1166 path=combine_path_with_cwd((tmp*"/"));
1167 if (parse_URL_TYPE(path)[0] == URLTYPE_DB)
1168 path = id_from_dbpath(path);
1172 foreach(pike_include_path, path) {
1173 path=combine_path(path,f);
1174 if (parse_URL_TYPE(path)[0] == URLTYPE_DB)
1175 path = id_from_dbpath(path);
1177 if(master_file_stat(path))
1189 string read_include(string f)
1193 if (search(f,"#")==0) // #include <%45>
1196 p = mConstants["_Database"]->find_object((int)f[1..]);
1197 //p = find_object((int)f[1..]);
1199 return p->get_source_code();
1202 return ::read_include(apply_mount_points(f));
1211 return CMD_TYPE_INT;
1212 else if ( stringp(var) )
1213 return CMD_TYPE_STRING;
1214 else if ( objectp(var) )
1215 return CMD_TYPE_OBJECT;
1216 else if ( floatp(var) )
1217 return CMD_TYPE_FLOAT;
1218 else if ( arrayp(var) )
1219 return CMD_TYPE_ARRAY;
1220 else if ( mappingp(var) )
1221 return CMD_TYPE_MAPPING;
1222 else if ( functionp(var) )
1223 return CMD_TYPE_FUNCTION;
1224 return CMD_TYPE_UNKNOWN;
1228 void set_root(string root)
1234 string dirname(string x)
1236 if ((stringp(sRoot)) && search(x, sRoot)==0)
1237 return dirname(x[strlen(sRoot)..]);
1238 return ::dirname(x);
1241 //string master_read_file(string file)
1243 // LMESSAGE("master_read_file("+file+")");
1244 // return ::master_read_file(file);
1247 string master_read_file(string file)
1254 werror("master_read_file("+file+")");
1257 [TypeURL, path ] = parse_URL_TYPE(file);
1261 //MESSAGE("calling compile_file("+file+")");
1262 //file = apply_mount_points(file);
1263 return ::master_read_file(file);
1264 //return ::compile_file(file);
1267 werror(sprintf("db(%s)\n",path));
1269 p = __DATABASE->find_object((int)path);
1271 throw(({"sourcefile deleted", backtrace()}));
1274 throw(({"failed to load sourcefile", backtrace()}));
1275 return p->get_source_code();
1278 werror(sprintf("db(%s)\n",path));
1283 werror(sprintf("db(%s)\n",path));
1285 p = MODULE_FILEPATH->path_to_object(path);
1286 return p->get_source_code();
1288 throw(({"Failed to load file"+file, backtrace()}));
1291 /*object findmodule(string fullname)
1295 LMESSAGE("findmodule("+fullname+", called by " + describe_object(CALLER));
1296 o=::findmodule(fullname);
1302 string describe_mapping(mapping m, int maxlen)
1304 mixed keys = indices(m);
1305 mixed values = values(m);
1307 for (int i=0;i<sizeof(keys);i++)
1309 out += stupid_describe(keys[i], maxlen) +
1310 ":" + detailed_describe(values[i], maxlen)
1311 + (i<sizeof(keys)-1 ? "," :"");
1316 string describe_array(array a, int maxlen)
1319 for (int i=0;i<sizeof(a);i++)
1321 out += detailed_describe(a[i], maxlen) + (i<sizeof(a)-1 ? "," :"");
1326 string describe_multiset(multiset m, int maxlen)
1328 mixed keys = indices(m);
1330 for (int i=0;i<sizeof(keys);i++)
1332 out += stupid_describe(keys[i], maxlen) + (i<sizeof(keys)-1 ? "," :"");
1337 string detailed_describe(mixed m, int maxlen)
1342 if (catch (typ=sprintf("%t",m)))
1343 typ = "object"; // Object with a broken _sprintf(), probably.
1351 if(sizeof(m) < maxlen)
1353 string t = sprintf("%O", m);
1354 if (sizeof(t) < (maxlen + 2)) {
1361 return sprintf("%O+[%d]",m[..maxlen-5],sizeof(m)-(maxlen-5));
1363 return "string["+sizeof(m)+"]";
1367 if(!sizeof(m)) return "({})";
1368 return "({" + describe_array(m,maxlen-2) +"})";
1371 if(!sizeof(m)) return "([])";
1372 return "([" + describe_mapping(m, maxlen-2) + "])";
1375 if(!sizeof(m)) return "(<>)";
1376 return "(<" + describe_multiset(m, maxlen-2) + ">)";
1377 return "multiset["+sizeof(m)+"]";
1380 if(string tmp=describe_program(m)) return tmp;
1381 if(object o=function_object(m))
1382 return (describe_object(o)||"")+"->"+function_name(m);
1385 if (catch (tmp = function_name(m)))
1386 // The function object has probably been destructed.
1388 return tmp || "function";
1392 if(string tmp=describe_program(m)) return tmp;
1397 if(string tmp=describe_object(m)) return tmp;
1404 * perform the call-out, but save the previous user-object
1407 void f_call(function f, object user, object caller, void|array args)
1411 // skip calls if function is no longer available
1412 if (!functionp(f)) {
1416 //make sure caller is f_call_out....
1417 object old_user = oActiveUser->get();
1418 object old_euid = oEffectiveUser->get();
1419 oActiveUser->set(user);
1420 oEffectiveUser->set(user);
1422 if ( objectp(caller) && functionp(caller->route_call) && function_name(f) != "drop" )
1423 caller->route_call(f, args);
1427 oActiveUser->set(old_user);
1428 oEffectiveUser->set(old_euid);
1430 FATAL("Error on call_out:"+sprintf("%O:\n%O\n", err[0], err[1]));
1434 * call a function delayed. The user object is saved.
1438 f_call_out(function f, float|int delay, mixed ... args)
1441 error("Unable to call NULL function !");
1443 object caller = CALLER;
1444 caller = Caller.get_caller(caller, backtrace());
1445 return call_out(f_call, delay, f, this_user(), caller, args);
1448 mixed f_call_out_info()
1450 return call_out_info();
1460 array get_dir(string dir)
1462 string fdir = apply_mount_points(dir);
1463 // MESSAGE("Getting dir of " + fdir);
1464 return predef::get_dir(fdir);
1467 int is_dir(string dir)
1469 return Stdio.is_dir(apply_mount_points(dir));
1474 * This Function is the mount-point aware of the rm command
1475 * rm removes a file from the filesystem
1478 * @return 0 if it fails. Nonero otherwise
1480 * @caveats this command is limited to removing filesystem files
1484 string truef = apply_mount_points(f);
1485 return predef::rm(truef);
1496 void run_thread(function f, object user, mixed ... args)
1498 if ( !objectp(user) ) {
1499 if ( objectp(oServer) ) {
1500 object umod = oServer->get_module("users");
1501 if ( objectp(umod) ) {
1502 user = umod->lookup("root");
1503 if ( objectp(user) )
1508 oActiveUser->set(user);
1509 oEffectiveUser->set(0);
1522 void start_thread(function f, mixed ... args)
1524 MESSAGE("Starting new Thread %O", f);
1525 predef::thread_create(run_thread, f, this_user(), @args);
1535 mixed file_stat(string f, void|int follow)
1537 string ff = apply_mount_points(f);
1538 return predef::file_stat(ff, follow);
1547 void use_port(int pid)
1553 int free_port(int pid)
1555 return mPorts[pid] != 1;
1558 void dispose_port(int pid)
1564 * Find out if a given object is a socket (this means it
1565 * has to be in the list of sockets.
1567 * @param object o - the socket object
1568 * @return true or false (0 or 1)
1570 int is_socket(object o)
1572 return (search(paSockets, object_program(o)) >= 0 );
1575 function find_function(string f) { return this_object()[f]; }
1577 #if (__MINOR__ > 3) // this is a backwards compatibility function
1578 object new(string|program program_file, mixed|void ...args)
1580 program prg = (program)program_file;
1581 if ( !programp(prg) )
1584 return (prg)(@args);
1590 void describe_threads()
1592 #if constant (thread_create)
1593 // Disable all threads to avoid potential locking problems while we
1594 // have the backtraces. It also gives an atomic view of the state.
1595 object threads_disabled = _disable_threads();
1597 werror("### Describing all Pike threads:\n\n");
1599 array(Thread.Thread) threads = all_threads();
1600 array(string|int) thread_ids =
1602 lambda (Thread.Thread t) {
1603 string desc = sprintf ("%O", t);
1604 if (sscanf (desc, "Thread.Thread(%d)", int i)) return i;
1607 sort (thread_ids, threads);
1610 for(i=0; i < sizeof(threads); i++) {
1611 werror("### Thread %s%s:\n",
1612 (string) thread_ids[i],
1613 threads[i] == backend_thread ? " (backend thread)" : ""
1615 werror(describe_backtrace(threads[i]->backtrace()) + "\n");
1618 werror ("### Total %d Pike threads\n", sizeof (threads));
1621 threads_disabled = 0;
1623 werror("Describing single thread:\n%s\n",
1624 describe_backtrace (backtrace()));