Branch: Tag:

1997-01-29

1997-01-29 04:59:46 by Per Hedbor <ph@opera.com>

1.2

Rev: server/base_server/color.pike:1.3
Rev: server/base_server/config/builders.pike:1.2
Rev: server/base_server/config/describers.pike:1.17
Rev: server/base_server/config/savers.pike:1.3
Rev: server/base_server/configuration.pike:1.11
Rev: server/base_server/http.pike:1.6
Rev: server/base_server/mainconfig.pike:1.30
Rev: server/base_server/module_support.pike:1.8
Rev: server/base_server/newdecode.pike:1.4
Rev: server/base_server/proxyauth.pike:1.2
Rev: server/base_server/read_config.pike:1.7
Rev: server/base_server/roxen.pike:1.32
Rev: server/etc/include/config.h:1.5
Rev: server/etc/include/fifo.pre.pike:1.2
Rev: server/etc/roxen_master.pike:1.18
Rev: server/etc/supports:1.12
Rev: server/languages/swedish.pike:1.6
Rev: server/modules/filesystems/filesystem.pike:1.8
Rev: server/modules/filters/hostredirect.pike:1.6
Rev: server/modules/graphics/graphic_text.pike:1.24
Rev: server/modules/logging/home_logger.pike:1.7
Rev: server/modules/scripting/cgi.pike:1.10
Rev: server/modules/tags/htmlparse.pike:1.21
Rev: server/protocols/ftp.pike:1.5
Rev: server/protocols/gopher.pike:1.4
Rev: server/protocols/http.pike:1.11
Rev: server/protocols/ssl.pike:1.5
Rev: server/protocols/ssleay.pike:1.4

