commit 306caa58d0887422d144ec1ba44e8a1d5c07f323
parent aa07a8f8323e058f60af8d4a1eb506b6c6a9451a
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:38:24 +0100
2.62
Diffstat:
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);