1997-01-29
1997-01-29 04:59:46 by Per Hedbor <ph@opera.com>
-
14179b4559576ec042ff13d66383d6902b37d1b0
(2303 lines)
(+442/-1861)
[
Show
| Annotate
]
Branch: 5.2
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("<servername>");
- 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;
}