1: - string cvs_version = "$Id: roxen.pike,v 1.31 1997/01/09 20:45:55 grubba Exp $"; + string cvs_version = "$Id: roxen.pike,v 1.32 1997/01/29 04:59:36 per Exp $";   #define IN_ROXEN -  +  + #include <fifo.h>   #include <module.h>   #include <variables.h>   #include <roxen.h> -  + #include <config.h>    -  + #define THREADS +    #ifdef NO_DNS   inherit "dummy_hosts";   #else
14:   inherit "disk_cache";   inherit "language";    - int num_connections; + object roxen=this_object(), current_configuration;    - object roxen=this_object(); + private program Configuration; /*set in create*/      object main_configuration_port;   mapping allmodules;
27:      // This is the real Roxen version. It should be changed before each   // release - string real_version = "Roxen Challenger/1.1"; + string real_version = "Roxen Challenger/1.2 alpha";      // A mapping from ports (objects, that is) to an array of information   // about that port.
94:    nbytesdecoded --;    nbytesdecoded --;    - #if 0 -  perror(sprintf("%d %d %d -> %s(%d)\n", pr2six[bufcoded[in-3]], -  pr2six[bufcoded[in-2]], pr2six[bufcoded[in-1]], -  bufplain[0..nbytesdecoded], nbytesdecoded+1)); - #endif -  +     return bufplain[0..nbytesdecoded];   }   // End of what was formely known as decode.pike, the base64 decoder
109:   private function build_root;   private object root;    - // Sub process ids. - private static array subs; +     -  +    // Fork, and then do a 'slow-quit' in the forked copy. Exit the   // original copy, after all listen ports are closed.   // Then the forked copy finish all current connections.
120:   {    int i;    object *f; -  if(main_configuration_port && objectp(main_configuration_port)) -  { +     int pid; -  if(search(subs, getpid()) == -1) -  { +     perror("Exiting Roxen.\n"); -  foreach(subs, pid) -  { -  if(pid != getpid()) -  kill(pid, signum("SIGUSR1")); -  } -  } -  } +    #ifdef SOCKET_DEBUG    perror("SOCKETS: fork_or_quit()\n Bye!\n");   #endif
157:   // This is called for each incoming connection.   private static void accept_callback( object port )   { -  int q=QUERY(NumAccept), l; +  int q=QUERY(NumAccept);    object file; -  +  array pn=portno[port];    -  // Silently enforced. We do _not_ want this if the number of -  // concurrently running processes is more than, or equal to, 2. -  -  if(QUERY(copies) > 1) {l=1;q=1;} -  -  if(!portno[port]) + #ifdef DEBUG +  if(!pn)    {    destruct(port->accept());    perror("$&$$& Garbage Collector bug!!\n");    return;    } -  + #endif    while(q--)    { -  // Lock due to bugs in OS-es (Solaris 2.*, IRIX 5.*, perhaps others)... -  // This is only needed if there are any copies that might try -  // to aquire the lock at the same time as this Roxen. -  -  if(l) catch { portno[port][-2]->aquire(); }; +     catch { file = port->accept(); }; -  if(l) catch { portno[port][-2]->free(); }; -  -  if(!portno[port][-1]) + #ifdef SOCKET_DEBUG +  if(!pn[-1])    {    report_error("In accept: Illegal protocol handler for port.\n");    if(file) destruct(file);    return;    } -  - #ifdef SOCKET_DEBUG +     perror(sprintf("SOCKETS: accept_callback(CONF(%s))\n", -  portno[port][1]&&portno[port][1]->name||"Configuration")); +  pn[1]&&pn[1]->name||"Configuration"));   #endif    if(!file)    {
210:    return;       case 24: -  if(failed_connections++) -  { +     report_fatal("Out of sockets. Restarting server gracefully.\n");    fork_or_quit(); -  } +     return;    }    } -  if(failed_connections>0) failed_connections--; -  + #ifdef SOCKET_DEBUG    mark_fd( file->query_fd(), "Connection from "+file->query_address()); -  -  object request; -  request = portno[port][-1](); -  request->assign( file, portno[port][1] ); + #endif +  pn[-1](file,pn[1]);   #ifdef SOCKET_DEBUG    perror(sprintf("SOCKETS: Ok. Connect on %O:%O from %O\n", -  portno[port][2], portno[port][0], file->query_address())); +  pn[2], pn[0], file->query_address()));   #endif    }   }    -  + #ifdef THREADS + #define THREAD_DEBUG +  + object (Queue) handle_queue = Queue(); +  + void handler_thread(int id) + { + #ifdef THREAD_DEBUG +  perror("Handler thread "+id+" started.\n"); + #endif +  array (mixed) h; +  while( h=handle_queue->read() ) +  { + #ifdef THREAD_DEBUG +  perror(id+" START.\n"); + #endif + #ifdef DEBUG +  array err= + #endif +  catch { h[0](@h[1]); }; + #ifdef DEBUG +  if(err) perror("Error in handler thread:\n"+describe_backtrace(err)+"\n"); + #endif + #ifdef THREAD_DEBUG +  perror(id+" DONE.\n"); + #endif +  h=0; +  } + } +  + int number_of_threads; + void start_handler_threads() + { +  perror("Starting "+QUERY(numthreads)+" threads to handle requests.\n"); + #if efun(thread_set_concurrency) +  thread_set_concurrency(QUERY(numthreads)+1); + #endif +  for(; number_of_threads < QUERY(numthreads); number_of_threads++) +  thread_create( handler_thread, number_of_threads ); + } + #endif +  + void handle(function f, mixed ... args) + { + #ifdef THREADS + /* thread_create(f, @args); */ +  handle_queue->write(({f, args })); + #else +  f(@args); + #endif + } +    // Listen to a port, connected to the configuration 'conf', binding   // only to the netinterface 'ether', using 'requestprogram' as a   // protocol handled.
243:   // 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.) - private static +    object create_listen_socket(mixed port_no, object conf,    string|void ether, program requestprogram)   {
270:    ether=0;    if(ether)    sscanf(ether, "addr:%s", ether); - #if 0 -  werror(sprintf("%O(%t), %O(%t), %O(%t)\n", -  port_no, port_no, accept_callback, accept_callback, -  ether, ether)); - #endif +        if(!port->bind(port_no, accept_callback, ether))    {
293:    }    }    } -  if(conf && QUERY(uselock) && (QUERY(copies) > 1)) -  portno[port]=({ port_no, conf, ether||"Any", -  ((program)"lock")( port_no + (ether?hash(ether):0)), -  requestprogram }); -  else +     portno[port]=({ port_no, conf, ether||"Any", 0, requestprogram });   #ifdef SOCKET_DEBUG    perror("SOCKETS: -> Ok.\n");
564:    }    remove_call_out( update_supports_from_roxen_com );    -  object o; -  o = current_configuration; -  current_configuration = 0; -  +     // Check again in one week.    QUERY(next_supports_update)=3600*24*7 + time(); -  store("Variables", variables, 0); +  store("Variables", variables, 0, 0);    -  current_configuration = o; +     call_out(update_supports_from_roxen_com, 3600*24*7);   }   
617:    return sup - nsup;   }    - // Parse the logging format strings. - private inline string fix_logging(string s) - { -  string pre, post, c; -  sscanf(s, "%*[\t ]", s); -  s = replace(s, ({"\\t", "\\n", "\\r" }), ({"\t", "\n", "\r" })); -  while(s[0] == ' ') s = s[1..10000]; -  while(s[0] == '\t') s = s[1..10000]; -  while(sscanf(s, "%s$char(%d)%s", pre, c, post)==3) -  s=sprintf("%s%c%s", pre, c, post); -  while(sscanf(s, "%s$wchar(%d)%s", pre, c, post)==3) -  s=sprintf("%s%2c%s", pre, c, post); -  while(sscanf(s, "%s$int(%d)%s", pre, c, post)==3) -  s=sprintf("%s%4c%s", pre, c, post); -  if(!sscanf(s, "%s$^%s", pre, post)) -  s+="\n"; -  else -  s=pre+post; -  return s; - } -  - private void parse_log_formats() - { -  string b; -  array foo=query("LogFormat")/"\n"; -  foreach(foo, b) -  if(strlen(b) && b[0] != '#' && sizeof(b/":")>1) -  current_configuration->log_format[(b/":")[0]] -  = fix_logging((b/":")[1..100000]*":"); - } -  -  -  -  - // Really write an entry to the log. - private void write_to_log( string host, string rest, string oh, function fun ) - { -  int s; -  if(!host) host=oh; -  if(!stringp(host)) -  host = "error:no_host"; -  if(fun) fun(replace(rest, "$host", host)); - } -  - // Logging format support functions. - nomask private inline string host_ip_to_int(string s) - { -  int a, b, c, d; -  sscanf(s, "%d.%d.%d.%d", a, b, c, d); -  return sprintf("%c%c%c%c",a, b, c, d); - } -  - nomask private inline string unsigned_to_bin(int a) - { -  return sprintf("%4c", a); - } -  - nomask private inline string unsigned_short_to_bin(int a) - { -  return sprintf("%2c", a); - } -  - nomask private inline string extract_user(string from) - { -  array tmp; -  if (!from || sizeof(tmp = from/":")<2) -  return "-"; -  -  return tmp[0]; // username only, no password - } -  +    public void log(mapping file, object request_id)   { -  string a; -  string form; -  function f; -  -  if(!request_id->conf) -  return; -  -  foreach(request_id->conf->logger_modules(), f) // Call all logging functions -  if(f(request_id,file)) -  return; -  -  if(!request_id->conf->log_function) -  return;// No file is open for logging. -  -  -  if(query("NoLog") && _match(request_id->remoteaddr, query("NoLog"))) -  return; -  -  if(!(form=request_id->conf->log_format[(string)file->error])) -  form = request_id->conf->log_format["*"]; -  -  if(!form) return; -  -  form=replace(form, -  ({ -  "$ip_number", "$bin-ip_number", "$cern_date", -  "$bin-date", "$method", "$resource", "$protocol", -  "$response", "$bin-response", "$length", "$bin-length", -  "$referer", "$user_agent", "$user", "$user_id", -  }), ({ -  (string)request_id->remoteaddr, -  host_ip_to_int(request_id->remoteaddr), -  cern_http_date(time(1)), -  unsigned_to_bin(time(1)), -  (string)request_id->method, -  (string)request_id->not_query, -  (string)request_id->prot, -  (string)(file->error||200), -  unsigned_short_to_bin(file->error||200), -  (string)(file->len>=0?file->len:"?"), -  unsigned_to_bin(file->len), -  (string) -  (sizeof(request_id->referer)?request_id->referer[0]:"-"), -  http_encode_string(sizeof(request_id->client)?request_id->client*" ":"-"), -  extract_user(request_id->realauth), -  (string)request_id->cookies->RoxenUserID, -  })); -  -  if(search(form, "host") != -1) -  ip_to_host(request_id->remoteaddr, write_to_log, form, -  request_id->remoteaddr, request_id->conf->log_function); -  else -  request_id->conf->log_function(form); +  if(!request_id->conf) return; +  request_id->conf->log(file, request_id);   }      // Support for unique user id's
782:    return current_user_id_number;   }    -  - // These are here for statistics and debug reasons only. - public string status() - { -  float tmp; -  string res=""; -  -  if(!current_configuration) -  return ("No current_configuration. No configurations enabled?\n"); -  -  if(!current_configuration->sent -  ||!current_configuration->received -  ||!current_configuration->hsent) -  return "Fatal error in status(): Bignum object gone.\n"; -  -  tmp = (current_configuration->sent->mb()/(float)(time(1)-start_time+1)* -  QUERY(copies)); -  res = sprintf("<table><tr align=right><td><b>Sent data:</b></td><td>%.2fMB" -  "</td><td>%.2f Kbit/sec</td>", -  current_configuration->sent->mb()*(float)(QUERY(copies)), -  tmp * 8192.0); -  -  res += sprintf("<td><b>Sent headers:</b></td><td>%.2fMB</td>", -  current_configuration->hsent->mb()*(float)QUERY(copies)); -  -  tmp=(((float)current_configuration->requests*(float)600)/ -  (float)((time(1)-start_time)+1)*QUERY(copies)); -  -  res += ("<tr align=right><td><b>Number of requests:</b></td><td>" -  + sprintf("%8d", current_configuration->requests*QUERY(copies)) -  + sprintf("</td><td>%.2f/min</td><td><b>Recieved data:</b></" -  "td><td>%.2f</td>", (float)tmp/(float)10, -  (current_configuration->received->mb() -  *(float)QUERY(copies)))); -  -  return res +"</table>"; - } -  +    public string full_status()   {    int tmp;
835:    ||!conf->received    ||!conf->hsent)    continue; -  foo[0]+=conf->sent->mb()/(float)(time(1)-start_time+1)*(float)QUERY(copies); -  foo[1]+=conf->sent->mb()*(float)QUERY(copies); -  foo[2]+=conf->hsent->mb()*(float)QUERY(copies); -  foo[3]+=conf->received->mb()*(float)QUERY(copies); -  foo[4]+=conf->requests * QUERY(copies); +  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;    }   #undef conf    for(tmp = 1; tmp < 4; tmp ++)
875:   // to the configuration object. The functions will still be here for   // compatibility for a while, though.    - public string *userinfo(string u, object id) - { -  if(id) -  current_configuration = id->conf; -  if(current_configuration->auth_module) -  return current_configuration->auth_module->userinfo(u); -  return 0; - } -  +    public string *userlist(object id)   { -  if(id) +  object current_configuration; +  if(!id) error("No id in userlist(object id)\n");    current_configuration = id->conf;    if(current_configuration->auth_module)    return current_configuration->auth_module->userlist();
895:      public string *user_from_uid(int u, object id)   { -  if(id) +  object current_configuration; +  if(!id) error("No id in user_from_uid(int uid, object id)\n");    current_configuration = id->conf;    if(current_configuration->auth_module)    return current_configuration->auth_module->user_from_uid(u);
929:   public varargs string type_from_filename( string file, int to )   {    mixed tmp; +  object current_configuration;    string ext=extension(file);    -  if(!current_configuration) -  current_configuration = find_configuration_for(previous_object()); -  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"; +  if(current_configuration = find_configuration_for(previous_object())) +  current_configuration->type_from_filename( file, to );   } -  return to?tmp:tmp[0]; -  } else { -  if(!(tmp=current_configuration->types_fun("default"))) -  tmp=({ "application/octet-stream", 0 }); -  } -  return 0; - } +     - private static int nest = 0; + #define COMPAT_ALIAS(X) mixed X(string file, object id){return id->conf->X(file,id);}    - #ifdef MODULE_LEVEL_SECURITY - private mapping misc_cache=([]); + COMPAT_ALIAS(find_dir); + COMPAT_ALIAS(stat_file); + COMPAT_ALIAS(access); + COMPAT_ALIAS(real_file); + COMPAT_ALIAS(is_file); + COMPAT_ALIAS(userinfo);    - int|mapping check_security(function a, object id, void|int slevel) - { -  array level; -  int need_auth; -  array seclevels; -  -  if(!(seclevels = misc_cache[ a ])) -  misc_cache[ a ] = seclevels = ({ -  function_object(a)->query_seclevels(), -  function_object(a)->query("_seclvl") -  }); -  -  if(slevel && (seclevels[1] > slevel)) // "Trustlevel" to low. -  return 1; -  -  -  if(!sizeof(seclevels[0])) -  return 0; // Ok if there are no patterns. -  -  catch -  { -  foreach(seclevels[0], level) -  switch(level[0]) -  { -  case MOD_ALLOW: // allow ip=... -  if(level[1](id->remoteaddr)) return 0; // Match. It's ok. -  return http_low_answer(403, "<h2>Access forbidden</h2>"); -  continue; -  -  case MOD_DENY: // deny ip=... -  if(level[1](id->remoteaddr)) throw(""); -  return http_low_answer(403, "<h2>Access forbidden</h2>"); -  continue; -  -  case MOD_USER: // allow user=... -  if(id->auth && id->auth[0] && level[1](id->auth[1])) return 0; -  need_auth = 1; -  continue; -  -  case MOD_PROXY_USER: // allow user=... -  if(id->misc->proxyauth && id->misc->proxyauth[0] && -  level[1](id->misc->proxyauth[1])) return 0; -  return http_proxy_auth_required("user"); -  } -  }; -  // If auth is needed (access might be allowed if you are the right user), -  // request authentification from the user. Otherwise this is a lost case, -  // the user will never be allowed access unless the patterns change. -  return need_auth ? http_auth_failed("user") : 1; - } -  - // Some clients does _not_ handle the magic 'internal-gopher-...'. - // So, lets do it here instead. - private mapping internal_gopher_image(string from) - { -  sscanf(from, "%s.gif", from); -  sscanf(from, "%s.jpg", from); -  from -= "."; -  // Disallow "internal-gopher-..", it won't really do much harm, but a list of -  // all files in '..' might be retrieved (that is, the actual directory -  // file was sent to the browser) -  return (["file":open("roxen-images/dir/"+from+".gif","r"), -  "type":"image/gif"]); - } -  - // Inspired by the internal-gopher-... thingie, this is the images - // from the configuration interface. :-) - private mapping internal_roxen_image(string from) - { -  sscanf(from, "%s.gif", from); -  sscanf(from, "%s.jpg", from); -  from -= "."; -  // Disallow "internal-roxen-..", it won't really do much harm, but a list of -  // all files in '..' might be retrieved (that is, the actual directory -  // file was sent to the browser) -  // /internal-roxen-../.. was never possible, since that would be remapped to -  // /.. -  return (["file":open("roxen-images/"+from+".gif", "r"),"type":"image/gif"]); - } -  - public mapping|int get_file(object id, int|void no_magic); -  - // The function that actually tries to find the data requested. All - // modules are mapped, in order, and the first one that returns a - // suitable responce is used. -  - static private mapping|int low_get_file(object id, int|void no_magic) - { - #ifdef MODULE_LEVEL_SECURITY -  int slevel; - #endif -  string file=id->not_query; -  string loc; -  function funp; -  mixed tmp, tmp2; -  mapping|object fid; -  -  -  current_configuration = id->conf; // This is needed -  -  if(!no_magic) -  { - #ifndef NO_INTERNAL_HACK -  // No, this is not beautiful... :) -  if(sscanf(id->not_query, "%*s/internal-%s", loc)) -  { -  if(sscanf(loc, "gopher-%[^/]", loc)) // The directory icons. -  return internal_gopher_image(loc); -  -  if(sscanf(loc, "spinner-%[^/]", loc) // Configuration interface images. -  ||sscanf(loc, "roxen-%[^/]", loc)) // Try /internal-roxen-power -  return internal_roxen_image(loc); -  } - #endif -  -  if(id->prestate->diract) -  { -  if(current_configuration->dir_module) -  tmp = current_configuration->dir_module->parse_directory(id); -  if(mappingp(tmp)) return tmp; -  } -  } -  -  // Well, this just _might_ be somewhat over-optimized, since it is -  // quite unreadable, but, you cannot win them all.. -  - #ifdef URL_MODULES -  // Map URL-modules. -  foreach(current_configuration->url_modules(id), funp) -  if((tmp=funp( id, file )) && (mappingp( tmp )||objectp( tmp )) ) -  { -  array err; -  -  if(tmp->error) -  return tmp; -  nest ++; -  err = catch { -  if( nest < 20 ) -  tmp = low_get_file( tmp, no_magic ); -  else -  error("Too deep recursion in roxen::get_file() while mapping " -  +file+".\n"); -  }; -  nest = 0; -  if(err) -  throw(err); -  return tmp; -  } - #endif -  - #ifdef EXTENSION_MODULES -  if(tmp=current_configuration->extension_modules(loc=extension(file), id)) -  foreach(tmp, funp) -  if(tmp=funp(loc, id)) -  { -  if(!objectp(tmp)) -  return tmp; -  fid = tmp; - #ifdef MODULE_LEVEL_SECURITY -  slevel = function_object(funp)->query("_seclvl"); - #endif -  break; -  } - #endif -  -  foreach(current_configuration->location_modules(id), tmp) -  { -  loc = tmp[0]; -  if(!search(file, loc)) -  { - #ifdef MODULE_LEVEL_SECURITY -  if(tmp2 = check_security(tmp[1], id, slevel)) -  if(intp(tmp2)) -  { -  continue; -  } else { -  return tmp2; -  } - #endif -  if(fid=tmp[1]( file[ strlen(loc) .. 1000000 ] + id->extra_extension, id)) -  { -  id->virtfile = loc; -  -  if(mappingp(fid)) -  return fid; -  else -  { - #ifdef MODULE_LEVEL_SECURITY -  slevel = misc_cache[ tmp[1] ][1];// misc_cache from check_security - #endif -  break; -  } -  } -  } else if(strlen(loc)-1==strlen(file)) { -  // This one is here to allow accesses to /local, even if -  // the mountpoint is /local/. It will slow things down, but... -  if(file+"/" == loc) -  return http_redirect(id->not_query + "/", id); -  } -  } -  -  if(fid == -1) -  { -  if(no_magic) return -1; -  if(current_configuration->dir_module) -  fid = current_configuration->dir_module->parse_directory(id); -  else -  return 0; -  if(mappingp(fid)) return (mapping)fid; -  } -  -  // Map the file extensions, but only if there is a file... -  if(objectp(fid) && -  (tmp=current_configuration-> -  file_extension_modules(loc=extension(id->not_query), id))) -  foreach(tmp, funp) -  { - #ifdef MODULE_LEVEL_SECURITY -  if(tmp=check_security(funp, id, slevel)) -  if(intp(tmp)) -  { -  continue; -  } -  else -  return tmp; - #endif -  if(tmp=funp(fid, loc, id)) -  { -  if(!objectp(tmp)) -  return tmp; -  if(fid) -  destruct(fid); -  fid = tmp; -  break; -  } -  } -  -  if(objectp(fid)) -  { -  if(stringp(id->extension)) -  id->not_query += id->extension; -  -  tmp=type_from_filename(id->not_query, 1); -  -  if(tmp) -  return ([ "file":fid, "type":tmp[0], "encoding":tmp[1] ]); -  -  return ([ "file":fid, ]); -  } -  return fid; - } -  +    public mapping|int get_file(object id, int|void no_magic)   { -  mixed res, res2; -  function tmp; -  res = low_get_file(id, no_magic); -  // finally map all filter type modules. -  // Filter modules are like TYPE_LAST modules, but they get called -  // for _all_ files. -  foreach(id->conf->filter_modules(id), tmp) -  if(res2=tmp(res,id)) -  { -  if(res && res->file && (res2->file != res->file)) -  destruct(res->file); -  res=res2; +  return id->conf->get_file(id, no_magic);   } -  return res; - } +     - // Map location-modules, and then build a listing of this virtual - // directory. -  - public array find_dir(string file, object id) + public mixed try_get_file(string s, object id, int|void status, int|void nocache)   { -  string loc; -  array dir = ({ }), d, tmp; -  -  file=replace(file, "//", "/"); -  -  current_configuration = id->conf; -  -  foreach(current_configuration->location_modules(), tmp) -  { -  loc = tmp[0]; -  if(file[0] != '/') -  file = "/" + file; -  -  if(!search(file, loc)) -  { - //#ifdef MODULE_LEVEL_SECURITY - // if(check_security(tmp[1], id)) continue; - //#endif -  if(d=function_object(tmp[1])->find_dir(file[strlen(loc)..1000000], id)) -  dir |= d; -  } else { -  if(search(loc, file)==0 && loc[strlen(file)-1]=='/' -  && (loc[0]==loc[-1]) && loc[-1]=='/') -  { -  loc=loc[strlen(file)..100000]; -  sscanf(loc, "%s/", loc); -  dir += ({ loc }); +  return id->conf->try_get_file(s,id,status,nocache);   } -  } -  } -  if(sizeof(dir)) -  return dir; - } +     - // Stat a virtual file. -  - public array stat_file(string file, object id) - { -  string loc; -  array s, tmp; -  -  current_configuration = id->conf; -  -  file=replace(file, "//", "/"); // "//" is really "/" here... -  -  // Map location-modules. -  foreach(current_configuration->location_modules(), tmp) -  { -  loc = tmp[0]; -  if((file == loc) || ((file+"/")==loc)) -  return ({ 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); -  if(!search(file, loc)) -  { - //#ifdef MODULE_LEVEL_SECURITY - // if(check_security(tmp[1], id)) continue; - //#endif -  if(s=function_object(tmp[1])->stat_file(file[strlen(loc)..], id)) -  return s; -  } -  } - } -  -  - // Access a virtual file? -  - public array access(string file, object id) - { -  string loc; -  array s, tmp; -  -  current_configuration = id->conf; -  -  file=replace(file, "//", "/"); // "//" is really "/" here... -  -  // Map location-modules. -  foreach(current_configuration->location_modules(), tmp) -  { -  loc = tmp[0]; -  if((file+"/")==loc) -  return file+="/"; -  if(!search(file, loc)) -  { - #ifdef MODULE_LEVEL_SECURITY -  if(check_security(tmp[1], id)) continue; - #endif -  if(s=function_object(tmp[1])->access(file[strlen(loc)..], id)) -  return s; -  } -  } - } -  - // Return the _real_ filename of a virtual file, if any. -  - public string real_file(string file, object id) - { -  string loc; -  string s; -  array tmp; -  file=replace(file, "//", "/"); // "//" is really "/" here... -  -  if(!id) error("No id passed to real_file"); -  -  // Map location-modules. -  current_configuration = id->conf; -  -  foreach(current_configuration->location_modules(), tmp) -  { -  loc = tmp[0]; -  if(!search(file, loc)) -  { - #ifdef MODULE_LEVEL_SECURITY -  if(check_security(tmp[1], id)) continue; - #endif -  if(s=function_object(tmp[1])->real_file(file[strlen(loc)..1000000], id)) -  return s; -  } -  } - } -  - // Convenience functions used in quite a lot of modules. Tries to - // read a file into memory, and then returns the resulting string. -  - // NOTE: A 'file' can be a cgi script, which will be executed, resulting in - // a horrible delay. -  - public mixed try_get_file(string s, object id, int|void status, -  int|void nocache) - { -  string res, q; -  object fake_id; -  mapping m; -  -  -  if(objectp(id)) -  fake_id = id->clone_me(); -  else -  error("No ID passed to 'try_get_file'\n"); -  -  if(!id->pragma["no-cache"] ) -  if(res = cache_lookup("file:"+id->conf->name, s)) -  return res; -  -  current_configuration = id->conf; -  -  if(sscanf(s, "%s?%s", s, q)) -  { -  string v, name, value; -  foreach(q/"&", v) -  if(sscanf(q, "%s=%s", name, value)) -  fake_id->variables[http_decode_string(name)]=value; -  fake_id->query=q; -  } -  -  fake_id->raw_url=s; -  fake_id->not_query=s; -  fake_id->misc->internal_get=1; -  -  if(!(m = get_file(fake_id))) -  { -  fake_id->end(); -  return 0; -  } -  fake_id->end(); -  -  if(status) return 1; -  - #ifdef COMPAT -  if(m["string"]) res = m["string"]; // Compability.. - #endif -  else if(m->data) res = m->data; -  else res=""; -  m->data = 0; -  -  if(m->file) -  { -  res += m->file->read(200000); -  destruct(m->file); -  m->file = 0; -  } -  -  if(m->raw) -  { -  res -= "\r"; -  if(!sscanf(res, "%*s\n\n%s", res)) -  sscanf(res, "%*s\n%s", res); -  } -  cache_set("file:"+id->conf->name, s, res); -  return res; - } -  - // Is 'what' a file in our virtual filesystem? - public int is_file(string what, object id) - { -  return !!stat_file(what, id); - } -  +    // Called from the configuration interface.   string check_variable(string name, string value)   {
1456: Inside #if defined(MODULE_LEVEL_SECURITY)
   }    break;    -  case "copies": -  if((int)value < 1) -  return "You must have at least one copy of roxen running"; -  break; -  +     case "ConfigurationURL":    case "MyWorldLocation":    if(strlen(value)<7 || value[-1] != '/' ||
1488: Inside #if defined(MODULE_LEVEL_SECURITY)
   return ([ "data":read_bytes("etc/restart.html"), "type":"text/html" ]);   }    + private array configuration_ports = ({ }); + int startpid;    -  +  +    // 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. -  - private array configuration_ports = ({ }); - int startpid; -  +    mapping shutdown()   {    catch(map_array(indices(portno)), destruct);
1511: Inside #if defined(MODULE_LEVEL_SECURITY)
   int pid;    catch(map_array(configuration_ports, destruct));    -  if(search(subs, getpid()) == -1) -  { +     perror("Shutting down Roxen.\n"); -  catch(map_array(subs, kill, signum("SIGUSR1"))); -  catch(map_array(subs, kill, signum("SIGKILL"))); -  +     // 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);
1535: Inside #if defined(MODULE_LEVEL_SECURITY)
  // kill(startpid, signum("SIGKILL"));    }    } -  } +        call_out(exit, 1, 0);    return ([ "data":replace(read_bytes("etc/shutdown.html"), "$PWD", getcwd()),
1556: Inside #if defined(MODULE_LEVEL_SECURITY)
     object load(string s) // Should perhaps be renamed to 'reload'.   { - #if defined(MODULE_DEBUG) && (DEBUG_LEVEL>20) -  perror(s+" "); - #endif +     if(file_size(s+".pike")>0)    if(__p=compile_file(s+".pike"))    {
1613: Inside #if defined(MODULE_LEVEL_SECURITY)
   return 0;   }    - // Some logging stuff, should probably move to either the actual - // configuration object, or into a module. That would be much more - // beautiful, really. - void init_log_file(object conf) - { -  int possfd; -  object lf, oc; -  -  if(!conf) return; -  -  remove_call_out(init_log_file); -  -  oc = current_configuration; -  current_configuration = conf; -  -  if(current_configuration->log_function) -  { -  destruct(function_object(current_configuration->log_function)); -  // Free the old one. -  } -  -  if(query("Log")) // Only try to open the log file if logging is enabled!! -  { -  if(query("LogFile") == "stdout") -  { -  current_configuration->log_function=stdout->write; -  possfd=-1; -  } else if(query("LogFile") == "stderr") { -  current_configuration->log_function=stderr->write; -  } else { -  if(strlen(query("LogFile"))) -  { -  int opened; -  lf=File(); -  opened=lf->open( query("LogFile"), "wac"); -  if(!opened) -  mkdirhier(query("LogFile")); -  if(!opened && !(lf->open( query("LogFile"), "wac"))) -  { -  destruct(lf); -  report_error("Failed to open logfile. ("+query("LogFile")+")\n" + -  "No logging will take place!\n"); -  current_configuration->log_function=0; -  } else { -  mark_fd(lf->query_fd(), "Roxen log file ("+query("LogFile")+")"); -  current_configuration->log_function=lf->write; -  // Function pointer, speeds everything up (a little..). -  possfd=lf->query_fd(); -  lf=0; -  } -  } else -  current_configuration->log_function=0; -  } -  call_out(init_log_file, 60, current_configuration); -  } else -  current_configuration->log_function=0; -  current_configuration = oc; - } -  - void do_dest(object|void o); -  -  - // This code should probably be moved to the configuration - // object. That would free roxen from some of the most ugly hacks (the - // query() function, and the current_configuration global variable (I - // do not like that one, but when I started with Spider, I only - // allowed one configuration, so to have the 'start' function with - // friends in the spider seemed like a good idea. Then the need for more - // than one configuration (known externaly as a virtual server) - // manifested itself. I did a quick hack, and this is the result. -  - void start(int num) - { -  array port; -  int possfd; -  int err=0; -  object lf; -  mapping new=([]), o2; -  -  if(!sscanf(QUERY(cachedir), "%*s/roxen_cache")) -  set("cachedir", QUERY(cachedir)+"roxen_cache/"); -  -  parse_log_formats(); -  -  init_log_file(current_configuration); -  -  map_array(indices(current_configuration->open_ports), do_dest); -  -  catch { -  foreach(query("Ports"), port ) -  { -  array tmp; -  function rp; -  array old = port; -  object o; -  -  if(rp = ((object)("protocols/"+port[1]))->real_port) -  if(tmp = rp(port)) -  port = tmp; -  object privs; -  if(port[0] < 1024) -  privs = ((program)"privs")("Opening listen port below 1024"); -  if(!(o=create_listen_socket(port[0], current_configuration, port[2], -  (program)("protocols/"+port[1])))) -  { -  perror("I failed to open the port "+old[0]+" at "+old[2] -  +" ("+old[1]+")\n"); -  err++; -  } else -  current_configuration->open_ports[o]=old; -  } -  }; -  -  if(!num && sizeof(query("Ports"))) -  { -  if(err == sizeof(query("Ports"))) -  { -  report_error("No ports available for "+current_configuration->name+"\n" -  "Tried:\n" -  "Port Protocol IP-Number \n" -  "---------------------------\n" -  + map_array(query("Ports"), lambda(array p) { -  return sprintf("%5d %-10s %-20s\n", @p); -  })*""); -  } -  } - } -  -  +    void create()   {    add_efun("roxen", this_object());    add_efun("spinner", this_object());    add_efun("load", load);    (object)"color"; -  +  (object)"base_server/fonts"; +  Configuration = (program)"configuration";   }    -  - // 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))) -  { - #if efun(gethostbyname) && efun(gethostname) -  f = gethostbyname(gethostname()); // First try.. -  if(f) -  foreach(f, f) foreach(f, t) if(search(t, ".") != -1 && !(int)t) -  if(!s || strlen(s) < strlen(t)) -  s=t; - #endif -  if(!s) -  { -  t = read_bytes("/etc/resolv.conf"); -  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; -  s = (gethostname()/".")[0] + "." + query("Domain"); -  s -= "\n"; -  return "http://" + s + "/"; - } -  +    // 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
1848:    }   }    - private program Configuration = (program)"configuration"; -  -  +    static mapping __vars = ([ ]);      // These two should be documented somewhere. They are to be used to
1882:    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; -  } + // 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; + // }       case 1:    case 2:
1925:    report_debug(report);   }    - // This is used to update the server-global and module variables - // between Roxen releases. It enables the poor roxen administrator to - // reuse the configuration file from a previous release. without any - // fuss. Configuration files from Roxen 1.0ß11 pre 11 and earlier - // are not differentiated, but since that release is quite old already - // when I write this, that is not really a problem.... -  -  - private void update_vars(int from) + object enable_configuration(string name)   { -  string report = ""; -  int i; -  string modname; -  mapping redir; -  mapping enabled_modules = retrieve("EnabledModules"); -  array p, res=({}); -  -  perr("Updating configuration file....\n"); -  perr("----------------------------------------------------\n"); -  switch(from) -  { -  case 0: -  -  // Pre b11p11 -  // Ports changed from int, int, int ... to -  // ({ int, "http", query("PEther") }) -  // -  -  if(sizeof(retrieve("spider#0"))) -  { -  p = query("Ports"); -  foreach(p, p) -  if(intp(p)) -  res += ({ ({ p, "http", query("PEther") }) }); -  -  perr("Updating ports variable.\n"); -  set("PEther", 0); -  set("Ports", res); -  } else { -  perr("Ports variable already fixed.\n"); +  object cf = Configuration(name); +  configurations += ({ cf }); +  return cf;   }    -  // Now comes the tricky part.. -  // Fix all thoose redirection modules. -  res = ({}); -  while(sizeof(redir = retrieve(modname = "redirect#"+i++))) -  { -  string from, to; -  if(redir->fileredirect) -  { -  res += ({ "\n\n" +redir->fileredirect }); -  remove( modname ); -  if(enabled_modules[modname] ) -  m_delete( enabled_modules, modname ); -  continue; -  } -  // from -> to -  remove( modname ); -  if(enabled_modules[modname] ) -  m_delete( enabled_modules, modname ); -  from = redir->from; -  to = redir->redirect; -  if(redir->internal) -  res += ({ from + " " + to }); -  else -  res += ({ from + " " + "%u" + to }); -  perr("Fixing redirect from " + from + " to "+to+"\n"); -  } -  -  if(sizeof(res)) // Hepp hopp -  { -  enabled_modules["redirect#0"] = 1; -  store("redirect#0", -  ([ -  "fileredirect":"# Automatically converted patterns...\n\n" -  + res*"\n" -  ]), 1); -  } -  -  // And now the etc/extentions bug... -  redir = retrieve("contenttypes#0"); -  -  if(!sizeof(redir)) -  enabled_modules["contenttypes#0"] = 1; -  else -  { -  redir->exts = replace(redir->exts, "etc/extentions", "etc/extensions"); -  store("contenttypes#0", redir, 1); -  perr("Fixing spelling error in contenttypes configuration.\n"); -  } -  -  // Is there a directory parser in there somewhere? -  -  perror("Making a list of all wanted index files...\n"); -  -  i=0; -  res=({ }); -  while(sizeof(redir = retrieve(modname = "userfs#"+i++))) -  { -  if(redir->indexfiles) -  { -  res |= redir->indexfiles; - #if 0 -  if(!redir->indexoverride) -  { -  perr("WARNING: The user filesystem mounted on " -  + redir->mountpoint +"\n" -  " had the indexfile override flag set to false.\n" -  " This variable no longer exists. Create a file named" -  " .nodiraccess\n" -  " in the directory to disable directory listings.\n"); -  } - #endif -  redir[".files"] = !redir[".files"]; -  store("userfs#"+(i-1), redir, 1); - #ifdef SUPPORT_HTACCESS -  if(redir[".htaccess"]) -  { -  if(!query("htaccess")) -  { -  perr("A filesystem used .htaccess parsing.\n" -  "This variable is now server global.\n" -  "This variable has now been set to 'Yes'\n"); -  set("htaccess", 1); -  } -  } - #endif -  } -  } -  i=0; -  while(sizeof(redir = retrieve(modname = "secure_fs#"+i++))) -  { -  if(redir->indexfiles) -  { -  res |= redir->indexfiles; - #if 0 -  if(!redir->indexoverride) -  { -  perr("WARNING: The secure filesystem mounted on " -  + redir->mountpoint +"\n" -  " had the indexfile override flag set to false.\n" -  " This variable no longer exists. Create a file named" -  " .nodiraccess\n" -  " in the directory to disable directory listings.\n"); -  } - #endif -  redir[".files"] = !redir[".files"]; -  store("secure_fs#"+(i-1), redir, 1); - #ifdef SUPPORT_HTACCESS -  if(redir[".htaccess"]) -  { -  if(!query("htaccess")) -  { -  perr("A secure filesystem used .htaccess parsing.\n" -  "This variable is now server global.\n" -  "This variable has now been set to 'Yes'\n"); -  set("htaccess", 1); -  } -  } - #endif -  } -  } -  i=0; -  while(sizeof(redir = retrieve(modname = "filesystem#"+i++))) -  { -  if(redir->indexfiles) -  { -  res |= redir->indexfiles; - #if 0 -  if(!redir->indexoverride) -  { -  perror("WARNING: The filesystem mounted on " -  + redir->mountpoint +"\n" -  " had the indexfile override flag set to false.\n" -  " This variable no longer exists. Create a file named" -  " .nodiraccess\n" -  " in the directory to disable directory listings.\n"); -  } - #endif -  redir[".files"] = !redir[".files"]; -  store("filesystem#"+(i-1), redir, 1); - #ifdef SUPPORT_HTACCESS -  if(redir[".htaccess"]) -  { -  if(!query("htaccess")) -  { -  perr("A user filesystem used .htaccess parsing.\n" -  "This variable is now server global.\n" -  "It has been set to 'Yes'\n"); -  set("htaccess", 1); -  } -  } - #endif -  } -  } -  perr("-> "+implode_nicely(res)+"\n"); -  -  for(i=0; i<10; i++) -  { -  remove("status#"+i); -  m_delete(enabled_modules, "status#"+i); -  } -  -  if(!sizeof(retrieve("directories#0")) -  && (sizeof(redir = retrieve("fastdir#0")))) -  { -  redir->indexfiles = res; -  store("fastdir#0", redir, 1); -  perr("Updated fast directory parser to include new list.\n"); -  } else { -  if(!(sizeof(redir = retrieve("directories#0")))) -  { -  enabled_modules["directories#0"] = 1; -  perr("Enabled a directory parsing module.\n"); -  redir = ([ ]); -  } -  redir->indexfiles = res; -  store("directories#0", redir, 1); -  perr("Updated directory parser to include new list.\n"); -  } -  perr("Saving new module list.\n"); -  store( "EnabledModules", enabled_modules, 1 ); -  -  case 1: -  case 2: -  perr("The 'No directory lists' variable is yet again available.\n"); -  case 3: -  // The htaccess support moved to a module. -  if(query(".htaccess")) -  { -  perr("The 'HTACCESS' support has been moved to a module.\n"); -  enable_module("htaccess#0"); -  } -  case 4: -  case 5: -  -  while(sizeof(redir = retrieve(modname = "lpcscript#"+i))) -  { -  remove( modname ); -  if(search(redir->exts, "pike") == -1) -  redir->exts += ({"pike"}); -  if(enabled_modules[modname] ) -  m_delete( enabled_modules, modname ); -  store("pikescript#"+i, redir, 1); -  enable_module("pikescript#"+i); -  perr("Renaming "+modname+" to pikescript#"+i+"\n"); -  i++; -  } -  store( "EnabledModules", enabled_modules, 1 ); -  -  case 6:// Current level. -  } -  -  perr("----------------------------------------------------\n"); -  report_debug(report); - } -  -  -  -  - // Used to hide some variables when logging is not enabled. -  - int log_is_not_enabled() - { -  return !query("Log"); - } -  -  - // This function should be moved into the configuration object, since - // that would really make the object-hierarchy much clearer. Then it - // would be possible to do - // clone(configp)->enable(name_of_configuration); instead of - // (config=clone(configp))->name=name_of_configuration; - // enable_configuration(config);, it would also remove the need for - // the global variable 'current_configuration', and also speed up some - // of the functions below. -  - object enable_configuration(string config) - { -  array modules_to_process; -  string tmp_string; -  -  perror("Enabling virtual server '"+config+"'\n"); -  -  current_configuration = Configuration(config); -  configurations += ({ current_configuration }); -  -  -  definvisvar("htaccess", 0, TYPE_FLAG); // COMPAT ONLY - #ifdef COMPAT - // b10 and below, compatibility removed. -  definvisvar("PEther", "ANY", TYPE_STRING); - #endif -  -  -  -  defvar("ZNoSuchFile", "<title>Sorry. I cannot find this resource</title>" -  "\n<h2 align=center><configimage src=roxen.gif alt=\"File not found\">\n" -  "<p><hr noshade>" -  "\n<i>Sorry</i></h2>\n" -  "<br clear>\n<font size=+2>The resource requested " -  "<i>$File</i>\ncannot be found.<p>\n\nIf you feel that this is a " -  "configuration error, please contact " -  "the administrators or the author of the <if referer>" -  "<a href=<referer>>referring</a> </if> <else>referring</else> page." -  "<p>\n</font>\n" -  "<hr noshade>" -  "<version>, at <a href=$Me>$Me</a>.\n", -  -  "Messages: No such file", TYPE_TEXT_FIELD, -  "What to return when there is no resource or file available " -  "at a certain location. $File will be replaced with the name " -  "of the resource requested, and $Me with the URL of this server "); -  -  -  defvar("comment", "", "Configuration interface comment", -  TYPE_TEXT_FIELD, -  "This text will be visible in the configuration interface, it " -  " can be quite useful to use as a memory helper."); -  -  defvar("name", "", "Configuration interface name", -  TYPE_STRING, -  "This is the name that will be used in the configuration " -  "interface. If this is left empty, the actual name of the " -  "virtual server will be used"); -  -  defvar("LogFormat", -  "404: $host $referer - [$cern_date] \"$method $resource $protocol\" 404 -\n" -  "500: $host ERROR - [$cern_date] \"$method $resource $protocol\" 500 -\n" -  "*: $host - - [$cern_date] \"$method $resource $protocol\" $response $length" -  , -  -  "Logging: Format", -  TYPE_TEXT_FIELD, -  -  "What format to use for logging. The syntax is:\n" -  "<pre>" -  "response-code or *: Log format for that response acode\n\n" -  "Log format is normal characters, or one or more of the " -  "variables below:\n" -  "\n" -  "\\n \\t \\r -- As in C, newline, tab and linefeed\n" -  "$char(int) -- Insert the (1 byte) character specified by the integer.\n" -  "$wchar(int) -- Insert the (2 byte) word specified by the integer.\n" -  "$int(int) -- Insert the (4 byte) word specified by the integer.\n" -  "$^ -- Supress newline at the end of the logentry\n" -  "$host -- The remote host name, or ip number.\n" -  "$ip_number -- The remote ip number.\n" -  "$bin-ip_number -- The remote host id as a binary integer number.\n" -  "\n" -  "$cern_date -- Cern Common Log file format date.\n" -  "$bin-date -- Time, but as an 32 bit iteger in network byteorder\n" -  "\n" -  "$method -- Request method\n" -  "$resource -- Resource identifier\n" -  "$protocol -- The protocol used (normally HTTP/1.0)\n" -  "$response -- The response code sent\n" -  "$bin-response -- The response code sent as a binary short number\n" -  "$length -- The length of the data section of the reply\n" -  "$bin-length -- Same, but as an 32 bit iteger in network byteorder\n" -  "$referer -- the header 'referer' from the request, or '-'.\n" -  "$user_agent -- the header 'User-Agent' from the request, or '-'.\n\n" -  "$user -- the name of the auth user used, if any\n" -  "$user_id -- A unique user ID, if cookies are supported,\n" -  " by the client, otherwise '0'\n" -  "</pre>", 0, log_is_not_enabled); -  -  defvar("Log", 1, "Logging: Enabled", TYPE_FLAG, "Log requests"); -  -  defvar("LogFile", QUERY(logdirprefix)+ -  short_name(current_configuration->name)+"/Log", -  -  "Logging: Log file", TYPE_FILE, "The log file. " -  "stdout for standard output, or stderr for standard error, or "+ -  "a file name. May be relative to "+getcwd()+".",0, log_is_not_enabled); -  -  defvar("NoLog", ({ }), -  -  "Logging: No Logging for", TYPE_STRING_LIST, -  "Don't log requests from hosts with an IP number which matches any " -  "of the patterns in this list. This also affects the access counter " -  "log.\n",0, log_is_not_enabled); -  -  defvar("Domain", get_domain(), -  -  "Domain", TYPE_STRING, -  "Your domainname, should be set automatically, if not, " -  "enter the real domain name here, and send a bug report to " -  "<a href=mailto:roxen-bugs@infovav.se>roxen-bugs@infovav.se" -  "</a>"); -  -  -  defvar("Ports", ({ }), -  "Listen ports", TYPE_PORTS, -  "The ports this virtual instance of Roxen will bind to.\n"); -  -  defvar("MyWorldLocation", get_my_url(), -  "Server URL", TYPE_STRING, -  "This is where your start page is located."); -  -  - // This should be somewhere else, I think. Same goes for HTTP related ones -  -  defvar("FTPWelcome", -  " +-------------------------------------------------\n" -  " +-- Welcome to the Roxen Challenger FTP server ---\n" -  " +-------------------------------------------------\n", -  "Messages: FTP Welcome", -  TYPE_TEXT_FIELD, -  "FTP Welcome answer; transmitted to new FTP connections if the file " -  "<i>/welcome.msg</i> doesn't exist.\n"); -  -  -  defvar("_v", CONFIGURATION_FILE_LEVEL, 0, TYPE_INT, 0, 0, 1); -  setvars(retrieve("spider#0")); -  -  if((sizeof(retrieve("spider#0")) && -  (!retrieve("spider#0")->_v) -  || (query("_v") < CONFIGURATION_FILE_LEVEL))) -  { -  update_vars(retrieve("spider#0")->_v?query("_v"):0); -  killvar("PEther"); // From Spinner 1.0b11 -  current_configuration->variables->_v[VAR_VALUE] = CONFIGURATION_FILE_LEVEL; -  store("spider#0", current_configuration->variables, 0); -  } -  -  set("_v", CONFIGURATION_FILE_LEVEL); -  -  modules_to_process = sort_array(indices(retrieve("EnabledModules"))); -  -  // Always enable the user database module first. -  if(search(modules_to_process, "userdb#0")>-1) -  modules_to_process = (({"userdb#0"}) -  + (modules_to_process-({"userdb#0"}))); -  -  -  array err; -  foreach( modules_to_process, tmp_string ) -  if(err = catch( enable_module( tmp_string ) )) -  perror("Failed to enable the module "+tmp_string+". Skipping\n" - #ifdef MODULE_DEBUG -  +describe_backtrace(err)+"\n" - #endif -  ); -  return current_configuration; - } -  +    // Enable all configurations   static private void enable_configurations()   { -  array configs; -  string config; +  array err;       enabling_configurations = 1;    catch { -  configs=list_all_configurations(); // From read_config.pike +     configurations = ({});    -  foreach(configs, config) +  foreach(list_all_configurations(), string config)    { -  enable_configuration(config); -  start(0); +  if(err=catch { +  enable_configuration(config)->start(); +  }) +  perror("Error while enabling configuration "+config+":\n"+ +  describe_backtrace(err)+"\n");    }    };    enabling_configurations = 0;
2433: Inside #if efun(gethostname)
  #if efun(gethostname)    host = gethostname();   #else - #if 0 -  if(configuration_interface()-> - #endif +     host = "127.0.0.1";   #endif    }
