crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs | LICENSE

commit 306caa58d0887422d144ec1ba44e8a1d5c07f323
parent aa07a8f8323e058f60af8d4a1eb506b6c6a9451a
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:38:24 +0100

2.62

Diffstat:
MChangeLog | 15+++++++++++++++
MMakefile | 2+-
Mtest/ftp.xml | 6++++--
Mxr/Dispatchers/dispatcher/dispatcher1.cc | 2+-
Mxr/Dispatchers/dispatcher/dispatcher3.cc | 2+-
Mxr/Dispatchers/httpdispatcher/dispatch.cc | 6++++--
Mxr/Dispatchers/tcpdispatcher/execute.cc | 11+++++++++++
Mxr/backend/backend1.cc | 2+-
Mxr/backenddef/backenddef | 9+++++++--
Mxr/backenddef/backenddef1.cc | 3++-
Mxr/backenddef/weight.cc | 2++
Mxr/balancer/balancer | 13++++++++-----
Mxr/balancer/balancer1.cc | 3++-
Mxr/balancer/serve.cc | 5++++-
Mxr/config/config | 3+++
Mxr/config/config1.cc | 1+
Mxr/config/parsecmdline.cc | 3+++
Axr/config/restart.cc | 11+++++++++++
Mxr/netbuffer/netread.cc | 8++++----
Mxr/netbuffer/netwrite.cc | 11++++++++++-
Mxr/sys/main.cc | 30+++++++++++++++++++++++-------
Mxrctl/xrctl | 2+-
22 files changed, 119 insertions(+), 31 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,18 @@ +2.62 [KK 2010-01-08] +- Fixed possible coredump cause in Balancer::serve(), which might + explain some troubles during high load (many really concurrent requests). + +2.61 [KK 2009-11-17] +- Netbuffer::netwrite() will abort after 5 attempts that yield 0 + written bytes. Probable cause of CPU hogging - thx Franz J. for + reporting. +- Fixed potential cause of crashes in base class for Dispatcher + (algorithm handler would not get initialized in the constructors). + +2.60 [KK 2009-11-02] +- Some errors demoted to informational messages to choke the amount of + log information that's generated. + 2.59 [KK 2009-10-14] - Bugfix in logger handling of xrctl, read http://xrforum.org/viewtopic.php?t=495 if you fancy. Thanks Hobbit diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER = 2.59 +VER = 2.62 PREFIX = $(DESTDIR)/usr BINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/share/man diff --git a/test/ftp.xml b/test/ftp.xml @@ -15,7 +15,8 @@ <verbose>true</verbose> </server> <backend> - <address>10.50.40.118:21</address> + <address>172.16.238.1:21</address> + <debug>true</debug> </backend> </service> @@ -26,9 +27,10 @@ <address>127.0.0.1:20</address> <dispatchmode>first-available</dispatchmode> <verbose>true</verbose> + <debug>true</debug> </server> <backend> - <address>10.50.40.118:20</address> + <address>172.16.238.1:20</address> </backend> </service> diff --git a/xr/Dispatchers/dispatcher/dispatcher1.cc b/xr/Dispatchers/dispatcher/dispatcher1.cc @@ -2,7 +2,7 @@ Dispatcher::Dispatcher(int cfd, struct in_addr cip): Thread(), client_ip(cip), target_backend(-1), client_fd(cfd), - backend_fd(-1), target_list(), clientip_str() { + backend_fd(-1), algo(0), target_list(), clientip_str() { start_dispatcher(); } diff --git a/xr/Dispatchers/dispatcher/dispatcher3.cc b/xr/Dispatchers/dispatcher/dispatcher3.cc @@ -2,7 +2,7 @@ Dispatcher::Dispatcher(int fd): Thread(), target_backend(-1), client_fd(fd), - backend_fd(-1), target_list(), clientip_str() { + backend_fd(-1), algo(0), target_list(), clientip_str() { start_dispatcher(); } diff --git a/xr/Dispatchers/httpdispatcher/dispatch.cc b/xr/Dispatchers/httpdispatcher/dispatch.cc @@ -14,8 +14,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_read_timeout())) - throw Error("Didn't receive a valid client request."); + if (!buf().netread(clientfd(), config.client_read_timeout())) { + msg ("Didn't receive a valid client request, stopping"); + return; + } msg ("Received client request: '" + buf().firstline() + "'\n"); // See if hostmatching or urlmatching is used. diff --git a/xr/Dispatchers/tcpdispatcher/execute.cc b/xr/Dispatchers/tcpdispatcher/execute.cc @@ -10,6 +10,7 @@ void TcpDispatcher::execute() { msg ((Mstr("Dispatch request for client fd ") + clientfd()) + "\n"); + // Try to determine the back end. try { Threadlist::desc("Dispatching"); dispatch(); @@ -22,6 +23,16 @@ void TcpDispatcher::execute() { return; } + // Verify that the target is within the allowed set. + if (targetbackend() < 0 || targetbackend() >= (int)balancer.nbackends()) { + cerr << "WARNING: target back end " << targetbackend() + << " out of range\n"; + socketclose(clientfd()); + socketclose(backendfd()); + return; + } + + // Dispatch! msg ((Mstr("Dispatching client fd ") + clientfd()) + (Mstr(" to ") + balancer.backend(targetbackend()).description()) + (Mstr(", fd ") + backendfd()) + "\n"); diff --git a/xr/backend/backend1.cc b/xr/backend/backend1.cc @@ -1,7 +1,7 @@ #include "backend" Backend::Backend () : - islive(true), isup(true), clsocket(-1), + bdef(), islive(true), isup(true), clsocket(-1), nconn(0), totconn(0), nconnerr(0), bytes_served(0), loadaverage(0.1), dnsentry(), index(-1) { diff --git a/xr/backenddef/backenddef b/xr/backenddef/backenddef @@ -5,13 +5,18 @@ #include "error/error" #include "profiler/profiler" #include "backendcheck/backendcheck" +#include "ThreadsAndMutexes/mutex/mutex" using namespace std; class BackendDef { public: - BackendDef(): srv(""), prt(-1), max(0), - host_match(""), wt(1), backend_check() {} + BackendDef(): + srv(""), prt(-1), max(0), host_match(""), url_match(""), + wt(1), backend_check() { + hostmatch(""); + urlmatch(""); + } BackendDef(string s, string p, string m = "", string w = "1"); void server(string s) { srv = s; } diff --git a/xr/backenddef/backenddef1.cc b/xr/backenddef/backenddef1.cc @@ -6,7 +6,8 @@ bool BackendDef::minmax_wt_set = false; BackendDef::BackendDef (string server, string port, string maxclients, string w) : - srv(server), prt(-1), max(0), host_match(""), wt(1) { + srv(server), prt(-1), max(0), host_match(""), url_match(""), + wt(1), backend_check() { if (sscanf (port.c_str(), "%d", &prt) < 1) throw Error("Bad backend port specifier: '" + port + diff --git a/xr/backenddef/weight.cc b/xr/backenddef/weight.cc @@ -3,6 +3,7 @@ void BackendDef::weight(unsigned w) { wt = w; + Mutex::lock(&minmax_wt_set); if (!minmax_wt_set) { min_wt = w; max_wt = w; @@ -13,4 +14,5 @@ void BackendDef::weight(unsigned w) { if (max_wt > w) max_wt = w; } + Mutex::unlock(&minmax_wt_set); } diff --git a/xr/balancer/balancer b/xr/balancer/balancer @@ -28,13 +28,15 @@ public: void deletebackend(unsigned i); void serve(); - unsigned nbackends() { return (backends.size()); } - Backend &backend (unsigned i) { return (backends[i]); } - bool terminate() const { return (term); } + unsigned nbackends() { return backends.size(); } + Backend &backend (unsigned i) { return backends[i]; } + bool terminate() const { return term; } void terminate (bool t) { term = t; } - bool report() const { return (rep); } + bool report() const { return rep; } void report (bool r) { rep = r; } - long requestnr() const { return (request_nr); } + void restart(bool t) { rest = t; } + bool restart() const { return rest; } + long requestnr() const { return request_nr; } unsigned connections(); @@ -47,6 +49,7 @@ private: vector<Backend> backends; bool term; bool rep; + bool rest; }; extern Balancer balancer; diff --git a/xr/balancer/balancer1.cc b/xr/balancer/balancer1.cc @@ -1,5 +1,6 @@ #include "balancer" Balancer::Balancer () : - server_fd(-1), request_nr(0), backends(), term(false), rep(false) { + server_fd(-1), request_nr(0), backends(), + term(false), rep(false), rest(false) { } diff --git a/xr/balancer/serve.cc b/xr/balancer/serve.cc @@ -72,8 +72,11 @@ void Balancer::serve() { report (false); reportmsg ("*** XR STATUS REPORT ENDS ***\n"); continue; + } else if (restart()) { + msg ("Restart requested\n"); + config.restart(); } else { - msg ("Non-meaningful interrupt, resuming\n"); + msg ("Non-meaningful interrupt or select timeout, resuming\n"); continue; } } diff --git a/xr/config/config b/xr/config/config @@ -188,6 +188,8 @@ public: void onfail(string s) { on_fail = s; } string const &onfail() const { return on_fail; } + /* Restart of program */ + void restart(); private: void setbackend (string const &s, string const &hostmatch, @@ -238,6 +240,7 @@ private: static unsigned dns_cache_timeout; static string on_start, on_end, on_fail; static bool remove_reservations; + static char **org_argv; }; extern Config config; diff --git a/xr/config/config1.cc b/xr/config/config1.cc @@ -44,6 +44,7 @@ string Config::on_start = ""; string Config::on_end = ""; string Config::on_fail = ""; bool Config::remove_reservations = false; +char **Config::org_argv = 0; Config::Config () { } diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc @@ -4,6 +4,9 @@ using namespace std; void Config::parsecmdline (int ac, char **av) { + // Remember original argv. + org_argv = av; + // Prepare invoking command line. string cmdline; for (int i = 0; i < ac; i++) { diff --git a/xr/config/restart.cc b/xr/config/restart.cc @@ -0,0 +1,11 @@ +#include "config" + +void Config::restart() { + for (int i = 0; org_argv[i]; i++) + cout << "Arg " << i << ": " << org_argv[i] << '\n'; + execvp("xr", org_argv); + ostringstream o; + o << "Failed to restart: errno=" << errno << ", " + << strerror(errno); + throw Error(o.str()); +} diff --git a/xr/netbuffer/netread.cc b/xr/netbuffer/netread.cc @@ -8,10 +8,10 @@ unsigned Netbuffer::netread (int fd, int timeout) { set.add(fd); set.wait_r(); if (! set.readable(fd)) { - ostringstream o; - o << "Fd " << fd << " failed to become readable within " - << int(timeout) << " sec"; - throw Error(o.str()); + msg(Mstr("Fd ") + Mstr(fd) + + Mstr(" failed to become readable within ") + Mstr(timeout) + + Mstr(" sec")); + return 0; } } diff --git a/xr/netbuffer/netwrite.cc b/xr/netbuffer/netwrite.cc @@ -27,8 +27,16 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { } // Send to the socket - unsigned totwritten = 0; + unsigned totwritten = 0, ntries = 0; while (totwritten < buf_sz) { + // Don't go beyond 5 tries. + if (++ntries > 4) { + ostringstream o; + o << "Network writing to fd " << fd << " failed, " + << totwritten << " bytes sent of " << buf_sz; + throw Error(o.str()); + } + // Wait for the socket to become writeable. if (timeout) { Fdset set (timeout); @@ -50,6 +58,7 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { // EINVAL / EINPROGRESS errors are handled as: retry // All other errors mean the link is broken if (nwritten >= 1) { + ntries = 0; if (config.debug()) { ostringstream o; o << "Sent " << nwritten << " bytes to fd " << fd << ": "; diff --git a/xr/sys/main.cc b/xr/sys/main.cc @@ -46,16 +46,27 @@ static void showlimits() { } } +static int org_argc; +static char **org_argv; static void sigcatcher (int sig) { debugmsg ("Seen signal " + sig + '\n'); - if (sig == SIGHUP) + switch (sig) { + case SIGHUP: + /* Generate a report to the log. Somewhat stale given the + * web interface. */ balancer.report(true); - else if (sig != SIGPIPE && sig != SIGSTOP) + break; + case SIGPIPE: + case SIGSTOP: + /* SIGPIPE is ignored (See below). Leaving in place for future + * versions. SIGSTOP is used for stopping separarte treads. */ balancer.terminate(true); - // Actually we wouldn't need to test for SIGPIPE, it's ignored (see below). - // Leaving the test in place for future versions, better an extra if - // than forgetting it later. - // SIGSTOP is used for stopping separare threads. + break; + case SIGSEGV: + /* Production-grade protection - don't ever crash! */ + balancer.restart(true); + break; + } } int main (int argc, char **argv) { @@ -63,10 +74,15 @@ int main (int argc, char **argv) { PROFILE("main"); static int relevant_sig[] = { - SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP + SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP, + // SIGSEGV }; try { + // Save original commandline + org_argc = argc; + org_argv = argv; + // Load configuration from the commandline, promote verbosity config.parsecmdline (argc, argv); diff --git a/xrctl/xrctl b/xrctl/xrctl @@ -377,8 +377,8 @@ sub is_running { # Unconditionally start a given service sub start_service { my $s = shift; - my @args = xr_cmdarr($s); my $xr = xfind_bin('xr'); + my @args = xr_cmdarr($s); my $logstr = log_file($s); my $logtype = substr($logstr, 0, 1); my $logout = substr($logstr, 1);