commit 6c73980ed2056b919b7af2fd15c2716199995268
parent 18ca72abe3c1e23f6448d970a84e8b0843d3b7e1
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:37:31 +0100
2.49
Diffstat:
17 files changed, 96 insertions(+), 42 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,3 +1,6 @@
+2.49 [KK 2009-03-27]
+- Implemented onfail hook (to complement onstart/onend, flag -y).
+
2.48 [KK 2009-03-26]
- Implemented onstart/onend hooks (flags -z, -Z).
- Sticky HTTP mode inspects the URI (parameter XRTarget) when no
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
# Top-level Makefile for XR
# -------------------------
-VER = 2.48
+VER = 2.49
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/xrctl.xml.5 b/doc/xrctl.xml.5
@@ -125,9 +125,14 @@ distributed with the sources for a full description.
echo Client $1 is going to back end $2 >> /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
+ back end
+ - onend: is run after succesful termination
+ - onfail: is run after unsuccesful termination
-->
<onstart>/where/ever/activitystart</onstart>
<onend>/where/ever/activityend</onend>
+ <onfail>/where/ever/activityaborted</onfail>
<!-- Access restrictions: we allow from two IP ranges, and deny
from one IP address. The overall results:will be:
diff --git a/xr/Checkers/checkupthread/execute.cc b/xr/Checkers/checkupthread/execute.cc
@@ -19,6 +19,12 @@ void Checkupthread::execute() {
balancer.backend(i).live(false);
msg ("Checkup call: backend " + target.description() +
" has gone asleep\n");
+ if (config.onfail().length()) {
+ ostringstream o;
+ o << config.onfail() << " 0.0.0.0 "
+ << target.description();
+ sysrun(o.str());
+ }
}
}
sleep (config.checkupsec());
diff --git a/xr/Dispatchers/dispatcher/checkdos.cc b/xr/Dispatchers/dispatcher/checkdos.cc
@@ -10,21 +10,15 @@ static void run_excess(string const &prog, char const *ip) {
o << prog << ' ' << ip;
msg ((Mstr("Max connection rate exceeded, invoking '") + o.str()) +
"'\n");
- int ret = system(o.str().c_str());
+ int ret = sysrun(o.str());
if (ret == -1)
throw Error(string("Failed to start system call: ") +
strerror(errno));
- else if (WIFEXITED(ret)) {
- int exitstat = WEXITSTATUS(ret);
- if (exitstat)
- warnmsg((Mstr("Program '") + o.str()) +
- (Mstr("' exited with exit status ") + exitstat) +
- "\n");
- else
- msg ("Program terminated normally.\n");
- } else
+ else if (ret)
warnmsg((Mstr("Program '") + o.str()) +
- "' terminated abnormally!\n");
+ (Mstr("' exited with exit status ") + ret) + "\n");
+ else
+ msg((Mstr("Program '") + o.str()) + "' finished.\n");
}
bool Dispatcher::check_dos() {
diff --git a/xr/Dispatchers/tcpdispatcher/execute.cc b/xr/Dispatchers/tcpdispatcher/execute.cc
@@ -1,24 +1,5 @@
#include "tcpdispatcher"
-static void sysrun(string const &s) {
- int ret = system(s.c_str());
- if (ret == -1) {
- warnmsg(Mstr("Failed to start command: ") + s + "\n");
- return;
- }
- if (WIFEXITED(ret)) {
- int stat = WEXITSTATUS(ret);
- if (stat)
- warnmsg(Mstr("Command" ) + s +
- Mstr(" exited with status ") + Mstr(stat) + "\n");
- else
- msg(Mstr("Command ") + s +
- Mstr(" terminated normally.\n"));
- return;
- }
- warnmsg(Mstr("Command ") + s + Mstr(" failed miserably!\n"));
-}
-
void TcpDispatcher::execute() {
Threadlist::clientfd(clientfd());
@@ -58,19 +39,28 @@ void TcpDispatcher::execute() {
sysrun(o.str());
}
+ bool failed = false;
try {
handle();
} catch (Error const &e) {
Mutex::lock(&cerr);
cerr << e.what() << "\n";
Mutex::unlock(&cerr);
+ failed = true;
+ if (config.onfail().length()) {
+ ostringstream o;
+ o << config.onfail() << ' ' << clientipstr() << ' '
+ << balancer.backend(targetbackend()).description();
+ msg(Mstr("Running onfail script: ") + o.str() + "\n");
+ sysrun(o.str());
+ }
}
socketclose (clientfd());
socketclose (backendfd());
balancer.backend(targetbackend()).endconnection();
- if (config.onend().length()) {
+ if (!failed && config.onend().length()) {
ostringstream o;
o << config.onend() << ' ' << clientipstr() << ' '
<< balancer.backend(targetbackend()).description();
diff --git a/xr/config/config b/xr/config/config
@@ -154,6 +154,8 @@ public:
string const &onstart() const { return on_start; }
void onend(string s) { on_end = s; }
string const &onend() const { return on_end; }
+ void onfail(string s) { on_fail = s; }
+ string const &onfail() const { return on_fail; }
private:
@@ -202,7 +204,7 @@ private:
static string soft_maxconn_excess_prog;
static string hard_maxconn_excess_prog;
static unsigned dns_cache_timeout;
- static string on_start, on_end;
+ static string on_start, on_end, on_fail;
};
extern Config config;
diff --git a/xr/config/config1.cc b/xr/config/config1.cc
@@ -40,6 +40,7 @@ string Config::hard_maxconn_excess_prog = "";
unsigned Config::dns_cache_timeout = 3600;
string Config::on_start = "";
string Config::on_end = "";
+string Config::on_fail = "";
Config::Config () {
}
diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc
@@ -16,7 +16,7 @@ void Config::parsecmdline (int ac, char **av) {
throw Error("Bad command line '" + cmdline + "'\n" + USAGE);
# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:g:hH:Il:" \
- "m:M:nPp:Q:r:R:Ss:t:T:u:U:vVW:w:xXz:Z:"
+ "m:M:nPp:Q:r:R:Ss:t:T:u:U:vVW:w:xXy:z:Z:"
# ifdef HAVE_GETOPT_LONG
static struct option longopts[] = {
{ "allow-from", required_argument, 0, 'a' },
@@ -56,6 +56,7 @@ void Config::parsecmdline (int ac, char **av) {
{ "web-interface", required_argument, 0, 'W' },
{ "add-xr-version", no_argument, 0, 'X' },
{ "add-x-forwarded-for", no_argument, 0, 'x' },
+ { "onfail", required_argument, 0, 'y' },
{ "onstart", required_argument, 0, 'z' },
{ "onend", required_argument, 0, 'Z' },
{ 0, 0, 0, 0 }
@@ -229,6 +230,9 @@ void Config::parsecmdline (int ac, char **av) {
case 'x':
add_x_forwarded_for = true;
break;
+ case 'y':
+ onfail(optarg);
+ break;
case 'z':
onstart(optarg);
break;
diff --git a/xr/etc/c-conf b/xr/etc/c-conf
@@ -157,18 +157,21 @@ sub uname() {
sub findbin($) {
my $bin = shift;
my $bestx = undef;
- my $bestver = 0;
+ my $bestver = -1;
foreach my $d (split (/:/, $ENV{PATH})) {
- for my $x (glob("$d/$bin"), glob("$d/$bin.exe"),
- glob("$d/$bin-*"), glob("$d/$bin-*.exe")) {
+ my @cand = (glob("$d/$bin"), glob("$d/$bin.exe"),
+ glob("$d/$bin-*"), glob("$d/$bin-*.exe"));
+ msg ("Candidates for '$bin' in '$d': [@cand]\n");
+ for my $x (@cand) {
if (-x $x) {
my $ver = $x;
$ver =~ s{^.*/[^\d]*}{};
$ver = sprintf("%g", $ver);
- # msg ("Candidate for '$bin': $x, version $ver\n");
+ msg ("Version of $x: $ver\n");
if ($bestver < $ver or !$bestx) {
$bestver = $ver;
$bestx = $x;
+ msg (" .. best so far\n");
}
}
}
diff --git a/xr/etc/status.xslt b/xr/etc/status.xslt
@@ -419,7 +419,7 @@
onchange="goto('/server/onstart/', 'onstart');"/>
</td>
</tr>
- <tr>
+ <tr>
<td></td>
<td>Onend command</td>
<td colspan="2" align="right">
@@ -429,6 +429,15 @@
</td>
</tr>
<tr>
+ <td></td>
+ <td>Onfail command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onfail" id="onfail"
+ value="{onfail}"
+ onchange="goto('/server/onfail/', 'onfail');"/>
+ </td>
+ </tr>
+ <tr>
<td>Network buffer size</td>
<td colspan="2">bytes</td>
<td>
diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt
@@ -154,11 +154,15 @@ may not exist on your platform):
-x, --add-x-forwarded-for
Adds X-Forwarded-For with external IP address to back end streams in
HTTP messages.
- -z CMD, --onstart CMD
- Runs CMD just before connecting a client to a back end. The arguments
+ -y CMD, --onfail CMD
+ Runs CMD when XR fails to connect to a back end. The arguments
to the command are: the client's IP address, and the back end address.
+ -z CMD, --onstart CMD
+ Runs CMD just before letting a back end handle a client's connection.
+ For the arguments of CMD see -y.
-Z CMD, --onend CMD
- Runs CMD after termination of a client. For the arguments of CMD see -z.
+ Runs CMD after successful termination of a client. For the
+ arguments of CMD see -y.
XR's messages are sent to stderr. Invoke XR daemons using something like
"xr -b ... [other flags] 2>&1 | logger &", or use xrctl.
diff --git a/xr/sys/sys b/xr/sys/sys
@@ -91,6 +91,7 @@ vector<string> str2parts (string const &s, char sep);
void mt_srand(unsigned long s);
unsigned long mt_rand(void);
bool check_acl(string const &ipstr, struct in_addr ipaddr);
+int sysrun (string const &s);
#ifndef HAVE_INET_ATON
int inet_aton (char const *name, struct in_addr *addr);
diff --git a/xr/sys/sysrun.cc b/xr/sys/sysrun.cc
@@ -0,0 +1,22 @@
+#include "sys"
+#include "config/config"
+
+int sysrun(string const &s) {
+ int ret = system(s.c_str());
+ if (ret == -1) {
+ warnmsg(Mstr("Failed to start command: ") + s + "\n");
+ return -1;
+ }
+ if (WIFEXITED(ret)) {
+ int stat = WEXITSTATUS(ret);
+ if (stat)
+ warnmsg(Mstr("Command" ) + s +
+ Mstr(" exited with status ") + Mstr(stat) + "\n");
+ else
+ msg(Mstr("Command ") + s +
+ Mstr(" terminated normally.\n"));
+ return stat;
+ }
+ warnmsg(Mstr("Command ") + s + Mstr(" failed miserably!\n"));
+ return ret;
+}
diff --git a/xr/webinterface/answer.cc b/xr/webinterface/answer.cc
@@ -415,6 +415,15 @@ void Webinterface::answer(Httpbuffer req) {
return;
}
+ // /server/onfail/
+ // /server/onfail/PROGRAM
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "onfail") {
+ config.onfail(parts[2]);
+ answer_status();
+ return;
+ }
+
// /server/addbackend/IP:PORT
if (parts.size() == 3 &&
parts[0] == "server" && parts[1] == "addbackend") {
diff --git a/xr/webinterface/answerstatus.cc b/xr/webinterface/answerstatus.cc
@@ -27,6 +27,7 @@ void Webinterface::answer_status() {
" <closesocketsfast>" << config.fastclose() << "</closesocketsfast>\n"
" <onstart>" << config.onstart() << "</onstart>\n"
" <onend>" << config.onend() << "</onend>\n"
+ " <onfail>" << config.onfail() << "</onfail>\n"
" <checks>\n"
" <wakeupinterval>" << config.wakeupsec() << "</wakeupinterval>\n"
" <checkupinterval>" << config.checkupsec() << "</checkupinterval>\n"