2453:      int cache_disabled_p() { return !QUERY(cache); }   int syslog_disabled() { return QUERY(LogA)!="syslog"; } - int copies_is_one() { return QUERY(copies)==1; } - int copies_is_not_one(){ return QUERY(copies)!=1; } +          private void define_global_variables( int argc, array (string) argv )   {    int p; -  current_configuration=0; -  -  +     // Hidden variables (compatibility ones, or internal or too    // dangerous (in the case of chroot, the variable is totally    // removed.    - #if 0 -  globvar("chroot", "", "Server root dir", TYPE_DIR, -  "If you don't know what chroot() will do, this is not the variable " -  "for you. If set, it be the new root directory for roxen.",1); - #endif -  +     globvar("set_cookie", 1, "Set unique user id cookies", TYPE_FLAG,    "If set, all users of your server whose clients supports "    "cookies will get a unique 'user-id-cookie', this can then be "
2601:    "requests to server 1, and one to server 2, and this variable is "    "set to 256, the 256 accesses to the first server might very well be"    " handled before the one to the second server.)", -  ({ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }), -  copies_is_not_one); +  ({ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }));          globvar("ConfigPorts", ({ ({ 22202, "http", "ANY", "" }) }),
2678:    ". See the file etc/supports for examples.");       -  globvar("IfModified", 1, "Honor If-Modified-Since headers", TYPE_FLAG, -  "If set, send a 'Not modified' response in reply to " -  "if-modified-since headers, as " -  "<a href=http://www.w3.org/pub/WWW/Protocols/HTTP/1.1/spec" -  "#If-Modified-Since>specified by the HTTP draft.</a>"); + // globvar("IfModified", 1, "Honor If-Modified-Since headers", TYPE_FLAG, + // "If set, send a 'Not modified' response in reply to " + // "if-modified-since headers, as " + // "<a href=http://www.w3.org/pub/WWW/Protocols/HTTP/1.1/spec" + // "#If-Modified-Since>specified by the HTTP draft.</a>");       globvar("audit", 0, "Audit trail", TYPE_FLAG,    "If set, log all changes of uid in the debug log.");
2725:    ". This will be appended to all logs.", 0, syslog_disabled);   #endif    -  globvar("copies", 1, "Number of copies to run", TYPE_INT, -  "The number of forked copies of roxen to run simultaneously.\n" + #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 " +  " be able to serve multiple requests, using a select loop bases " +  " system.\n"    "<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>"); -  + #endif       globvar("AutoUpdate", 1, "Update the supports database automatically",    TYPE_FLAG,
2739:       globvar("next_supports_update", time()+3600, "", TYPE_INT,"",0,1);    +  setvars(retrieve("Variables", 0));    -  globvar("uselock", - #if efun(uname) -  ((uname()->sysname=="SunOS")&&((int)uname()->release==5)) -  ||((uname()->sysname=="IRIX")&&((int)uname()->release==5)), - #else -  0, - #endif -  "Use a mutex-lock or semaphore to serialize accept calls", -  TYPE_FLAG, -  "This is needed on SunOS 5.* (Solaris 2.*) and IRIX 5.*. " -  "It might be needed" -  " under other OS-es as well. It will slow down the accept call" -  " by a percent or two, but without it, the call might hang forever." -  "It is only used if the number of copies to run is set to more " -  "than one.", 0, copies_is_one); -  -  setvars(retrieve("Variables")); -  -  if(sizeof(retrieve("Variables")) && -  (!retrieve("Variables")->_v || +  if(sizeof(retrieve("Variables", 0)) && +  (!retrieve("Variables", 0)->_v ||    (QUERY(_v) < CONFIGURATION_FILE_LEVEL)))    { -  update_global_vars(retrieve("Variables")->_v?QUERY(_v):0); +  update_global_vars(retrieve("Variables", 0)->_v?QUERY(_v):0);    QUERY(_v) = CONFIGURATION_FILE_LEVEL; -  store("Variables", variables, 0); +  store("Variables", variables, 0, 0);    }    set("_v", CONFIGURATION_FILE_LEVEL);   
2780:    docurl=QUERY(docurl);   }    +  +  + // 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); +  }; + } +  +    // 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.
2830:    }   }    - #ifdef DUMPVARS +     - // All code here is used to dump a list of all variables in all - // modules, and the global and configuration global ones, to HTML - // files. If the define DUMPVARS is present, this is the only thing - // Roxen will do at all. -  - private void dump_variables(string file, mapping variables, array info, -  string creator, string url) - { -  object f; -  mkdir("vardump"); -  rm ("vardump/" + replace(file, "/", "\\")+".html"); -  f = open("vardump/" + replace(file, "/", "\\")+".html", "wc"); -  if(!f) return; -  -  f->write("<head>\n<title>Roxen: "+info[1]+"</title>\n" -  "</head>\n" -  "<body bgcolor=#c0c0c0 text=#000000 link=#005000 vlink=#500000>\n" -  "" -  "<hr noshade>\n"); -  -  f->write("<h1>"+info[1]+"</h1>\n\n"); -  f->write("<font size=+1>"+info[2]+"</font><p>"); -  -  f->write(describe_module_type(info[0])+"<p>"); -  -  f->write("<font color=black>File:</font> <i>"+file+"</i><br>\n"); -  -  if(creator) -  f->write("<font color=black>Creator:</font> <i>"+creator+"</i><br>\n"); -  -  if(url) -  f->write("<font color=black>Home URL:</font> <i><a href="+url+">"+url+"</a></i><br>\n"); -  -  f->write("<hr noshade>\n\n"); -  f->write("<h2>Variables</h2>"); -  f->write("<dl>\n"); -  -  array v; -  string n; -  foreach(sort_array(indices(variables)), n) -  { -  v = variables[n]; -  if(v[VAR_CONFIGURABLE]) -  { -  f->write("<dt><b>"+v[VAR_NAME]+"</b>\n" -  +(strlen(describe_variable_as_text(v,1))? -  "<i><br>Default value:</i>\n" -  "<font color=red>"+describe_variable_as_text(v,1) -  +"</font><br>" -  :"") -  +"<dd>" + v[VAR_DOC_STR] + "<br>" -  "<i><font color=black>"+describe_type(v[VAR_TYPE], v[VAR_MISC]) -  + "</font></i><p>\n\n\n"); -  } -  } -  f->write("</dl>\n"); -  destruct(f); - } - #endif -  -  +    // 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)
2923:    perror(" load ok - ");   #endif    foo = o->register_module(); - #ifdef DUMPVARS -  dump_variables(file, o->variables, foo, o->module_creator, o->module_url); - #endif +    #ifdef MODULE_DEBUG    perror("registered.");   #endif -  return ({ foo[1], foo[2]+"<p><i>"+replace(o->file_name_and_stuff(), -  "0<br>", file+"<br>") +  return ({ foo[1], foo[2]+"<p><i>"+ +  replace(o->file_name_and_stuff(), "0<br>", file+"<br>")    +"</i>", foo[0] }); -  +     }(path + file))))    { - #ifdef MODULE_DEBUG - // perror("MODULES: "+module_info[0]+ "\n"+module_info[1]+"\n"); - #endif +     allmodules[ file-("."+extension(file)) ] = module_info;    } else { - #ifdef MODULE_DEBUG -  perror("\n"+err[0]+_master->set_inhibit_compile_errors( 1 )); - #endif +  perror(file+": "+describe_backtrace(err)+ +  _master->set_inhibit_compile_errors( 1 ));    }   #ifdef MODULE_DEBUG    perror("\n");
3082:    perror("I cannot create the pid file ("+where+").\n");   }    - #if efun(send_fd) +    // 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. -  + int _shuffle(object from, object to) + { +  if(shuffle_fd) +  if(send_fd(shuffle_fd,from->query_fd())&& +  send_fd(shuffle_fd,to->query_fd())) +  { + #if 0 +  destruct(from); +  destruct(to); + #endif +  return 1; +  } + #if efun(Pipe) +  object p = Pipe(); +  p->input(from); +  p->output(to); +  return 1; + #else +  // Fallback. Very unlikely. +  from->set_id(to->write); +  from->set_nonblocking(lambda(function w,string s){w(s);},lambda(){}, +  lambda(function w){destruct(function_object(w));}); + #endif +  return 1; + } +  + #ifdef THREADS + object shuffle_queue = Queue(); +  + void shuffle_thread() + { +  while(mixed s=shuffle_queue->read()) +  _shuffle(@s); + } + void shuffle(object a, object b) + { +  shuffle_queue->write(({a,b})); + } + #else + function shuffle = _shuffle; + #endif +  +    object shuffler;   void init_shuffler()   {    object out;    object out2; -  + #ifdef THREADS +  thread_create(shuffle_thread); + #endif +     if(file_size("bin/shuffle") > 100)    {    if(shuffler)
3108:   }   #endif    -  - // 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. The functions below this one are here for - // process and signal magic, it is basically the support needed - // to use multi-processing with a central process as master and - // control of the other processes. - void do_dest(object|void o) - { -  if(objectp(o)) -  catch -  { -  destruct(o); -  }; - } -  -  +    static private int _recurse;      void exit_when_done()
3137:    {    werror("Exiting roxen (spurious signals received).\n");    stop_all_modules(); +  kill(getpid(), 9); +  kill(0, -9);    exit(0);    } -  if(main_configuration_port && objectp(main_configuration_port)) -  { -  int pid; -  if(search(subs, getpid()) == -1) -  { -  // perror("Exiting.\n"); -  foreach(subs, pid) -  { -  if(pid != getpid()) -  kill(pid, signum("SIGUSR1")); -  } -  } -  } +     // First kill off all listening sockets..    foreach(indices(portno)||({}), o)    do_dest(o);
3164: Inside #if efun(_pipe_debug)
   {    werror("Exiting roxen (all connections closed).\n");    stop_all_modules(); +  kill(getpid(), 9); +  kill(0, -9); +  perror("Odd. I am not dead yet.\n");    exit(0);    }    }, 0.1);
