master._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
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.
7  *
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.
12  *
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
16  *
17  * $Id: master.pike,v 1.10 2010/08/21 19:38:00 astra Exp $
18  */
19 inherit "/master";
20 #include <macros.h>
21 #include <coal.h>
22 #include <assert.h>
23 #include <database.h>
24 #include <attributes.h>
25 #include <config.h>
26 #include <classes.h>
27 #include <exception.h>
28 //! Run the server in a chroot environment
29 class master : public master{
30 public:
31 
32 
33 
34 
35 
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")
40 
41 #undef MESSAGE_ERR
42 #define MESSAGE_ERR(x) (oServer->get_module("log")->log_error(x))
43 
44 //#define MOUNT_TRACE 1
45 
46  object old_master;// = master();
47  object first, last, border;
48 int llog;
49  int iInMemory;
50 
51 private mapping alias = ([ ]);
52 
53 #define debug_upgrade 0
54 #define debug_noncrit 0
55 
56 
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;
70 private int iSwapped;
71 private mapping mPorts;
72 
73 #ifdef THREAD_READ
74 private Thread.Mutex cmd_mutex = Thread.Mutex();
75 private object oCmdLock;
76 #endif
77 
78 
79 void create()
80 {
81  oaPorts = ({ });
82  paSockets = ({ });
83  oaUsers = ({ });
84  mFunctions = ([ ]);
85  mErrors = ([ ]);
86  mPorts = ([ ]);
87 
88  LMESSAGE("New Master exchange !\n");
89  old_master = master();
90  object new_master = this_object();
91 
92  foreach( indices(old_master), string varname ) {
93  catch { new_master[varname] = old_master[varname]; };
94  }
95  oActiveUser = thread_local();
96  oEffectiveUser = thread_local();
97  oOldEffectiveUser = thread_local();
98  iMinCacheTime = 600;
99  iLastSwap = time();
100 
101  oServer = 0;
102 }
103 
104 
105 mixed get_constant ( string constant_name )
106 {
107  if ( !mappingp( mConstants ) ) return 0;
108  return mConstants[constant_name];
109 }
110 
111 
112 string stupid_describe(mixed d, int l)
113 {
114  return sprintf("%O", d);
115 }
116 
117 private:
118 private void insert(object proxy)
119 {
120  mixed err = catch {
121  proxy["oNext"] = first;
122  proxy["oPrev"] = 0;
123  if (!first)
124  last = proxy;
125  else
126  first["oPrev"] = proxy;
127  first = proxy;
128  };
129  if ( err != 0 )
130  MESSAGE("Failed to insert proxy:\n"+sprintf("%O\n", err));
131 }
132 
133 public:
134 
135 public void remove(object proxy)
136 {
137  if (proxy == first)
138  first = proxy["oNext"];
139  else
140  proxy["oPrev"]["oNext"] = proxy["oNext"];
141 
142  if (proxy == last)
143  last = proxy["oPrev"];
144  else
145  proxy["oNext"]["oPrev"] = proxy["oPrev"];
146  proxy["oNext"] = 0;
147  proxy["oPrev"] = 0;
148 }
149 
150 public void front(object proxy)
151 {
152  if (proxy->check_swap()) {
153  if (first!=proxy)
154  {
155  if (!proxy["oNext"] && !proxy["oPrev"])
156  insert(proxy);
157  else
158  {
159  remove(proxy);
160  insert(proxy);
161  }
162  }
163  }
164 }
165 
166 private:
167 private void tail(object proxy)
168 {
169  if (last!=proxy)
170  {
171 
172  if (!proxy["oNext"] && !proxy["oPrev"])
173  append(proxy);
174  else {
175  if (border==proxy)
176  border = proxy["oPrev"];
177  remove(proxy);
178  append(proxy);
179  }
180  }
181 }
182 
183 public:
184 
185 public int swap(int max_swap_time, int external)
186 {
187  object oDrop;
188  object oServer = mConstants["_Server"];
189 
190  if (!iCacheSize)
191  {
192  if (objectp(oServer)) {
193  iCacheSize = oServer->get_config("cachesize");
194  iMinCacheTime = oServer->get_config("cachetime");
195  if ( iCacheSize == 0 )
196  iCacheSize = 100000;
197  if (iMinCacheTime == 0)
198  iMinCacheTime = 600;
199  }
200  }
201 
202  // do only try to swap from time to time
203  if ( !external && (time() - iLastSwap) < iMinCacheTime )
204  return -1;
205 
206  iLastSwap = time();
207 
208  if (iInMemory > (iCacheSize < 100 ? 100: iCacheSize))
209  {
210  int swappedOut = 0;
211  int visits = 0;
212  function mtime = oServer->f_get_time_millis;
213  int tt = mtime();
214 
215  oDrop = last;
216 
217  if (external)
218  MESSAGE("Swapp/start with %O/%d projected/%d max visits/%d max time",
219  oDrop, (iInMemory-iCacheSize)/20, iInMemory / 5, max_swap_time);
220 
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)) )
224  {
225  while (objectp(oDrop) && oDrop->status() != PSTAT_SAVE_OK) {
226  visits+=(oDrop->status() == PSTAT_SAVE_PENDING);
227  oDrop = oDrop["oPrev"];
228  }
229  if (!objectp(oDrop))
230  continue;
231 
232  visits++;
233  if ( oDrop->swap(iMinCacheTime) ) {
234  iSwapped++;
235  swappedOut++;
236  oDrop = oDrop["oPrev"];
237  }
238  else {
239  oDrop = oDrop["oPrev"];
240  }
241  }
242  if (external) {
243  MESSAGE( "Swapping %d (%d in memory / %d objects cache size) in %d ms, "+
244  "%d visited",
245  swappedOut, iInMemory, iCacheSize, (mtime()-tt), visits);
246  }
247  return swappedOut;
248  }
249  return 0;
250 }
251 
252 void got_loaded(object proxy)
253 {
254  if (proxy->check_swap()) {
255  iInMemory++;
256  front(proxy);
257  }
258  // do not spend more than 10 ms in swapping / every x minutes (minCacheTime)
259  swap(10, 0);
260 }
261 
262 
263 void got_dropped(object proxy)
264 {
265  iInMemory--;
266  tail(proxy);
267 }
268 
269 int get_in_memory() {
270  return iInMemory;
271 }
272 
273 int get_swapped() {
274  return iSwapped;
275 }
276 
277 void append(object proxy)
278 {
279  mixed err = catch {
280  proxy["oNext"]= 0;
281  proxy["oPrev"]= last;
282  if (!last)
283  first = proxy;
284  else
285  last["oNext"] = proxy;
286  last = proxy;
287  };
288 }
289 
290 array(array) p_list()
291 {
292  array(array) res = ({});
293  object proxy = first;
294  string name;
295  array errres;
296  mixed fun;
297 
298  while (objectp(proxy))
299  {
300 
301  fun=proxy->find_function("query_attribute");
302  if (!functionp(fun))
303  name = "---";
304  else
305  {
306  errres = catch {name = fun(OBJ_NAME);};
307  if (arrayp(errres))
308  name = errres[0][0..20];
309  if (!stringp(name))
310  name = "***";
311  }
312 
313  res +=
314  ({
315  ({ (string) proxy->get_object_id(),
316  ( (proxy->status()==1) ? " " +
317  describe_program(object_program(proxy->get_object()))
318  : "on disk" ),
319  (stringp(name) ? name : "empty"),
320  PSTAT(proxy->status())
321  })
322  });
323  // MESSAGE("running through: object "+proxy->get_object_id());
324  proxy = proxy["oNext"];
325  }
326  // MESSAGE("List done ...");
327  return res;
328 }
329 
330 array(program) dependents(program p)
331 {
332  program prog;
333  string progName;
334  array(program) ret = ({});
335  foreach (indices(programs), progName) {
336  prog = programs[progName];
337  if ( !programp(prog) )
338  continue;
339  array(program) inheritlist = Program.all_inherits(prog);
340  if ( search(inheritlist, p) >=0 ) {
341  ret += ({prog});
342  }
343  }
344  return ret;
345 }
346 
347 
348 array pnames(array(program) progs)
349 {
350  program prog;
351  array names = ({});
352  foreach (progs, prog) {
353  names += ({ describe_program(prog) });
354  }
355  return names;
356 }
357 
358 /**
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
364  *
365  * got_error and got_warning provide the messages sent to the ErrorContainer
366  */
367 
368 
369 class ErrorContainer
370 {
371 public:
372  string d;
373  string errors="", warnings="";
374 
375  string get() {
376  return errors;
377  }
378 
379  final mixed `[](mixed num) {
380  switch ( num ) {
381  case 0:
382  return errors;
383  case 1:
384  return ({ });
385  }
386  return "";
387  }
388 
389  string get_warnings() {
390  return warnings;
391  }
392 
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)..];
396  }
397  if( is_warning)
398  warnings+=
399  sprintf("%s:%s\t%s\n", file, line ? (string) line : "-", err);
400  else
401  errors +=
402  sprintf("%s:%s\t%s\n", file, line ? (string) line : "-", err);
403  }
404 
405  // called from master()->compile_error
406  void compile_error(string file, int line, string err) {
407  got_error(file, line, "Error: " + err);
408  }
409 
410  void compile_warning(string file, int line, string err) {
411  got_error(file, line, "Warning: " + err, 1);
412  }
413 
414  void create() {
415  d = getcwd();
416  if (sizeof(d) && (d[-1] != '/') && (d[-1] != '\\'))
417  d += "/";
418  }
419 };
420 
421 
422 object getErrorContainer()
423 {
424  return ErrorContainer();
425 }
426 
427 /**
428  * clear all broken compilations
429  */
430 void clear_compilation_failures()
431 {
432  foreach (indices (programs), string fname)
433  if (!programs[fname]) m_delete (programs, fname);
434 }
435 
436 void dump_proxies()
437 {
438  FATAL("Dumping proxies.... (%d in memory, %d swapped)", iInMemory, iSwapped);
439  mapping visited = ([ ]);
440  object o = first;
441  int i = 0;
442  while ( objectp(o) && o->status ) {
443  i++;
444  if ( visited[o] ) {
445  werror("Circular dependency in master list: %O", o->get_object());
446  _exit(1);
447  }
448  visited[o] = 1;
449  if (o->status()>PSTAT_DISK) {
450  werror("OBJ: %O\n", o);
451  }
452  else {
453  werror("on disk: %O\n", o);
454  }
455  o = o["oNext"];
456  }
457  werror("----- Found %d proxies\n", i);
458 }
459 
460 /**
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)
467  */
468 int|string upgrade(program p, void|int force)
469 {
470  if (!p)
471  {
472  clear_compilation_failures();
473  return "Failed to find program";
474  }
475 
476  if (p == programs["/kernel/proxy.pike"])
477  throw(({"Its impossible to upgrade a proxy object - You have to "+
478  "restart the server", backtrace()}));
479 
480  clear_compilation_failures();
481  array(program) apDependents = dependents(p)+({ p });
482  string fname = search(programs, p);
483 
484  if ( !stringp(fname) )
485  return 0;
486 
487  int type;
488  mixed id;
489  [type,id] = parse_URL_TYPE(fname);
490  if ( type != URLTYPE_DB && intp(id) && id > 0 )
491  fname = "/DB:#"+id+".pike";
492 
493  program tmp;
494 
495  ErrorContainer e = ErrorContainer();
496 
497  m_delete(mErrors, fname);
498  set_inhibit_compile_errors(e);
499  mixed err = catch{
500  tmp = compile_string(master_read_file(fname), fname);
501  };
502  set_inhibit_compile_errors(0);
503 
504  if (err!=0) // testcompile otherwise don't drop !
505  {
506  clear_compilation_failures();
507  mErrors[fname]= e->get() /"\n";
508  FATAL("Error while upgrading: %O\n", e->get());
509 
510  return "Failed to compile "+fname+"\n"+
511  e->get() + "\n" +
512  e->get_warnings();
513  }
514 
515  // assume compilation is ok, or do we have to check all dependents ?
516 
517 
518 
519  object o = first;
520  array aNeedDrop = ({ });
521 
522  int i = 0;
523  while ( objectp(o) && o->status && i < iInMemory )
524  {
525  i++;
526  if (o->status()>PSTAT_DISK) // if not in memory don't drop
527  {
528  if ( search(apDependents, object_program(o->get_object())) >= 0 )
529  {
530  if (!zero_type(o->check_upgrade) && o->check_upgrade())
531  {
532  aNeedDrop += ({o});
533  }
534  }
535  }
536  o = o["oNext"];
537  }
538 
539 
540  foreach(aNeedDrop, object o)
541  {
542  if (functionp(o->upgrade))
543  {
544  o->upgrade();
545  }
546  }
547 
548 
549  mixed UpgradeErr = catch {
550  foreach(aNeedDrop, object o)
551  {
552  int dropped = o->drop();
553  }
554  foreach(apDependents, program prg) {
555  string pname = search(programs, prg);
556  if ( programp(alias[prg]) )
557  m_delete(programs, search(programs,alias[prg]));
558 
559  m_delete(programs, pname);
560  }
561  };
562  if (UpgradeErr)
563  FATAL("Error in upgrade!\n"+describe_backtrace(UpgradeErr));
564 
565  return sizeof(aNeedDrop);
566 }
567 
568 void
569 register_constants()
570 {
571  mConstants = all_constants();
572 }
573 
574 void
575 register_server(object s)
576 {
577  if ( !objectp(oServer) )
578  oServer = s;
579 }
580 
581 object
582 get_server()
583 {
584  return oServer;
585 }
586 
587 void
588 register_user(object u)
589 {
590  int i;
591 
592  if ( search(oaPorts, CALLER) == -1 )
593  THROW("Caller is not a port object !", E_ACCESS);
594 
595  for ( i = sizeof(oaUsers) - 1; i >= 0; i-- ) {
596  if ( oaUsers[i] == u )
597  return;
598  else if ( oaUsers[i]->is_closed() ) {
599  destruct(oaUsers[i]);
600  }
601  }
602  oaUsers -= ({ 0 });
603  oaUsers += ({ u });
604 }
605 
606 void unregister_user()
607 {
608  if ( !is_user(CALLER) )
609  error("Calling object is not a user !");
610  oaUsers -= ({ CALLER });
611 }
612 
613 bool is_user(object u)
614 {
615  int i;
616  for ( i = sizeof(oaUsers) - 1; i >= 0; i-- ) {
617  if ( oaUsers[i] == u )
618  return true;
619  }
620  return false;
621 }
622 
623 bool is_module(object m)
624 {
625  if ( objectp(oServer) )
626  return oServer->is_module(m);
627  return 0;
628 }
629 
630 /**
631  *
632  *
633  * @param
634  * @return
635  * @see
636  */
637 int set_this_user(object obj)
638 {
639 #ifdef THREAD_READ
640  if ( obj == 0 ) {
641  oActiveUser->set(0);
642  oEffectiveUser->set(0);
643  oOldEffectiveUser->set(0);
644  if ( objectp(oCmdLock) )
645  destruct(oCmdLock); // unlocked again
646  return 1;
647  }
648 #endif
649 
650  if ( (!is_module(CALLER) && !is_user(CALLER)) ||
651  (objectp(obj) && !is_user(obj)) )
652  {
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));
657  }
658  error("Failed to set active user!\n");
659  return 0;
660  }
661 
662 #ifdef THREAD_READ
663  if (catch(oCmdLock = cmd_mutex->lock()) ) {
664  FATAL("Failed to obtain lock - Backtrace - going on ...");
665  }
666 
667 #endif
668  if ( objectp(obj) ) {
669  oActiveUser->set(obj); // use proxy
670  oEffectiveUser->set(0);
671  oOldEffectiveUser->set(0);
672  }
673  else {
674  oActiveUser->set(0);
675  oEffectiveUser->set(0);
676  }
677  return 1;
678 }
679 
680 object seteuid(object user)
681 {
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();
688 
689  object users = oServer->get_module("users");
690  object root;
691  if ( objectp(users) )
692  root = users->lookup("root");
693 
694  if ( caller->get_creator() == root ||
695  caller->get_creator() == user ||
696  !objectp(user) ||
697  user == oOldEffectiveUser->get() )
698  {
699  oOldEffectiveUser->set(oEffectiveUser->get());
700  oEffectiveUser->set(user);
701  return user;
702  }
703  throw( ({ sprintf( "Failed to set effective user %O (caller: %O, creator: %O)!", user, caller, caller->get_creator() ), backtrace() }) );
704 }
705 
706 object this_user()
707 {
708  if ( !objectp(oActiveUser) ) return 0;
709 
710  object tu = oActiveUser->get();
711  if ( !objectp(tu) )
712  return 0;
713  return tu->get_user_object();
714 }
715 
716 object this_socket()
717 {
718  if ( !objectp(oActiveUser) ) return 0;
719 
720  object tu = oActiveUser->get();
721  if ( !objectp(tu) )
722  return 0;
723  return tu;
724 }
725 
726 
727 object geteuid()
728 {
729  return oEffectiveUser->get();
730 }
731 
732 void
733 register_port(object s)
734 {
735  if ( CALLER == oServer ) {
736  oaPorts += ({ s });
737  paSockets += ({ s->get_socket_program() });
738  }
739 }
740 
741 array get_ports()
742 {
743  oaPorts -= ({ 0 });
744  return oaPorts;
745 }
746 
747 object get_port(string name)
748 {
749  foreach(oaPorts, object port )
750  if ( objectp(port) && port->get_port_name() == name )
751  return port;
752  return 0;
753 }
754 
755 array get_users()
756 {
757  return copy_value(oaUsers);
758 }
759 
760 
761 /**
762  * bool
763  * system_object(object obj)
764  * {
765  * program prg;
766  *
767  * if ( obj == mConstants["_Security"] || obj == mConstants["_Database"] )
768  * return true;
769  * if ( is_user(obj) )
770  * return true;
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" )
780  * return true;
781  * return false;
782  * }
783  */
784 
785 mixed parse_URL_TYPE(string f)
786 {
787  string path;
788  int id;
789  string ext;
790  // its DB-type
791  if ( sscanf(f, "/DB:%s", path) > 0 )
792  {
793  if (sscanf(path, "#%d.%s", id, ext))
794  {
795  if ( ext == "pike" )
796  return ({ URLTYPE_DB, id });
797  else
798  return ({ URLTYPE_DBO, id });
799  }
800  else
801  return ({ URLTYPE_DBFT, path });
802  }
803  else if ( sscanf(f, "steam:%s", path) > 0 ) {
804  return ({ URLTYPE_DBFT, path });
805  }
806  return ({URLTYPE_FS,0});
807 }
808 
809 #if 1
810 
811 array(array) mount_points;
812 
813 int mount(string source, string dest)
814 {
815  if ( objectp(oServer) && CALLER != oServer )
816  error("Unauthorized call to mount() !");
817 
818  MESSAGE("Mounting %s on %s", source, dest);
819 
820  // make sure we have proper prefixes
821  if (source[strlen(source)-1]!='/')
822  source += "/";
823  if (dest[strlen(dest)-1]!='/')
824  dest += "/";
825 
826  if (source == "/")
827  set_root(dest);
828  // insert them according to strlen
829  int i;
830  if (!arrayp(mount_points))
831  mount_points = ({ ({ source, dest }) });
832  else
833  {
834  i = 0;
835  while( i < sizeof(mount_points) &&
836  (strlen(mount_points[i][0])<strlen(source)))
837  {
838  i++;
839  }
840 
841  mount_points= mount_points[..i-1] +
842  ({({ source, dest })}) +
843  mount_points[i..];
844  }
845 }
846 
847 void run_sandbox(string cdir, void|string user)
848 {
849  function change_root, switch_user;
850 
851  if ( !stringp(cdir) )
852  return;
853 
854 #if constant(System)
855  change_root= System.chroot;
856  switch_user= System.setuid;
857 #else
858  change_root = chroot;
859  switch_user = setuid;
860 #endif
861 
862  change_root = chroot;
863 
864  if ( change_root(cdir) ) {
865  current_path = "/";
866  MESSAGE("Running in chroot environment... (%s)\n", cdir);
867  object dir = Stdio.File("/etc","r");
868  if ( !objectp(dir) )
869  error("Failed to find /etc directory - aborting !");
870 
871  array user_info;
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;
877  break;
878  }
879  }
880  // fallback on user nobody:
881  if ( !arrayp(user_info) ) {
882  user = "nobody";
883  foreach ( get_all_users(), array tmp_user_info ) {
884  if ( tmp_user_info[0] != user ) continue;
885  user_info = tmp_user_info;
886  break;
887  }
888  }
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] );
892  }
893  }
894  mount_points = ({ ({ "/", "/" }), ({ "/include", "/include" }) });
895  pike_include_path += ({ "/include" });
896  pike_module_path += ({ "/libraries" });
897  }
898  else {
899  MESSAGE("change_root(%s) Failed !", getcwd());
900  }
901 }
902 
903 
904 string apply_mount_points(string orig)
905 {
906 
907  int i;
908  string res;
909 
910  if (!arrayp(mount_points))
911  return orig;
912 
913  if ( search(orig, "/DB:#") == 0 )
914  return orig;
915 
916  if (orig[0]!='/' && orig[0]!='#')
917  orig = "/"+orig;
918  res = orig;
919  for (i=sizeof(mount_points);i--;)
920  if (search(orig, mount_points[i][0]) == 0)
921  {
922  res= mount_points[i][1]+orig[strlen(mount_points[i][0])..];
923  break;
924  }
925  return res;
926 }
927 
928 object master_file_stat(string x, void|int follow)
929 {
930  object p;
931  int TypeURL;
932  mixed path;
933 
934 #ifdef MOUNT_TRACE
935  werror("[master_file_stat("+x+") ->");
936 #endif
937  [TypeURL, path] = parse_URL_TYPE(x);
938  switch (TypeURL)
939  {
940  case URLTYPE_FS:
941  x = apply_mount_points(x);
942 #ifdef MOUNT_TRACE
943  werror("fs("+x+")\n");
944 #endif
945  return ::master_file_stat(x);
946  case URLTYPE_DB:
947 
948 #ifdef MOUNT_TRACE
949  werror(sprintf("db(%d)]\n",path));
950 #endif
951  p = __DATABASE->find_object(path);
952  if (objectp(p)) {
953  array s = p->stat();
954  if ( arrayp(s) && sizeof(s) > 6 )
955  return Stdio.Stat(s[..6]);
956  return 0;
957  }
958  case URLTYPE_DBO:
959 #ifdef MOUNT_TRACE
960  werror(sprintf("dbo(%d)]\n",path));
961 #endif
962  return 0;
963  case URLTYPE_DBFT:
964 #ifdef MOUNT_TRACE
965  werror(sprintf("dbft(%s)]\n", path));
966 #endif
967  p = MODULE_FILEPATH->path_to_object(path);
968  if (objectp(p)) {
969  array s = p->stat();
970  if ( arrayp(s) && sizeof(s) > 6 )
971  return Stdio.Stat(s[..6]);
972  return 0;
973  }
974  }
975  return 0;
976 }
977 
978 array(program)
979 get_programs()
980 {
981  return copy_value(values(programs));
982 }
983 
984 /**
985  * Access the program pointer currently registered for programname
986  *
987  * @param string pname - the program to look up
988  * @return program - the associated program
989  * @see upgrade, new
990  */
991 program lookup_program(string pname)
992 {
993  return programs[pname];
994 }
995 
996 program compile_string(string source,
997  void|string filename,
998  object|void handler,
999  void|program p,
1000  void|object o,
1001  void|int _show_if_constant_errors)
1002 {
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);
1006 }
1007 
1008 
1009 program compile_file(string file,
1010  object|void handler,
1011  void|program p,
1012  void|object o)
1013 {
1014  int TypeURL;
1015  string path;
1016  string content;
1017  object tmp;
1018 
1019  llog = 0;
1020  //LMESSAGE("compile_file("+file+")");
1021  [ TypeURL, path ] = parse_URL_TYPE(file);
1022 
1023  switch (TypeURL)
1024  {
1025  case URLTYPE_FS:
1026  file = apply_mount_points(file);
1027  tmp = Stdio.File(file, "r");
1028  content = tmp->read();
1029  tmp->close();
1030  break;
1031  //return ::compile_file(file);
1032  case URLTYPE_DBO:
1033  return 0; // dump files not supported in database
1034  case URLTYPE_DB:
1035  tmp = __DATABASE->find_object((int)path);
1036  content = tmp->get_source_code();
1037  break;
1038  case URLTYPE_DBFT:
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() }));
1042  }
1043  else
1044  content = tmp->get_source_code();
1045  break;
1046  }
1047  if (objectp(tmp)) {
1048  program _loading;
1049  if ( !stringp(content) )
1050  {
1051  content = "";
1052  FATAL("Warning: No content of file %O to compile...\n", file);
1053  }
1054  // _loading = compile(cpp(content, file));
1055  if ( stringp(file) )
1056  m_delete(mErrors, file);
1057  _loading= compile(cpp(content,
1058  file,
1059  1,
1060  handler,
1061  compat_major,
1062  compat_minor),
1063  handler,
1064  compat_major,
1065  compat_minor,
1066  p,
1067  o);
1068  return _loading;
1069  }
1070  llog = 0;
1071  throw(({"Cant resolve filename\n", backtrace()}));
1072 }
1073 
1074 program cast_to_program(string pname, string current_file, void|object handler)
1075 {
1076  program p;
1077  int i;
1078  if ( (i=search(pname, "/DB:")) == 0 ) {
1079  if ( search(pname, ".pike") == 0 )
1080  pname += ".pike";
1081  p = lookup_program(pname);
1082  if ( programp(p) ) return p;
1083  return compile_file(pname);
1084  }
1085  p = ::cast_to_program(pname, current_file);
1086  return p;
1087 }
1088 
1089 protected:
1090  program low_findprog(string pname,
1091  string ext,
1092  object|void handler,
1093  void|int mkobj)
1094 {
1095  //return ::low_findprog(apply_mount_points(pname), ext, handler, mkobj);
1096  return ::low_findprog(pname, ext, handler, mkobj);
1097 }
1098 
1099 public:
1100 
1101 
1102 mixed resolv(string symbol, string filename, object handler)
1103 {
1104 #ifdef MOUNT_TRACE
1105  werror("[resolve("+symbol+","+filename+sprintf(",%O)\n",handler));
1106 #endif
1107  mixed erg=::resolv(symbol, filename, handler);
1108 #ifdef MOUNT_TRACE
1109  werror("[resolve returns:"+sprintf("%O\n",erg));
1110 #endif
1111  return erg;
1112 }
1113 
1114 string id_from_dbpath(string db_path)
1115 {
1116  int type_URL;
1117  string _path;
1118 
1119  [type_URL, _path] = parse_URL_TYPE(db_path);
1120  if (type_URL == URLTYPE_DB)
1121  {
1122  if (search(_path,"#")==0)
1123  return _path;
1124  else
1125  {
1126  object p;
1127  p = MODULE_FILEPATH->path_to_object(db_path);
1128  if(objectp(p))
1129  return "#"+ p->get_object_id();
1130  return 0;
1131  }
1132  }
1133  return db_path;
1134 }
1135 
1136 mapping get_errors()
1137 {
1138  return mErrors;
1139 }
1140 
1141 array get_error(string file)
1142 {
1143  if (mErrors[file])
1144  return ({ file+"\n" }) + mErrors[file];
1145  else
1146  return 0;
1147 }
1148 
1149 void compile_error(string file, int line, string err)
1150 {
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);
1155 }
1156 
1157 string handle_include(string f, string current_file, int local_include)
1158 {
1159  array tmp;
1160  string path;
1161 
1162  if(local_include)
1163  {
1164  tmp=current_file/"/";
1165  tmp[-1]=f;
1166  path=combine_path_with_cwd((tmp*"/"));
1167  if (parse_URL_TYPE(path)[0] == URLTYPE_DB)
1168  path = id_from_dbpath(path);
1169  }
1170  else
1171  {
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);
1176  else {
1177  if(master_file_stat(path))
1178  break;
1179  else
1180  path=0;
1181  }
1182  }
1183  }
1184  return path;
1185 
1186 }
1187 
1188 
1189 string read_include(string f)
1190 {
1191  llog = 0;
1192  llog = 0;
1193  if (search(f,"#")==0) // #include <%45>
1194  {
1195  object p;
1196  p = mConstants["_Database"]->find_object((int)f[1..]);
1197  //p = find_object((int)f[1..]);
1198  if (objectp(p))
1199  return p->get_source_code();
1200  return 0;
1201  }
1202  return ::read_include(apply_mount_points(f));
1203 }
1204 
1205 #endif
1206 
1207 int
1208 get_type(mixed var)
1209 {
1210  if ( intp(var) )
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;
1225 }
1226 
1227 string sRoot;
1228 void set_root(string root)
1229 {
1230  sRoot = root;
1231 }
1232 
1233 
1234 string dirname(string x)
1235 {
1236  if ((stringp(sRoot)) && search(x, sRoot)==0)
1237  return dirname(x[strlen(sRoot)..]);
1238  return ::dirname(x);
1239 }
1240 
1241 //string master_read_file(string file)
1242 //{
1243 // LMESSAGE("master_read_file("+file+")");
1244 // return ::master_read_file(file);
1245 //}
1246 
1247 string master_read_file(string file)
1248 {
1249  int TypeURL;
1250  string path;
1251  mixed p;
1252 
1253 #ifdef MOUNT_TRACE
1254  werror("master_read_file("+file+")");
1255 #endif
1256 
1257  [TypeURL, path ] = parse_URL_TYPE(file);
1258  switch (TypeURL)
1259  {
1260  case URLTYPE_FS:
1261  //MESSAGE("calling compile_file("+file+")");
1262  //file = apply_mount_points(file);
1263  return ::master_read_file(file);
1264  //return ::compile_file(file);
1265  case URLTYPE_DB:
1266 #ifdef MOUNT_TRACE
1267  werror(sprintf("db(%s)\n",path));
1268 #endif
1269  p = __DATABASE->find_object((int)path);
1270  if (p==1)
1271  throw(({"sourcefile deleted", backtrace()}));
1272  else
1273  if (!objectp(p))
1274  throw(({"failed to load sourcefile", backtrace()}));
1275  return p->get_source_code();
1276  case URLTYPE_DBO:
1277 #ifdef MOUNT_TRACE
1278  werror(sprintf("db(%s)\n",path));
1279 #endif
1280  return 0;
1281  case URLTYPE_DBFT:
1282 #ifdef MOUNT_TRACE
1283  werror(sprintf("db(%s)\n",path));
1284 #endif
1285  p = MODULE_FILEPATH->path_to_object(path);
1286  return p->get_source_code();
1287  }
1288  throw(({"Failed to load file"+file, backtrace()}));
1289 }
1290 
1291 /*object findmodule(string fullname)
1292 {
1293  object o;
1294  llog = 0;
1295  LMESSAGE("findmodule("+fullname+", called by " + describe_object(CALLER));
1296  o=::findmodule(fullname);
1297  llog = 0;
1298  return o;
1299 }
1300 */
1301 
1302 string describe_mapping(mapping m, int maxlen)
1303 {
1304  mixed keys = indices(m);
1305  mixed values = values(m);
1306  string out= "";
1307  for (int i=0;i<sizeof(keys);i++)
1308  {
1309  out += stupid_describe(keys[i], maxlen) +
1310  ":" + detailed_describe(values[i], maxlen)
1311  + (i<sizeof(keys)-1 ? "," :"");
1312  }
1313  return out;
1314 }
1315 
1316 string describe_array(array a, int maxlen)
1317 {
1318  string out="";
1319  for (int i=0;i<sizeof(a);i++)
1320  {
1321  out += detailed_describe(a[i], maxlen) + (i<sizeof(a)-1 ? "," :"");
1322  }
1323  return out;
1324 }
1325 
1326 string describe_multiset(multiset m, int maxlen)
1327 {
1328  mixed keys = indices(m);
1329  string out= "";
1330  for (int i=0;i<sizeof(keys);i++)
1331  {
1332  out += stupid_describe(keys[i], maxlen) + (i<sizeof(keys)-1 ? "," :"");
1333  }
1334  return out;
1335 }
1336 
1337 string detailed_describe(mixed m, int maxlen)
1338 {
1339  if (maxlen == 0)
1340  maxlen = 2000;
1341  string typ;
1342  if (catch (typ=sprintf("%t",m)))
1343  typ = "object"; // Object with a broken _sprintf(), probably.
1344  switch(typ)
1345  {
1346  case "int":
1347  case "float":
1348  return (string)m;
1349 
1350  case "string":
1351  if(sizeof(m) < maxlen)
1352  {
1353  string t = sprintf("%O", m);
1354  if (sizeof(t) < (maxlen + 2)) {
1355  return t;
1356  }
1357  t = 0;
1358  }
1359  if(maxlen>10)
1360  {
1361  return sprintf("%O+[%d]",m[..maxlen-5],sizeof(m)-(maxlen-5));
1362  }else{
1363  return "string["+sizeof(m)+"]";
1364  }
1365 
1366  case "array":
1367  if(!sizeof(m)) return "({})";
1368  return "({" + describe_array(m,maxlen-2) +"})";
1369 
1370  case "mapping":
1371  if(!sizeof(m)) return "([])";
1372  return "([" + describe_mapping(m, maxlen-2) + "])";
1373 
1374  case "multiset":
1375  if(!sizeof(m)) return "(<>)";
1376  return "(<" + describe_multiset(m, maxlen-2) + ">)";
1377  return "multiset["+sizeof(m)+"]";
1378 
1379  case "function":
1380  if(string tmp=describe_program(m)) return tmp;
1381  if(object o=function_object(m))
1382  return (describe_object(o)||"")+"->"+function_name(m);
1383  else {
1384  string tmp;
1385  if (catch (tmp = function_name(m)))
1386  // The function object has probably been destructed.
1387  return "function";
1388  return tmp || "function";
1389  }
1390 
1391  case "program":
1392  if(string tmp=describe_program(m)) return tmp;
1393  return typ;
1394 
1395  default:
1396  if (objectp(m))
1397  if(string tmp=describe_object(m)) return tmp;
1398  return typ;
1399  }
1400 }
1401 
1402 
1403 /**
1404  * perform the call-out, but save the previous user-object
1405  *
1406  */
1407 void f_call(function f, object user, object caller, void|array args)
1408 {
1409  mixed err;
1410 
1411  // skip calls if function is no longer available
1412  if (!functionp(f)) {
1413  return;
1414  }
1415 
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);
1421  err = catch{
1422  if ( objectp(caller) && functionp(caller->route_call) && function_name(f) != "drop" )
1423  caller->route_call(f, args);
1424  else
1425  f(@args);
1426  };
1427  oActiveUser->set(old_user);
1428  oEffectiveUser->set(old_euid);
1429  if ( err )
1430  FATAL("Error on call_out:"+sprintf("%O:\n%O\n", err[0], err[1]));
1431 }
1432 
1433 /**
1434  * call a function delayed. The user object is saved.
1435  *
1436  */
1437 mixed
1438 f_call_out(function f, float|int delay, mixed ... args)
1439 {
1440  if (!functionp(f))
1441  error("Unable to call NULL function !");
1442 
1443  object caller = CALLER;
1444  caller = Caller.get_caller(caller, backtrace());
1445  return call_out(f_call, delay, f, this_user(), caller, args);
1446 }
1447 
1448 mixed f_call_out_info()
1449 {
1450  return call_out_info();
1451 }
1452 
1453 /**
1454  *
1455  *
1456  * @param
1457  * @return
1458  * @see
1459  */
1460 array get_dir(string dir)
1461 {
1462  string fdir = apply_mount_points(dir);
1463  // MESSAGE("Getting dir of " + fdir);
1464  return predef::get_dir(fdir);
1465 }
1466 
1467 int is_dir(string dir)
1468 {
1469  return Stdio.is_dir(apply_mount_points(dir));
1470 }
1471 
1472 
1473 /**
1474  * This Function is the mount-point aware of the rm command
1475  * rm removes a file from the filesystem
1476  *
1477  * @param string f
1478  * @return 0 if it fails. Nonero otherwise
1479  * @see get_dir
1480  * @caveats this command is limited to removing filesystem files
1481  */
1482 int rm(string f)
1483 {
1484  string truef = apply_mount_points(f);
1485  return predef::rm(truef);
1486 }
1487 
1488 /**
1489  *
1490  *
1491  * @param
1492  * @return
1493  * @see
1494  */
1495 protected:
1496  void run_thread(function f, object user, mixed ... args)
1497 {
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) )
1504  user->force_load();
1505  }
1506  }
1507  }
1508  oActiveUser->set(user);
1509  oEffectiveUser->set(0);
1510  f(@args);
1511 }
1512 
1513 public:
1514 
1515 /**
1516  *
1517  *
1518  * @param
1519  * @return
1520  * @see
1521  */
1522 void start_thread(function f, mixed ... args)
1523 {
1524  MESSAGE("Starting new Thread %O", f);
1525  predef::thread_create(run_thread, f, this_user(), @args);
1526 }
1527 
1528 /**
1529  *
1530  *
1531  * @param
1532  * @return
1533  * @see
1534  */
1535 mixed file_stat(string f, void|int follow)
1536 {
1537  string ff = apply_mount_points(f);
1538  return predef::file_stat(ff, follow);
1539 }
1540 
1541 #if 0
1542 mapping get_ports()
1543 {
1544  return mPorts;
1545 }
1546 
1547 void use_port(int pid)
1548 {
1549  mPorts[pid] == 1;
1550 }
1551 #endif
1552 
1553 int free_port(int pid)
1554 {
1555  return mPorts[pid] != 1;
1556 }
1557 
1558 void dispose_port(int pid)
1559 {
1560  mPorts[pid] = 0;
1561 }
1562 
1563 /**
1564  * Find out if a given object is a socket (this means it
1565  * has to be in the list of sockets.
1566  *
1567  * @param object o - the socket object
1568  * @return true or false (0 or 1)
1569  */
1570 int is_socket(object o)
1571 {
1572  return (search(paSockets, object_program(o)) >= 0 );
1573 }
1574 
1575 function find_function(string f) { return this_object()[f]; }
1576 
1577 #if (__MINOR__ > 3) // this is a backwards compatibility function
1578 object new(string|program program_file, mixed|void ...args)
1579 {
1580  program prg = (program)program_file;
1581  if ( !programp(prg) )
1582  return 0;
1583 
1584  return (prg)(@args);
1585 }
1586 #endif
1587 
1588 
1589 
1590 void describe_threads()
1591 {
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();
1596 
1597  werror("### Describing all Pike threads:\n\n");
1598 
1599  array(Thread.Thread) threads = all_threads();
1600  array(string|int) thread_ids =
1601  map (threads,
1602  lambda (Thread.Thread t) {
1603  string desc = sprintf ("%O", t);
1604  if (sscanf (desc, "Thread.Thread(%d)", int i)) return i;
1605  else return desc;
1606  });
1607  sort (thread_ids, threads);
1608 
1609  int i;
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)" : ""
1614  );
1615  werror(describe_backtrace(threads[i]->backtrace()) + "\n");
1616  }
1617 
1618  werror ("### Total %d Pike threads\n", sizeof (threads));
1619 
1620  threads = 0;
1621  threads_disabled = 0;
1622 #else
1623  werror("Describing single thread:\n%s\n",
1624  describe_backtrace (backtrace()));
1625 #endif
1626 
1627 }
1628 
1629 
1630 };