commit 85ade5f61924adf0296eac7eeb06405f6d28d6bd parent 6c73980ed2056b919b7af2fd15c2716199995268 Author: finwo <finwo@pm.me> Date: Sat, 3 Jan 2026 19:37:34 +0100 2.50 Diffstat:
27 files changed, 128 insertions(+), 81 deletions(-)
diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,14 @@ +2.50 [KK 2009-03-30] +- Bugfix in activation of the onfail script in the checkup thread. +- Compilation flag -Werror only passed to the compiler when on the + development system Thera.local (see xr/etc/Makefile.class). +- Onstart, onend and onfail scripts (when present) are invoked with a + third argument, the number of connections at the time +- Set-Cookie header directive (see the HTTP dispatcher) attaches an + explicit path "/". +- Rewired the tcp dispatcher and http dispatcher to use a common + buffer inside the tcp dispatcher (instead of 2 separate ones). + 2.49 [KK 2009-03-27] - Implemented onfail hook (to complement onstart/onend, flag -y). diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER = 2.49 +VER = 2.50 PREFIX = $(DESTDIR)/usr BINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/share/man diff --git a/doc/xr.odt b/doc/xr.odt Binary files differ. diff --git a/doc/xr.pdf b/doc/xr.pdf Binary files differ. diff --git a/doc/xrctl.xml.5 b/doc/xrctl.xml.5 @@ -120,9 +120,11 @@ distributed with the sources for a full description. <!-- XR can run specific scripts when client activity starts or ends. When given, the scripts are run with the arguments: - the client IP, and the back end (server:port). A very - simple script /where/ever/activitystart might e.g. do: - echo Client $1 is going to back end $2 >> /tmp/activity.log + the client IP, and the back end (server:port), and the + number of connections to that back end. A very simple script + /where/ever/activitystart might e.g. do: + echo Client $1 going to back end $2, $3 connections so far \ + >> /tmp/activity.log A very simple script /where/ever/activityend might e.g. do: echo Client $1 is done with back end $2 >> /tmp/activity.log - onstart: is run when a client is about to be handled at a diff --git a/test/onend b/test/onend @@ -1,3 +1,4 @@ #!/bin/sh -echo Client $1 was handled by $2 and is now stopping >> /tmp/activity.log +echo Client $1 was handled by $2 and is now stopping, $3 connections remain \ + >> /tmp/activity.log diff --git a/test/onfail b/test/onfail @@ -0,0 +1,4 @@ +#!/bin/sh + +echo Back end $2 with $3 connections failed for client $1 \ + >> /tmp/activity.log diff --git a/test/onstart b/test/onstart @@ -1,5 +1,6 @@ #!/bin/sh -echo Client $1 will be handled by $2 >> /tmp/activity.log +echo Client $1 will be handled by $2, $3 connections so far \ + >> /tmp/activity.log diff --git a/xr/Checkers/checkupthread/execute.cc b/xr/Checkers/checkupthread/execute.cc @@ -6,23 +6,23 @@ void Checkupthread::execute() { while (1) { if (config.checkupsec()) { + msg ("Running checkup thread\n"); for (unsigned i = 0; i < balancer.nbackends(); i++) { Backend target(balancer.backend(i).backenddef()); target.check(); - if (! balancer.backend(i).live() && - target.live() ) { + if (target.live()) { balancer.backend(i).live(true); msg ("Checkup call: backend " + target.description() + - " has awoken\n"); - } else if (balancer.backend(i).live() && - ! target.live()) { + " is alive\n"); + } else { balancer.backend(i).live(false); msg ("Checkup call: backend " + target.description() + - " has gone asleep\n"); + " is unavailable\n"); if (config.onfail().length()) { ostringstream o; o << config.onfail() << " 0.0.0.0 " - << target.description(); + << target.description() << ' ' + << balancer.backend(i).connections(); sysrun(o.str()); } } diff --git a/xr/Dispatchers/httpdispatcher/dispatch.cc b/xr/Dispatchers/httpdispatcher/dispatch.cc @@ -12,10 +12,10 @@ void HttpDispatcher::dispatch() { // Get the client's request. May need for cookie inspection or for the // host header. - while (!buf.headersreceived()) - if (!buf.netread(clientfd(), config.client_timeout())) + while (!buf().headersreceived()) + if (!buf().netread(clientfd(), config.client_timeout())) throw Error("Didn't receive a valid client request."); - msg ("Received client request: '" + buf.firstline() + "'\n"); + msg ("Received client request: '" + buf().firstline() + "'\n"); // See if hostmatching is used. This is true when a backend // matches against a non-dot host. @@ -27,7 +27,7 @@ void HttpDispatcher::dispatch() { } // Build new target list if host matching applies. if (hostmatchused) { - host_header = buf.headerval ("Host"); + host_header = buf().headerval ("Host"); msg ("Will try to dispatch request host '" + host_header + "'\n"); @@ -52,9 +52,9 @@ void HttpDispatcher::dispatch() { // Dispatch as a normal backend if sticky HTTP is off, or if the // sticky target is badly specified. if (!config.stickyhttp() || - (sscanf (buf.cookievalue ("XRTarget").c_str(), + (sscanf (buf().cookievalue ("XRTarget").c_str(), "%d", &stickytarget) < 1 && - sscanf (buf.paramvalue ("XRTarget").c_str(), + sscanf (buf().paramvalue ("XRTarget").c_str(), "%d", &stickytarget) < 1) || stickytarget >= balancer.nbackends()) { issticky(false); diff --git a/xr/Dispatchers/httpdispatcher/handle.cc b/xr/Dispatchers/httpdispatcher/handle.cc @@ -6,20 +6,20 @@ void HttpDispatcher::handle() { // The client request was already retrieved before starting the // dispatcher. We can continue by applying server-directed headers. if (config.addxrversion()) - buf.setheader ("XR", VER); + buf().setheader ("XR", VER); if (config.addxforwardedfor()) - buf.addheader ("X-Forwarded-For", clientipstr()); + buf().addheader ("X-Forwarded-For", clientipstr()); for (unsigned n = 0; n < config.nserverheaders(); n++) - buf.setheader (config.serverheader(n)); + buf().setheader (config.serverheader(n)); // Patch up the Host: header if requested so. if (config.replacehostheader()) - buf.replaceheader("Host:", + buf().replaceheader("Host:", balancer.backend(targetbackend()).server()); // Flush client info received so far to the back end. debugmsg("Sending client request to back end\n"); - buf.netwrite(backendfd(), config.backend_timeout()); + buf().netwrite(backendfd(), config.backend_timeout()); // Let's see if we will need to modify the server headers. bool modify_serverheaders = false; @@ -28,7 +28,7 @@ void HttpDispatcher::handle() { modify_serverheaders = true; // Store the client request. May want to log it later. - string client_request = buf.firstline(); + string client_request = buf().firstline(); // Go into copy-thru mode. If required, catch the server headers on // their first appearance and modify them. @@ -42,34 +42,34 @@ void HttpDispatcher::handle() { if ((sock = readset.readable()) < 0) break; - buf.reset(); + buf().reset(); - if (!buf.netread(sock)) + if (!buf().netread(sock)) break; if (sock == backendfd() && modify_serverheaders) { debugmsg("Back end response seen, applying modifications\n"); modify_serverheaders = false; - while (! buf.headersreceived()) - if (!buf.netread (sock, config.backend_timeout())) + while (! buf().headersreceived()) + if (!buf().netread (sock, config.backend_timeout())) throw Error("Failed to get headers from back end"); if (config.addxrversion()) - buf.setheader("XR", VER); + buf().setheader("XR", VER); if (config.stickyhttp() && !issticky()) { ostringstream o; - o << "XRTarget=" << targetbackend(); - buf.setheader("Set-Cookie", o.str()); + o << "XRTarget=" << targetbackend() << "; path=/"; + buf().setheader("Set-Cookie", o.str()); } } // The back end response may now get flushed to the client. // If the response code is 4** or 5**, log it as a warning. if (!backend_response_checked && - sock == backendfd() && buf.headersreceived()) { - string respcode = buf.stringat(9, 3); + sock == backendfd() && buf().headersreceived()) { + string respcode = buf().stringat(9, 3); if (respcode[0] == '4' || respcode[0] == '5') warnmsg("HTTP back end indicates fault: '" + - buf.firstline() + "' as response to '" + + buf().firstline() + "' as response to '" + client_request + "'\n"); backend_response_checked = true; } @@ -81,7 +81,7 @@ void HttpDispatcher::handle() { timeout = config.backend_timeout(); // Re-patch Host header if requested if (config.replacehostheader()) - buf.replaceheader("Host:", + buf().replaceheader("Host:", balancer.backend(targetbackend()).server()); } else { othersock = clientfd(); @@ -91,9 +91,9 @@ void HttpDispatcher::handle() { debugmsg (Mstr("Had data on ") + sock + (Mstr(", sending to ") + othersock) + "\n"); - buf.netwrite(othersock, timeout); + buf().netwrite(othersock, timeout); if (sock == backendfd()) - balancer.backend(targetbackend()).addbytes(buf.bufsz()); + balancer.backend(targetbackend()).addbytes(buf().bufsz()); } } diff --git a/xr/Dispatchers/httpdispatcher/httpdispatcher b/xr/Dispatchers/httpdispatcher/httpdispatcher @@ -16,8 +16,6 @@ public: private: void senderrorpage(string const &desc); - - Httpbuffer buf; bool is_sticky; }; diff --git a/xr/Dispatchers/httpdispatcher/senderrorpage.cc b/xr/Dispatchers/httpdispatcher/senderrorpage.cc @@ -27,8 +27,6 @@ void HttpDispatcher::senderrorpage(string const &reason) { } catch (Error const &e) { // Silently discard, we are not interested in errors // that ocur when an error page is being sent - // Mutex::lock(&cerr); - // cerr << e.what() << " (while sending error page)\n"; - // Mutex::unlock(&cerr); + msg (Mstr(e.what()) + Mstr(" (while sending error page)\n")); } } diff --git a/xr/Dispatchers/tcpdispatcher/dispatch.cc b/xr/Dispatchers/tcpdispatcher/dispatch.cc @@ -10,8 +10,12 @@ void TcpDispatcher::dispatch() { // Reset the expectancy of back ends. Dispatchers down the line (stored-ip) // will update that later. - for (unsigned i = 0; i < balancer.nbackends(); i++) - balancer.backend(i).anticipated(0); + for (unsigned i = 0; i < balancer.nbackends(); i++) { + msg (Mstr("Resetting anticipation for back end ") + + Mstr(i) + "\n"); + balancer.backend(i).anticipated(0); + } + msg (Mstr("Anticipation forwarding reset.\n")); // Build up the target list, if not yet done so. The HTTP dispatcher // might've created it already for host-based matching (in which case diff --git a/xr/Dispatchers/tcpdispatcher/execute.cc b/xr/Dispatchers/tcpdispatcher/execute.cc @@ -34,7 +34,8 @@ void TcpDispatcher::execute() { if (config.onstart().length()) { ostringstream o; o << config.onstart() << ' ' << clientipstr() << ' ' - << balancer.backend(targetbackend()).description(); + << balancer.backend(targetbackend()).description() + << ' ' << balancer.backend(targetbackend()).connections(); msg (Mstr("Running onstart script: ") + o.str() + "\n"); sysrun(o.str()); } @@ -50,7 +51,8 @@ void TcpDispatcher::execute() { if (config.onfail().length()) { ostringstream o; o << config.onfail() << ' ' << clientipstr() << ' ' - << balancer.backend(targetbackend()).description(); + << balancer.backend(targetbackend()).description() << ' ' + << balancer.backend(targetbackend()).connections(); msg(Mstr("Running onfail script: ") + o.str() + "\n"); sysrun(o.str()); } @@ -63,7 +65,8 @@ void TcpDispatcher::execute() { if (!failed && config.onend().length()) { ostringstream o; o << config.onend() << ' ' << clientipstr() << ' ' - << balancer.backend(targetbackend()).description(); + << balancer.backend(targetbackend()).description() << ' ' + << balancer.backend(targetbackend()).connections(); msg (Mstr("Running onend script: ") + o.str() + "\n"); sysrun(o.str()); } diff --git a/xr/Dispatchers/tcpdispatcher/tcpdispatcher b/xr/Dispatchers/tcpdispatcher/tcpdispatcher @@ -3,6 +3,7 @@ #include "Dispatchers/dispatcher/dispatcher" #include "netbuffer/netbuffer" +#include "httpbuffer/httpbuffer" class TcpDispatcher: public Dispatcher { public: @@ -15,8 +16,11 @@ public: unsigned readchunk (int src); + Httpbuffer &buf() { return netbuffer; } + private: - Netbuffer netbuffer; + Httpbuffer netbuffer; // same as netbuffer, but + // httpdispatcher reuses it }; #endif diff --git a/xr/backend/anticipated.cc b/xr/backend/anticipated.cc @@ -2,8 +2,8 @@ void Backend::anticipated(unsigned a) { Mutex::lock(&nanticipated); - msg((Mstr("Backend ") + description()) + - (Mstr(" now anticipates ") + a) + " connections\n"); nanticipated = a; Mutex::unlock(&nanticipated); + msg((Mstr("Backend ") + description()) + + (Mstr(" now anticipates ") + a) + " connections\n"); } diff --git a/xr/etc/Makefile.class b/xr/etc/Makefile.class @@ -2,17 +2,22 @@ SRC = $(wildcard *.cc) OBJ = $(patsubst %.cc, $(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o, $(SRC)) DIR = $(shell pwd | sed 's:.*/::') SYS = $(shell uname) +HST = $(shell hostname) + +ifeq ($(HST), Thera.local) + ERRFLAG = -Werror +endif class-compile: $(OBJ) $(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o: %.cc @echo "Compiling: " `pwd` $< $(CONF_CC) $(PROF) $(PROFILER) $(CONF_OPTFLAGS) \ - -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \ + -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' -DHST='"$(HST)"' \ -DMAINTAINER='"$(MAINTAINER)"' -DDISTSITE='"$(DISTSITE)"' \ -DSYS='"$(SYS)"' -D$(SYS) $(MEMDEBUG) \ -DCONF_CC='"$(CONF_CC)"' -DCONF_LIB='"$(CONF_LIB)"' \ -DCONF_OPTFLAGS='"$(CONF_OPTFLAGS)"' $(CONF_STRNSTR) \ $(CONF_GETOPT) $(CONF_GETOPT_LONG) $(CONF_INET_ATON) \ -I$(BASE)/xr \ - -c -g -Wall -Werror -o $@ $< + -c -g -Wall $(ERRFLAG) -o $@ $< diff --git a/xr/netbuffer/checkspace.cc b/xr/netbuffer/checkspace.cc @@ -11,15 +11,17 @@ void Netbuffer::check_space(unsigned extra) { if (extra == config.buffersize() && config.stype() == Servertype::t_http) buf_alloced <<= 1; - msg (Mstr("Reserving ") + buf_alloced + " bytes for network buffer\n"); + debugmsg (Mstr("Netbuffer: reserving ") + buf_alloced + + " bytes for network buffer\n"); LOCK_MALLOC; buf_data = (char*)malloc(buf_alloced); UNLOCK_MALLOC; if (! buf_data) throw Error("Memory fault in Netbuffer::check_space"); } else if (buf_sz + extra > buf_alloced) { - msg((Mstr("Reallocating net buffer from ") + buf_alloced) + - (Mstr(" to ") + (buf_alloced + extra)) + " bytes\n"); + debugmsg((Mstr("Netbuffer: reallocating net buffer from ") + + buf_alloced) + + (Mstr(" to ") + (buf_alloced + extra)) + " bytes\n"); buf_alloced += extra; LOCK_MALLOC; buf_data = (char*)realloc(buf_data, buf_alloced); diff --git a/xr/netbuffer/copy.cc b/xr/netbuffer/copy.cc @@ -1,6 +1,8 @@ #include "netbuffer" void Netbuffer::copy (Netbuffer const &other) { + debugmsg("Netbuffer: copying other\n"); + buf_sz = other.buf_sz; buf_alloced = other.buf_alloced; LOCK_MALLOC; diff --git a/xr/netbuffer/destroy.cc b/xr/netbuffer/destroy.cc @@ -1,6 +1,8 @@ #include "netbuffer" void Netbuffer::destroy() { + debugmsg(Mstr("Netbuffer: destroying ") + Mstr(buf_alloced) + + Mstr( " bytes\n")); free(buf_data); buf_data = 0; buf_sz = 0; diff --git a/xr/netbuffer/insertat.cc b/xr/netbuffer/insertat.cc @@ -2,6 +2,8 @@ bool Netbuffer::insertat(unsigned index, char const *s, unsigned len) { PROFILE("Netbuffer::insertat"); + debugmsg(Mstr("Netbuffer: inserting ") + Mstr(s) + Mstr(" at ") + + Mstr(index) + "\n"); if (!len) len = strlen(s); diff --git a/xr/netbuffer/netbuffer1.cc b/xr/netbuffer/netbuffer1.cc @@ -1,4 +1,5 @@ #include "netbuffer" Netbuffer::Netbuffer(): buf_data(0), buf_sz(0), buf_alloced(0) { + debugmsg(Mstr("Netbuffer: creating zero size buffer\n")); } diff --git a/xr/netbuffer/opassign.cc b/xr/netbuffer/opassign.cc @@ -1,6 +1,7 @@ #include "netbuffer" Netbuffer const &Netbuffer::operator= (Netbuffer const &other) { + debugmsg(Mstr("Netbuffer: copying other\n")); if (this != &other) { destroy(); copy (other); diff --git a/xr/netbuffer/reset.cc b/xr/netbuffer/reset.cc @@ -1,5 +1,6 @@ #include "netbuffer" void Netbuffer::reset() { + debugmsg("Netbuffer: resetting\n"); buf_sz = 0; } diff --git a/xr/netbuffer/setstring.cc b/xr/netbuffer/setstring.cc @@ -1,10 +1,11 @@ #include "netbuffer" void Netbuffer::setstring(string const &s) { + debugmsg(Mstr("Netbuffer: setting to ") + s + "\n"); + destroy(); check_space(s.size() + 1); buf_sz = s.size(); memcpy (buf_data, s.c_str(), buf_sz); buf_data[buf_sz] = 0; - debugmsg((Mstr("Set netbuffer to string, ") + buf_sz) + " bytes\n"); } diff --git a/xrctl/xrctl b/xrctl/xrctl @@ -49,35 +49,39 @@ my $xp = new XMLParser($xml); # Load up the system config. my %sysconf; -my $sysxp = new XMLParser($xp->data('system')); -for my $tag qw(pscmd logger uselogger logdir - maxlogsize loghistory path) { - $sysconf{$tag} = $sysxp->data($tag); - msg("System config $tag: $sysconf{$tag}\n") if ($sysconf{$tag} ne ''); -} -if ($sysconf{path} eq '') { - msg ("No path in configuration, using environment\n"); - $sysconf{path} = $ENV{PATH}; -} -if ($sysconf{logger} ne 'logger') { - msg ("Using non-default logger\n"); - $default_logger = $sysconf{logger}; -} -if ($sysconf{pscmd} eq '') { - $sysconf{pscmd} = xfind_bin('ps'); - if (`uname` =~ /SunOS/) { - $sysconf{pscmd} .= ' -ef pid,comm'; - } else { - $sysconf{pscmd} .= ' ax -o pid,command'; +my $sysblock = $xp->data('system'); +if ($sysblock ne '') { + my $sysxp = new XMLParser($xp->data('system')); + for my $tag qw(pscmd logger uselogger logdir + maxlogsize loghistory path) { + $sysconf{$tag} = $sysxp->data($tag); + msg("System config $tag: $sysconf{$tag}\n") if ($sysconf{$tag} ne ''); + } + if ($sysconf{path} eq '') { + msg ("No path in configuration, using environment\n"); + $sysconf{path} = $ENV{PATH}; + } + if ($sysconf{logger} ne 'logger') { + msg ("Using non-default logger\n"); + $default_logger = $sysconf{logger}; + } + if ($sysconf{pscmd} eq '') { + $sysconf{pscmd} = xfind_bin('ps'); + if (`uname` =~ /SunOS/) { + $sysconf{pscmd} .= ' -ef pid,comm'; + } else { + $sysconf{pscmd} .= ' ax -o pid,command'; + } } + msg ("PS command: $sysconf{pscmd}\n"); } -msg ("PS command: $sysconf{pscmd}\n"); # Load up the service names. my @service_name; for (my $i = 0; ; $i++) { my $serviceblock = $xp->data('service', $i) or last; - my $servicexp = new XMLParser($serviceblock); + my $servicexp = new XMLParser($serviceblock) + or die ("No <service> blocks in configuration\n"); my $name = $servicexp->data('name') or die ("<service> block lacks <name>\n"); push (@service_name, $name); @@ -476,6 +480,7 @@ sub xr_cmdarr { $default_dnscachetimeout), flag($ss, '--onstart', 'onstart'), flag($ss, '--onend', 'onend'), + flag($ss, '--onfail', 'onfail'), flag($ss, '--log-traffic-dir', 'logtrafficdir', '')); for my $k (sort (keys (%boolflags))) { push (@cmd, $boolflags{$k}) if (istrue($ss->data($k))); @@ -681,8 +686,7 @@ package XMLParser; sub new { my ($proto, $doc) = @_; my $self = {}; - - die ("Invalid or missing XML document\n") unless ($doc); + die ("Missing XML document\n") unless($doc); my $docstr = ''; for my $p (split (/\n/, $doc)) {