3171:    call_out(lambda(){    werror("Exiting roxen (timeout).\n");    stop_all_modules(); +  kill(getpid(), 9); +  kill(0, -9);    exit(0);    }, 600, 0); // Slow buggers..   }
3178:   void exit_it()   {    perror("Recursive signals.\n"); +  kill(getpid(), 9); +  kill(0, -9);    exit(0);   }    - array fork_it(); -  - array do_fork_it() + void fork_it()   { -  catch(map_array(configuration_ports, destruct)); -  if(objectp(main_configuration_port)) -  destruct(main_configuration_port); -  main_configuration_port = 0; -  signal(signum("SIGUSR1"), exit_it); -  signal(signum("SIGUSR2"), exit_it); -  signal(signum("SIGHUP"), exit_it); -  signal(signum("SIGINT"), exit_it); -  return fork_it(); - } -  - array fork_it() - { -  int howmany; -  howmany = QUERY(copies)-1; -  -  if(subs) -  { -  int pid; -  perror("Reaping old sub-processes.\n"); -  foreach(subs, pid) -  kill(pid, signum("SIGUSR1")); -  } -  subs = ({ }); -  -  if(howmany) -  perror("Forking new sub-processes ("+howmany+").\n"); -  -  while(howmany--) -  { -  int pid; -  if(pid = fork()) -  { -  subs += ({ pid }); -  } else { -  trace(0); // Debug.. -  signal(signum("SIGUSR1"), exit_when_done); -  signal(signum("SIGHUP"), exit_when_done); -  signal(signum("SIGINT"), exit_when_done); - #if efun(send_fd) -  init_shuffler(); // No locking here. Each process need one on it's own. + #ifdef THREADS +  start_handler_threads();   #endif -  create_host_name_lookup_processes(); -  main_configuration_port = 0; -  catch { -  if(root) -  root->dest(); // This saves quite a lot of memory.. -  }; -  subs = ({}); -  return 0; -  } -  } +    #if efun(send_fd)    init_shuffler(); // No locking here.. Each process need one on it's own.   #endif    create_host_name_lookup_processes(); -  signal(signum("SIGUSR1"), do_fork_it); +  signal(signum("SIGUSR1"), exit_when_done);    signal(signum("SIGUSR2"), exit_when_done);    signal(signum("SIGHUP"), exit_when_done);    signal(signum("SIGINT"), exit_when_done); -  return subs; +    }       -  +    // 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.   
3258:       start_time=time(1);    -  stdin->close(); -  stdout->close(); -  destruct(stdout); -  destruct(stdin); -  +     add_efun("write", perror);      
3270:    mark_fd(1, "Stdout");    mark_fd(2, "Stderr");    - #ifndef DUMPVARS -  configuration_dir = find_arg(argv, "d", ({ "config-dir", -  "configurations", -  "configuration-directory" }), +  configuration_dir = find_arg(argv, "d",({"config-dir","configuration-directory" }),    ({ "ROXEN_CONFIGDIR", "CONFIGURATIONS" }),    "../configurations");   
3286: Inside #if undefined(DUMPVARS)
      create_pid_file(find_arg(argv, "p", "pid-file", "ROXEN_PID_FILE"));    -  if(tmp = find_arg(argv, "r", "root")) -  fix_root(tmp); +  // Dangerous... +  if(tmp = find_arg(argv, "r", "root")) fix_root(tmp);       argv -= ({ 0 });    argc=sizeof(argv);       perror("Restart initiated at "+ctime(time()));    - #endif -  +     define_global_variables(argc, argv);    - #ifdef DUMPVARS -  perror("Dumping module variables...\n"); -  dump_variables("Global", variables, ({ 0, "Global variables", -  "The variables below are all" -  " global, and affect all virtual" -  " servers.", 0, 0 }), 0, 0); -  configurations = ({ }); -  -  variables->ModuleDirs[VAR_VALUE] |= ({ "localmodules/" }); -  -  enable_configuration("&lt;servername&gt;"); -  dump_variables("Configuration", current_configuration->variables, -  ({ 0, "Configuration global variables", -  "The variables below are global to a specific " -  "configuration, and affect all modules in it.\n", 0, 0}), -  0, 0); -  -  rescan_modules(); -  exit(0); - #endif -  +     create_pid_file(QUERY(pidfile));       restore_current_user_id_number();
3339:   // well.. :-)       if(root) +  { +  destruct(configuration_interface());    configuration_interface()->build_root(root); -  +  }       call_out(update_supports_from_roxen_com,    QUERY(next_supports_update)-time());
3347:    if(set_u_and_gid())    perror("Setting UID and GID ...\n");    -  if(fork_it()) -  { -  int pid; +  fork_it(); +     initiate_configuration_port( 1 );    perror("Time to boot: "+(time()-start_time)+" seconds.\n");    perror("-------------------------------------\n\n"); -  } +    // start_time=time(); // Used by the "uptime" info later on.    return -1;   }