7a61de1998-03-26Per Hedbor constant cvs_version = "$Id: roxen.pike,v 1.179 1998/03/26 11:37:30 per Exp $";
4dd97c1996-12-04Per Hedbor #define IN_ROXEN
8540e11997-06-14Francesco Chemolli #include <roxen.h> #include <config.h>
8557d51997-02-19David Hedbor #ifdef THREADS
14179b1997-01-29Per Hedbor #include <fifo.h>
8557d51997-02-19David Hedbor #endif
b1fca01996-11-12Per Hedbor #include <module.h> #include <variables.h>
14179b1997-01-29Per Hedbor 
48fa361997-04-05Per Hedbor inherit "read_config";
c79b261998-02-05Johan Schön #ifdef __NT__ #define NO_DNS #endif
b1fca01996-11-12Per Hedbor #ifdef NO_DNS inherit "dummy_hosts"; #else inherit "hosts"; #endif
70f2771997-12-15Per Hedbor inherit "module_support";
b1fca01996-11-12Per Hedbor inherit "socket"; inherit "disk_cache"; inherit "language";
e5bad21998-02-10Per Hedbor #if constant(spider.shuffle) program pipe = (program)"smartpipe"; #else program pipe = Pipe.pipe; #endif
603e0a1997-07-20Henrik Grubbström (Grubba) // This is the real Roxen version. It should be changed before each // release
d199531998-03-05Henrik Grubbström (Grubba) constant __roxen_version__ = "1.2"; constant __roxen_build__ = "21";
e5bad21998-02-10Per Hedbor #ifdef __NT__
d199531998-03-05Henrik Grubbström (Grubba) constant real_version = "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__+" NT";
e5bad21998-02-10Per Hedbor #else
d199531998-03-05Henrik Grubbström (Grubba) constant real_version = "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__;
e5bad21998-02-10Per Hedbor #endif
603e0a1997-07-20Henrik Grubbström (Grubba) 
3e646f1997-05-15David Hedbor #if _DEBUG_HTTP_OBJECTS mapping httpobjects = ([]); static int idcount; int new_id(){ return idcount++; } #endif
0f8c871997-06-01Henrik Grubbström (Grubba) #ifdef MODULE_DEBUG #define MD_PERROR(X) perror X; #else #define MD_PERROR(X) #endif /* MODULE_DEBUG */
14179b1997-01-29Per Hedbor object roxen=this_object(), current_configuration;
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor private program Configuration; /*set in create*/
b1fca01996-11-12Per Hedbor 
48fa361997-04-05Per Hedbor array configurations = ({});
b1fca01996-11-12Per Hedbor object main_configuration_port;
e493e81997-07-11Per Hedbor mapping allmodules, somemodules=([]);
b1fca01996-11-12Per Hedbor  // A mapping from ports (objects, that is) to an array of information
e5bad21998-02-10Per Hedbor // about that port. This will hopefully be moved to objects cloned // from the configuration object in the future.
b1fca01996-11-12Per Hedbor mapping portno=([]);
e382ed1997-07-16Henrik Grubbström (Grubba) constant decode = MIME.decode_base64;
b1fca01996-11-12Per Hedbor 
beaca01998-02-20Per Hedbor // Function pointer and the root of the configuration interface
b1fca01996-11-12Per Hedbor // object. private function build_root; private object root;
6ce8591997-09-14Henrik Grubbström (Grubba) #ifdef THREADS // This mutex is used by privs.pike object euid_egid_lock = Thread.Mutex(); #endif /* THREADS */
506ede1997-11-11Henrik Grubbström (Grubba) int privs_level;
4f4bc11998-02-04Per Hedbor int die_die_die;
506ede1997-11-11Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor // Fork, and then do a 'slow-quit' in the forked copy. Exit the // original copy, after all listen ports are closed.
3835ca1998-01-16Henrik Grubbström (Grubba) // Then the forked copy can finish all current connections.
b1fca01996-11-12Per Hedbor private static void fork_or_quit() { int i; object *f;
14179b1997-01-29Per Hedbor  int pid; perror("Exiting Roxen.\n");
b1fca01996-11-12Per Hedbor #ifdef SOCKET_DEBUG perror("SOCKETS: fork_or_quit()\n Bye!\n"); #endif
abdc631998-01-21Henrik Grubbström (Grubba)  #if constant(fork) && !defined(THREADS)
b1fca01996-11-12Per Hedbor  if(fork())
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(-1); // Restart. // Now we're running in the forked copy.
abdc631998-01-21Henrik Grubbström (Grubba)  // FIXME: This probably doesn't work correctly on threaded servers, // since only one thread is left running after the fork().
b1fca01996-11-12Per Hedbor #if efun(_pipe_debug) call_out(lambda() { // Wait for all connections to finish
aaef2a1997-03-02Henrik Grubbström (Grubba)  call_out(Simulate.this_function(), 20);
b1fca01996-11-12Per Hedbor  if(!_pipe_debug()[0]) exit(0); }, 1);
abdc631998-01-21Henrik Grubbström (Grubba) #endif /* efun(_pipe_debug) */
b1fca01996-11-12Per Hedbor  call_out(lambda(){ exit(0); }, 600); // Slow buggers.. f=indices(portno); for(i=0; i<sizeof(f); i++) catch(destruct(f[i]));
abdc631998-01-21Henrik Grubbström (Grubba) #else /* !constant(fork) */ // FIXME: // Should probably attempt something similar to the above, // but this should be sufficient for the time being. exit(-1); // Restart #endif /* constant(fork) */
b1fca01996-11-12Per Hedbor } // This is called for each incoming connection. private static void accept_callback( object port ) { object file;
06583f1997-09-03Per Hedbor  int q=QUERY(NumAccept);
14179b1997-01-29Per Hedbor  array pn=portno[port];
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor #ifdef DEBUG if(!pn)
b1fca01996-11-12Per Hedbor  { destruct(port->accept()); perror("$&$$& Garbage Collector bug!!\n"); return; }
14179b1997-01-29Per Hedbor #endif
b1fca01996-11-12Per Hedbor  while(q--) { catch { file = port->accept(); };
14179b1997-01-29Per Hedbor #ifdef SOCKET_DEBUG if(!pn[-1])
b1fca01996-11-12Per Hedbor  { report_error("In accept: Illegal protocol handler for port.\n"); if(file) destruct(file); return; } perror(sprintf("SOCKETS: accept_callback(CONF(%s))\n",
14179b1997-01-29Per Hedbor  pn[1]&&pn[1]->name||"Configuration"));
b1fca01996-11-12Per Hedbor #endif if(!file) { switch(port->errno()) { case 0: case 11: return; default: #ifdef DEBUG perror("Accept failed.\n");
4f4bc11998-02-04Per Hedbor #if constant(real_perror)
b1fca01996-11-12Per Hedbor  real_perror();
4f4bc11998-02-04Per Hedbor #endif
95e2b41997-05-25Wilhelm Köhler #endif /* DEBUG */
b1fca01996-11-12Per Hedbor  return; case 24:
14179b1997-01-29Per Hedbor  report_fatal("Out of sockets. Restarting server gracefully.\n"); fork_or_quit();
b1fca01996-11-12Per Hedbor  return; } }
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
b1fca01996-11-12Per Hedbor  mark_fd( file->query_fd(), "Connection from "+file->query_address());
14179b1997-01-29Per Hedbor #endif pn[-1](file,pn[1]);
b1fca01996-11-12Per Hedbor #ifdef SOCKET_DEBUG perror(sprintf("SOCKETS: Ok. Connect on %O:%O from %O\n",
14179b1997-01-29Per Hedbor  pn[2], pn[0], file->query_address())); #endif } }
3aaaa71997-06-12Wilhelm Köhler void unthreaded_handle(function f, mixed ... args) { f(@args); }
34fbbc1998-02-05Henrik Grubbström (Grubba) function handle = unthreaded_handle; #ifdef THREADS #define THREAD_DEBUG
4f4bc11998-02-04Per Hedbor object do_thread_create(string id, function f, mixed ... args) { object t = thread_create(f, @args); catch(t->set_name( id )); werror(id+" started\n"); return t; }
14179b1997-01-29Per Hedbor object (Queue) handle_queue = Queue(); void handler_thread(int id) {
4f4bc11998-02-04Per Hedbor  array (mixed) h, q;
b8811d1997-09-01Per Hedbor  while(1)
4f4bc11998-02-04Per Hedbor  { if(q=catch {
45cae31998-03-06Henrik Grubbström (Grubba)  do { if((h=handle_queue->read()) && h && h[0]) { h[0](@h[1]); h=0; } } while(1); }) { report_error("Uncaught error in handler thread: " + describe_backtrace(q) + "Client will not get any response from Roxen.\n"); if (q = catch {h = 0;}) { report_error("Uncaught error in handler thread: " + describe_backtrace(q) + "Client will not get any response from Roxen.\n"); } }
4f4bc11998-02-04Per Hedbor  }
b1fca01996-11-12Per Hedbor }
3aaaa71997-06-12Wilhelm Köhler void threaded_handle(function f, mixed ... args) { handle_queue->write(({f, args })); }
14179b1997-01-29Per Hedbor int number_of_threads; void start_handler_threads() {
a60c4c1997-07-03Henrik Grubbström (Grubba)  if (QUERY(numthreads) <= 1) { QUERY(numthreads) = 1; perror("Starting 1 thread to handle requests.\n"); } else { perror("Starting "+QUERY(numthreads)+" threads to handle requests.\n"); }
14179b1997-01-29Per Hedbor  for(; number_of_threads < QUERY(numthreads); number_of_threads++)
4f4bc11998-02-04Per Hedbor  do_thread_create( "Handle thread ["+number_of_threads+"]", handler_thread, number_of_threads );
3aaaa71997-06-12Wilhelm Köhler  if(number_of_threads > 0) handle = threaded_handle;
14179b1997-01-29Per Hedbor }
06583f1997-09-03Per Hedbor 
4f4bc11998-02-04Per Hedbor mapping accept_threads = ([]);
06583f1997-09-03Per Hedbor void accept_thread(object port,array pn) {
4f4bc11998-02-04Per Hedbor  accept_threads[port] = this_thread();
06583f1997-09-03Per Hedbor  program port_program = pn[-1]; mixed foo = pn[1]; array err; object o;
4f4bc11998-02-04Per Hedbor  while(!die_die_die) { o = port->accept(); err = catch { if(o) port_program(o,foo); }; if(err) perror("Error in accept_thread: %O\n",describe_backtrace(err)); }
06583f1997-09-03Per Hedbor }
95e2b41997-05-25Wilhelm Köhler #endif /* THREADS */
14179b1997-01-29Per Hedbor 
06583f1997-09-03Per Hedbor 
b1fca01996-11-12Per Hedbor // Listen to a port, connected to the configuration 'conf', binding // only to the netinterface 'ether', using 'requestprogram' as a // protocol handled. // If you think that the argument order is quite unintuitive and odd, // you are right, the order is the same as the implementation order. // Old spinners only listened to a port number, then the // configurations came, then the need to bind to a specific // ethernetinterface, and then the need to have more than one concurrent // protocol (http, ftp, ssl, etc.)
14179b1997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor object create_listen_socket(mixed port_no, object conf,
9f46de1997-04-08Per Hedbor  string|void ether, program requestprogram, array prt)
b1fca01996-11-12Per Hedbor { object port; #ifdef SOCKET_DEBUG perror(sprintf("SOCKETS: create_listen_socket(%d,CONF(%s),%s)\n", port_no, conf?conf->name:"Configuration port", ether)); #endif if(!requestprogram) error("No request handling module passed to create_listen_socket()\n"); if(!port_no) {
7ed1021998-01-28Henrik Grubbström (Grubba)  port = Stdio.Port( "stdin", accept_callback );
c79b261998-02-05Johan Schön  port->set_id(port);
b1fca01996-11-12Per Hedbor  if(port->errno()) { report_error("Cannot listen to stdin.\n" "Errno is "+port->errno()+"\n"); } } else {
7ed1021998-01-28Henrik Grubbström (Grubba)  port = Stdio.Port();
c79b261998-02-05Johan Schön  port->set_id(port);
b1fca01996-11-12Per Hedbor  if(!stringp(ether) || (lower_case(ether) == "any")) ether=0; if(ether) sscanf(ether, "addr:%s", ether);
9dcc101998-03-10David Hedbor #if defined(THREADS) && 0
06583f1997-09-03Per Hedbor  if(!port->bind(port_no, 0, ether)) #else
b1fca01996-11-12Per Hedbor  if(!port->bind(port_no, accept_callback, ether))
06583f1997-09-03Per Hedbor #endif
b1fca01996-11-12Per Hedbor  { #ifdef SOCKET_DEBUG
581bf11997-09-06Per Hedbor  perror("SOCKETS: -> Failed.\n");
b1fca01996-11-12Per Hedbor #endif
581bf11997-09-06Per Hedbor  report_error("Failed to open socket on "+ether+":"+port_no+ " (already bound?)\nErrno is: "+ port->errno()+"\n"); return 0;
b1fca01996-11-12Per Hedbor  } }
14179b1997-01-29Per Hedbor  portno[port]=({ port_no, conf, ether||"Any", 0, requestprogram });
9dcc101998-03-10David Hedbor #if defined(THREADS) && 0
4f4bc11998-02-04Per Hedbor  call_out(do_thread_create,0,"Accept thread ["+port_no+":"+(ether||"ANY]"), accept_thread, port,portno[port]);
06583f1997-09-03Per Hedbor #endif
b1fca01996-11-12Per Hedbor #ifdef SOCKET_DEBUG perror("SOCKETS: -> Ok.\n"); #endif return port; } // The configuration interface is loaded dynamically for faster // startup-time, and easier coding in the configuration interface (the // Roxen environment is already finished when it is loaded) object configuration_interface_obj; int loading_config_interface; int enabling_configurations; object configuration_interface() { if(enabling_configurations) return 0; if(loading_config_interface) { perror("Recursive calls to configuration_interface()\n" + describe_backtrace(backtrace())+"\n"); } if(!configuration_interface_obj) { perror("Loading configuration interface.\n");
039ce41997-08-21Henrik Grubbström (Grubba)  loading_config_interface = 1;
2740711997-08-21Henrik Grubbström (Grubba)  array err = catch {
0f28da1997-08-13Per Hedbor  configuration_interface_obj=((program)"mainconfig")();
d24d291997-08-19Per Hedbor  root = configuration_interface_obj->root;
0f28da1997-08-13Per Hedbor  };
039ce41997-08-21Henrik Grubbström (Grubba)  loading_config_interface = 0;
2740711997-08-21Henrik Grubbström (Grubba)  if(!configuration_interface_obj) {
c79b261998-02-05Johan Schön  report_error(sprintf("Failed to load the configuration interface!\n"));
2740711997-08-21Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  } return configuration_interface_obj; } // Create a new configuration from scratch. // 'type' is as in the form. 'none' for a empty configuration. int add_new_configuration(string name, string type) { return configuration_interface()->low_enable_configuration(name, type); } // Call the configuration interface function. This is more or less // equivalent to a virtual configuration with the configurationinterface // mounted on '/'. This will probably be the case in future versions
fb74971997-09-16Per Hedbor #ifdef THREADS object configuration_lock = Thread.Mutex(); #endif
b1fca01996-11-12Per Hedbor mixed configuration_parse(mixed ... args) {
fb74971997-09-16Per Hedbor #ifdef THREADS object key; catch(key = configuration_lock->lock()); #endif
b1fca01996-11-12Per Hedbor  if(args) return configuration_interface()->configuration_parse(@args); }
025d221997-06-01Henrik Grubbström (Grubba) mapping(string:array(int)) error_log=([]);
ec2fe11997-06-09Henrik Grubbström (Grubba) string last_error="";
b1fca01996-11-12Per Hedbor // Write a string to the configuration interface error log and to stderr.
9b9f701997-08-12Per Hedbor void nwrite(string s, int|void perr, int|void type)
b1fca01996-11-12Per Hedbor {
ec2fe11997-06-09Henrik Grubbström (Grubba)  last_error = s;
9b9f701997-08-12Per Hedbor  if (!error_log[type+","+s]) { error_log[type+","+s] = ({ time() });
025d221997-06-01Henrik Grubbström (Grubba)  } else {
9b9f701997-08-12Per Hedbor  error_log[type+","+s] += ({ time() });
025d221997-06-01Henrik Grubbström (Grubba)  }
9b9f701997-08-12Per Hedbor  if(type>1) roxen_perror(s);
b1fca01996-11-12Per Hedbor } // When was Roxen started? int start_time; string version() {
71a11e1997-08-13Henrik Grubbström (Grubba)  return QUERY(default_ident)?real_version:QUERY(ident);
b1fca01996-11-12Per Hedbor } // The db for the nice '<if supports=..>' tag. mapping (string:array (array (object|multiset))) supports; private multiset default_supports = (< >); private static inline array positive_supports(array from) { array res = copy_value(from); int i; for(i=0; i<sizeof(res); i++) if(res[i][0] == '-') res[i] = 0; return res - ({ 0 }); } private inline array negative_supports(array from) { array res = copy_value(from); int i; for(i=0; i<sizeof(res); i++) if(res[i][0] != '-') res[i] = 0; else res[i] = res[i][1..]; return res - ({ 0 }); } private static mapping foo_defines = ([ ]); // '#define' in the 'supports' file. static private string current_section; // Used below. // '#section' in the 'supports' file. private void parse_supports_string(string what) { string foo; array lines; int i; lines=replace(what, "\\\n", " ")/"\n"-({""}); foreach(lines, foo) { array bar, gazonk; if(foo[0] == '#') { string file; string name, to; if(sscanf(foo, "#include <%s>", file)) {
5e89211997-02-13Per Hedbor  if(foo=Stdio.read_bytes(file))
b1fca01996-11-12Per Hedbor  parse_supports_string(foo); else report_error("Supports: Cannot include file "+file+"\n"); } else if(sscanf(foo, "#define %[^ ] %s", name, to)) { name -= "\t"; foo_defines[name] = to; // perror("#defining '"+name+"' to "+to+"\n"); } else if(sscanf(foo, "#section %[^ ] {", name)) { // perror("Entering section "+name+"\n"); current_section = name; if(!supports[name]) supports[name] = ({}); } else if((foo-" ") == "#}") { // perror("Leaving section "+current_section+"\n"); current_section = 0; } else { // perror("Comment: "+foo+"\n"); } } else { int rec = 10; string q=replace(foo,",", " "); foo=""; // Handle all defines. while((strlen(foo)!=strlen(q)) && --rec) { foo=q; q = replace(q, indices(foo_defines), values(foo_defines)); } foo=q; if(!rec) perror("Too deep recursion while replacing defines.\n"); // perror("Parsing supports line '"+foo+"'\n"); bar = replace(foo, ({"\t",","}), ({" "," "}))/" " -({ "" }); foo=""; if(sizeof(bar) < 2) continue; if(bar[0] == "default")
01d0811996-11-12Mirar (Pontus Hagland)  default_supports = aggregate_multiset(@bar[1..]);
b1fca01996-11-12Per Hedbor  else { gazonk = bar[1..]; supports[current_section]
01d0811996-11-12Mirar (Pontus Hagland)  += ({ ({ Regexp(bar[0])->match, aggregate_multiset(@positive_supports(gazonk)), aggregate_multiset(@negative_supports(gazonk)),
b1fca01996-11-12Per Hedbor  })}); } } } } public void initiate_supports() { supports = ([ 0:({ }) ]); foo_defines = ([ ]); current_section = 0; parse_supports_string(QUERY(Supports)); foo_defines = 0; } array _new_supports = ({}); void done_with_roxen_com() { string new, old; new = _new_supports * "";
8942801997-11-28Henrik Grubbström (Grubba)  new = (new/"\r\n\r\n")[1..]*"\r\n\r\n";
5e89211997-02-13Per Hedbor  old = Stdio.read_bytes( "etc/supports" );
b1fca01996-11-12Per Hedbor  if(strlen(new) < strlen(old)-200) // Error in transfer? return; if(old != new) {
a60c4c1997-07-03Henrik Grubbström (Grubba)  perror("Got new supports data from www.roxen.com\n");
b1fca01996-11-12Per Hedbor  perror("Replacing old file with new data.\n");
8afc811998-02-04Per Hedbor #ifndef THREADS
f7d9811997-09-12Per Hedbor  object privs=Privs("Replacing etc/supports");
8afc811998-02-04Per Hedbor #endif
b1fca01996-11-12Per Hedbor  mv("etc/supports", "etc/supports~");
5e89211997-02-13Per Hedbor  Stdio.write_file("etc/supports", new); old = Stdio.read_bytes( "etc/supports" );
8afc811998-02-04Per Hedbor #if efun(chmod)
c79b261998-02-05Johan Schön #if efun(geteuid)
8afc811998-02-04Per Hedbor  if(geteuid() != getuid()) chmod("etc/supports",0660); #endif
c79b261998-02-05Johan Schön #endif
b1fca01996-11-12Per Hedbor  if(old != new) { perror("FAILED to update the supports file.\n"); mv("etc/supports~", "etc/supports");
8afc811998-02-04Per Hedbor #ifndef THREADS
5c6bc51997-06-04Henrik Grubbström (Grubba)  privs = 0;
8afc811998-02-04Per Hedbor #endif
5c6bc51997-06-04Henrik Grubbström (Grubba)  } else {
8afc811998-02-04Per Hedbor #ifndef THREADS
5c6bc51997-06-04Henrik Grubbström (Grubba)  privs = 0;
8afc811998-02-04Per Hedbor #endif
b1fca01996-11-12Per Hedbor  initiate_supports();
5c6bc51997-06-04Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  } #ifdef DEBUG else perror("No change to the supports file.\n"); #endif } void got_data_from_roxen_com(object this, string foo) { if(!foo) return; _new_supports += ({ foo }); } void connected_to_roxen_com(object port) { if(!port) { #ifdef DEBUG
a60c4c1997-07-03Henrik Grubbström (Grubba)  perror("Failed to connect to www.roxen.com:80.\n");
b1fca01996-11-12Per Hedbor #endif return 0; } #ifdef DEBUG
a60c4c1997-07-03Henrik Grubbström (Grubba)  perror("Connected to www.roxen.com.:80\n");
b1fca01996-11-12Per Hedbor #endif _new_supports = ({}); port->set_id(port);
47f2cf1997-11-27Henrik Grubbström (Grubba)  string v = version(); if (v != real_version) { v = v + " (" + real_version + ")"; }
99864e1997-11-26Henrik Grubbström (Grubba)  port->write("GET /supports HTTP/1.0\r\n"
47f2cf1997-11-27Henrik Grubbström (Grubba)  "User-Agent: " + v + "\r\n" "Host: www.roxen.com:80\r\n" "Pragma: no-cache\r\n"
99864e1997-11-26Henrik Grubbström (Grubba)  "\r\n");
b1fca01996-11-12Per Hedbor  port->set_nonblocking(got_data_from_roxen_com, got_data_from_roxen_com, done_with_roxen_com); } public void update_supports_from_roxen_com() {
37ec6c1997-09-15Henrik Grubbström (Grubba)  // FIXME: // This code has a race-condition, but it only occurs once a week...
f6d62d1997-03-26Per Hedbor  if(QUERY(next_supports_update) <= time())
b1fca01996-11-12Per Hedbor  {
f6d62d1997-03-26Per Hedbor  if(QUERY(AutoUpdate)) {
bc42561997-06-12Henrik Grubbström (Grubba)  async_connect("www.roxen.com.", 80, connected_to_roxen_com);
b1fca01996-11-12Per Hedbor #ifdef DEBUG
bc42561997-06-12Henrik Grubbström (Grubba)  perror("Connecting to www.roxen.com.:80\n");
b1fca01996-11-12Per Hedbor #endif
f6d62d1997-03-26Per Hedbor  } remove_call_out( update_supports_from_roxen_com );
b1fca01996-11-12Per Hedbor  // Check again in one week.
f6d62d1997-03-26Per Hedbor  QUERY(next_supports_update)=3600*24*7 + time(); store("Variables", variables, 0, 0); } call_out(update_supports_from_roxen_com, QUERY(next_supports_update)-time());
b1fca01996-11-12Per Hedbor } // Return a list of 'supports' values for the current connection. public multiset find_supports(string from) { multiset (string) sup = (< >); multiset (string) nsup = (< >); array (function|multiset) s; string v; array f;
3835ca1998-01-16Henrik Grubbström (Grubba)  if(!(< "unknown", "" >)[from])
b1fca01996-11-12Per Hedbor  { foreach(indices(supports), v) { if(!v || !search(from, v)) { // perror("Section "+v+" match "+from+"\n"); f = supports[v]; foreach(f, s) if(s[0](from)) { sup |= s[1]; nsup |= s[2]; } } } if(!sizeof(sup)) { sup = default_supports; #ifdef DEBUG
3835ca1998-01-16Henrik Grubbström (Grubba)  perror("Unknown client: \""+from+"\"\n");
b1fca01996-11-12Per Hedbor #endif } } else { sup = default_supports; } return sup - nsup; } public void log(mapping file, object request_id) {
14179b1997-01-29Per Hedbor  if(!request_id->conf) return; request_id->conf->log(file, request_id);
b1fca01996-11-12Per Hedbor } // Support for unique user id's private object current_user_id_file; private int current_user_id_number, current_user_id_file_last_mod; private void restore_current_user_id_number() { if(!current_user_id_file) current_user_id_file = open(configuration_dir + "LASTUSER~", "rwc"); if(!current_user_id_file) { call_out(restore_current_user_id_number, 2); return; } current_user_id_number = (int)current_user_id_file->read(100); current_user_id_file_last_mod = current_user_id_file->stat()[2]; perror("Restoring unique user ID information. (" + current_user_id_number + ")\n");
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
b1fca01996-11-12Per Hedbor  mark_fd(current_user_id_file->query_fd(), "Unique user ID logfile.\n");
3235691998-03-26Per Hedbor #endif
b1fca01996-11-12Per Hedbor }
f6d62d1997-03-26Per Hedbor 
b1fca01996-11-12Per Hedbor int increase_id() { if(!current_user_id_file) { restore_current_user_id_number(); return current_user_id_number+time(); } if(current_user_id_file->stat()[2] != current_user_id_file_last_mod) restore_current_user_id_number(); current_user_id_number++; //perror("New unique id: "+current_user_id_number+"\n"); current_user_id_file->seek(0); current_user_id_file->write((string)current_user_id_number); current_user_id_file_last_mod = current_user_id_file->stat()[2]; return current_user_id_number; } public string full_status() { int tmp; string res=""; array foo = ({0.0, 0.0, 0.0, 0.0, 0}); if(!sizeof(configurations)) return "<B>No virtual servers enabled</B>\n";
5281751997-11-26Henrik Grubbström (Grubba)  foreach(configurations, object conf)
b1fca01996-11-12Per Hedbor  {
88e1cb1996-12-07David Hedbor  if(!conf->sent ||!conf->received ||!conf->hsent) continue;
5281751997-11-26Henrik Grubbström (Grubba)  foo[0] += conf->sent->mb()/(float)(time(1)-start_time+1); foo[1] += conf->sent->mb(); foo[2] += conf->hsent->mb(); foo[3] += conf->received->mb(); foo[4] += conf->requests;
b1fca01996-11-12Per Hedbor  }
5281751997-11-26Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  for(tmp = 1; tmp < 4; tmp ++) { if(foo[tmp] < 1024.0)
27b0e11996-11-26Per Hedbor  foo[tmp] = sprintf("%.2f MB", foo[tmp]);
b1fca01996-11-12Per Hedbor  else
27b0e11996-11-26Per Hedbor  foo[tmp] = sprintf("%.2f GB", foo[tmp]/1024.0);
b1fca01996-11-12Per Hedbor  }
5281751997-11-26Henrik Grubbström (Grubba)  res = sprintf("<table><tr><td><b>Sent data:</b></td><td>%s" "</td><td>%.2f Kbit/sec</td></tr><tr>\n", foo[1], foo[0] * 8192.0);
b1fca01996-11-12Per Hedbor 
c6c9071997-09-03Peter Bortas  res += "<td><b>Sent headers:</b></td><td>"+ foo[2] +"</td></tr>\n";
b1fca01996-11-12Per Hedbor 
c6c9071997-09-03Peter Bortas  tmp=(int)(foo[4]*600.0)/((time(1)-start_time)+1);
b1fca01996-11-12Per Hedbor 
5281751997-11-26Henrik Grubbström (Grubba)  res += (sprintf("<tr><td><b>Number of requests:</b></td>" "<td>%8d</td><td>%.2f/min</td></tr>\n" "<tr><td><b>Received data:</b></td>" "<td>%s</td></tr>\n", foo[4], (float)tmp/(float)10, foo[3]));
b1fca01996-11-12Per Hedbor  return res +"</table>"; } // These are now more or less outdated, the modules really _should_ // pass the information about the current configuration to roxen, // to enable async operations. This information is in id->conf. // // In the future, most, if not all, of these functions will be moved // to the configuration object. The functions will still be here for // compatibility for a while, though.
d589871997-03-09Henrik Grubbström (Grubba) public string *userlist(void|object id)
b1fca01996-11-12Per Hedbor {
48fa361997-04-05Per Hedbor  if(id) current_configuration = id->conf;
d336db1998-03-11David Hedbor  if(current_configuration && current_configuration->auth_module)
b1fca01996-11-12Per Hedbor  return current_configuration->auth_module->userlist(); return 0; }
d589871997-03-09Henrik Grubbström (Grubba) public string *user_from_uid(int u, void|object id)
b1fca01996-11-12Per Hedbor {
48fa361997-04-05Per Hedbor  if(id) current_configuration = id->conf;
d336db1998-03-11David Hedbor  if(current_configuration && current_configuration->auth_module)
b1fca01996-11-12Per Hedbor  return current_configuration->auth_module->user_from_uid(u); } public string last_modified_by(object file, object id) { int *s; int uid; mixed *u; if(objectp(file)) s=file->stat(); if(!s || sizeof(s)<5) return "A. Nonymous"; uid=s[5]; u=user_from_uid(uid, id); if(u) return u[0]; return "A. Nonymous"; } // FIXME private object find_configuration_for(object bar) { object maybe; if(!bar) return configurations[0]; foreach(configurations, maybe) if(maybe->otomod[bar]) return maybe; return configurations[-1]; } // FIXME
c856841998-01-21Henrik Grubbström (Grubba) public array|string type_from_filename( string|void file, int|void to )
b1fca01996-11-12Per Hedbor { mixed tmp;
14179b1997-01-29Per Hedbor  object current_configuration;
b1fca01996-11-12Per Hedbor  string ext=extension(file); if(!current_configuration)
aaef2a1997-03-02Henrik Grubbström (Grubba)  current_configuration = find_configuration_for(Simulate.previous_object());
b1fca01996-11-12Per Hedbor  if(!current_configuration->types_fun) return to?({ "application/octet-stream", 0 }):"application/octet-stream"; while(file[-1] == '/') file = file[0..strlen(file)-2]; // Security patch? if(tmp = current_configuration->types_fun(ext)) { mixed tmp2,nx; if(tmp[0] == "strip") { tmp2=file/"."; if(sizeof(tmp2) > 2) nx=tmp2[-2]; if(nx && (tmp2=current_configuration->types_fun(nx))) tmp[0] = tmp2[0]; else if(tmp2=current_configuration->types_fun("default")) tmp[0] = tmp2[0]; else tmp[0]="application/octet-stream"; } return to?tmp:tmp[0]; } else { if(!(tmp=current_configuration->types_fun("default"))) tmp=({ "application/octet-stream", 0 });
44e3371997-06-12Per Hedbor  return to?tmp:tmp[0]; // Per..
b1fca01996-11-12Per Hedbor  } return 0; }
14179b1997-01-29Per Hedbor #define COMPAT_ALIAS(X) mixed X(string file, object id){return id->conf->X(file,id);}
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor COMPAT_ALIAS(find_dir); COMPAT_ALIAS(stat_file); COMPAT_ALIAS(access); COMPAT_ALIAS(real_file); COMPAT_ALIAS(is_file); COMPAT_ALIAS(userinfo);
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor public mapping|int get_file(object id, int|void no_magic) { return id->conf->get_file(id, no_magic); }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor public mixed try_get_file(string s, object id, int|void status, int|void nocache) { return id->conf->try_get_file(s,id,status,nocache); }
b1fca01996-11-12Per Hedbor 
4f4bc11998-02-04Per Hedbor int config_ports_changed = 0;
14179b1997-01-29Per Hedbor // Called from the configuration interface. string check_variable(string name, string value) { switch(name)
b1fca01996-11-12Per Hedbor  {
4f4bc11998-02-04Per Hedbor  case "ConfigPorts": config_ports_changed = 1;
e10e191998-02-17Martin Stjernholm  break;
14179b1997-01-29Per Hedbor  case "cachedir": if(!sscanf(value, "%*s/roxen_cache")) { object node; node = (configuration_interface()->root->descend("Globals", 1)-> descend("Proxy disk cache: Base Cache Dir", 1)); if(node && !node->changed) node->change(1); mkdirhier(value+"roxen_cache/foo");
13f7651997-09-15Henrik Grubbström (Grubba)  call_out(set, 0, "cachedir", value+"roxen_cache/");
14179b1997-01-29Per Hedbor  } break;
27b0e11996-11-26Per Hedbor 
14179b1997-01-29Per Hedbor  case "ConfigurationURL": case "MyWorldLocation": if(strlen(value)<7 || value[-1] != '/' || !(sscanf(value,"%*s://%*s/")==2)) return "The URL should follow this format: protocol://computer[:port]/"; }
b1fca01996-11-12Per Hedbor }
14179b1997-01-29Per Hedbor void stop_all_modules()
b1fca01996-11-12Per Hedbor {
14179b1997-01-29Per Hedbor  foreach(configurations, object conf) conf->stop();
b1fca01996-11-12Per Hedbor }
14179b1997-01-29Per Hedbor // Perhaps somewhat misnamed, really... This function will close all // listen ports, fork a new copy to handle the last connections, and // then quit the original process. The 'start' script should then // start a new copy of roxen automatically. mapping restart() { stop_all_modules();
3835ca1998-01-16Henrik Grubbström (Grubba)  call_out(fork_or_quit, 5, -1);
1c20e61997-08-12David Hedbor  return ([ "data": replace(Stdio.read_bytes("etc/restart.html"), ({"$docurl", "$PWD"}), ({roxen->docurl, getcwd()})), "type":"text/html" ]);
14179b1997-01-29Per Hedbor } private array configuration_ports = ({ });
e145221997-10-10David Hedbor int startpid, roxenpid;
14179b1997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor // This has to be refined in some way. It is not all that nice to do // it like this (write a file in /tmp, and then exit.) The major part // of code to support this is in the 'start' script.
1c20e61997-08-12David Hedbor void kill_me()
b1fca01996-11-12Per Hedbor {
8afc811998-02-04Per Hedbor  // Change to root user if possible ( to kill the start script... )
c79b261998-02-05Johan Schön #if efun(seteuid)
8afc811998-02-04Per Hedbor  seteuid(getuid()); setegid(getgid());
c79b261998-02-05Johan Schön #endif #if efun(setuid)
8afc811998-02-04Per Hedbor  setuid(0);
c79b261998-02-05Johan Schön #endif
14179b1997-01-29Per Hedbor  stop_all_modules(); if(main_configuration_port && objectp(main_configuration_port))
b1fca01996-11-12Per Hedbor  {
14179b1997-01-29Per Hedbor  // Only _really_ do something in the main process. int pid; perror("Shutting down Roxen.\n");
3835ca1998-01-16Henrik Grubbström (Grubba)  #ifdef USE_SHUTDOWN_FILE
14179b1997-01-29Per Hedbor  // Fallback for systems without geteuid, Roxen will (probably) // not be able to kill the start-script if this is the case. rm("/tmp/Roxen_Shutdown_"+startpid);
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  object f; f=open("/tmp/Roxen_Shutdown_"+startpid, "wc"); if(!f) perror("cannot open shutdown file.\n"); else f->write(""+getpid());
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  if(startpid != getpid())
b1fca01996-11-12Per Hedbor  {
14179b1997-01-29Per Hedbor  kill(startpid, signum("SIGINTR")); kill(startpid, signum("SIGHUP"));
5e89211997-02-13Per Hedbor  kill(getppid(), signum("SIGINTR")); kill(getppid(), signum("SIGHUP"));
14179b1997-01-29Per Hedbor // kill(startpid, signum("SIGKILL"));
b1fca01996-11-12Per Hedbor  }
3835ca1998-01-16Henrik Grubbström (Grubba)  #endif /* USE_SHUTDOWN_FILE */
b1fca01996-11-12Per Hedbor  }
3835ca1998-01-16Henrik Grubbström (Grubba)  // Die nicely (we're shutting down).
db85af1997-10-15Henrik Grubbström (Grubba)  call_out(exit, 2, 0);
1c20e61997-08-12David Hedbor } mapping shutdown() { call_out(kill_me, 5, 0); return ([ "data":replace(Stdio.read_bytes("etc/shutdown.html"), ({"$docurl", "$PWD"}), ({roxen->docurl, getcwd()})),
14179b1997-01-29Per Hedbor  "type":"text/html" ]); }
b1fca01996-11-12Per Hedbor 
82f5191997-03-02Per Hedbor private string docurl;
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor // I will remove this in a future version of roxen. private program __p; private mapping my_loaded = ([]); program last_loaded() { return __p; }
b1fca01996-11-12Per Hedbor 
68a0a21997-06-12Henrik Grubbström (Grubba) string last_module_name;
14179b1997-01-29Per Hedbor string filename(object o) {
68a0a21997-06-12Henrik Grubbström (Grubba)  return my_loaded[object_program(o)]||last_module_name;
14179b1997-01-29Per Hedbor }
b1fca01996-11-12Per Hedbor 
34447f1998-03-20Per Hedbor program my_compile_file(string file) { return compile_file( file ); }
e382ed1997-07-16Henrik Grubbström (Grubba) // ([ filename:stat_array ]) mapping(string:array) module_stat_cache = ([]);
8c62221997-08-06Henrik Grubbström (Grubba) object load(string s, object conf) // Should perhaps be renamed to 'reload'.
14179b1997-01-29Per Hedbor {
08065d1997-05-27Per Hedbor  string cvs;
e493e81997-07-11Per Hedbor  array st;
08065d1997-05-27Per Hedbor  sscanf(s, "/cvs:%s", cvs);
82f5191997-03-02Per Hedbor // perror("Module is "+s+"?");
e493e81997-07-11Per Hedbor  if(st=file_stat(s+".pike"))
5e89211997-02-13Per Hedbor  {
82f5191997-03-02Per Hedbor // perror("Yes, compile "+s+"?");
08065d1997-05-27Per Hedbor  if((cvs?(__p=master()->cvs_load_file( cvs+".pike" ))
34447f1998-03-20Per Hedbor  :(__p=my_compile_file(s+".pike"))))
14179b1997-01-29Per Hedbor  {
82f5191997-03-02Per Hedbor // perror("Yes.");
14179b1997-01-29Per Hedbor  my_loaded[__p]=s+".pike";
e493e81997-07-11Per Hedbor  module_stat_cache[s-dirname(s)]=st;
8c62221997-08-06Henrik Grubbström (Grubba)  return __p(conf);
5e89211997-02-13Per Hedbor  } else perror(s+".pike exists, but compilation failed.\n"); }
e493e81997-07-11Per Hedbor  if(st=file_stat(s+".lpc"))
08065d1997-05-27Per Hedbor  if(cvs?(__p=master()->cvs_load_file( cvs+".lpc" )):
34447f1998-03-20Per Hedbor  (__p=my_compile_file(s+".lpc")))
2a2a5b1996-12-01Per Hedbor  { my_loaded[__p]=s+".lpc";
e493e81997-07-11Per Hedbor  module_stat_cache[s-dirname(s)]=st;
8c62221997-08-06Henrik Grubbström (Grubba)  return __p(conf);
5e89211997-02-13Per Hedbor  } else perror(s+".lpc exists, but compilation failed.\n");
e493e81997-07-11Per Hedbor  if(st=file_stat(s+".module"))
5e89211997-02-13Per Hedbor  if(__p=load_module(s+".so"))
2a2a5b1996-12-01Per Hedbor  {
5e89211997-02-13Per Hedbor  my_loaded[__p]=s+".so";
e493e81997-07-11Per Hedbor  module_stat_cache[s-dirname(s)]=st;
8c62221997-08-06Henrik Grubbström (Grubba)  return __p(conf);
5e89211997-02-13Per Hedbor  } else perror(s+".so exists, but compilation failed.\n");
b1fca01996-11-12Per Hedbor  return 0; // FAILED.. }
ab2f671996-11-12Mirar (Pontus Hagland) array(string) expand_dir(string d)
5e4ede1996-11-12Per Hedbor { string nd;
ab2f671996-11-12Mirar (Pontus Hagland)  array(string) dirs=({d});
5e4ede1996-11-12Per Hedbor 
5e89211997-02-13Per Hedbor //perror("Expand dir "+d+"\n");
82f5191997-03-02Per Hedbor  catch { foreach((get_dir(d) || ({})) - ({"CVS"}) , nd) if(file_stat(d+nd)[1]==-2) dirs+=expand_dir(d+nd+"/"); }; // This catch is needed....
ab2f671996-11-12Mirar (Pontus Hagland)  return dirs;
5e4ede1996-11-12Per Hedbor }
ab2f671996-11-12Mirar (Pontus Hagland) array(string) last_dirs=0,last_dirs_expand;
8c62221997-08-06Henrik Grubbström (Grubba) object load_from_dirs(array dirs, string f, object conf)
14179b1997-01-29Per Hedbor { string dir; object o;
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  if (dirs!=last_dirs) {
5e89211997-02-13Per Hedbor  last_dirs_expand=({}); foreach(dirs, dir) last_dirs_expand+=expand_dir(dir);
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  foreach (last_dirs_expand,dir)
8c62221997-08-06Henrik Grubbström (Grubba)  if ( (o=load(dir+f, conf)) ) return o;
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  return 0; }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor void create()
b1fca01996-11-12Per Hedbor {
9b9f701997-08-12Per Hedbor  catch { module_stat_cache = decode_value(Stdio.read_bytes(".module_stat_cache")); allmodules = decode_value(Stdio.read_bytes(".allmodules")); };
5e89211997-02-13Per Hedbor  add_constant("roxen", this_object());
aaef2a1997-03-02Henrik Grubbström (Grubba)  add_constant("spinner", this_object()); add_constant("load", load);
82f5191997-03-02Per Hedbor  (object)"color.pike";
8e727d1997-03-11Per Hedbor  (object)"fonts.pike";
14179b1997-01-29Per Hedbor  Configuration = (program)"configuration";
b1fca01996-11-12Per Hedbor } // Get the current domain. This is not as easy as one could think. private string get_domain(int|void l) { array f; string t, s; // ConfigurationURL is set by the 'install' script. if(!(!l && sscanf(QUERY(ConfigurationURL), "http://%s:%*s", s))) {
c79b261998-02-05Johan Schön #if efun(gethostbyname) #if efun(gethostname)
b1fca01996-11-12Per Hedbor  f = gethostbyname(gethostname()); // First try.. if(f)
ea9a311997-03-01Henrik Grubbström (Grubba)  foreach(f, f) if (arrayp(f)) { foreach(f, t) if(search(t, ".") != -1 && !(int)t) if(!s || strlen(s) < strlen(t)) s=t; }
b1fca01996-11-12Per Hedbor #endif
c79b261998-02-05Johan Schön #endif
b1fca01996-11-12Per Hedbor  if(!s) {
e5bad21998-02-10Per Hedbor  t = Stdio.read_bytes("/etc/resolv.conf");
b1fca01996-11-12Per Hedbor  if(t) { if(!sscanf(t, "domain %s\n", s)) if(!sscanf(t, "search %s%*[ \t\n]", s)) s="nowhere"; } else { s="nowhere"; } s = "host."+s; } } sscanf(s, "%*s.%s", s); if(s && strlen(s)) { if(s[-1] == '.') s=s[..strlen(s)-2]; if(s[0] == '.') s=s[1..]; } else { s="unknown"; } return s; } // This is the most likely URL for a virtual server. Again, this // should move into the actual 'configuration' object. It is not all // that nice to have all this code lying around in here. private string get_my_url() { string s;
c79b261998-02-05Johan Schön #if efun(gethostname)
b1fca01996-11-12Per Hedbor  s = (gethostname()/".")[0] + "." + query("Domain");
c79b261998-02-05Johan Schön #else s = "localhost"; #endif
b1fca01996-11-12Per Hedbor  s -= "\n"; return "http://" + s + "/"; }
14179b1997-01-29Per Hedbor // Set the uid and gid to the ones requested by the user. If the sete* // functions are available, and the define SET_EFFECTIVE is enabled, // the euid and egid is set. This might be a minor security hole, but // it will enable roxen to start CGI scripts with the correct // permissions (the ones the owner of that script have).
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor int set_u_and_gid()
b1fca01996-11-12Per Hedbor {
c79b261998-02-05Johan Schön #ifndef __NT__
14179b1997-01-29Per Hedbor  string u, g; array pw;
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  u=QUERY(User);
8662771997-08-12Per Hedbor  sscanf(u, "%s:%s", u, g); if(strlen(u))
14179b1997-01-29Per Hedbor  { if(getuid()) { perror("It is not possible to change uid and gid if the server\n" "is not started as root.\n"); } else {
8662771997-08-12Per Hedbor  if(pw = getpwnam(u)) { u = (string)pw[2]; if(!g) g = (string)pw[3]; } else pw = getpwuid((int)u);
14179b1997-01-29Per Hedbor #if efun(initgroups)
d0da251997-09-08David Hedbor  catch { if(pw) initgroups(pw[0], (int)g); // Doesn't always work - David. };
b1fca01996-11-12Per Hedbor #endif
14179b1997-01-29Per Hedbor #if efun(setegid) && defined(SET_EFFECTIVE)
8662771997-08-12Per Hedbor  setegid((int)g);
14179b1997-01-29Per Hedbor #else
8662771997-08-12Per Hedbor  setgid((int)g);
14179b1997-01-29Per Hedbor #endif #if efun(seteuid) && defined(SET_EFFECTIVE)
8662771997-08-12Per Hedbor  seteuid((int)u);
14179b1997-01-29Per Hedbor #else
8662771997-08-12Per Hedbor  setuid((int)u);
14179b1997-01-29Per Hedbor #endif
8662771997-08-12Per Hedbor  report_notice("Setting UID to "+u+" and GID to "+g); return 1;
14179b1997-01-29Per Hedbor  } }
c79b261998-02-05Johan Schön #endif
14179b1997-01-29Per Hedbor }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor static mapping __vars = ([ ]);
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor // These two should be documented somewhere. They are to be used to // set global, but non-persistent, variables in Roxen. By using // these functions modules can "communicate" with one-another. This is // not really possible otherwise. mixed set_var(string var, mixed to) { __vars[var] = to; }
ba73a21996-12-10Per Hedbor 
14179b1997-01-29Per Hedbor mixed query_var(string var) { return __vars[var]; }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor // The update_*_vars functions are here to automatically change the // configurationfileformat between releases, so the user can reuse the // old configuration files. They are very useful, atleast for me.
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor private void update_global_vars(int from) { string report = ""; #define perr(X) do { report += X; perror(X); } while(0) perr("Updating global variables file....\n"); perr("----------------------------------------------------\n"); switch(from) { case 0: // if(!QUERY(IfModified)) // { // perr("Setting the 'honor If-Modified-Since: flag to true. The " // "bug\nin Roxen seems to be gone now.\n"); // QUERY(IfModified) = 1; // }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  case 1: case 2: case 3:
71a11e1997-08-13Henrik Grubbström (Grubba)  perr("The configuration port variable is now a standard port.\n" "Adding the port '"+QUERY(ConfigurationPort)+" http "+ QUERY(ConfigurationIP)+"\n"); QUERY(ConfigPorts) = ({ ({ QUERY(ConfigurationPort), "http", QUERY(ConfigurationIP), "" }) });
14179b1997-01-29Per Hedbor  case 4: case 5:
b1fca01996-11-12Per Hedbor 
71a11e1997-08-13Henrik Grubbström (Grubba)  if(search(QUERY(ident), "Spinner")!=-1) { QUERY(ident) = real_version; perr("Updating version field to "+real_version+"\n"); }
b1fca01996-11-12Per Hedbor 
71a11e1997-08-13Henrik Grubbström (Grubba)  if(search(QUERY(ident), "Enterprice")!=-1) QUERY(ident) = real_version; else if(search(QUERY(ident), "Challenger")!=-1) QUERY(ident) = real_version; if(((!search(QUERY(ident), "Roxen Challenger/1.0")) || (!search(QUERY(ident), "Roxen Challenger/1.1"))) && (replace(QUERY(ident),"·"," ") != real_version)) {
14179b1997-01-29Per Hedbor  QUERY(ident)=real_version; perr("Updating version field to "+real_version+"\n"); } else { perr("Not updating version field ("+QUERY(ident)+") since it is " "either already updated, or modified by the administrator.\n"); }
71a11e1997-08-13Henrik Grubbström (Grubba)  case 6:
14179b1997-01-29Per Hedbor  // Current level
b1fca01996-11-12Per Hedbor  }
14179b1997-01-29Per Hedbor  perr("----------------------------------------------------\n"); report_debug(report); }
5e4ede1996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor object enable_configuration(string name) { object cf = Configuration(name); configurations += ({ cf });
9f46de1997-04-08Per Hedbor  current_configuration = cf;
fdf36e1997-08-13Henrik Grubbström (Grubba)  report_notice("Enabled the virtual server \""+name+"\".");
ce4ac81997-09-08David Hedbor 
14179b1997-01-29Per Hedbor  return cf;
b1fca01996-11-12Per Hedbor } // Enable all configurations
8662771997-08-12Per Hedbor void enable_configurations()
b1fca01996-11-12Per Hedbor {
14179b1997-01-29Per Hedbor  array err;
b1fca01996-11-12Per Hedbor  enabling_configurations = 1;
48fa361997-04-05Per Hedbor  configurations = ({}); foreach(list_all_configurations(), string config) { if(err=catch { enable_configuration(config)->start(); })
ce4ac81997-09-08David Hedbor  perror("Error while loading configuration "+config+":\n"+ describe_backtrace(err)+"\n"); }; foreach(configurations, object config) { if(err=catch { config->enable_all_modules(); }) perror("Error while loading modules in configuration "+config->name+":\n"+
48fa361997-04-05Per Hedbor  describe_backtrace(err)+"\n");
b1fca01996-11-12Per Hedbor  }; enabling_configurations = 0; } // return the URL of the configuration interface. This is not as easy // as it sounds, unless the administrator has entered it somewhere. public string config_url() { if(strlen(QUERY(ConfigurationURL)-" ")) return QUERY(ConfigurationURL)-" "; array ports = QUERY(ConfigPorts), port, tmp; if(!sizeof(ports)) return "CONFIG"; int p; string prot; string host; foreach(ports, tmp)
9f46de1997-04-08Per Hedbor  if(tmp[1][0..2]=="ssl")
b1fca01996-11-12Per Hedbor  { port=tmp; break; } if(!port) foreach(ports, tmp) if(tmp[1]=="http") { port=tmp; break; } if(!port) port=ports[0]; if(port[2] == "ANY") // host = quick_ip_to_host( port[2] ); // else { #if efun(gethostname) host = gethostname(); #else host = ""; #endif }
9f46de1997-04-08Per Hedbor  prot = (port[1][0..2]!="ssl"?port[1]:"https");
b1fca01996-11-12Per Hedbor  p = port[0]; return (prot+"://"+host+":"+p+"/"); } // The following three functions are used to hide variables when they // are not used. This makes the user-interface clearer and quite a lot // less clobbered. int cache_disabled_p() { return !QUERY(cache); } int syslog_disabled() { return QUERY(LogA)!="syslog"; }
71a11e1997-08-13Henrik Grubbström (Grubba) private int ident_disabled_p() { return QUERY(default_ident); }
b1fca01996-11-12Per Hedbor  private void define_global_variables( int argc, array (string) argv ) { int p;
9b9f701997-08-12Per Hedbor  globvar("set_cookie", 0, "Set unique user id cookies", TYPE_FLAG,
89786e1997-10-09Peter Bortas  "If set to Yes, all users of your server whose clients support "
b1fca01996-11-12Per Hedbor  "cookies will get a unique 'user-id-cookie', this can then be " "used in the log and in scripts to track individual users.");
9b9f701997-08-12Per Hedbor  globvar("set_cookie_only_once",1,"Set ID cookies only once",TYPE_FLAG,
89786e1997-10-09Peter Bortas  "If set to Yes, Roxen will attempt to set unique user ID cookies " "only upon receiving the first request (and again after some " "minutes). Thus, if the user doesn't allow the cookie to be set, " "he won't be bothered with multiple requests",0,
a773c61997-07-06Henrik Grubbström (Grubba)  lambda() {return !QUERY(set_cookie);});
b1fca01996-11-12Per Hedbor  globvar("show_internals", 1, "Show the internals", TYPE_FLAG, "Show 'Internal server error' messages to the user. " "This is very useful if you are debugging your own modules "
d8aa651996-12-09David Hedbor  "or writing Pike scripts.");
b1fca01996-11-12Per Hedbor 
1e5ee81997-08-21Per Hedbor  // Hidden variables (compatibility ones, or internal or too // dangerous /* globvar("BS", 0, "Configuration interface: Compact layout",*/ /* TYPE_FLAG|VAR_EXPERT,*/ /* "Sick and tired of all those images? Set this variable to 'Yes'!");*/ /* globvar("BG", 1, "Configuration interface: Background",*/ /* TYPE_FLAG|VAR_EXPERT,*/ /* "Should the background be set by the configuration interface?");*/
b1fca01996-11-12Per Hedbor  globvar("_v", CONFIGURATION_FILE_LEVEL, 0, TYPE_INT, 0, 0, 1);
8e727d1997-03-11Per Hedbor  globvar("default_font_size", 32, 0, TYPE_INT, 0, 0, 1);
1e5ee81997-08-21Per Hedbor 
8e727d1997-03-11Per Hedbor  globvar("default_font", "lucida", "Fonts: Default font", TYPE_FONT, "The default font to use when modules request a font.");
06f6b01997-09-03Henrik Grubbström (Grubba)  globvar("font_dirs", ({"../local/nfonts/", "nfonts/" }),
85a2e51997-06-23Per Hedbor  "Fonts: Font directories", TYPE_DIR_LIST,
8e727d1997-03-11Per Hedbor  "This is where the fonts are located.");
e4bc531996-11-15Per Hedbor 
9b9f701997-08-12Per Hedbor  globvar("logdirprefix", "../logs/", "Log directory prefix", TYPE_DIR|VAR_MORE,
e4bc531996-11-15Per Hedbor  "This is the default file path that will be prepended to the log " " file path in all the default modules and the virtual server.");
b1fca01996-11-12Per Hedbor  // Cache variables. The actual code recides in the file // 'disk_cache.pike' globvar("cache", 0, "Proxy disk cache: Enabled", TYPE_FLAG,
89786e1997-10-09Peter Bortas  "If set to Yes, caching will be enabled.");
b1fca01996-11-12Per Hedbor  globvar("garb_min_garb", 1, "Proxy disk cache: Clean size", TYPE_INT, "Minimum number of Megabytes removed when a garbage collect is done", 0, cache_disabled_p); globvar("cache_minimum_left", 5, "Proxy disk cache: Minimum "
f8978c1997-03-20Wilhelm Köhler  "available free space and inodes (in %)", TYPE_INT, "If less than this amount of disk space or inodes (in %) is left, "
89786e1997-10-09Peter Bortas  "the cache will remove a few files. This check may work " "half-hearted if the diskcache is spread over several filesystems.",
3aaaa71997-06-12Wilhelm Köhler  0, #if efun(filesystem_stat) cache_disabled_p #else 1 #endif /* filesystem_stat */ );
b1fca01996-11-12Per Hedbor  globvar("cache_size", 25, "Proxy disk cache: Size", TYPE_INT,
27b0e11996-11-26Per Hedbor  "How many MB may the cache grow to before a garbage collect is done?",
b1fca01996-11-12Per Hedbor  0, cache_disabled_p);
f8978c1997-03-20Wilhelm Köhler  globvar("cache_max_num_files", 0, "Proxy disk cache: Maximum number " "of files", TYPE_INT, "How many cache files (inodes) may " "be on disk before a garbage collect is done ? May be left " "zero to disable this check.", 0, cache_disabled_p);
b1fca01996-11-12Per Hedbor  globvar("bytes_per_second", 50, "Proxy disk cache: Bytes per second", TYPE_INT, "How file size should be treated during garbage collect. " " Each X bytes counts as a second, so that larger files will" " be removed first.", 0, cache_disabled_p);
88e1cb1996-12-07David Hedbor  globvar("cachedir", "/tmp/roxen_cache/", "Proxy disk cache: Base Cache Dir",
b1fca01996-11-12Per Hedbor  TYPE_DIR, "This is the base directory where cached files will reside. " "To avoid mishaps, 'roxen_cache/' is always prepended to this " "variable.", 0, cache_disabled_p);
e4bc531996-11-15Per Hedbor  globvar("hash_num_dirs", 500, "Proxy disk cache: Number of hash directories", TYPE_INT, "This is the number of directories to hash the contents of the disk " "cache into. Changing this value currently invalidates the whole " "cache, since the cache cannot find the old files. In the future, "
88e1cb1996-12-07David Hedbor  " the cache will be recalculated when this value is changed.", 0, cache_disabled_p);
e4bc531996-11-15Per Hedbor 
f8978c1997-03-20Wilhelm Köhler  globvar("cache_keep_without_content_length", 1, "Proxy disk cache: " "Keep without Content-Length", TYPE_FLAG, "Keep files " "without Content-Length header information in the cache ?", 0, cache_disabled_p); globvar("cache_check_last_modified", 0, "Proxy disk cache: "
89786e1997-10-09Peter Bortas  "Refresh on Last-Modified", TYPE_FLAG, "If set, refreshes files without Expire header information " "when they have reached double the age they had when they got " "cached. This may be useful for some regularly updated docs as " "online newspapers.",
f8978c1997-03-20Wilhelm Köhler  0, cache_disabled_p);
a0b4fd1997-08-15Henrik Grubbström (Grubba)  globvar("cache_last_resort", 0, "Proxy disk cache: "
89786e1997-10-09Peter Bortas  "Last resort (in days)", TYPE_INT, "How many days shall files without Expires and without " "Last-Modified header information be kept ?",
f8978c1997-03-20Wilhelm Köhler  0, cache_disabled_p); globvar("cache_gc_logfile", "", "Proxy disk cache: " "Garbage collector logfile", TYPE_FILE, "Information about garbage collector runs, removed and refreshed " "files, cache and disk status goes here.",
48fa361997-04-05Per Hedbor  0, cache_disabled_p);
f8978c1997-03-20Wilhelm Köhler 
e4bc531996-11-15Per Hedbor  /// End of cache variables..
b1fca01996-11-12Per Hedbor 
953ca31997-08-18Per Hedbor  globvar("docurl2", "http://www.roxen.com/documentation/context.pike?page=", "Documentation URL", TYPE_STRING|VAR_MORE,
b1fca01996-11-12Per Hedbor  "The URL to prepend to all documentation urls throughout the " "server. This URL should _not_ end with a '/'."); globvar("pidfile", "/tmp/roxen_pid:$uid", "PID file",
9b9f701997-08-12Per Hedbor  TYPE_FILE|VAR_MORE,
b1fca01996-11-12Per Hedbor  "In this file, the server will write out it's PID, and the PID " "of the start script. $pid will be replaced with the pid, and " "$uid with the uid of the user running the process.");
71a11e1997-08-13Henrik Grubbström (Grubba)  globvar("default_ident", 1, "Identify: Use default identification string", TYPE_FLAG|VAR_MORE,
89786e1997-10-09Peter Bortas  "Setting this variable to No will display the \"Identify as\" node " "where you can state what Roxen should call itself when talking " "to clients, otherwise it will present it self as \""+ real_version +"\".<br>" "It is possible to disable this so that you can enter an "
71a11e1997-08-13Henrik Grubbström (Grubba)  "identification-string that does not include the actual version of " "Roxen, as recommended by the HTTP/1.0 draft 03:<p><blockquote><i>"
b1fca01996-11-12Per Hedbor  "Note: Revealing the specific software version of the server " "may allow the server machine to become more vulnerable to " "attacks against software that is known to contain security " "holes. Server implementors are encouraged to make this field " "a configurable option.</i></blockquote>");
89786e1997-10-09Peter Bortas 
71a11e1997-08-13Henrik Grubbström (Grubba)  globvar("ident", replace(real_version," ","·"), "Identify: Identify as", TYPE_STRING /* |VAR_MORE */,
89786e1997-10-09Peter Bortas  "Enter the name that Roxen should use when talking to clients. ",
71a11e1997-08-13Henrik Grubbström (Grubba)  0, ident_disabled_p);
b1fca01996-11-12Per Hedbor 
9b9f701997-08-12Per Hedbor  globvar("DOC", 1, "Configuration interface: Help texts", TYPE_FLAG|VAR_MORE,
b1fca01996-11-12Per Hedbor  "Do you want documentation? (this is an example of documentation)");
89786e1997-10-09Peter Bortas  globvar("NumAccept", 1, "Number of accepts to attempt", TYPE_INT_LIST|VAR_MORE, "You can here state the maximum number of accepts to attempt for " "each read callback from the main socket. <p> Increasing this value " "will make the server " "faster for users making many simultaneous connections to it, or"
b1fca01996-11-12Per Hedbor  " if you have a very busy server. <p> It won't work on some systems" ", though, eg. IBM AIX 3.2<p> To see if it works, change this" " variable, <b> but don't press save</b>, and then try connecting to" " your server. If it works, come back here and press the save button"
89786e1997-10-09Peter Bortas  ". <p> If it doesn't work, just restart the server and be happy "
b1fca01996-11-12Per Hedbor  "with having '1' in this field.<p>"
89786e1997-10-09Peter Bortas  "The higher you set this value, the less load balancing between " "virtual servers. (If there are 256 more or less simultaneous "
b1fca01996-11-12Per Hedbor  "requests to server 1, and one to server 2, and this variable is "
89786e1997-10-09Peter Bortas  "set to 256, the 256 accesses to the first server might very well " "be handled before the one to the second server.)",
14179b1997-01-29Per Hedbor  ({ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }));
b1fca01996-11-12Per Hedbor  globvar("ConfigPorts", ({ ({ 22202, "http", "ANY", "" }) }), "Configuration interface: Ports", TYPE_PORTS,
89786e1997-10-09Peter Bortas  "These are the ports through which you can configure the" "server.<br>Note that you should at least have one open port, since " "otherwise you won't be able to configure your server.");
b1fca01996-11-12Per Hedbor  globvar("ConfigurationURL", "", "Configuration interface: URL", TYPE_STRING, "The URL of the configuration interface. This is used to " "generate redirects now and then (when you press save, when " "a module is added, etc.)."); globvar("ConfigurationPassword", "", "Configuration interface: Password",
8662771997-08-12Per Hedbor  TYPE_PASSWORD|VAR_EXPERT,
b1fca01996-11-12Per Hedbor  "The password you will have to enter to use the configuration " "interface. Please note that changing this password in the " "configuration interface will _not_ require an additional entry " "of the password, so it is easy to make a typo. It is recommended " "that you use the <a href=/(changepass)/Globals/>form instead</a>."); globvar("ConfigurationUser", "", "Configuration interface: User",
8662771997-08-12Per Hedbor  TYPE_STRING|VAR_EXPERT,
b1fca01996-11-12Per Hedbor  "The username you will have to enter to use the configuration " "interface");
8662771997-08-12Per Hedbor  globvar("ConfigurationIPpattern","*", "Configuration interface: IP-Pattern",
9b9f701997-08-12Per Hedbor  TYPE_STRING|VAR_MORE,
89786e1997-10-09Peter Bortas  "Only clients running on computers with IP numbers matching" "this pattern will be able to use the configuration" "interface.");
b1fca01996-11-12Per Hedbor  globvar("User", "", "Change uid and gid to", TYPE_STRING, "When roxen is run as root, to be able to open port 80 "
8662771997-08-12Per Hedbor  "for listening, change to this user-id and group-id when the port "
1e5ee81997-08-21Per Hedbor  " has been opened. If you specify a symbolic username, the " "default group of that user will be used. " "The syntax is user[:group].");
70f2771997-12-15Per Hedbor  #ifdef EXTERNAL_HOSTNAME_PROCESS
b1fca01996-11-12Per Hedbor  globvar("NumHostnameLookup", 2, "Number of hostname lookup processes",
8662771997-08-12Per Hedbor  TYPE_INT|VAR_MORE,
89786e1997-10-09Peter Bortas  "You can here state the number of simultaneos host-name lookup " "processes Roxen should run. Roxen must be restarted for a change " "of this variable to take effect. If you constantly see a large " "host name lookup queue size in the configuration interface " "'Actions->Status' section, consider increasing this variable. " "A good guidline is: "
b1fca01996-11-12Per Hedbor  "<ul>\n" "<li> 1 for normal operation\n" "<li> 1 extra for each 300 000 accesses/day\n" "<li> 1 for each proxy\n" "<li> 1 for each 100 proxy users\n"
70f2771997-12-15Per Hedbor  "</ul>\n",0,1); #endif
b1fca01996-11-12Per Hedbor 
06f6b01997-09-03Henrik Grubbström (Grubba)  globvar("ModuleDirs", ({ "../local/modules/", "modules/" }),
bbff021997-08-26Henrik Grubbström (Grubba)  "Module directories", TYPE_DIR_LIST,
89786e1997-10-09Peter Bortas  "This is a list of directories where Roxen should look for " "modules. Can be relative paths, from the "
01d0811996-11-12Mirar (Pontus Hagland)  "directory you started roxen, " + getcwd() + " this time."
b1fca01996-11-12Per Hedbor  " The directories are searched in order for modules."); globvar("Supports", "#include <etc/supports>\n",
9b9f701997-08-12Per Hedbor  "Client supports regexps", TYPE_TEXT_FIELD|VAR_MORE,
b1fca01996-11-12Per Hedbor  "What do the different clients support?\n<br>" "The default information is normally fetched from the file "+
01d0811996-11-12Mirar (Pontus Hagland)  getcwd()+"etc/supports, and the format is:<pre>"
b1fca01996-11-12Per Hedbor  "<a href=$docurl/configuration/regexp.html>regular-expression</a>" " feature, -feature, ...\n" "</pre>" "If '-' is prepended to the name of the feature, it will be removed" " from the list of features of that client. All patterns that match" " each given client-name are combined to form the final feature list" ". See the file etc/supports for examples.");
ba73a21996-12-10Per Hedbor  globvar("audit", 0, "Audit trail", TYPE_FLAG,
52dc091998-02-28Johan Schön  "If Audit trail is set to Yes, all changes of uid will be "
89786e1997-10-09Peter Bortas  "logged in the Event log.");
ba73a21996-12-10Per Hedbor 
b1fca01996-11-12Per Hedbor #if efun(syslog)
9b9f701997-08-12Per Hedbor  globvar("LogA", "file", "Logging method", TYPE_STRING_LIST|VAR_MORE,
b1fca01996-11-12Per Hedbor  "What method to use for logging, default is file, but " "syslog is also available. When using file, the output is really" " sent to stdout and stderr, but this is handled by the " "start script", ({ "file", "syslog" })); globvar("LogSP", 1, "Syslog: Log PID", TYPE_FLAG, "If set, the PID will be included in the syslog", 0, syslog_disabled); globvar("LogCO", 0, "Syslog: Log to system console", TYPE_FLAG, "If set and syslog is used, the error/debug message will be printed" " to the system console as well as to the system log", 0, syslog_disabled); globvar("LogST", "Daemon", "Syslog: Log type", TYPE_STRING_LIST, "When using SYSLOG, which log type should be used", ({ "Daemon", "Local 0", "Local 1", "Local 2", "Local 3", "Local 4", "Local 5", "Local 6", "Local 7", "User" }), syslog_disabled); globvar("LogWH", "Errors", "Syslog: Log what", TYPE_STRING_LIST, "When syslog is used, how much should be sent to it?<br><hr>" "Fatal: Only messages about fatal errors<br>"+ "Errors: Only error or fatal messages<br>"+ "Warning: Warning messages as well<br>"+ "Debug: Debug messager as well<br>"+ "All: Everything<br>", ({ "Fatal", "Errors", "Warnings", "Debug", "All" }), syslog_disabled);
9b9f701997-08-12Per Hedbor  globvar("LogNA", "Roxen", "Syslog: Log as", TYPE_STRING,
89786e1997-10-09Peter Bortas  "When syslog is used, this will be the identification of the" "Roxen daemon. The entered value will be appended to all logs.", 0, syslog_disabled);
b1fca01996-11-12Per Hedbor #endif
14179b1997-01-29Per Hedbor #ifdef THREADS globvar("numthreads", 5, "Number of threads to run", TYPE_INT, "The number of simultaneous threads roxen will use.\n" "<p>Please note that even if this is one, Roxen will still "
3aaaa71997-06-12Wilhelm Köhler  "be able to serve multiple requests, using a select loop based " "system.\n"
b1fca01996-11-12Per Hedbor  "<i>This is quite useful if you have more than one CPU in " "your machine, or if you have a lot of slow NFS accesses.</i>");
14179b1997-01-29Per Hedbor #endif
b1fca01996-11-12Per Hedbor  globvar("AutoUpdate", 1, "Update the supports database automatically", TYPE_FLAG,
89786e1997-10-09Peter Bortas  "If set to Yes, the etc/supports file will be updated automatically "
a60c4c1997-07-03Henrik Grubbström (Grubba)  "from www.roxen.com now and then. This is recomended, since "
89786e1997-10-09Peter Bortas  "you will then automatically get supports information for new "
b1fca01996-11-12Per Hedbor  "clients, and new versions of old ones."); globvar("next_supports_update", time()+3600, "", TYPE_INT,"",0,1);
892c1c1997-10-08Henrik Grubbström (Grubba) #ifdef ENABLE_NEIGHBOURHOOD
20e2251997-08-25Henrik Grubbström (Grubba)  globvar("neighborhood", 0,
6420831997-08-21Per Hedbor  "Neighborhood: Register with other Roxen servers on the local network" ,TYPE_FLAG|VAR_MORE, "If this option is set, Roxen will automatically broadcast it's " "existence to other Roxen servers on the local network");
87d7871997-09-06Per Hedbor  globvar("neigh_tcp_ips", ({}), "Neighborhood: TCP hosts", TYPE_STRING_LIST|VAR_MORE, "This is the list of direct host<-->host links to establish. " "The local host is always present (if the neighbourhood functionality " "is at all enabled)");
b8811d1997-09-01Per Hedbor  globvar("neigh_ips", ({lambda(){
1acaf61997-09-18Per Hedbor  catch { mixed foo = gethostbyname(gethostname()); string n = reverse(foo[1][0]); sscanf(n,"%*d.%s", n); n=reverse(n)+".";
6420831997-08-21Per Hedbor  // Currently only defaults to C-nets..
1acaf61997-09-18Per Hedbor  return n+"255"; }; return "";
b8811d1997-09-01Per Hedbor  }()}), "Neighborhood: Broadcast addresses", TYPE_STRING_LIST|VAR_MORE, "");
6420831997-08-21Per Hedbor  globvar("neigh_com", "", "Neighborhood: Server informational comment", TYPE_TEXT|VAR_MORE, "A short string describing this server");
892c1c1997-10-08Henrik Grubbström (Grubba) #endif /* ENABLE_NEIGHBOURHOOD */
14179b1997-01-29Per Hedbor  setvars(retrieve("Variables", 0));
b1fca01996-11-12Per Hedbor 
cc2d581997-01-29Per Hedbor  if(QUERY(_v) < CONFIGURATION_FILE_LEVEL)
b1fca01996-11-12Per Hedbor  {
14179b1997-01-29Per Hedbor  update_global_vars(retrieve("Variables", 0)->_v?QUERY(_v):0);
b1fca01996-11-12Per Hedbor  QUERY(_v) = CONFIGURATION_FILE_LEVEL;
14179b1997-01-29Per Hedbor  store("Variables", variables, 0, 0);
cc2d581997-01-29Per Hedbor  set("_v", CONFIGURATION_FILE_LEVEL);
b1fca01996-11-12Per Hedbor  } for(p = 1; p < argc; p++) { string c, v; if(sscanf(argv[p],"%s=%s", c, v) == 2) if(variables[c]) variables[c][VAR_VALUE]=compat_decode_value(v); else perror("Unknown variable: "+c+"\n"); }
953ca31997-08-18Per Hedbor  docurl=QUERY(docurl2);
b1fca01996-11-12Per Hedbor }
14179b1997-01-29Per Hedbor  // To avoid stack error :-) This is really a bug in Pike, that is // probably fixed by now, but since I needed a catch() as well, I // never did come around to removing the hack. void do_dest(object|void o) { catch { destruct(o); }; }
88ba641997-12-04Per Hedbor // return all available fonts. Taken from the font_dirs list.
291a801997-07-10Per Hedbor array font_cache; array available_fonts(int cache) { array res = ({}); if(cache && font_cache) return font_cache; foreach(QUERY(font_dirs), string dir) { dir+="32/"; array d; if(array d = get_dir(dir)) { foreach(d,string f) { if(f=="CVS") continue; array a; if((a=file_stat(dir+f)) && (a[1]==-2)) res |= ({ replace(f,"_"," ") }); } } } sort(res); return font_cache = res; }
14179b1997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor // Somewhat misnamed, since there can be more then one // configuration-interface port nowdays. But, anyway, this function // opens and listens to all configuration interface ports. void initiate_configuration_port( int|void first ) { object o; array port;
4f4bc11998-02-04Per Hedbor  // Hm. if(!first && !config_ports_changed ) return 0; config_ports_changed = 0; #ifndef THREADS
e5bad21998-02-10Per Hedbor  if(catch(Array.map(configuration_ports, destruct))) catch(Array.map(configuration_ports, do_dest));
b1fca01996-11-12Per Hedbor  catch(do_dest(main_configuration_port)); configuration_ports = ({ });
4f4bc11998-02-04Per Hedbor #endif
b1fca01996-11-12Per Hedbor  main_configuration_port=0;
9f46de1997-04-08Per Hedbor  current_configuration = 0;
b1fca01996-11-12Per Hedbor  if(sizeof(QUERY(ConfigPorts))) { foreach(QUERY(ConfigPorts), port) {
39edcd1997-08-25Henrik Grubbström (Grubba)  array old = port; mixed erro; erro = catch { if ((< "ssl", "ssleay" >)[port[1]]) { // Obsolete versions of the SSL protocol. report_warning("Obsolete SSL protocol-module \""+port[1]+"\".\n" "Converted to SSL3.\n"); port[1] = "ssl3"; } program requestprogram = (program)(getcwd()+"/protocols/"+port[1]); function rp; array tmp; if(!requestprogram) { report_error("No request program for "+port[1]+"\n"); continue; } if(rp = requestprogram()->real_port) if(tmp = rp(port, 0)) port = tmp; object privs; if(port[0] < 1024)
f7d9811997-09-12Per Hedbor  privs = Privs("Opening listen port below 1024");
39edcd1997-08-25Henrik Grubbström (Grubba)  if(o=create_listen_socket(port[0],0,port[2],requestprogram,port)) { perror("Configuration port: "+port[1]+" port number "+ port[0]+" interface " +port[2]+"\n"); main_configuration_port = o; configuration_ports += ({ o }); } else { report_error("The configuration port " + port[0] + " on the interface " + port[2] + " (" + port[1] + ") " "could not be opened\n"); } }; if (erro) { report_error("Failed to open configuration port " + old[0] + " at " + old[2] + " (" + old[1] + "): " + (stringp(erro)?erro:describe_backtrace(erro)));
b1fca01996-11-12Per Hedbor  } } if(!main_configuration_port) { report_error("No configuration ports could be created.\n" "Is roxen already running?\n"); if(first)
3835ca1998-01-16Henrik Grubbström (Grubba)  exit( -1 ); // Restart.
b1fca01996-11-12Per Hedbor  } } else { perror("No configuration port. I hope this is what you want.\n" "Unless the configuration interface as a location module\n" "is enabled, you will not be allowed access to the configuration\n" "interface. You can re-enable the configuration port like this:\n" "./configvar ConfigurationPort=22202\n"); } }
e493e81997-07-11Per Hedbor #include <stat.h>
b1fca01996-11-12Per Hedbor // Find all modules, so a list of them can be presented to the // user. This is not needed when the server is started. void scan_module_dir(string d) {
6ad3161997-08-20Per Hedbor  if(sscanf(d, "%*s.pmod")!=0) return;
e493e81997-07-11Per Hedbor  MD_PERROR(("\n\nLooking for modules in "+d+" "));
01d0811996-11-12Mirar (Pontus Hagland)  string file,path=d; mixed err;
990b441997-07-18Per Hedbor  array q = (get_dir( d )||({})) - ({".","..","CVS","RCS" }); if(!sizeof(q)) {
3912ec1997-09-05Henrik Grubbström (Grubba)  MD_PERROR(("No modules in here. Continuing elsewhere\n"));
990b441997-07-18Per Hedbor  return; }
3bba751997-08-20Per Hedbor  if(search(q, ".no_modules")!=-1) {
3912ec1997-09-05Henrik Grubbström (Grubba)  MD_PERROR(("No modules in here. Continuing elsewhere\n"));
3bba751997-08-20Per Hedbor  return; }
e493e81997-07-11Per Hedbor  MD_PERROR(("There are "+language("en","number")(sizeof(q))+" files.\n"));
01d0811996-11-12Mirar (Pontus Hagland) 
e493e81997-07-11Per Hedbor  foreach( q, file )
b1fca01996-11-12Per Hedbor  {
0f8c871997-06-01Henrik Grubbström (Grubba)  _master->set_inhibit_compile_errors("");
fd0b6f1996-12-02Per Hedbor  if ( file[0]!='.' && !backup_extension(file) && (file[-1]!='z'))
b1fca01996-11-12Per Hedbor  {
e493e81997-07-11Per Hedbor  array stat = file_stat(path+file); if(!stat || (stat[ST_SIZE] < 0))
5e4ede1996-11-12Per Hedbor  {
e493e81997-07-11Per Hedbor  if(err = catch ( scan_module_dir(path+file+"/") )) MD_PERROR((sprintf("Error in module rescanning directory code:" " %s\n",describe_backtrace(err))));
0f8c871997-06-01Henrik Grubbström (Grubba)  } else {
e493e81997-07-11Per Hedbor  MD_PERROR(("Considering "+file+" - ")); if((module_stat_cache[path+file] && module_stat_cache[path+file][ST_MTIME])==stat[ST_MTIME]) { MD_PERROR(("Already tried this one.\n")); continue; } module_stat_cache[path+file]=stat;
28d38d1997-03-12Per Hedbor  switch(extension(file)) {
0f8c871997-06-01Henrik Grubbström (Grubba)  case "pike": case "lpc":
dd47911997-04-12Per Hedbor  if(catch{
0f8c871997-06-01Henrik Grubbström (Grubba)  if((open(path+file,"r")->read(4))=="#!NO") {
e382ed1997-07-16Henrik Grubbström (Grubba)  MD_PERROR(("Not a module\n"));
e493e81997-07-11Per Hedbor  file=0;
28d38d1997-03-12Per Hedbor  }
0f8c871997-06-01Henrik Grubbström (Grubba)  }) {
f1cee51997-06-06Henrik Grubbström (Grubba)  MD_PERROR(("Couldn't open file\n"));
e493e81997-07-11Per Hedbor  file=0;
0f8c871997-06-01Henrik Grubbström (Grubba)  }
e493e81997-07-11Per Hedbor  if(!file) break;
0f8c871997-06-01Henrik Grubbström (Grubba)  case "mod": case "so":
28d38d1997-03-12Per Hedbor  string *module_info; if (!(err=catch( module_info = lambda ( string file ) { array foo; object o;
6193221997-05-31Henrik Grubbström (Grubba)  program p;
0f8c871997-06-01Henrik Grubbström (Grubba) 
34447f1998-03-20Per Hedbor  if (catch(p = my_compile_file(file)) || (!p)) {
0f8c871997-06-01Henrik Grubbström (Grubba)  MD_PERROR((" compilation failed"));
3d0e7c1997-06-01Henrik Grubbström (Grubba)  throw("Compilation failed.\n");
6193221997-05-31Henrik Grubbström (Grubba)  }
68a0a21997-06-12Henrik Grubbström (Grubba)  // Set the module-filename, so that create in the // new object can get it. roxen->last_module_name = file;
6193221997-05-31Henrik Grubbström (Grubba)  array err = catch(o = p());
68a0a21997-06-12Henrik Grubbström (Grubba)  roxen->last_module_name = 0;
6193221997-05-31Henrik Grubbström (Grubba)  if (err) {
0f8c871997-06-01Henrik Grubbström (Grubba)  MD_PERROR((" load failed"));
6193221997-05-31Henrik Grubbström (Grubba)  throw(err); } else if (!o) {
0f8c871997-06-01Henrik Grubbström (Grubba)  MD_PERROR((" load failed")); throw("Failed to initialize module.\n"); } else { MD_PERROR((" load ok - ")); if (!o->register_module) { MD_PERROR(("register_module missing")); throw("No registration function in module.\n"); }
6193221997-05-31Henrik Grubbström (Grubba)  } foo = o->register_module(); if (!foo) {
0f8c871997-06-01Henrik Grubbström (Grubba)  MD_PERROR(("registration failed")); throw("Failed to register module.\n");
6193221997-05-31Henrik Grubbström (Grubba)  } else {
0f8c871997-06-01Henrik Grubbström (Grubba)  MD_PERROR(("registered."));
6193221997-05-31Henrik Grubbström (Grubba)  }
0f8c871997-06-01Henrik Grubbström (Grubba)  return({ foo[1], foo[2]+"<p><i>"+ replace(o->file_name_and_stuff(), "0<br>", file+"<br>") +"</i>", foo[0] }); }(path + file)))) { // Load OK
28d38d1997-03-12Per Hedbor  allmodules[ file-("."+extension(file)) ] = module_info; } else {
0f8c871997-06-01Henrik Grubbström (Grubba)  // Load failed.
9b9f701997-08-12Per Hedbor  module_stat_cache[path+file]=0;
28d38d1997-03-12Per Hedbor  _master->errors += "\n";
6193221997-05-31Henrik Grubbström (Grubba)  if (arrayp(err)) {
0f8c871997-06-01Henrik Grubbström (Grubba)  _master->errors += path + file + ": " + describe_backtrace(err) + "\n";
6193221997-05-31Henrik Grubbström (Grubba)  } else {
0f8c871997-06-01Henrik Grubbström (Grubba)  _master->errors += path + file + ": " + err;
6193221997-05-31Henrik Grubbström (Grubba)  }
28d38d1997-03-12Per Hedbor  }
b1fca01996-11-12Per Hedbor  }
0f8c871997-06-01Henrik Grubbström (Grubba)  MD_PERROR(("\n"));
b1fca01996-11-12Per Hedbor  } }
0f8c871997-06-01Henrik Grubbström (Grubba)  if(strlen(_master->errors)) {
9b9f701997-08-12Per Hedbor  report_debug("Compilation errors found while scanning modules in "+ d+":\n"+ _master->errors+"\n");
0f8c871997-06-01Henrik Grubbström (Grubba)  } _master->set_inhibit_compile_errors(0);
b1fca01996-11-12Per Hedbor  } } void rescan_modules() { string file, path; mixed err;
9b9f701997-08-12Per Hedbor  report_notice("Scanning module directories for modules");
e382ed1997-07-16Henrik Grubbström (Grubba)  if (!allmodules) { allmodules=copy_value(somemodules); }
0f8c871997-06-01Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  foreach(QUERY(ModuleDirs), path) {
dd47911997-04-12Per Hedbor  array err; err = catch(scan_module_dir( path ));
6193221997-05-31Henrik Grubbström (Grubba)  if(err) {
9b9f701997-08-12Per Hedbor  report_error("While scanning module dir (\""+path+"\"): " + describe_backtrace(err) + "\n");
6193221997-05-31Henrik Grubbström (Grubba)  } }
9b9f701997-08-12Per Hedbor  catch { rm(".module_stat_cache"); rm(".allmodules"); Stdio.write_file(".module_stat_cache", encode_value(module_stat_cache)); Stdio.write_file(".allmodules", encode_value(allmodules)); }; report_notice("Done with module directory scan. Found "+ sizeof(allmodules)+" modules.");
b1fca01996-11-12Per Hedbor } // ================================================= // Parse options to Roxen. This function is quite generic, see the // main() function for more info about how it is used. private string find_arg(array argv, array|string shortform, array|string|void longform, array|string|void envvars, string|void def) { string value; int i; for(i=1; i<sizeof(argv); i++) { if(argv[i] && strlen(argv[i]) > 1) { if(argv[i][0] == '-') { if(argv[i][1] == '-') { string tmp; int nf; if(!sscanf(argv[i], "%s=%s", tmp, value)) { if(i < sizeof(argv)-1) value = argv[i+1]; else value = argv[i]; tmp = argv[i]; nf=1; }
62eac81997-10-03Henrik Grubbström (Grubba)  if(arrayp(longform) && search(longform, tmp[2..]) != -1)
b1fca01996-11-12Per Hedbor  { argv[i] = 0; if(i < sizeof(argv)-1) argv[i+nf] = 0; return value;
62eac81997-10-03Henrik Grubbström (Grubba)  } else if(longform && longform == tmp[2..]) {
b1fca01996-11-12Per Hedbor  argv[i] = 0; if(i < sizeof(argv)-1) argv[i+nf] = 0; return value; } } else { if((arrayp(shortform) && search(shortform, argv[i][1..1]) != -1) || stringp(shortform) && shortform == argv[i][1..1]) { if(strlen(argv[i]) == 2) { if(i < sizeof(argv)-1) value =argv[i+1]; argv[i] = argv[i+1] = 0; return value; } else {
62eac81997-10-03Henrik Grubbström (Grubba)  value=argv[i][2..];
b1fca01996-11-12Per Hedbor  argv[i]=0; return value; } } } } } } if(arrayp(envvars)) foreach(envvars, value) if(getenv(value)) return getenv(value); if(stringp(envvars)) if(getenv(envvars)) return getenv(envvars); return def; } // do the chroot() call. This is not currently recommended, since // roxen dynamically loads modules, all module files must be // available at the new location. private void fix_root(string to) {
c79b261998-02-05Johan Schön #ifndef __NT__
b1fca01996-11-12Per Hedbor  if(getuid()) { perror("It is impossible to chroot() if the server is not run as root.\n"); return; } if(!chroot(to)) { perror("Roxen: Cannot chroot to "+to+": "); #if efun(real_perror) real_perror(); #endif return; } perror("Root is now "+to+".\n");
c79b261998-02-05Johan Schön #endif
b1fca01996-11-12Per Hedbor } void create_pid_file(string where) {
c79b261998-02-05Johan Schön #ifndef __NT__
b1fca01996-11-12Per Hedbor  if(!where) return; where = replace(where, ({ "$pid", "$uid" }), ({ (string)getpid(), (string)getuid() })); rm(where);
5e89211997-02-13Per Hedbor  if(catch(Stdio.write_file(where, sprintf("%d\n%d", getpid(), getppid()))))
b1fca01996-11-12Per Hedbor  perror("I cannot create the pid file ("+where+").\n");
c79b261998-02-05Johan Schön #endif
b1fca01996-11-12Per Hedbor } // External multi-threaded data shuffler. This leaves roxen free to // serve new requests. The file descriptors of the open files and the // clients are sent to the program, then the shuffler just shuffles // the data to the client.
beaca01998-02-20Per Hedbor void shuffle(object from, object to,
95e2b41997-05-25Wilhelm Köhler  object|void to2, function(:void)|void callback)
14179b1997-01-29Per Hedbor {
beaca01998-02-20Per Hedbor #if efun(spider.shuffle) if(!to2)
5bc1991997-01-29Per Hedbor  {
beaca01998-02-20Per Hedbor  object p = pipe(); p->input(from);
95e2b41997-05-25Wilhelm Köhler  p->set_done_callback(callback);
beaca01998-02-20Per Hedbor  p->output(to);
a60c4c1997-07-03Henrik Grubbström (Grubba)  } else {
beaca01998-02-20Per Hedbor #endif // 'smartpipe' does not support multiple outputs. object p = Pipe.pipe(); if (callback) p->set_done_callback(callback); p->output(to);
33b7701998-03-20Johan Schön  if(to2) p->output(to2);
beaca01998-02-20Per Hedbor  p->input(from); #if efun(spider.shuffle)
b1fca01996-11-12Per Hedbor  }
beaca01998-02-20Per Hedbor #endif
b1fca01996-11-12Per Hedbor }
3aaaa71997-06-12Wilhelm Köhler 
4f4bc11998-02-04Per Hedbor 
b1fca01996-11-12Per Hedbor static private int _recurse; void exit_when_done() { object o; int i;
38dca81996-12-10Per Hedbor  perror("Interrupt request received. Exiting,\n");
4f4bc11998-02-04Per Hedbor  die_die_die=1; // trace(9);
b1fca01996-11-12Per Hedbor  if(++_recurse > 4)
38dca81996-12-10Per Hedbor  { werror("Exiting roxen (spurious signals received).\n"); stop_all_modules();
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(-1); // Restart. // kill(getpid(), 9); // kill(0, -9);
38dca81996-12-10Per Hedbor  }
14179b1997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor  // First kill off all listening sockets..
4f4bc11998-02-04Per Hedbor  foreach(indices(portno)||({}), o) { #ifdef THREADS object fd = files.file(); fd->connect( portno[o][2]!="Any"?portno[o][2]:"", portno[o][0] ); destruct(fd); #endif
b1fca01996-11-12Per Hedbor  do_dest(o);
4f4bc11998-02-04Per Hedbor  }
b1fca01996-11-12Per Hedbor  // Then wait for all sockets, but maximum 10 minutes.. call_out(lambda() {
aaef2a1997-03-02Henrik Grubbström (Grubba)  call_out(Simulate.this_function(), 5);
38dca81996-12-10Per Hedbor  if(!_pipe_debug()[0]) { werror("Exiting roxen (all connections closed).\n"); stop_all_modules();
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(-1); // Restart.
14179b1997-01-29Per Hedbor  perror("Odd. I am not dead yet.\n");
38dca81996-12-10Per Hedbor  } }, 0.1); call_out(lambda(){ werror("Exiting roxen (timeout).\n"); stop_all_modules();
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(0); // Restart.
38dca81996-12-10Per Hedbor  }, 600, 0); // Slow buggers..
b1fca01996-11-12Per Hedbor } void exit_it() { perror("Recursive signals.\n");
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(-1); // Restart.
b1fca01996-11-12Per Hedbor }
892c1c1997-10-08Henrik Grubbström (Grubba) #ifdef ENABLE_NEIGHBOURHOOD object neighborhood; #endif /* ENABLE_NEIGHBOURHOOD */
b1fca01996-11-12Per Hedbor // And then we have the main function, this is the oldest function in // Roxen :) It has not changed all that much since Spider 2.0.
c856841998-01-21Henrik Grubbström (Grubba) int main(int|void argc, array (string)|void argv)
b1fca01996-11-12Per Hedbor {
c245691997-10-25Per Hedbor  initiate_languages();
b1fca01996-11-12Per Hedbor  mixed tmp; start_time=time(1);
5e89211997-02-13Per Hedbor  add_constant("write", perror);
9b9f701997-08-12Per Hedbor  report_notice("Starting roxen\n");
b1fca01996-11-12Per Hedbor 
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
b1fca01996-11-12Per Hedbor  mark_fd(0, "Stdin"); mark_fd(1, "Stdout"); mark_fd(2, "Stderr");
3235691998-03-26Per Hedbor #endif
b1fca01996-11-12Per Hedbor 
51643e1997-08-21Per Hedbor  configuration_dir = find_arg(argv, "d",({"config-dir","configuration-directory" }), ({ "ROXEN_CONFIGDIR", "CONFIGURATIONS" }), "../configurations");
b1fca01996-11-12Per Hedbor 
a92b951997-08-05Martin Stjernholm  if(configuration_dir[-1] != '/')
b1fca01996-11-12Per Hedbor  configuration_dir += "/";
0978ee1997-09-08David Hedbor  startpid = getppid();
e145221997-10-10David Hedbor  roxenpid = getpid();
b1fca01996-11-12Per Hedbor  create_pid_file(find_arg(argv, "p", "pid-file", "ROXEN_PID_FILE"));
14179b1997-01-29Per Hedbor  // Dangerous... if(tmp = find_arg(argv, "r", "root")) fix_root(tmp);
b1fca01996-11-12Per Hedbor  argv -= ({ 0 });
51643e1997-08-21Per Hedbor  argc = sizeof(argv);
b1fca01996-11-12Per Hedbor  perror("Restart initiated at "+ctime(time()));
af97431997-08-21Per Hedbor 
b1fca01996-11-12Per Hedbor  define_global_variables(argc, argv);
892c1c1997-10-08Henrik Grubbström (Grubba) #ifdef ENABLE_NEIGHBOURHOOD
1add881997-08-21Per Hedbor  neighborhood = (object)"neighborhood";
892c1c1997-10-08Henrik Grubbström (Grubba) #endif /* ENABLE_NEIGHBOURHOOD */
6420831997-08-21Per Hedbor 
b1fca01996-11-12Per Hedbor  create_pid_file(QUERY(pidfile)); #if efun(syslog) init_logger(); #endif init_garber(); initiate_supports();
0f28da1997-08-13Per Hedbor  initiate_configuration_port( 1 );
b1fca01996-11-12Per Hedbor  enable_configurations();
f6d62d1997-03-26Per Hedbor #if 0 restore_current_user_id_number(); #endif
b1fca01996-11-12Per Hedbor // Rebuild the configuration interface tree if the interface was // loaded before the configurations was enabled (a configuration is a // virtual server, perhaps the name should be changed internally as // well.. :-)
14179b1997-01-29Per Hedbor  if(root) { destruct(configuration_interface());
b1fca01996-11-12Per Hedbor  configuration_interface()->build_root(root);
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor  call_out(update_supports_from_roxen_com, QUERY(next_supports_update)-time()); if(set_u_and_gid()) perror("Setting UID and GID ...\n");
48fa361997-04-05Per Hedbor #ifdef THREADS start_handler_threads();
4f4bc11998-02-04Per Hedbor  catch( this_thread()->set_name("Backend") );
3aaaa71997-06-12Wilhelm Köhler #if efun(thread_set_concurrency)
41d0f91998-02-20Per Hedbor  thread_set_concurrency(QUERY(numthreads)+1);
48fa361997-04-05Per Hedbor #endif
4f4bc11998-02-04Per Hedbor 
34fbbc1998-02-05Henrik Grubbström (Grubba) #endif /* THREADS */
990cbb1997-08-12David Hedbor 
3835ca1998-01-16Henrik Grubbström (Grubba)  // Signals which cause a restart (exitcode != 0)
b399651997-07-22Henrik Grubbström (Grubba)  foreach( ({ "SIGUSR1", "SIGUSR2", "SIGHUP", "SIGINT" }), string sig) {
9b9f701997-08-12Per Hedbor  catch { signal(signum(sig), exit_when_done); };
b399651997-07-22Henrik Grubbström (Grubba)  }
3835ca1998-01-16Henrik Grubbström (Grubba)  // Signals which cause a shutdown (exitcode == 0) foreach( ({ "SIGQUIT" }), string sig) { catch { signal(signum(sig), kill_me); }; }
0f28da1997-08-13Per Hedbor 
9b9f701997-08-12Per Hedbor  report_notice("Roxen started in "+(time()-start_time)+" seconds.\n");
4f4bc11998-02-04Per Hedbor #ifdef __RUN_TRACE trace(1); #endif
b1fca01996-11-12Per Hedbor // start_time=time(); // Used by the "uptime" info later on. return -1; }
7a61de1998-03-26Per Hedbor  string diagnose_error(array from) { }