crossroads

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

commit 2b64756bceb30f5cbafc627a696c52918850d68d
parent 0c0645fa76857155d5252c997d1ea87aa9691cb1
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:38:51 +0100

2.72

Diffstat:
MChangeLog | 9+++++++++
MMakefile | 2+-
Mtest/sampleconf.xml | 22++++++++++++++--------
Mxr/Checkers/checkupthread/execute.cc | 17++++++++++-------
Mxr/Checkers/wakeupthread/execute.cc | 9++++++---
Mxr/DispatchAlgorithms/firstactive/target.cc | 11+++++++++--
Mxr/Dispatchers/dispatcher/checkacl.cc | 10++++++----
Mxr/Dispatchers/dispatcher/checkdos.cc | 43++++++++++++++++++++++++++-----------------
Dxr/Dispatchers/dispatcher/clientipstr.cc | 7-------
Mxr/Dispatchers/dispatcher/dispatcher | 25++++++++++++-------------
Rxr/Dispatchers/dispatcher/dispatcher2.cc -> xr/Dispatchers/dispatcher/dispatcher0.cc | 0
Mxr/Dispatchers/dispatcher/dispatcher1.cc | 6+++---
Dxr/Dispatchers/dispatcher/dispatcher3.cc | 8--------
Mxr/Dispatchers/httpdispatcher/handle.cc | 27+++++++++++++++------------
Mxr/Dispatchers/httpdispatcher/httpdispatcher | 7++++---
Dxr/Dispatchers/httpdispatcher/httpdispatcher1.cc | 5-----
Mxr/Dispatchers/tcpdispatcher/dispatch.cc | 13++++++++++---
Mxr/Dispatchers/tcpdispatcher/execute.cc | 26+++++++++++---------------
Mxr/Dispatchers/tcpdispatcher/handle.cc | 13++++++++-----
Mxr/Dispatchers/tcpdispatcher/tcpdispatcher | 6+++---
Dxr/Dispatchers/tcpdispatcher/tcpdispatcher1.cc | 5-----
Dxr/Dispatchers/udpdispatcher/dispatch.cc | 5-----
Dxr/Dispatchers/udpdispatcher/execute.cc | 5-----
Dxr/Dispatchers/udpdispatcher/handle.cc | 5-----
Dxr/Dispatchers/udpdispatcher/udpdispatcher | 16----------------
Dxr/Dispatchers/udpdispatcher/udpdispatcher1.cc | 4----
Mxr/Makefile | 3++-
Axr/SocketHandling/basesocket/accept.cc | 28++++++++++++++++++++++++++++
Axr/SocketHandling/basesocket/basesocket | 28++++++++++++++++++++++++++++
Axr/SocketHandling/basesocket/basesocket1.cc | 4++++
Axr/SocketHandling/basesocket/basesocket2.cc | 4++++
Axr/SocketHandling/basesocket/basesocket3.cc | 5+++++
Axr/SocketHandling/basesocket/bind.cc | 40++++++++++++++++++++++++++++++++++++++++
Axr/SocketHandling/basesocket/clientaddr.cc | 5+++++
Axr/SocketHandling/basesocket/close.cc | 16++++++++++++++++
Axr/SocketHandling/basesocket/fd1.cc | 15+++++++++++++++
Axr/SocketHandling/basesocket/fd2.cc | 6++++++
Axr/SocketHandling/basesocket/opeq.cc | 5+++++
Axr/SocketHandling/socket/accept.cc | 10++++++++++
Axr/SocketHandling/socket/description.cc | 8++++++++
Axr/SocketHandling/socket/opis.cc | 32++++++++++++++++++++++++++++++++
Axr/SocketHandling/socket/socket | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/SocketHandling/socket/socket0.cc | 17+++++++++++++++++
Axr/SocketHandling/socket/socket1.cc | 11+++++++++++
Axr/SocketHandling/socket/socket2.cc | 11+++++++++++
Axr/SocketHandling/socket/socket3.cc | 11+++++++++++
Axr/SocketHandling/socket/socket4.cc | 11+++++++++++
Mxr/ThreadsAndMutexes/threadlist/backendfd.cc | 4++--
Mxr/ThreadsAndMutexes/threadlist/clientfd.cc | 4++--
Mxr/ThreadsAndMutexes/threadlist/threadlist | 5+++--
Mxr/backend/backend | 20+++++++++++---------
Mxr/backend/backend1.cc | 2+-
Mxr/backend/backend2.cc | 2+-
Mxr/backend/check.cc | 55+++++++++++++++++--------------------------------------
Mxr/backend/connect.cc | 30++++++++----------------------
Mxr/balancer/balancer | 3++-
Mxr/balancer/balancer1.cc | 2+-
Mxr/balancer/init.cc | 14++++++--------
Mxr/balancer/serve.cc | 114+++++++++++++++++++++++++++++++++----------------------------------------------
Mxr/etc/Makefile.class | 2+-
Mxr/etc/usage.txt | 2++
Mxr/fdset/fdset | 24++++++++++++++----------
Mxr/netbuffer/destroy.cc | 3++-
Mxr/netbuffer/netbuffer | 4++--
Mxr/netbuffer/netread.cc | 19++++++++++---------
Mxr/netbuffer/netwrite.cc | 20+++++++++++---------
Mxr/servertype/servertype | 1-
Mxr/servertype/type1.cc | 4+---
Mxr/servertype/typestr.cc | 2--
Dxr/sys/serversocket.cc | 52----------------------------------------------------
Dxr/sys/socketclose.cc | 19-------------------
Mxr/sys/sys | 3---
Mxr/webinterface/answer.cc | 4++--
Mxr/webinterface/execute.cc | 45+++++++++++++++++++++++----------------------
Mxr/webinterface/serve.cc | 4++--
Mxr/webinterface/webinterface | 5+++--
Axr/webinterface/webinterface0.cc | 5+++++
Mxr/webinterface/webinterface1.cc | 6+-----
Mxrctl/xrctl | 652++++++++++++++++++++++++++++++++++++++++---------------------------------------
79 files changed, 975 insertions(+), 783 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,12 @@ +2.72 [KK 2011-01-11] +- Web interface correctly retrieved in "xrctl list" (including the + display name and all). +- Small bugfix in TCP-connect-checking of dead back ends (see + xr/backend/check.cc). +- Bugfix in the first-available dispatch algorithm. +- Socket handling rewritten, using Socket/Basesocket classes. Consider + this version alpha-code until the new socket handling proves itself! + 2.71 [KK 2010-11-25] - Recentness of doc/xr.pdf is checked (against doc/xr.odt) during "make commit" diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER ?= 2.71 +VER ?= 2.72 PREFIX ?= $(DESTDIR)/usr BINDIR ?= $(PREFIX)/sbin MANDIR ?= $(PREFIX)/share/man diff --git a/test/sampleconf.xml b/test/sampleconf.xml @@ -43,11 +43,16 @@ <name>ssh</name> <server> <!-- Type (tcp/http, here: tcp), and IP-address/port to bind - to. Use "0" for IP-address to bind to all interfaces. The - web interface will listen to localhost, port 20.001. --> + to. Use "0" for IP-address to bind to all interfaces. --> <type>tcp</type> <address>0:20000</address> + <!-- The web interface will listen to localhost, port 20.001. The + (optional) display title at the web interface is "SSH + Balancer". It requires basic authentication, username "admin", + password "secret". --> <webinterface>0:20001</webinterface> + <webinterfacename>SSH Balancer</webinterfacename> + <webinterfaceauth>admin:secret</webinterfaceauth> <!-- Clients may be idle for 30 minutes, then they are logged out. --> <clienttimeout>1800</clienttimeout> @@ -75,7 +80,8 @@ <server> <!-- Server binding. XR will listen to any IP interface, on port 20.010. It'll be an HTTP balancer. The web interface will - be on port 20.011. --> + be on port 20.011. There will be no display name on the web + interface, and no required authentication. --> <address>0:20010</address> <type>http</type> <webinterface>127.0.0.1:20011</webinterface> @@ -140,11 +146,11 @@ <http> <!-- Since this is an HTTP balancer, let's add some goodies: - no header for the XR version, - a header X-Forwarded-For: client-ip - no sticky http sessions - modification of the Host: header to the back end server name - two serverheaders to insert --> + - no header for the XR version, + - a header X-Forwarded-For: client-ip + - no sticky http sessions + - modification of the Host: header to the back end server name + two serverheaders to insert --> <addxrversion>off</addxrversion> <addxforwardedfor>on</addxforwardedfor> <stickyhttp>off</stickyhttp> diff --git a/xr/Checkers/checkupthread/execute.cc b/xr/Checkers/checkupthread/execute.cc @@ -6,20 +6,20 @@ void Checkupthread::execute() { while (1) { if (config.checkupsec()) { - msg ("Running checkup thread\n"); + msg("Running checkup thread\n"); for (unsigned i = 0; i < balancer.nbackends(); i++) { Backend target(balancer.backend(i).backenddef()); try { target.check(); if (target.live()) { balancer.backend(i).live(true); - msg ("Checkup call: backend " + target.description() + - " is alive\n"); + msg("Checkup call: backend " << target.description() + << " is alive\n"); } else { balancer.backend(i).live(false); balancer.backend(i).markconnecterror(); - msg ("Checkup call: backend " + target.description() + - " is unavailable\n"); + msg("Checkup call: backend " << target.description() + << " is unavailable\n"); if (config.onfail().length()) { ostringstream o; o << config.onfail() << " 0.0.0.0 " @@ -28,11 +28,14 @@ void Checkupthread::execute() { sysrun(o.str()); } } + } catch (Error &e) { + debugmsg("Error in checkup thread: " << e.what() << + '\n'); } catch (...) { - socketclose(target.sock()); + debugmsg("Error in checkup thread\n"); } } - sleep (config.checkupsec()); + sleep(config.checkupsec()); } else sleep(30); } diff --git a/xr/Checkers/wakeupthread/execute.cc b/xr/Checkers/wakeupthread/execute.cc @@ -14,14 +14,17 @@ void Wakeupthread::execute() { try { target.check(); if (target.live()) { - msg ("Wakeup call: backend " + - target.description() + + msg ("Wakeup call: backend " << + target.description() << " has awoken\n"); balancer.backend(i).live(true); } else balancer.backend(i).markconnecterror(); + } catch (Error &e) { + debugmsg("Exception in wakeup thread: " << + e.what() << '\n'); } catch (...) { - socketclose(target.sock()); + debugmsg("Exception in wakeup thread\n"); } } } diff --git a/xr/DispatchAlgorithms/firstactive/target.cc b/xr/DispatchAlgorithms/firstactive/target.cc @@ -2,8 +2,15 @@ unsigned Firstactive::target(struct in_addr clientip, BackendVector const &targetlist) { - if ( targetlist.size() == 0 || - ! balancer.backend(targetlist[0]).available() ) + if (config.debug()) { + debugmsg("First-active algorithm: " << targetlist.size() << + " back end(s) to consider\n"); + for (unsigned int i = 0; i < targetlist.size(); i++) + debugmsg(" Considering back end " << targetlist[i] << + " which is " << + balancer.backend(targetlist[i]).description() << '\n'); + } + if (targetlist.size() == 0) throw Error("First-active algorithm: no available back ends"); return (targetlist[0]); } diff --git a/xr/Dispatchers/dispatcher/checkacl.cc b/xr/Dispatchers/dispatcher/checkacl.cc @@ -4,20 +4,22 @@ bool Dispatcher::check_acl() { if (config.nallow()) { bool allowed = false; for (unsigned n = 0; n < config.nallow(); n++) - if (ipmatch(clientip(), config.allow(n))) { + if (ipmatch(clientfd().clientaddr().sin_addr, config.allow(n))) { allowed = true; break; } if (!allowed) { - msg("Not serving client IP " + clientipstr() + + msg("Not serving client IP " << + inet2string(clientfd().clientaddr().sin_addr) << ": no match in allow list\n"); return false; } } if (config.ndeny()) { for (unsigned n = 0; n < config.ndeny(); n++) - if (ipmatch(clientip(), config.deny(n))) { - msg("Not serving client IP " + clientipstr() + + if (ipmatch(clientfd().clientaddr().sin_addr, config.deny(n))) { + msg("Not serving client IP " << + inet2string(clientfd().clientaddr().sin_addr) << ": match in deny list\n"); return false; } diff --git a/xr/Dispatchers/dispatcher/checkdos.cc b/xr/Dispatchers/dispatcher/checkdos.cc @@ -1,6 +1,6 @@ #include "dispatcher" -typedef map < unsigned long, std::queue<time_t> > AccessMap; +typedef map < long, std::queue<time_t> > AccessMap; static AccessMap accesslog; static time_t accesslog_lastclean = 0; @@ -34,6 +34,13 @@ bool Dispatcher::check_dos() { if (config.connrate_time() && (config.hardmaxconnrate() || (config.softmaxconnrate() && config.defertime()))) { + + // The map lookup/insert key. + long key; + memset(&key, 0, sizeof(key)); + memcpy(&key, &(clientfd().clientaddr().sin_addr), + sizeof(clientfd().clientaddr().sin_addr)); + time_t now, min_ts; now = time(0); min_ts = now - config.connrate_time(); @@ -41,7 +48,7 @@ bool Dispatcher::check_dos() { config.softmaxconnrate()); mutex_lock (&lock); - accesslog[clientip().s_addr].push(now); + accesslog[key].push(now); mutex_unlock (&lock); if (accesslog_lastclean < min_ts) { @@ -79,41 +86,43 @@ bool Dispatcher::check_dos() { // The "big log" doesn't need to be fully cleaned, // but this particular IP should be! mutex_lock(&lock); - while ( accesslog[clientip().s_addr].front() < min_ts - || accesslog[clientip().s_addr].size() > max_conns ) { - accesslog[clientip().s_addr].pop(); + while ( (accesslog[key].front() < min_ts) + || + (accesslog[key].size() > max_conns) + ) { + accesslog[key].pop(); } mutex_unlock(&lock); } - if (config.hardmaxconnrate() && - accesslog[clientip().s_addr].size() >= config.hardmaxconnrate() ) { + if ( config.hardmaxconnrate() && + (accesslog[key].size() >= config.hardmaxconnrate()) ) { // This IP has violated the "HARD" limit! Reject the connection ostringstream o; - o << "Client " << clientipstr() + o << "Client " << inet2string(clientfd().clientaddr().sin_addr) << " has hit the HARD maximum number of connections (" << config.hardmaxconnrate() << " conections in " << config.connrate_time() << " seconds; " - << accesslog[clientip().s_addr].size() + << accesslog[key].size() << " connections recorded). Client is refused.\n"; warnmsg (o.str()); - socketclose(clientfd()); - run_excess(config.hardmaxconnexcess(), clientipstr().c_str()); + run_excess(config.hardmaxconnexcess(), + inet2string(clientfd().clientaddr().sin_addr).c_str()); return false; - } else if (config.softmaxconnrate() && - (accesslog[clientip().s_addr].size() >= - config.softmaxconnrate())) { + } else if ( config.softmaxconnrate() && + (accesslog[key].size() >= config.softmaxconnrate()) ) { // This IP has violated the "SOFT" Limit. Go to sleep for a while. ostringstream o; - o << "Client " << clientipstr() + o << "Client " << inet2string(clientfd().clientaddr().sin_addr) << " has hit the SOFT maximum number of connections (" << config.softmaxconnrate() << " connections in " << config.connrate_time() << " sedonds; " - << accesslog[clientip().s_addr].size() + << accesslog[key].size() << " connections recorded). Client is deferred for " << config.defertime() << " microseconds.\n"; warnmsg (o.str()); - run_excess(config.softmaxconnexcess(), clientipstr().c_str()); + run_excess(config.softmaxconnexcess(), + inet2string(clientfd().clientaddr().sin_addr).c_str()); usleep(config.defertime()); } } diff --git a/xr/Dispatchers/dispatcher/clientipstr.cc b/xr/Dispatchers/dispatcher/clientipstr.cc @@ -1,7 +0,0 @@ -#include "dispatcher" - -string const &Dispatcher::clientipstr() { - if (clientip_str == "") - clientip_str = inet2string(clientip()); - return clientip_str; -} diff --git a/xr/Dispatchers/dispatcher/dispatcher b/xr/Dispatchers/dispatcher/dispatcher @@ -10,6 +10,7 @@ #include "ThreadsAndMutexes/threadlist/threadlist" #include "backendvector/backendvector" #include "netbuffer/netbuffer" +#include "SocketHandling/socket/socket" // Dispatching algorithm workers #include "DispatchAlgorithms/algorithm/algorithm" @@ -29,8 +30,7 @@ class Dispatcher: public Thread { public: - Dispatcher(int fd, struct in_addr ip); - Dispatcher(int fd); + Dispatcher(Socket &s); virtual ~Dispatcher(); virtual void execute() = 0; @@ -42,14 +42,13 @@ public: int targetbackend() const { return target_backend; } void targetbackend(int t) { target_backend = t; } - struct in_addr clientip() const { return client_ip; } - void clientip(struct in_addr i) { client_ip = i; - clientip_str = ""; } - string const &clientipstr(); - int clientfd() const { return client_fd; } - void clientfd(int c) { client_fd = c; } - int backendfd() const { return backend_fd; } - void backendfd(int b) { backend_fd = b; } + + Socket &clientfd() { return client_fd; } + void clientfd(Socket &c) { client_fd = c; } + + Socket &backendfd() { return backend_fd; } + void backendfd(Socket &b) { backend_fd = b; } + Algorithm *algorithm() const { return algo; } BackendVector &targetlist() { return target_list; } @@ -57,11 +56,11 @@ public: private: void start_dispatcher(); - struct in_addr client_ip; - int target_backend, client_fd, backend_fd; + + int target_backend; + Socket client_fd, backend_fd; Algorithm *algo; BackendVector target_list; - string clientip_str; }; #endif diff --git a/xr/Dispatchers/dispatcher/dispatcher2.cc b/xr/Dispatchers/dispatcher/dispatcher0.cc diff --git a/xr/Dispatchers/dispatcher/dispatcher1.cc b/xr/Dispatchers/dispatcher/dispatcher1.cc @@ -1,8 +1,8 @@ #include "dispatcher" -Dispatcher::Dispatcher(int cfd, struct in_addr cip): - Thread(), client_ip(cip), target_backend(-1), client_fd(cfd), - backend_fd(-1), algo(0), target_list(), clientip_str() { +Dispatcher::Dispatcher(Socket &clientsock): + Thread(), target_backend(-1), client_fd(clientsock), + backend_fd(-1), algo(0), target_list() { start_dispatcher(); } diff --git a/xr/Dispatchers/dispatcher/dispatcher3.cc b/xr/Dispatchers/dispatcher/dispatcher3.cc @@ -1,8 +0,0 @@ -#include "dispatcher" - -Dispatcher::Dispatcher(int fd): - Thread(), target_backend(-1), client_fd(fd), - backend_fd(-1), algo(0), target_list(), clientip_str() { - - start_dispatcher(); -} diff --git a/xr/Dispatchers/httpdispatcher/handle.cc b/xr/Dispatchers/httpdispatcher/handle.cc @@ -8,7 +8,8 @@ void HttpDispatcher::handle() { if (config.addxrversion()) buf().setheader ("XR", VER); if (config.addxforwardedfor()) - buf().addheader ("X-Forwarded-For", clientipstr()); + buf().addheader ("X-Forwarded-For", + inet2string(clientfd().clientaddr().sin_addr)); for (unsigned n = 0; n < config.nserverheaders(); n++) buf().setheader (config.serverheader(n)); @@ -16,7 +17,7 @@ void HttpDispatcher::handle() { if (config.replacehostheader()) 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_write_timeout()); @@ -40,16 +41,16 @@ void HttpDispatcher::handle() { readset.add(backendfd()); readset.wait_r(); - int sock; + Socket sock; if (readset.readable(clientfd())) sock = clientfd(); else if (readset.readable(backendfd())) sock = backendfd(); else break; - + buf().reset(); - + if (!buf().netread(sock)) break; @@ -67,8 +68,8 @@ void HttpDispatcher::handle() { buf().setheader("Set-Cookie", o.str()); } } - - // The back end response may now get flushed to the client. + + // 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()) { @@ -81,21 +82,23 @@ void HttpDispatcher::handle() { } // Flush info to the other connected side. - int othersock, timeout; + Socket othersock; + int timeout; if (sock == clientfd()) { othersock = backendfd(); timeout = config.backend_read_timeout(); // Re-patch Host header if requested if (config.replacehostheader()) - buf().replaceheader("Host:", - balancer.backend(targetbackend()).server()); + buf().replaceheader("Host:", + balancer.backend(targetbackend()) + .server()); } else { othersock = clientfd(); timeout = config.client_read_timeout(); } - debugmsg ("Had data on " << sock << ", sending to " << othersock - << '\n'); + debugmsg ("Had data on " << sock.fd() << + ", sending to " << othersock.fd() << '\n'); buf().netwrite(othersock, timeout); if (sock == backendfd()) diff --git a/xr/Dispatchers/httpdispatcher/httpdispatcher b/xr/Dispatchers/httpdispatcher/httpdispatcher @@ -4,15 +4,16 @@ #include "sys/sys" #include "Dispatchers/tcpdispatcher/tcpdispatcher" #include "httpbuffer/httpbuffer" +#include "SocketHandling/socket/socket" class HttpDispatcher: public TcpDispatcher { public: - HttpDispatcher (int fd, struct in_addr ip); + HttpDispatcher(Socket &s): TcpDispatcher(s) {} void dispatch(); void handle(); - bool issticky() const { return (is_sticky); } - void issticky (bool s) { is_sticky = s; } + bool issticky() const { return is_sticky; } + void issticky(bool s) { is_sticky = s; } private: void senderrorpage(string const &desc); diff --git a/xr/Dispatchers/httpdispatcher/httpdispatcher1.cc b/xr/Dispatchers/httpdispatcher/httpdispatcher1.cc @@ -1,5 +0,0 @@ -#include "httpdispatcher" - -HttpDispatcher::HttpDispatcher (int fd, struct in_addr ip) : - TcpDispatcher (fd, ip) { -} diff --git a/xr/Dispatchers/tcpdispatcher/dispatch.cc b/xr/Dispatchers/tcpdispatcher/dispatch.cc @@ -25,8 +25,15 @@ void TcpDispatcher::dispatch() { // Call the dispatch algorithm until we can connect, // or until the algorithm is out of back ends (throws exception). while (!connected) { - targetbackend(algorithm()->target(clientip(), targetlist())); - Backend tb = balancer.backend(targetbackend()); + targetbackend(algorithm()->target(clientfd().clientaddr().sin_addr, + targetlist())); + + // Define a dummy back end according to the current selection. + // Connect to it. If that succeeds, get the socket from it. + Backend tb; + tb.server(balancer.backend(targetbackend()).server()); + tb.port(balancer.backend(targetbackend()).port()); + if (!tb.connect()) { balancer.backend(targetbackend()).live(false); if (config.verbose()) @@ -36,7 +43,7 @@ void TcpDispatcher::dispatch() { connected = true; backendfd(tb.sock()); msg ("Will dispatch client to back end " << tb.description() << - " on fd " << tb.sock() << '\n'); + " on fd " << tb.sock().fd() << '\n'); break; } } diff --git a/xr/Dispatchers/tcpdispatcher/execute.cc b/xr/Dispatchers/tcpdispatcher/execute.cc @@ -2,13 +2,13 @@ void TcpDispatcher::execute() { Threadlist::clientfd(clientfd()); - Threadlist::clientip(clientip()); + Threadlist::clientip(clientfd().clientaddr().sin_addr); if (!check_dos() || !check_acl()) return; - debugmsg("Dispatch request for client fd " << clientfd() << '\n'); + debugmsg("Dispatch request for client fd " << clientfd().fd() << '\n'); // Try to determine the back end. try { @@ -16,23 +16,19 @@ void TcpDispatcher::execute() { dispatch(); } catch (Error const &e) { warnmsg(e.what() << '\n'); - socketclose(clientfd()); - socketclose(backendfd()); return; } // Verify that the target is within the allowed set. if (targetbackend() < 0 || targetbackend() >= (int)balancer.nbackends()) { warnmsg("Target back end " << targetbackend() << "out of range\n"); - socketclose(clientfd()); - socketclose(backendfd()); return; } // Dispatch! - msg("Dispatching client fd " << clientfd() << " to " << + msg("Dispatching client fd " << clientfd().fd() << " to " << balancer.backend(targetbackend()).description() << ", fd " << - backendfd() << '\n'); + backendfd().fd() << '\n'); Threadlist::desc("Serving"); Threadlist::backend(targetbackend()); @@ -41,7 +37,8 @@ void TcpDispatcher::execute() { balancer.backend(targetbackend()).startconnection(); if (config.onstart().length()) { ostringstream o; - o << config.onstart() << ' ' << clientipstr() << ' ' + o << config.onstart() << ' ' + << inet2string(clientfd().clientaddr().sin_addr) << balancer.backend(targetbackend()).description() << ' ' << balancer.backend(targetbackend()).connections(); msg("Running onstart script: " << o.str() << '\n'); @@ -56,7 +53,8 @@ void TcpDispatcher::execute() { failed = true; if (config.onfail().length()) { ostringstream o; - o << config.onfail() << ' ' << clientipstr() << ' ' + o << config.onfail() << ' ' + << inet2string(clientfd().clientaddr().sin_addr) << balancer.backend(targetbackend()).description() << ' ' << balancer.backend(targetbackend()).connections(); msg("Running onfail script: " << o.str() << '\n'); @@ -64,19 +62,17 @@ void TcpDispatcher::execute() { } } - socketclose (clientfd()); - socketclose (backendfd()); - balancer.backend(targetbackend()).endconnection(); if (!failed && config.onend().length()) { ostringstream o; - o << config.onend() << ' ' << clientipstr() << ' ' + o << config.onend() << ' ' + << inet2string(clientfd().clientaddr().sin_addr) << balancer.backend(targetbackend()).description() << ' ' << balancer.backend(targetbackend()).connections(); msg("Running onend script: " << o.str() << '\n'); sysrun(o.str()); } - msg ("Done dispatching to back end fd " << backendfd() << " at " << + msg ("Done dispatching to back end fd " << backendfd().fd() << " at " << balancer.backend(targetbackend()).description() << '\n'); } diff --git a/xr/Dispatchers/tcpdispatcher/handle.cc b/xr/Dispatchers/tcpdispatcher/handle.cc @@ -3,7 +3,8 @@ void TcpDispatcher::handle() { debugmsg("TCP dispatcher: About to shuttle between client fd " << - clientfd() << " and backend fd " << backendfd() << '\n'); + clientfd().fd() << " and backend fd " << backendfd().fd() << + '\n'); while (1) { Fdset readset(maxtimeout(config.client_read_timeout(), @@ -12,7 +13,8 @@ void TcpDispatcher::handle() { readset.add(backendfd()); readset.wait_r(); - int sock, othersock, timeout; + Socket sock, othersock; + int timeout; if (readset.readable(clientfd())) { sock = clientfd(); othersock = backendfd(); @@ -26,13 +28,14 @@ void TcpDispatcher::handle() { if (!netbuffer.netread(sock)) break; - debugmsg("Had data on " << sock << ", sending to " << othersock << - '\n'); + debugmsg("Had data on " << sock.fd() << + ", sending to " << othersock.fd() << '\n'); netbuffer.netwrite (othersock, timeout); if (sock == backendfd()) balancer.backend(targetbackend()).addbytes(netbuffer.bufsz()); else - IPStore::activity(clientip(), targetbackend()); + IPStore::activity(clientfd().clientaddr().sin_addr, + targetbackend()); netbuffer.reset(); } diff --git a/xr/Dispatchers/tcpdispatcher/tcpdispatcher b/xr/Dispatchers/tcpdispatcher/tcpdispatcher @@ -5,17 +5,17 @@ #include "netbuffer/netbuffer" #include "httpbuffer/httpbuffer" #include "ipstore/ipstore" +#include "SocketHandling/socket/socket" class TcpDispatcher: public Dispatcher { public: - - TcpDispatcher (int fd, struct in_addr ip); + TcpDispatcher(Socket &clientsock): Dispatcher(clientsock), netbuffer() {} virtual void execute(); virtual void dispatch(); virtual void handle(); - unsigned readchunk (int src); + unsigned readchunk(int src); Httpbuffer &buf() { return netbuffer; } diff --git a/xr/Dispatchers/tcpdispatcher/tcpdispatcher1.cc b/xr/Dispatchers/tcpdispatcher/tcpdispatcher1.cc @@ -1,5 +0,0 @@ -#include "tcpdispatcher" - -TcpDispatcher::TcpDispatcher(int cfd, struct in_addr cip): - Dispatcher(cfd, cip), netbuffer() { -} diff --git a/xr/Dispatchers/udpdispatcher/dispatch.cc b/xr/Dispatchers/udpdispatcher/dispatch.cc @@ -1,5 +0,0 @@ -#include "udpdispatcher" - -void UdpDispatcher::dispatch() { - throw Error("UDP dispatcher: dispatch not yet implemented"); -} diff --git a/xr/Dispatchers/udpdispatcher/execute.cc b/xr/Dispatchers/udpdispatcher/execute.cc @@ -1,5 +0,0 @@ -#include "udpdispatcher" - -void UdpDispatcher::execute() { - throw Error("UDP dispatcher: execute not yet implemented"); -} diff --git a/xr/Dispatchers/udpdispatcher/handle.cc b/xr/Dispatchers/udpdispatcher/handle.cc @@ -1,5 +0,0 @@ -#include "udpdispatcher" - -void UdpDispatcher::handle() { - throw Error("UDP dispatcher: handle not yet implemented"); -} diff --git a/xr/Dispatchers/udpdispatcher/udpdispatcher b/xr/Dispatchers/udpdispatcher/udpdispatcher @@ -1,16 +0,0 @@ -#ifndef _UDPDISPATCHER_ -#define _UDPDISPATCHER_ - -#include "Dispatchers/dispatcher/dispatcher" - -class UdpDispatcher: public Dispatcher { -public: - UdpDispatcher(int fd); - virtual void execute(); - virtual void dispatch(); - virtual void handle(); -private: - Netbuffer netbuffer; -}; - -#endif diff --git a/xr/Dispatchers/udpdispatcher/udpdispatcher1.cc b/xr/Dispatchers/udpdispatcher/udpdispatcher1.cc @@ -1,4 +0,0 @@ -#include "udpdispatcher" - -UdpDispatcher::UdpDispatcher(int fd) : Dispatcher(fd), netbuffer() { -} diff --git a/xr/Makefile b/xr/Makefile @@ -38,7 +38,8 @@ subdirs: $(BUILDDIR)/usage.h $(BUILDDIR)/status.xslt.h DISTSITE='$(DISTSITE)' MEMDEBUG=$(MEMDEBUG)\ CONF_CC='$(CONF_CC)' CONF_LIB='$(CONF_LIB)' \ CONF_GETOPT=$(CONF_GETOPT) CONF_GETOPT_LONG=$(CONF_GETOPT_LONG) \ - CONF_INET_ATON=$(CONF_INET_ATON) CONF_OPTFLAGS='$(CONF_OPTFLAGS)' \ + CONF_INET_ATON=$(CONF_INET_ATON) \ + CONF_OPTFLAGS='$(CONF_OPTFLAGS)' \ CONF_STRNSTR=$(CONF_STRNSTR) \ $(MAKE) -C $$f -f $(BASE)/xr/etc/Makefile.class \ || exit 1; \ diff --git a/xr/SocketHandling/basesocket/accept.cc b/xr/SocketHandling/basesocket/accept.cc @@ -0,0 +1,28 @@ +#include "basesocket" + +Basesocket *Basesocket::accept() { + int f; + + int size; + struct sockaddr_in claddr; + + debugmsg("About to accept client on server socket " << _fd << '\n'); + if ( (f = ::accept(_fd, (struct sockaddr *)&claddr, + (socklen_t *)&size)) >= 0 ) { + Basesocket *ret = new Basesocket(f); + ret->_clientaddr = claddr; + debugmsg("Client " << inet2string(ret->_clientaddr.sin_addr) << + " accepted as basesocket " << f << '\n'); + return ret; + } + + if (EMFILE_SLEEP && errno == EMFILE) { + warnmsg("Out of file descriptors while accepting network " + "connection, taking a short nap\n"); + sleep(1); + } + throw Error(string("Failed to accept client: ") + + strerror(errno)); +} + + diff --git a/xr/SocketHandling/basesocket/basesocket b/xr/SocketHandling/basesocket/basesocket @@ -0,0 +1,28 @@ +#ifndef _BASESOCKET_ +#define _BASESOCKET_ + +#include "sys/sys" +#include "config/config" + +class Basesocket { +public: + Basesocket(); + Basesocket(int fd); + ~Basesocket(); + + int fd(); + void fd(int f); + + void bind(string const &addr, int port); + bool operator==(Basesocket const &other) const; + void close(); + Basesocket *accept(); + struct sockaddr_in clientaddr(); + +private: + int _fd; + int _initialized; + struct sockaddr_in _clientaddr; +}; + +#endif diff --git a/xr/SocketHandling/basesocket/basesocket1.cc b/xr/SocketHandling/basesocket/basesocket1.cc @@ -0,0 +1,4 @@ +#include "basesocket" + +Basesocket::Basesocket(): _fd(-1), _initialized(false) { +} diff --git a/xr/SocketHandling/basesocket/basesocket2.cc b/xr/SocketHandling/basesocket/basesocket2.cc @@ -0,0 +1,4 @@ +#include "basesocket" + +Basesocket::Basesocket(int f): _fd(f), _initialized(true) { +} diff --git a/xr/SocketHandling/basesocket/basesocket3.cc b/xr/SocketHandling/basesocket/basesocket3.cc @@ -0,0 +1,5 @@ +#include "basesocket" + +Basesocket::~Basesocket() { + close(); +} diff --git a/xr/SocketHandling/basesocket/bind.cc b/xr/SocketHandling/basesocket/bind.cc @@ -0,0 +1,40 @@ +#include "basesocket" + +void Basesocket::bind(string const &addr, int port) { + // Make socket reusable. + int val = 1; + if (setsockopt (fd(), SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + ostringstream o; + o << "Failed to set reuse socket options on fd " << fd() + << ": " << strerror(errno); + throw Error(o.str()); + } + + // Prepare binding. + struct sockaddr_in saddr; + memset (&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + + // Assign interface to listen to + if (addr[0] != '0') { + debugmsg ("Binding socket " << fd() << " to specific IP address " << + addr << '\n'); + if ( (saddr.sin_addr.s_addr = inet_addr (addr.c_str())) == + INADDR_NONE ) + throw Error("Cannot convert IP " + addr + " to network bytes"); + } else { + debugmsg ("Binding socket " << fd() << " to all local IP addresses\n"); + saddr.sin_addr.s_addr = htonl (INADDR_ANY); + } + + // Bind and listen + if (::bind (fd(), (sockaddr*) &saddr, sizeof(saddr)) < 0) + throw Error(string("Failed to bind to IP/port: ") + strerror(errno)); + if (listen (fd(), 5) < 0) + throw Error(string("Failed to listen to IP/port: ") + + strerror(errno)); + msg("Listening for activity on fd " << fd() << '\n'); +} + + diff --git a/xr/SocketHandling/basesocket/clientaddr.cc b/xr/SocketHandling/basesocket/clientaddr.cc @@ -0,0 +1,5 @@ +#include "basesocket" + +struct sockaddr_in Basesocket::clientaddr() { + return _clientaddr; +} diff --git a/xr/SocketHandling/basesocket/close.cc b/xr/SocketHandling/basesocket/close.cc @@ -0,0 +1,16 @@ +#include "basesocket" + +void Basesocket::close() { + if (_initialized && _fd > 0) { + debugmsg("Shutting down socket " << _fd << '\n'); + if (config.fastclose()) { + struct linger l; + l.l_onoff = 1; + l.l_linger = 2; + setsockopt (_fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); + } + shutdown(_fd, SHUT_RDWR); + ::close(_fd); + _fd = -1; + } +} diff --git a/xr/SocketHandling/basesocket/fd1.cc b/xr/SocketHandling/basesocket/fd1.cc @@ -0,0 +1,15 @@ +#include "basesocket" + +int Basesocket::fd() { + if (_initialized) + return _fd; + + _initialized = true; + if ( (_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) + throw Error(string("Failed to create socket: ") + + strerror(errno)); + + debugmsg("Socket " << _fd << " created\n"); + + return _fd; +} diff --git a/xr/SocketHandling/basesocket/fd2.cc b/xr/SocketHandling/basesocket/fd2.cc @@ -0,0 +1,6 @@ +#include "basesocket" + +void Basesocket::fd(int f) { + _fd = f; + _initialized = true; +} diff --git a/xr/SocketHandling/basesocket/opeq.cc b/xr/SocketHandling/basesocket/opeq.cc @@ -0,0 +1,5 @@ +#include "basesocket" + +bool Basesocket::operator==(Basesocket const &other) const { + return _fd == other._fd; +} diff --git a/xr/SocketHandling/socket/accept.cc b/xr/SocketHandling/socket/accept.cc @@ -0,0 +1,10 @@ +#include "socket" + +Socket Socket::accept() { + // Try to accept the client connection (or throw up) + Basesocket *client = _basesocket->accept(); + + // Got a connection. Create a new smartsocket and return it. + return Socket(client); +} + diff --git a/xr/SocketHandling/socket/description.cc b/xr/SocketHandling/socket/description.cc @@ -0,0 +1,8 @@ +#include "socket" + +string Socket::description() { + ostringstream o; + + o << "refcount:" << *_refcount << ", base:" << _basesocket; + return o.str(); +} diff --git a/xr/SocketHandling/socket/opis.cc b/xr/SocketHandling/socket/opis.cc @@ -0,0 +1,32 @@ +#include "socket" + +Socket const &Socket::operator=(Socket const &other) { + if (this != &other) { + + #ifdef SOCKET_DEBUG + if (*_refcount == 1) { + debugmsg("Socket: destroying for assignment " << description() + << '\n'); + } else { + debugmsg("Socket: subtracting for assignment " << description() + << '\n'); + } + #endif + + (*_refcount)--; + if (! *_refcount) { + delete _refcount; + delete _basesocket; + } + + _refcount = other._refcount; + (*_refcount)++; + _basesocket = other._basesocket; + + #ifdef SOCKET_DEBUG + debugmsg("Socket: assigned " << description() << '\n'); + #endif + } + + return *this; +} diff --git a/xr/SocketHandling/socket/socket b/xr/SocketHandling/socket/socket @@ -0,0 +1,51 @@ +#ifndef _SOCKET_ +#define _SOCKET_ + +#include "sys/sys" +#include "error/error" +#include "config/config" +#include "SocketHandling/basesocket/basesocket" + +// To have the smart-socket approach generate debug info: +#define SOCKET_DEBUG + +class Socket { +public: + Socket(); + Socket(Socket const &other); + Socket(int fd); + Socket(Basesocket *b); + ~Socket(); + + Socket const &operator=(Socket const &other); + Socket accept(); + + int fd() { + return _basesocket->fd(); + } + void fd(int f) { + _basesocket->fd(f); + } + void bind(string const &addr, int port) { + _basesocket->bind(addr, port); + } + bool operator==(Socket const &other) const { + return *_basesocket == *(other._basesocket); + } + void close() { + _basesocket->close(); + *_refcount = 1; + } + + struct sockaddr_in clientaddr() { + return _basesocket->clientaddr(); + } + + string description(); + +private: + int *_refcount; + Basesocket *_basesocket; +}; + +#endif diff --git a/xr/SocketHandling/socket/socket0.cc b/xr/SocketHandling/socket/socket0.cc @@ -0,0 +1,17 @@ +#include "socket" + +Socket::~Socket() { + #ifdef SOCKET_DEBUG + if (*_refcount == 1) { + debugmsg("Socket: discarding " << description() << '\n'); + } else { + debugmsg("Socket: subtracting " << description() << '\n'); + } + #endif + + (*_refcount)--; + if (! *_refcount) { + delete _refcount; + delete _basesocket; + } +} diff --git a/xr/SocketHandling/socket/socket1.cc b/xr/SocketHandling/socket/socket1.cc @@ -0,0 +1,11 @@ +#include "socket" + +Socket::Socket() { + _refcount = new int; + *_refcount = 1; + _basesocket = new Basesocket; + + #ifdef SOCKET_DEBUG + debugmsg("Socket: created fresh, " << description() << '\n'); + #endif +} diff --git a/xr/SocketHandling/socket/socket2.cc b/xr/SocketHandling/socket/socket2.cc @@ -0,0 +1,11 @@ +#include "socket" + +Socket::Socket(Socket const &other) { + _refcount = other._refcount; + _basesocket = other._basesocket; + (*_refcount)++; + + #ifdef SOCKET_DEBUG + debugmsg("Socket: created copy, " << description() << '\n'); + #endif +} diff --git a/xr/SocketHandling/socket/socket3.cc b/xr/SocketHandling/socket/socket3.cc @@ -0,0 +1,11 @@ +#include "socket" + +Socket::Socket(int f) { + _refcount = new int; + *_refcount = 1; + _basesocket = new Basesocket(f); + + #ifdef SOCKET_DEBUG + debugmsg("Socket: created with fd, " << description() << '\n'); + #endif +} diff --git a/xr/SocketHandling/socket/socket4.cc b/xr/SocketHandling/socket/socket4.cc @@ -0,0 +1,11 @@ +#include "socket" + +Socket::Socket(Basesocket *b) { + _refcount = new int; + *_refcount = 1; + _basesocket = b; + + #ifdef SOCKET_DEBUG + debugmsg("Socket: created with basesocket, " << description() << '\n'); + #endif +} diff --git a/xr/ThreadsAndMutexes/threadlist/backendfd.cc b/xr/ThreadsAndMutexes/threadlist/backendfd.cc @@ -1,5 +1,5 @@ #include "threadlist" -void Threadlist::backendfd(int f) { - th_map[pthread_self()].backendfd(f); +void Threadlist::backendfd(Socket &f) { + th_map[pthread_self()].backendfd(f.fd()); } diff --git a/xr/ThreadsAndMutexes/threadlist/clientfd.cc b/xr/ThreadsAndMutexes/threadlist/clientfd.cc @@ -1,5 +1,5 @@ #include "threadlist" -void Threadlist::clientfd(int f) { - th_map[pthread_self()].clientfd(f); +void Threadlist::clientfd(Socket &f) { + th_map[pthread_self()].clientfd(f.fd()); } diff --git a/xr/ThreadsAndMutexes/threadlist/threadlist b/xr/ThreadsAndMutexes/threadlist/threadlist @@ -3,6 +3,7 @@ #include "sys/sys" #include "ThreadsAndMutexes/threadinfo/threadinfo" +#include "SocketHandling/socket/socket" typedef map<pthread_t, Threadinfo> Threadmap; @@ -15,8 +16,8 @@ public: static Threadinfo info(pthread_t id); static void desc(string const &s); static void backend(int b); - static void clientfd(int f); - static void backendfd(int f); + static void clientfd(Socket &f); + static void backendfd(Socket &f); static void clientip(struct in_addr adr); private: diff --git a/xr/backend/backend b/xr/backend/backend @@ -9,18 +9,20 @@ #include "backendcheck/backendcheck" #include "httpbuffer/httpbuffer" #include "dnsentry/dnsentry" +#include "SocketHandling/socket/socket" using namespace std; class Backend { public: - Backend (); - Backend (BackendDef const &b); + Backend(); + Backend(BackendDef const &b); virtual ~Backend(); + bool connect(); void markconnecterror(); - int sock() const { return clsocket; } + Socket &sock() { return clsocket; } void check(); string description() const; @@ -29,10 +31,10 @@ public: string availablestr() const; bool live() const { return islive; }; - void live (bool state); + void live(bool state); string livestr() const; - void up (bool state); + void up(bool state); bool up() const { return isup; } string upstr() const; @@ -43,7 +45,7 @@ public: void port(int p) { bdef.port(p); } unsigned maxconn() const { return bdef.maxconn(); } - void maxconn (unsigned m) { bdef.maxconn(m); } + void maxconn(unsigned m) { bdef.maxconn(m); } string const &hostmatch() const { return bdef.hostmatch(); } void hostmatch(string const &s) { bdef.hostmatch(s); } @@ -54,7 +56,7 @@ public: regex_t const &urlregex() const { return bdef.urlregex(); } unsigned weight() const { return bdef.weight(); } - void weight (unsigned w) { bdef.weight(w); } + void weight(unsigned w) { bdef.weight(w); } unsigned adjustedweight() const { return bdef.adjustedweight(); } unsigned connections() const { return nconn; } @@ -66,7 +68,7 @@ public: double loadavg() const { return loadaverage; } void loadavg(double l) { loadaverage = l; } - void addbytes (unsigned n); + void addbytes(unsigned n); void startconnection(); void endconnection(); @@ -89,7 +91,7 @@ private: BackendDef bdef; bool islive; bool isup; - int clsocket; + Socket clsocket; unsigned nconn, totconn, nconnerr; double bytes_served; double loadaverage; diff --git a/xr/backend/backend1.cc b/xr/backend/backend1.cc @@ -1,7 +1,7 @@ #include "backend" Backend::Backend () : - bdef(), islive(true), isup(true), clsocket(-1), + bdef(), islive(true), isup(true), clsocket(), nconn(0), totconn(0), nconnerr(0), bytes_served(0), loadaverage(0.1), dnsentry(), index(-1) { diff --git a/xr/backend/backend2.cc b/xr/backend/backend2.cc @@ -1,7 +1,7 @@ #include "backend" Backend::Backend (BackendDef const &b) : - bdef(b), islive(true), isup(true), clsocket(-1), nconn(0), totconn(0), + bdef(b), islive(true), isup(true), clsocket(), nconn(0), totconn(0), nconnerr(0), bytes_served(0), loadaverage(0.1), dnsentry(), index(-1) { } diff --git a/xr/backend/check.cc b/xr/backend/check.cc @@ -1,7 +1,7 @@ #include "backend" void Backend::check() { - debugmsg("About to check back end " << description() << ". " << + debugmsg("About to check back end " << description() << ": " << backendcheck().description() << '\n'); ostringstream o; @@ -10,44 +10,24 @@ void Backend::check() { switch (backendcheck().checktype()) { case BackendCheck::c_connect: - if (backendcheck().server() == "" && backendcheck().port() == 0) { - // Most common: TCP connect to the actual back end - tester = *this; - // Retry CONNCHECK times (see etc/Makefile.class). - for (int i = 0; i < CONNCHECKS; i++) { - tester.connect(); - socketclose(tester.sock()); - debugmsg("TCP-connect testing back end " << tester.description() << - " try " << i << ": " << - (tester.live() ? "alive" : "not-alive") - << '\n'); - } - if ( (tester.live() && !live()) || - (!tester.live() && live()) ) { - debugmsg("State of back end " << tester.description() << - " is now " << (tester.live() ? "alive" : "not-alive") << - '\n'); - live(tester.live()); - } - /* This was: - connect(); - socketclose(sock()); - */ - } else { - // TCP connects to an alternative server or port. - // We instantiate a dummy backend and let it connect to the "other" - // values. - tester = *this; - if (backendcheck().server() != "") - tester.server(backendcheck().server()); - if (backendcheck().port() != 0) - tester.port(backendcheck().port()); + // Assume we test at the true back end address/port. + // Override if necessary. + tester = *this; + if (backendcheck().server() != "") + tester.server(backendcheck().server()); + if (backendcheck().port() != 0) + tester.port(backendcheck().port()); + + // Attempt CONNECT CHECK times (see /etc/Makefile.class) + for (int i = 0; i < CONNCHECKS; i++) { tester.connect(); - socketclose (tester.sock()); - live(tester.live()); - debugmsg("Alternative back end for testing " << - tester.description() << " is " << livestr() << '\n'); + if (tester.live()) + break; } + live(tester.live()); + debugmsg("Check of back end " << description() << + " by connecting to " << tester.description() << + ", result: " << livestr() << '\n'); break; case BackendCheck::c_get: @@ -78,7 +58,6 @@ void Backend::check() { else debugmsg("Back end assumed dead.\n"); } - socketclose(tester.sock()); break; case BackendCheck::c_external: diff --git a/xr/backend/connect.cc b/xr/backend/connect.cc @@ -8,11 +8,6 @@ bool Backend::connect() { debugmsg("About to connect to back end " << description() << '\n'); - // Create client socket - if ( (clsocket = socket (PF_INET, SOCK_STREAM, 0)) < 0 ) - throw Error(string("Failed to create client socket: ") + - strerror(errno)); - // Resolve hostname, prepare binding struct sockaddr_in backendaddr; @@ -23,29 +18,23 @@ bool Backend::connect() { // Client socket goes into nonblocking mode, so we can connect // and enforce a timeout later. int flags; - if ( (flags = fcntl (clsocket, F_GETFL, 0)) == -1 ) { - socketclose (clsocket); - throw Error(string("Failed to get fd flags: ") + strerror(errno)); - } - if (fcntl (clsocket, F_SETFL, flags | O_NONBLOCK) == -1) { - socketclose (clsocket); + if ( (flags = fcntl (clsocket.fd(), F_GETFL, 0)) == -1 ) + throw Error(string("Failed to get fd flags: ") + strerror(errno)); + if (fcntl (clsocket.fd(), F_SETFL, flags | O_NONBLOCK) == -1) throw Error(string("Failed to fd in nonblocking mode: ") + strerror(errno)); - } // Do the connect - int conres = ::connect (clsocket, (struct sockaddr *)&backendaddr, + int conres = ::connect (clsocket.fd(), (struct sockaddr *)&backendaddr, sizeof(backendaddr)); int conerrno = errno; debugmsg("Connect result: " << conres << - ", errno: " << conerrno << '\n'); + ", errno: " << conerrno << ' ' << strerror(conerrno) << '\n'); // Put socket again in blocking mode. - if (fcntl (clsocket, F_SETFL, flags) == -1) { - socketclose (clsocket); + if (fcntl (clsocket.fd(), F_SETFL, flags) == -1) throw Error(string("Failed to put fd in blocking mode: ") + strerror(errno)); - } // Check on the outcome of the connect if (!conres || conerrno == EINPROGRESS) { @@ -57,10 +46,8 @@ bool Backend::connect() { # ifdef CONNECTCHECK_ONLY_WRITABLE if (fdset.writeable(clsocket)) islive = true; - else { + else markconnecterror(); - socketclose(clsocket); - } # else if (fdset.writeable(clsocket) && !fdset.readable(clsocket)) islive = true; @@ -70,14 +57,13 @@ bool Backend::connect() { ", readable: " << (fdset.readable(clsocket) ? "yes" : "no") << '\n'); - socketclose(clsocket); markconnecterror(); } # endif } debugmsg("Back end " << description() << " is " << livestr() << - " (socket " << clsocket << ")\n"); + " (socket " << clsocket.fd() << ")\n"); return (islive); } diff --git a/xr/balancer/balancer b/xr/balancer/balancer @@ -9,6 +9,7 @@ #include "config/config" #include "fdset/fdset" #include "error/error" +#include "SocketHandling/socket/socket" // Check ups of back ends and the web interface #include "Checkers/wakeupthread/wakeupthread" @@ -44,7 +45,7 @@ private: void init_tcp (); void init_fd (); - int server_fd; + Socket server_fd; long request_nr; vector<Backend> backends; bool term; diff --git a/xr/balancer/balancer1.cc b/xr/balancer/balancer1.cc @@ -1,6 +1,6 @@ #include "balancer" Balancer::Balancer () : - server_fd(-1), request_nr(0), backends(), + server_fd(), request_nr(0), backends(), term(false), rep(false), rest(false), webinterface(0) { } diff --git a/xr/balancer/init.cc b/xr/balancer/init.cc @@ -2,15 +2,13 @@ void Balancer::init() { // Set the listening socket. - if (config.stype() != Servertype::t_udp) { - if (config.sport()) - server_fd = serversocket(config.sipaddr(), config.sport(), - "balancer", Servertype::t_tcp); - else - server_fd = 0; + if (! config.sport()) { + // In inetd mode, set the server socket to fd 0. + // Else it will be initialized. + server_fd.fd(0); } else { - server_fd = serversocket(config.sipaddr(), config.sport(), - "balancer", Servertype::t_udp); + // In TCP mode: Prepare the binding. + server_fd.bind(config.sipaddr(), config.sport()); } // Start the web interface if requested. diff --git a/xr/balancer/serve.cc b/xr/balancer/serve.cc @@ -1,25 +1,22 @@ #include "balancer" #include "Dispatchers/tcpdispatcher/tcpdispatcher" #include "Dispatchers/httpdispatcher/httpdispatcher" -#include "Dispatchers/udpdispatcher/udpdispatcher" // #define SHOWDEBUG void Balancer::serve() { - int clsock = -1; - // Start up wakeup/checkup handlers. These are always started - even // when config.wakeupsec() and config.checkupsec() are not defined // and have value 0. Via the web interface, the values can be later // changed, but we want to have the checkers running always. if (!config.foregroundmode() && config.sport()) { - msg ("Starting wakeup thread.\n"); + msg("Starting wakeup thread.\n"); Wakeupthread *wt = new Wakeupthread(); if (!wt) throw Error("Memory fault in Balancer::serve"); wt->start(); - msg ("Starting checkup thread.\n"); + msg("Starting checkup thread.\n"); Checkupthread *ct = new Checkupthread(); if (!ct) throw Error("Memory fault in Balancer::serve"); @@ -29,33 +26,33 @@ void Balancer::serve() { // Write the PID file. if (config.pidfile() != "") { FILE *f; - if (! (f = fopen (config.pidfile().c_str(), "w")) ) + if (! (f = fopen(config.pidfile().c_str(), "w")) ) throw Error(string("Cannot write pid file ") + config.pidfile() + ": " + strerror(errno)); - fprintf (f, "%u\n", getpid()); - fclose (f); + fprintf(f, "%u\n", getpid()); + fclose(f); } // Wait for activity, serve it. - msg ("Awaiting activity on fd " << server_fd << '\n'); + msg("Awaiting activity on fd " << server_fd.fd() << '\n'); MEM(Memory::mem_mark("Balancer start")); MEM(Memory::mem_follow(true)); while (true) { MEM(Memory::mem_display()); Fdset fdset(0); - fdset.add (server_fd); + fdset.add(server_fd); fdset.wait_r(); if (! fdset.readable(server_fd)) { // We caught a signal. Either a request to report status, // or to terminate. - msg ("Interrupt seen\n"); + msg("Interrupt seen\n"); if (terminate()) { - msg ("Termination requested, XR will stop.\n"); + msg("Termination requested, XR will stop.\n"); break; } else if (report()) { - msg ("Report requested\n"); - reportmsg ("*** XR STATUS REPORT STARTS ***\n"); + msg("Report requested\n"); + reportmsg("*** XR STATUS REPORT STARTS ***\n"); for (unsigned i = 0; i < nbackends(); i++) { reportmsg("Back end " << backend(i).description() << '\n'); reportmsg(" Status: " << @@ -68,55 +65,48 @@ void Balancer::serve() { " bytes, " << backend(i).clientsserved() << " clients\n"); } - report (false); - reportmsg ("*** XR STATUS REPORT ENDS ***\n"); + report(false); + reportmsg("*** XR STATUS REPORT ENDS ***\n"); continue; } else if (restart()) { - msg ("Restart requested\n"); + msg("Restart requested\n"); config.restart(); } else { - msg ("Non-meaningful interrupt or select timeout, resuming\n"); + msg("Non-meaningful interrupt or select timeout, " + "resuming\n"); continue; } } // Got activity! Check total # of connections. - msg ("Got activity on fd " << server_fd << '\n'); + msg("Got activity on fd " << server_fd.fd() << '\n'); request_nr++; if (config.maxconn() && connections() >= config.maxconn()) { - msg ("Not serving connection: already " << connections() << + msg("Not serving connection: already " << connections() << " connection(s) (max " << config.maxconn() << ")\n"); continue; } - if (server_fd) { - // In daemon mode (server_fd > 0): serve and loop again - struct sockaddr_in clname; - int size = sizeof(clname); - - // Accept the client if this is a TCP connection. - if (config.stype() != Servertype::t_udp) { - if ( (clsock = accept (server_fd, (struct sockaddr *) &clname, - (socklen_t*) &size)) < 0 ) { - warnmsg("Failed to accept network connection: " << - strerror(errno) << '\n'); - if (EMFILE_SLEEP && errno == EMFILE) { - warnmsg("Taking a nap for " << EMFILE_SLEEP << - " sec\n"); - sleep(EMFILE_SLEEP); - } - continue; - } - msg ("Accepted connection from " << - inet2string(clname.sin_addr) << " as client fd " << - clsock << '\n'); + if (server_fd.fd()) { + // In daemon mode (server_fd > 0): Accept, serve and loop again + Socket clsock; + try { + clsock = server_fd.accept(); + } catch (Error const &e) { + warnmsg(e.what() << '\n'); + clsock.close(); + continue; } + + msg("Accepted connection from " << + inet2string(clsock.clientaddr().sin_addr) << + " as client fd " << clsock.fd() << '\n'); // Show how we look if (config.verbose()) { ostringstream o; - msg ("Balancer is serving " << connections() << " clients\n"); - msg ("Current back end states:\n"); + msg("Balancer is serving " << connections() << " clients\n"); + msg("Current back end states:\n"); for (unsigned i = 0; i < nbackends(); i++) { msg(" Back end " << backend(i).description() << ": " << backend(i).connections() << " connections, max " << @@ -129,31 +119,29 @@ void Balancer::serve() { Dispatcher *d; switch (config.stype()) { case Servertype::t_tcp: - d = new TcpDispatcher(clsock, clname.sin_addr); + d = new TcpDispatcher(clsock); break; case Servertype::t_http: - d = new HttpDispatcher(clsock, clname.sin_addr); - break; - case Servertype::t_udp: - d = new UdpDispatcher(server_fd); + d = new HttpDispatcher(clsock); break; default: throw Error("Internal error, can't choose dispatcher"); break; } + if (!d) - throw Error("Memory fault in Balancer::serve"); + throw Error("Memory fault: cannot instantiate dispatcher\n"); // Allocation boundary printout if (config.debug()) { void *mem = malloc(16); - free (mem); + free(mem); debugmsg("Allocation boundary at dispatcher start: " << mem << '\n'); } #ifdef SHOWDEBUG void *mem = malloc(16); - free (mem); + free(mem); cout << "XR allocation at dispatcher start: " << mem << '\n'; #endif @@ -161,18 +149,13 @@ void Balancer::serve() { } else { // If fd-serving, serve and close. Don't thread it up. TcpDispatcher *d; - struct in_addr dummy; - inet_aton ("0.0.0.0", &dummy); - + switch (config.stype()) { case Servertype::t_tcp: - d = new TcpDispatcher (server_fd, dummy); + d = new TcpDispatcher(server_fd); break; case Servertype::t_http: - d = new HttpDispatcher (server_fd, dummy); - break; - case Servertype::t_udp: - throw Error("UDP dispatching not allowed in inetd-mode"); + d = new HttpDispatcher(server_fd); break; default: throw Error("Internal error, can't choose dispatcher"); @@ -186,10 +169,10 @@ void Balancer::serve() { // If we exceed the max # of requests, stop.. if (config.quitafter()) { - msg ("Request " << requestnr() << " underway of max " << + msg("Request " << requestnr() << " underway of max " << config.quitafter() << '\n'); if (requestnr() >= (long)config.quitafter()) { - msg ("Max requests served, will stop.\n"); + msg("Max requests served, will stop.\n"); break; } } @@ -197,10 +180,9 @@ void Balancer::serve() { // We're stopping now. If a PID stamp was created, remove it. if (config.pidfile() != "") - unlink (config.pidfile().c_str()); + unlink(config.pidfile().c_str()); // Wait for running threads to die off. - socketclose (server_fd); delete webinterface; unsigned prev_conn = 0x19081962; while (1) { @@ -208,10 +190,10 @@ void Balancer::serve() { if (!curr_conn) break; if (curr_conn != prev_conn) { - msg ("There are still " << curr_conn << " connections\n"); + msg("There are still " << curr_conn << " connections\n"); prev_conn = curr_conn; } - sleep (1); + sleep(1); } - msg ("XR is idle, stopping.\n"); + msg("XR is idle, stopping.\n"); } diff --git a/xr/etc/Makefile.class b/xr/etc/Makefile.class @@ -24,7 +24,7 @@ endif class-compile: $(OBJ) $(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o: %.cc - @echo "Compiling: " `pwd`$< + @echo "Compiling: " `pwd`/$< @$(CONF_CC) $(PROF) $(PROFILER) $(CONF_OPTFLAGS) \ -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' -DHST='"$(HST)"' \ -DMAINTAINER='"$(MAINTAINER)"' -DDISTSITE='"$(DISTSITE)"' \ diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt @@ -182,4 +182,6 @@ may not exist on your platform): XR's errors and warnings are sent to stderr, debugging and verbose messages go to stdout. Invoke XR daemons using something like "xr --backend ... [other flags] 2>&1 | logger &", or use xrctl. +Send signal SIGHUP (-1) to xr to have it report back end states. Other +typical signals request termination. diff --git a/xr/fdset/fdset b/xr/fdset/fdset @@ -4,6 +4,7 @@ #include "../sys/sys" #include "../error/error" #include "../config/config" +#include "../SocketHandling/socket/socket" using namespace std; @@ -11,22 +12,25 @@ class Fdset { public: Fdset(int t); - int timeout() const { return tsec; } - void timeout (int t) { tsec = t; } + int timeout() const { return tsec; } + void timeout(int t) { tsec = t; } - void add (int fd) { set.push_back(fd); } + void add(int fd) { set.push_back(fd); } + void add(Socket &s) { set.push_back(s.fd()); } - unsigned size() const { return set.size(); } + unsigned size() const { return set.size(); } - int fd (unsigned index) { return set[index]; } + int fd(unsigned index) { return set[index]; } void wait(bool wait_read, bool wait_write); - void wait_rw() { wait(true, true); } - void wait_r() { wait(true, false); } - void wait_w() { wait(false, true); } + void wait_rw() { wait(true, true); } + void wait_r() { wait(true, false); } + void wait_w() { wait(false, true); } - bool readable(int fd) { return FD_ISSET(fd, &readset); } - bool writeable(int fd) { return FD_ISSET(fd, &writeset); } + bool readable(int fd) { return FD_ISSET(fd, &readset); } + bool readable(Socket &s) { return FD_ISSET(s.fd(), &readset); } + bool writeable(int fd) { return FD_ISSET(fd, &writeset); } + bool writeable(Socket &s) { return FD_ISSET(s.fd(), &writeset); } private: diff --git a/xr/netbuffer/destroy.cc b/xr/netbuffer/destroy.cc @@ -1,7 +1,8 @@ #include "netbuffer" void Netbuffer::destroy() { - debugmsg("Netbuffer: destroying " << buf_alloced << " bytes\n"); + if (buf_alloced) + debugmsg("Netbuffer: destroying " << buf_alloced << " bytes\n"); free(buf_data); buf_data = 0; buf_sz = 0; diff --git a/xr/netbuffer/netbuffer b/xr/netbuffer/netbuffer @@ -34,8 +34,8 @@ public: char const *bufdata() const { return buf_data; } unsigned bufsz() const { return buf_sz; } - unsigned netread (int fd, int timeout = 0); - unsigned netwrite (int fd, int timeout) const; + unsigned netread (Socket &s, unsigned timeout = 0); + unsigned netwrite (Socket &s, unsigned timeout) const; unsigned strfind (char const *s) const; unsigned charfind (char ch, unsigned start = 0) const; diff --git a/xr/netbuffer/netread.cc b/xr/netbuffer/netread.cc @@ -1,38 +1,39 @@ #include "netbuffer" +#include "SocketHandling/socket/socket" -unsigned Netbuffer::netread (int fd, int timeout) { +unsigned Netbuffer::netread (Socket &s, unsigned timeout) { PROFILE("Netbuffer::netread"); if (timeout) { Fdset set(timeout); - set.add(fd); + set.add(s); set.wait_r(); - if (! set.readable(fd)) { - msg("Fd "<< fd << " failed to become readable within " << + if (! set.readable(s)) { + msg("Fd "<< s.fd() << " failed to become readable within " << timeout << " sec\n"); return 0; } } - + check_space(config.buffersize()); // Read from the network. If this fails, don't throw an exception // because it's quite common (too much logging otherwise). - ssize_t nread = read (fd, buf_data + buf_sz, config.buffersize()); + ssize_t nread = read (s.fd(), buf_data + buf_sz, config.buffersize()); if (nread < 0) { - msg("Read failed on fd " << fd + ": " << strerror(errno) << '\n'); + msg("Read failed on fd " << s.fd() + ": " << strerror(errno) << '\n'); return 0; } buf_sz += nread; if (config.debug() && nread) { ostringstream o; - o << "Got " << nread << " bytes from fd " << fd << ": "; + o << "Got " << nread << " bytes from fd " << s.fd() << ": "; for (unsigned i = 0; i < (unsigned)nread; i++) o << printable(buf_data[i]); o << "\n"; debugmsg (o.str()); } - + return nread; } diff --git a/xr/netbuffer/netwrite.cc b/xr/netbuffer/netwrite.cc @@ -1,10 +1,11 @@ #include "netbuffer" #include "balancer/balancer" +#include "SocketHandling/socket/socket" -unsigned Netbuffer::netwrite (int fd, int timeout) const { +unsigned Netbuffer::netwrite (Socket &s, unsigned timeout) const { PROFILE("Netbuffer::netwrite"); - debugmsg("About to write " << buf_sz << " buytes to fd " << fd + debugmsg("About to write " << buf_sz << " buytes to fd " << s.fd() << ", timeout " << timeout << '\n'); if (!buf_sz) @@ -13,7 +14,8 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { // Log to dump directory if requested if (config.dumpdir().length()) { ostringstream of; - of << config.dumpdir() << "/" << balancer.requestnr() << "." << fd; + of << config.dumpdir() << "/" << balancer.requestnr() << "." + << s.fd(); FILE *f; if ( (!(f = fopen (of.str().c_str(), "a"))) && (!(f = fopen (of.str().c_str(), "w"))) ) { @@ -31,7 +33,7 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { // Don't go beyond 5 tries. if (++ntries > 4) { ostringstream o; - o << "Network writing to fd " << fd << " failed, " + o << "Network writing to fd " << s.fd() << " failed, " << totwritten << " bytes sent of " << buf_sz; throw Error(o.str()); } @@ -39,11 +41,11 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { // Wait for the socket to become writeable. if (timeout) { Fdset set (timeout); - set.add (fd); + set.add (s); set.wait_w(); - if (! set.writeable(fd)) { + if (! set.writeable(s)) { ostringstream o; - o << "Fd " << fd << " failed to become writable within " + o << "Fd " << s.fd() << " failed to become writable within " << timeout << " sec"; throw Error(o.str()); } @@ -51,7 +53,7 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { // Push bytes ssize_t nwritten; - nwritten = write (fd, buf_data + totwritten, buf_sz - totwritten); + nwritten = write (s.fd(), buf_data + totwritten, buf_sz - totwritten); // If any bytes were written, we're ok // EINVAL / EINPROGRESS errors are handled as: retry @@ -60,7 +62,7 @@ unsigned Netbuffer::netwrite (int fd, int timeout) const { ntries = 0; if (config.debug()) { ostringstream o; - o << "Sent " << nwritten << " bytes to fd " << fd << ": "; + o << "Sent " << nwritten << " bytes to fd " << s.fd() << ": "; for (unsigned i = totwritten; i < totwritten + nwritten; i++) o << printable(buf_data[i]); o << "\n"; diff --git a/xr/servertype/servertype b/xr/servertype/servertype @@ -9,7 +9,6 @@ public: enum Type { t_tcp, t_http, - t_udp, }; Servertype(): t(t_tcp) { } diff --git a/xr/servertype/type1.cc b/xr/servertype/type1.cc @@ -7,9 +7,7 @@ void Servertype::type (string id) { t = t_tcp; else if (id == "http") t = t_http; - else if (id == "udp") - t = t_udp; else throw Error("Bad server type '" + id + - "', supported are 'tcp' or 'http' or 'udp'"); + "', supported are 'tcp' or 'http'"); } diff --git a/xr/servertype/typestr.cc b/xr/servertype/typestr.cc @@ -7,8 +7,6 @@ string Servertype::typestr() const { return ("tcp"); else if (t == t_http) return ("http"); - else if (t == t_udp) - return ("udp"); else throw Error("Server type unknown in Servertype::typestr"); } diff --git a/xr/sys/serversocket.cc b/xr/sys/serversocket.cc @@ -1,52 +0,0 @@ -#include "sys" -#include "error/error" -#include "profiler/profiler" -#include "config/config" - -int serversocket (string addr, int port, string desc, Servertype::Type type) { - PROFILE("serversocket"); - - // Create the server socket, set options - int sock; - if (type != Servertype::t_udp) - sock = socket(PF_INET, SOCK_STREAM, 0); - else - sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) - throw Error("Failed to create " + desc + " socket: " + - strerror(errno)); - int val = 1; - if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) - throw Error("Failed to set socket options for " + - desc + ": " + strerror(errno)); - - // Prepare binding - struct sockaddr_in saddr; - memset (&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - - // Assign interface to listen to - if (addr[0] != '0') { - msg ("Binding " + desc + " to specific IP address " + addr + "\n"); - if ( (saddr.sin_addr.s_addr = inet_addr (addr.c_str())) == INADDR_NONE ) - throw Error("Cannot convert " + desc + " IP '" + - addr + "' to network bytes"); - } else { - msg ("Binding " + desc + " to all local IP addresses\n"); - saddr.sin_addr.s_addr = htonl (INADDR_ANY); - } - - // Bind and listen - if (bind (sock, (sockaddr*) &saddr, sizeof(saddr)) < 0) - throw Error("Failed to bind " + desc + - " to IP/port: " + strerror(errno)); - if (type != Servertype::t_udp) - if (listen (sock, 5) < 0) - throw Error("Failed to listen to " + desc + - " IP/port: " + strerror(errno)); - - msg ("Server for " + desc + " listening\n"); - - return (sock); -} diff --git a/xr/sys/socketclose.cc b/xr/sys/socketclose.cc @@ -1,19 +0,0 @@ -#include "sys" -#include "../config/config" - -void socketclose (int fd) { - PROFILE("socketclose"); - - if (fd > 2) { - debugmsg("Closing socket " << fd << '\n'); - - if (config.fastclose()) { - struct linger l; - l.l_onoff = 1; - l.l_linger = 2; - setsockopt (fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); - } - shutdown(fd, SHUT_RDWR); - close(fd); - } -} diff --git a/xr/sys/sys b/xr/sys/sys @@ -77,11 +77,8 @@ using namespace std; /* Generic funtions */ -int serversocket (string addr, int port, string description, - Servertype::Type t); string inet2string(struct in_addr in); bool ipmatch (struct in_addr addr, struct in_addr mask); -void socketclose (int fd); vector<string> str2parts (string const &s, char sep); void mt_srand(unsigned long s); unsigned long mt_rand(void); diff --git a/xr/webinterface/answer.cc b/xr/webinterface/answer.cc @@ -6,8 +6,8 @@ static void stop_backend_thread(pthread_t id) { msg("Stopping thread " << id << " (backend socket " << info.backendfd() << ", client socket " << info.clientfd() + ")\n"); - socketclose(info.backendfd()); - socketclose(info.clientfd()); + close(info.backendfd()); + close(info.clientfd()); Threadlist::deregister(id); } diff --git a/xr/webinterface/execute.cc b/xr/webinterface/execute.cc @@ -12,9 +12,7 @@ void Webinterface::execute() { while (true) { try { msg("Starting web interface\n"); - sfd = serversocket (config.webinterfaceip(), - config.webinterfaceport(), - "web interface", Servertype::t_tcp); + sfd.bind(config.webinterfaceip(), config.webinterfaceport()); } catch (Error const &e) { mutex_lock(&cerr); if (config.prefixtimestamp()) { @@ -29,23 +27,16 @@ void Webinterface::execute() { break; } - msg("Web interface started on socket " << sfd << "\n"); + msg("Web interface started on socket " << sfd.fd() << "\n"); while (!balancer.terminate()) { try { Fdset fdset(0); fdset.add (sfd); fdset.wait_r(); if (fdset.readable(sfd)) { - int size; - struct sockaddr_in clname; - if ( (cfd = accept (sfd, (struct sockaddr *) &clname, - (socklen_t *)&size)) < 0 ) { - warnmsg("Web interface: failed to accept network " - "connection: " << strerror(errno) << '\n'); - } else { - serve(); - socketclose(cfd); - } + cfd = sfd.accept(); + serve(); + cfd.close(); } } catch (Error const &e) { mutex_lock(&cerr); @@ -56,23 +47,33 @@ void Webinterface::execute() { cerr << e.what() << " (webinterface)\n"; mutex_unlock(&cerr); - if (cfd >= 0) { + if (cfd.fd() >= 0) { ostringstream m; - m << "<h1>Web interface error</h1>\n" - "XR's web interface could not handle your request.<p/>\n" - "<i>" << e.what() << "</i>\n"; + m << + "<html>\n" + " <head>\n" + " <title>Web interface error</title>\n" + " </head>\n" + " <body>\n" + " <h1>Web interface error</h1>\n" + " XR's web interface could not handle your request.\n" + " <p/>\n" + " <i>" << e.what() << "</i>\n" + " </body>\n" + "</html>\n"; ostringstream o; o << "HTTP/1.0 500 Server Error\r\n" "X-Reason: " << e.what() << "\r\n" "Content-Length: " << m.str().length() << "\r\n" "\r\n" << m.str(); - Netbuffer buf(o.str()); - buf.netwrite(cfd, config.client_write_timeout()); + Netbuffer buf(o.str()); + buf.netwrite(cfd, config.client_write_timeout()); + cfd.close(); } - socketclose(cfd); } } + msg("Web interface stopping.\n"); - socketclose(sfd); + sfd.close(); } diff --git a/xr/webinterface/serve.cc b/xr/webinterface/serve.cc @@ -1,7 +1,7 @@ #include "webinterface" -void Webinterface::serve () { - debugmsg("Webinterface serving request on client fd " << cfd << '\n'); +void Webinterface::serve() { + debugmsg("Webinterface serving request on client fd " << cfd.fd() << '\n'); Httpbuffer clientrequest; clientrequest.netread(cfd, config.client_read_timeout()); diff --git a/xr/webinterface/webinterface b/xr/webinterface/webinterface @@ -6,10 +6,11 @@ #include "ThreadsAndMutexes/threadlist/threadlist" #include "fdset/fdset" #include "httpbuffer/httpbuffer" +#include "SocketHandling/socket/socket" class Webinterface: public Thread { public: - Webinterface() { cfd = 0; sfd = 0; } + Webinterface(); virtual ~Webinterface(); void execute(); @@ -21,7 +22,7 @@ private: void answer_blob (string const &b); - int cfd, sfd; + Socket cfd, sfd; }; #endif diff --git a/xr/webinterface/webinterface0.cc b/xr/webinterface/webinterface0.cc @@ -0,0 +1,5 @@ +#include "webinterface" + +Webinterface::~Webinterface() { + msg("Stopping web interface\n"); +} diff --git a/xr/webinterface/webinterface1.cc b/xr/webinterface/webinterface1.cc @@ -1,8 +1,4 @@ #include "webinterface" -Webinterface::~Webinterface() { - if (cfd) { - msg("Stopping web interface socket " << sfd << '\n'); - socketclose(sfd); - } +Webinterface::Webinterface(): cfd(), sfd() { } diff --git a/xrctl/xrctl b/xrctl/xrctl @@ -40,8 +40,8 @@ my $default_dnscachetimeout = 3600; # Cmd line flags my %opts = (v => 0, - c => $default_conf, - ); + c => $default_conf, + ); usage() unless (getopts('vc:', \%opts)); usage() if ($#ARGV == -1); @@ -60,33 +60,33 @@ 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 prefixtimestamp) { - $sysconf{$tag} = $sysxp->data($tag); - msg("System config $tag: $sysconf{$tag}\n") if ($sysconf{$tag} ne ''); + maxlogsize loghistory path prefixtimestamp) { + $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}; + 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}; + 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'; - } + $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"); if ($sysconf{prefixtimestamp}) { - $default_prefixtimestamp = 1 if istrue($sysconf{prefixtimestamp}); + $default_prefixtimestamp = 1 if istrue($sysconf{prefixtimestamp}); } else { - $default_prefixtimestamp = 1 - if (!istrue($sysconf{uselogger}) or !find_bin('logger')); + $default_prefixtimestamp = 1 + if (!istrue($sysconf{uselogger}) or !find_bin('logger')); } msg ("Log lines will be prefixed with a timestamp\n") if ($default_prefixtimestamp); @@ -141,35 +141,35 @@ if ($cmd eq 'list') { sub cmd_list { for my $s (@_) { - print ("Service: $s\n"); - print (" Process name : ", process_name($s), "\n"); - print (" Logging : ", log_file($s), "\n"); - print (" XR command : ", xr_command($s), "\n"); + print ("Service: $s\n"); + print (" Process name : ", process_name($s), "\n"); + print (" Logging : ", log_file($s), "\n"); + print (" XR command : ", xr_command($s), "\n"); } } sub cmd_start { for my $s (@_) { - die ("Cannot start service $s, already running\n") - if (is_running($s)); + die ("Cannot start service $s, already running\n") + if (is_running($s)); } for my $s (@_) { - print ("Service $s: "); - start_service($s); - print ("started\n"); + print ("Service $s: "); + start_service($s); + print ("started\n"); } } sub cmd_stop { my @pids; for my $s (@_) { - my @p = is_running($s) - or die ("Cannot stop service $s, not running\n"); - print ("Service $s: running at @p\n"); - push (@pids, @p); + my @p = is_running($s) + or die ("Cannot stop service $s, not running\n"); + print ("Service $s: running at @p\n"); + push (@pids, @p); } for my $p (@pids) { - msg ("About to stop PID: '$p'\n"); + msg ("About to stop PID: '$p'\n"); } kill (15, @pids) if ($#pids > -1); print ("Services @_: stopped\n"); @@ -178,13 +178,13 @@ sub cmd_stop { sub cmd_kill { my @pids; for my $s (@_) { - my @p = is_running($s) - or die ("Cannot stop service $s, not running\n"); - print ("Service $s: running at @p\n"); - push (@pids, @p); + my @p = is_running($s) + or die ("Cannot stop service $s, not running\n"); + print ("Service $s: running at @p\n"); + push (@pids, @p); } for my $p (@pids) { - msg ("About to kill PID: '$p'\n"); + msg ("About to kill PID: '$p'\n"); } kill (9, @pids) if ($#pids > -1); print ("Services @_: killed\n"); @@ -192,132 +192,132 @@ sub cmd_kill { sub cmd_force { for my $s (@_) { - print ("Service $s: "); - if (is_running($s)) { - print ("already running\n"); - } else { - start_service($s); - print ("started\n"); - } + print ("Service $s: "); + if (is_running($s)) { + print ("already running\n"); + } else { + start_service($s); + print ("started\n"); + } } } sub cmd_stopstart { my @pids; for my $s (@_) { - my @p = is_running($s) - or die ("Cannot stopstart service $s, not running\n"); - push (@pids, @p); + my @p = is_running($s) + or die ("Cannot stopstart service $s, not running\n"); + push (@pids, @p); } print ("Service(s) @_: "); kill (15, @pids) if ($#pids > -1); print ("stoppped\n"); for my $s (@_) { - print ("Service $s: "); - start_service($s); - print ("started\n"); + print ("Service $s: "); + start_service($s); + print ("started\n"); } } sub cmd_killstart { my @pids; for my $s (@_) { - my @p = is_running($s) - or die ("Cannot killstart service $s, not running\n"); - push (@pids, @p); + my @p = is_running($s) + or die ("Cannot killstart service $s, not running\n"); + push (@pids, @p); } print ("Service(s) @_: "); kill (9, @pids) if ($#pids > -1); print ("killed\n"); for my $s (@_) { - print ("Service $s: "); - start_service($s); - print ("started\n"); + print ("Service $s: "); + start_service($s); + print ("started\n"); } } sub cmd_status { for my $s (@_) { - print ("Service $s: "); - print (BOLD, RED, "not ", RESET) unless (is_running($s)); - print ("running\n"); + print ("Service $s: "); + print (BOLD, RED, "not ", RESET) unless (is_running($s)); + print ("running\n"); } } sub cmd_rotate { if (istrue($sysconf{uselogger}) and find_bin($default_logger)) { - print ("Rotating not necessary, logging goes via logger\n"); - return; + print ("Rotating not necessary, logging goes via logger\n"); + return; } for my $s (@_) { - print ("Service $s: "); - my $f = log_file($s); - print ("log file $f, "); - if (substr($f, 0, 1) ne '>') { - print ("not a file\n"); - next; - } - $f = substr($f, 1); - if (! -f $f) { - print ("not present\n"); - next; - } - if ((stat($f))[7] < $sysconf{maxlogsize}) { - print ("no rotation necessary\n"); - next; - } - unlink("$f.$sysconf{loghistory}", - "$f.$sysconf{loghistory}.bz2", - "$f.$sysconf{loghistory}.gz"); - for (my $i = $sysconf{loghistory} - 1; $i >= 0; $i--) { - my $src = "$f.$i"; - my $dst = sprintf("$f.%d", $i + 1); - rename($src, $dst); - rename("$src.bz2", "$dst.bz2"); - rename("$src.gz", "$dst.gz"); - } - rename($f, "$f.0"); - print("rotated, "); - my $zipper; - if ($zipper = find_bin('bzip2') or $zipper = find_bin('gzip')) { - system ("$zipper $f.0"); - print ("zipped, "); - } - if (my @p = is_running($s)) { - kill (15, @p) if ($#p > -1); - print ("stopped, "); - start_service($s); - print ("started, "); - } - print ("done\n"); + print ("Service $s: "); + my $f = log_file($s); + print ("log file $f, "); + if (substr($f, 0, 1) ne '>') { + print ("not a file\n"); + next; + } + $f = substr($f, 1); + if (! -f $f) { + print ("not present\n"); + next; + } + if ((stat($f))[7] < $sysconf{maxlogsize}) { + print ("no rotation necessary\n"); + next; + } + unlink("$f.$sysconf{loghistory}", + "$f.$sysconf{loghistory}.bz2", + "$f.$sysconf{loghistory}.gz"); + for (my $i = $sysconf{loghistory} - 1; $i >= 0; $i--) { + my $src = "$f.$i"; + my $dst = sprintf("$f.%d", $i + 1); + rename($src, $dst); + rename("$src.bz2", "$dst.bz2"); + rename("$src.gz", "$dst.gz"); + } + rename($f, "$f.0"); + print("rotated, "); + my $zipper; + if ($zipper = find_bin('bzip2') or $zipper = find_bin('gzip')) { + system ("$zipper $f.0"); + print ("zipped, "); + } + if (my @p = is_running($s)) { + kill (15, @p) if ($#p > -1); + print ("stopped, "); + start_service($s); + print ("started, "); + } + print ("done\n"); } } sub cmd_configtest { for my $s (@_) { - print ("Service $s: "); - my $cmd = xr_command($s) . ' --tryout'; - if (system ($cmd)) { - print ("FAILED, command: $cmd\n"); - } else { - print ("configuration ok\n"); - } + print ("Service $s: "); + my $cmd = xr_command($s) . ' --tryout'; + if (system ($cmd)) { + print ("FAILED, command: $cmd\n"); + } else { + print ("configuration ok\n"); + } } } sub cmd_generateconfig { print ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", - "<configuration>\n", - "\n", - " <!-- System description -->\n", - " <system>\n"); + "<configuration>\n", + "\n", + " <!-- System description -->\n", + " <system>\n"); for my $k (sort (keys (%sysconf))) { - print (" <$k>$sysconf{$k}</$k>\n") if ($sysconf{$k} ne ''); + print (" <$k>$sysconf{$k}</$k>\n") if ($sysconf{$k} ne ''); } print (" </system>\n"); for my $s (@_) { - generateconfig($s); + generateconfig($s); } print ("</configuration>\n"); @@ -362,14 +362,14 @@ sub is_running { or die ("Cannot start '$sysconf{pscmd}': $!\n"); my @ret; while (my $line = <$if>) { - chomp ($line); - $line =~ s/^\s*//; - my ($pid, $cmd) = split(/\s+/, $line); - # msg("Command '$cmd' at pid '$pid' (line $line)\n"); - if ($cmd =~ /^xr-$s/) { - push (@ret, $pid); - msg ("Candidate PID: $pid\n"); - } + chomp ($line); + $line =~ s/^\s*//; + my ($pid, $cmd) = split(/\s+/, $line); + # msg("Command '$cmd' at pid '$pid' (line $line)\n"); + if ($cmd =~ /^xr-$s/) { + push (@ret, $pid); + msg ("Candidate PID: $pid\n"); + } } return (@ret); } @@ -396,15 +396,15 @@ sub start_service { open (STDIN, '/dev/null') or die ("Cannot read /dev/null: $!\n"); if ($logtype eq '|') { - open (STDOUT, "|$logout") - or die ("Cannot pipe stdout to $logout: $!\n"); - open (STDERR, "|$logout") - or die ("Cannot pipe stderr to $logout: $!\n"); + open (STDOUT, "|$logout") + or die ("Cannot pipe stdout to $logout: $!\n"); + open (STDERR, "|$logout") + or die ("Cannot pipe stderr to $logout: $!\n"); } else { - open (STDOUT, ">>$logout") - or die ("Cannot append stdout to $logout: $!\n"); - open (STDERR, ">>$logout") - or die ("Cannot append stderr to $logout: $!\n"); + open (STDOUT, ">>$logout") + or die ("Cannot append stdout to $logout: $!\n"); + open (STDERR, ">>$logout") + or die ("Cannot append stderr to $logout: $!\n"); } exec ({$xr} @args); exit (1); @@ -422,15 +422,15 @@ sub find_bin { my @parts = split (/\s/, $bin); if (substr($parts[0], 0, 1) eq '/' and -x $parts[0]) { - msg("Binary '$bin' is executable as-is\n"); - return $bin; + msg("Binary '$bin' is executable as-is\n"); + return $bin; } for my $d (split (/:/, $sysconf{path})) { - if (-x "$d/$parts[0]" and -f "$d/$parts[0]") { - msg ("Binary '$parts[0]' found as '$d/$parts[0]'\n"); - $parts[0] = "$d/$parts[0]"; - return (join (' ', @parts)); - } + if (-x "$d/$parts[0]" and -f "$d/$parts[0]") { + msg ("Binary '$parts[0]' found as '$d/$parts[0]'\n"); + $parts[0] = "$d/$parts[0]"; + return (join (' ', @parts)); + } } msg ("Binary '$bin' not found along $sysconf{path}\n"); return (undef); @@ -453,15 +453,15 @@ sub log_file { my $service = shift; my $logger = find_bin($default_logger); if (istrue($sysconf{uselogger}) and defined($logger)) { - if ($default_logger eq 'logger') { - return ("|$logger -t 'xr-$service'"); - } else { - $logger =~ s/\{service\}/$service/g; - return ("|$logger"); - } + if ($default_logger eq 'logger') { + return ("|$logger -t 'xr-$service'"); + } else { + $logger =~ s/\{service\}/$service/g; + return ("|$logger"); + } } else { - return ('>' . $sysconf{logdir} . '/' . - process_name($service) . '.log'); + return ('>' . $sysconf{logdir} . '/' . + process_name($service) . '.log'); } } @@ -472,10 +472,10 @@ sub xr_command { msg ("Exec command: @parts\n"); my $ret = xfind_bin('xr'); for (my $i = 1; $i <= $#parts; $i++) { - my $sub = $parts[$i]; - $sub =~ s/^\s+//; - $sub =~ s/\s+$//; - $ret .= ' ' . shquote($sub); + my $sub = $parts[$i]; + $sub =~ s/^\s+//; + $sub =~ s/\s+$//; + $ret .= ' ' . shquote($sub); } msg ("Shell command: $ret\n"); return ($ret); @@ -506,100 +506,100 @@ sub xr_cmdarr { # Flags that should go on the command line if the bool-tag is true my %boolflags = (closesocketsfast => '--close-sockets-fast', - verbose => '--verbose', - debug => '--debug', - removereservations => '--remove-reservations'); + verbose => '--verbose', + debug => '--debug', + removereservations => '--remove-reservations'); # Web interface def comes from two tags my $w = $ss->data('webinterface'); if ($w) { - if (my $name = $ss->data('webinterfacename')) { - $w .= ":$name"; - push(@cmd, '--web-interface', $w); - } + if (my $name = $ss->data('webinterfacename')) { + $w .= ":$name"; + } + push(@cmd, '--web-interface', $w); } # Handle general flags and boolflags push (@cmd, - flag($ss, '--web-interface-auth', 'webinterfaceauth', ''), - flag($ss, '--dispatch-mode', 'dispatchmode', - $default_dispatchmode), - flag($ss, '--max-connections', 'maxconnections', - $default_maxconnections), - flag($ss, '--client-timeout', 'clienttimeout', - $default_client_timeout), - flag($ss, '--backend-timeout', 'backendtimeout', - $default_backend_timeout), - flag($ss, '--buffer-size', 'buffersize', - $default_buffersize), - flag($ss, '--wakeup-interval', 'wakeupinterval', - $default_wakeupinterval), - flag($ss, '--checkup-interval', 'checkupinterval', - $default_checkupinterval), - flag($ss, '--time-interval', 'timeinterval', - $default_timeinterval), - flag($ss, '--hard-maxconnrate', 'hardmaxconnrate', - $default_hardmaxconnrate), - flag($ss, '--soft-maxconnrate', 'softmaxconnrate', - $default_softmaxconnrate), - flag($ss, '--defer-time', 'defertime', - $default_defertime), - flag($ss, '--hard-maxconn-excess', 'hardmaxconnexcess', - $default_hardmaxconnexcess), - flag($ss, '--soft-maxconn-excess', 'softmaxconnexcess', - $default_softmaxconnexcess), - flag($ss, '--dns-cache-timeout', 'dnscachetimeout', - $default_dnscachetimeout), - flag($ss, '--onstart', 'onstart'), - flag($ss, '--onend', 'onend'), - flag($ss, '--onfail', 'onfail'), - flag($ss, '--log-traffic-dir', 'logtrafficdir', '')); + flag($ss, '--web-interface-auth', 'webinterfaceauth', ''), + flag($ss, '--dispatch-mode', 'dispatchmode', + $default_dispatchmode), + flag($ss, '--max-connections', 'maxconnections', + $default_maxconnections), + flag($ss, '--client-timeout', 'clienttimeout', + $default_client_timeout), + flag($ss, '--backend-timeout', 'backendtimeout', + $default_backend_timeout), + flag($ss, '--buffer-size', 'buffersize', + $default_buffersize), + flag($ss, '--wakeup-interval', 'wakeupinterval', + $default_wakeupinterval), + flag($ss, '--checkup-interval', 'checkupinterval', + $default_checkupinterval), + flag($ss, '--time-interval', 'timeinterval', + $default_timeinterval), + flag($ss, '--hard-maxconnrate', 'hardmaxconnrate', + $default_hardmaxconnrate), + flag($ss, '--soft-maxconnrate', 'softmaxconnrate', + $default_softmaxconnrate), + flag($ss, '--defer-time', 'defertime', + $default_defertime), + flag($ss, '--hard-maxconn-excess', 'hardmaxconnexcess', + $default_hardmaxconnexcess), + flag($ss, '--soft-maxconn-excess', 'softmaxconnexcess', + $default_softmaxconnexcess), + flag($ss, '--dns-cache-timeout', 'dnscachetimeout', + $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))); + push (@cmd, $boolflags{$k}) if (istrue($ss->data($k))); } # Timeouts when specified using separate tags my $t = $ss->data('clientreadtimeout'); if (defined($t)) { - my $val = $t; - $t = $ss->data('clientwritetimeout'); - $val .= ":$t" if (defined($t)); - push (@cmd, '--client-timeout', $val); + my $val = $t; + $t = $ss->data('clientwritetimeout'); + $val .= ":$t" if (defined($t)); + push (@cmd, '--client-timeout', $val); } $t = $ss->data('backendreadtimeout'); if (defined($t)) { - my $val = $t; - $t = $ss->data('backendwritetimeout'); - $val .= ":$t" if (defined($t)); - push (@cmd, '--backend-timeout', $val); + my $val = $t; + $t = $ss->data('backendwritetimeout'); + $val .= ":$t" if (defined($t)); + push (@cmd, '--backend-timeout', $val); } # ACL's for (my $i = 0; ; $i++) { - my $mask = $ss->data('allowfrom', $i) or last; - push (@cmd, '--allow-from', $mask); + my $mask = $ss->data('allowfrom', $i) or last; + push (@cmd, '--allow-from', $mask); } for (my $i = 0; ; $i++) { - my $mask = $ss->data('denyfrom', $i) or last; - push (@cmd, '--deny-from', $mask); + my $mask = $ss->data('denyfrom', $i) or last; + push (@cmd, '--deny-from', $mask); } # HTTP goodies push (@cmd, '--add-xr-version') if ($ss->data('addxrversion') and - istrue($ss->data('addxrversion'))); + istrue($ss->data('addxrversion'))); push (@cmd, '--add-x-forwarded-for') if ($ss->data('addxforwardedfor') and - istrue($ss->data('addxforwardedfor'))); + istrue($ss->data('addxforwardedfor'))); push (@cmd, '--sticky-http') if ($ss->data('stickyhttp') and - istrue($ss->data('stickyhttp'))); + istrue($ss->data('stickyhttp'))); push (@cmd, '--replace-host-header') if ($ss->data('replacehostheader') and - istrue($ss->data('replacehostheader'))); + istrue($ss->data('replacehostheader'))); for (my $i = 0; ; $i++) { - my $h = $ss->data('header', $i) or last; - push (@cmd, '--add-server-header', $h); + my $h = $ss->data('header', $i) or last; + push (@cmd, '--add-server-header', $h); } # The <backend> blocks for this service @@ -607,56 +607,56 @@ sub xr_cmdarr { my $last_urlmatch = $default_urlmatch; my $last_backendcheck = $default_backendcheck; for (my $i = 0; ; $i++) { - my $bp = xml_backendparser($sp, $i) or last; - - # Handle host match - my $hm = $bp->data('hostmatch'); - if ($hm and $hm ne $last_hostmatch) { - push (@cmd, '--host-match', $hm); - } elsif ($hm eq '' and $last_hostmatch ne '') { - push (@cmd, '--host-match', $default_hostmatch); - } - $last_hostmatch = $hm; - - # Handle url match - my $um = $bp->data('urlmatch'); - if ($um and $um ne $last_urlmatch) { - push (@cmd, '--url-match', $um); - } elsif ($um eq '' and $last_urlmatch ne '') { - push (@cmd, '--url-match', $default_urlmatch); - } - $last_urlmatch = $um; - - # Handle back end checks - my $bc = $bp->data('backendcheck'); - if ($bc and $bc ne $last_backendcheck) { - push (@cmd, '--backend-check', $bc); - } elsif ($bc eq '' and $last_backendcheck ne '') { - push (@cmd, '--backend-check', $default_backendcheck); - } - $last_backendcheck = $bc; - - # Get address, weight and max connections - my $ad = $bp->data('address') - or die ("Backend in service '$service' lacks <address>\n"); - my $mx = $bp->data('maxconnections'); - $mx = $default_maxconnections if (!$mx); - $ad .= ":$mx"; - my $wt = $bp->data('weight'); - $wt = $default_weight if (!$wt); - $ad .= ":$wt"; - - push (@cmd, '--backend', $ad); + my $bp = xml_backendparser($sp, $i) or last; + + # Handle host match + my $hm = $bp->data('hostmatch'); + if ($hm and $hm ne $last_hostmatch) { + push (@cmd, '--host-match', $hm); + } elsif ($hm eq '' and $last_hostmatch ne '') { + push (@cmd, '--host-match', $default_hostmatch); + } + $last_hostmatch = $hm; + + # Handle url match + my $um = $bp->data('urlmatch'); + if ($um and $um ne $last_urlmatch) { + push (@cmd, '--url-match', $um); + } elsif ($um eq '' and $last_urlmatch ne '') { + push (@cmd, '--url-match', $default_urlmatch); + } + $last_urlmatch = $um; + + # Handle back end checks + my $bc = $bp->data('backendcheck'); + if ($bc and $bc ne $last_backendcheck) { + push (@cmd, '--backend-check', $bc); + } elsif ($bc eq '' and $last_backendcheck ne '') { + push (@cmd, '--backend-check', $default_backendcheck); + } + $last_backendcheck = $bc; + + # Get address, weight and max connections + my $ad = $bp->data('address') + or die ("Backend in service '$service' lacks <address>\n"); + my $mx = $bp->data('maxconnections'); + $mx = $default_maxconnections if (!$mx); + $ad .= ":$mx"; + my $wt = $bp->data('weight'); + $wt = $default_weight if (!$wt); + $ad .= ":$wt"; + + push (@cmd, '--backend', $ad); } # All done my @ret; # msg("Generated flags/arguments:\n"); for my $c (@cmd) { - if ($c ne '') { - push (@ret, $c); - # msg (" $c"); - } + if ($c ne '') { + push (@ret, $c); + # msg (" $c"); + } } # msg ("\n"); @@ -670,12 +670,12 @@ sub shquote($) { return $s unless ($s =~ /[\(\)\'\"\| \*\[\]\^\$]/); if ($s !~ /'/) { - $s = "'$s'"; + $s = "'$s'"; } elsif ($s !~ /"/) { - $s = "\"$s\""; + $s = "\"$s\""; } else { - $s =~ s/"/\\"/g; - $s = "\"$s\""; + $s =~ s/"/\\"/g; + $s = "\"$s\""; } return $s; @@ -687,10 +687,10 @@ sub flag { my ($parser, $longopt, $tag, $default) = @_; msg ("Flag tag $tag: ", $parser->data($tag), " (default: '$default')\n"); if ($parser->data($tag) ne '' && - $parser->data($tag) ne $default) { - msg ("Flag values meaningful: ", - $longopt, ' ', $parser->data($tag), "\n"); - return ($longopt, $parser->data($tag)); + $parser->data($tag) ne $default) { + msg ("Flag values meaningful: ", + $longopt, ' ', $parser->data($tag), "\n"); + return ($longopt, $parser->data($tag)); } return (undef); } @@ -699,7 +699,7 @@ sub flag { sub istrue { my $val = shift; return (1) if ($val eq 'true' or $val eq 'on' or - $val eq 'yes' or $val != 0); + $val eq 'yes' or $val != 0); return (undef); } @@ -708,10 +708,10 @@ sub xml_serviceparser { my $service = shift; for (my $i = 0; ; $i++) { - my $xml = $xp->data('service', $i) or return (undef); - msg ("XML service block: $xml\n"); - my $sub = new XMLParser($xml); - return ($sub) if ($sub->data('name') eq $service); + my $xml = $xp->data('service', $i) or return (undef); + msg ("XML service block: $xml\n"); + my $sub = new XMLParser($xml); + return ($sub) if ($sub->data('name') eq $service); } return (undef); } @@ -745,57 +745,57 @@ sub generateconfig { $webint =~ s/^0:/localhost:/; if ($webint eq '') { - print ("\n", - " <!-- Configuration for service $s not generated,\n", - " no web interface known -->\n"); - return; + print ("\n", + " <!-- Configuration for service $s not generated,\n", + " no web interface known -->\n"); + return; } print ("\n", - " <!-- Configuration for service $s,\n", - " obtained at web interface $webint -->\n", - " <service>\n", - " <name>$s</name>\n"); + " <!-- Configuration for service $s,\n", + " obtained at web interface $webint -->\n", + " <service>\n", + " <name>$s</name>\n"); # Get the configuration from a running XR. Try LWP::UserAgent or # fall back to wget. my $response_blob; eval ("require LWP::UserAgent;"); if ($@) { - msg ("LWP::UserAgent not present, trying wget\n"); - my $wget = find_bin('wget') - or die ("Neither LWP::UserAgent nor wget found.\n", - "Cannot contact service web interface $webint.\n"); - open (my $if, "wget --no-proxy -q -O- http://$webint/ |") - or die ("Cannot start wget: $!\n"); - while (my $line = <$if>) { - $response_blob .= $line; - } - close ($if) or die ("Wget indicates failure\n"); + msg ("LWP::UserAgent not present, trying wget\n"); + my $wget = find_bin('wget') + or die ("Neither LWP::UserAgent nor wget found.\n", + "Cannot contact service web interface $webint.\n"); + open (my $if, "wget --no-proxy -q -O- http://$webint/ |") + or die ("Cannot start wget: $!\n"); + while (my $line = <$if>) { + $response_blob .= $line; + } + close ($if) or die ("Wget indicates failure\n"); } else { - my $ua = LWP::UserAgent->new(); - my $res = $ua->get("http://$webint/"); - die ("Failed to contact web interface at $webint:\n", - $res->status_line(), "\n") unless ($res->is_success()); + my $ua = LWP::UserAgent->new(); + my $res = $ua->get("http://$webint/"); + die ("Failed to contact web interface at $webint:\n", + $res->status_line(), "\n") unless ($res->is_success()); - $response_blob = $res->content(); + $response_blob = $res->content(); } # Print the config. my $active = 0; for my $l (split (/\n/, $response_blob)) { - if ($l =~ /<server>/) { - print (" $l\n"); - $active = 1; - } elsif ($l =~ /<\/status>/) { - $active = 0; - } elsif ($l =~ /<activity>/) { - $active = 0; - } elsif ($l =~ /<\/activity>/) { - $active = 1; - } elsif ($active) { - print (" $l\n"); - } + if ($l =~ /<server>/) { + print (" $l\n"); + $active = 1; + } elsif ($l =~ /<\/status>/) { + $active = 0; + } elsif ($l =~ /<activity>/) { + $active = 0; + } elsif ($l =~ /<\/activity>/) { + $active = 1; + } elsif ($active) { + print (" $l\n"); + } } print (" </service>\n"); @@ -814,7 +814,7 @@ sub new { my $docstr = ''; for my $p (split (/\n/, $doc)) { - $docstr .= $p; + $docstr .= $p; } # Whitespace between tags is trash @@ -823,15 +823,15 @@ sub new { # Remove comments from the doc FINDCOMM: for (my $i = 0; $i <= length($docstr); $i++) { - next unless (substr($docstr, $i, 4) eq '<!--'); - for (my $end = $i + 4; $end <= length($docstr); $end++) { - if (substr($docstr, $end, 3) eq '-->') { - # print ("Comment: ", substr($docstr, $i, $end + 3 - $i), "\n"); - $docstr = substr($docstr, 0, $i) . substr($docstr, $end + 3); - $i--; - next FINDCOMM; - } - } + next unless (substr($docstr, $i, 4) eq '<!--'); + for (my $end = $i + 4; $end <= length($docstr); $end++) { + if (substr($docstr, $end, 3) eq '-->') { + # print ("Comment: ", substr($docstr, $i, $end + 3 - $i), "\n"); + $docstr = substr($docstr, 0, $i) . substr($docstr, $end + 3); + $i--; + next FINDCOMM; + } + } } # Activity logs is trash @@ -847,22 +847,24 @@ sub new { sub data { my ($self, $tag, $order) = @_; + # print("Searching for <$tag> order $order\n"); die ("XML::data: no tag to search for\n") unless ($tag); $order = 0 unless ($order); my $xml = $self->{xml}; my $ret = undef; for (0..$order) { - my $start = _findfirst($xml, "<$tag>"); - return (undef) unless (defined ($start)); - $xml = substr($xml, $start + length("<$tag>")); - # print ("start $start $xml\n"); - my $end = _findfirst($xml, "</$tag>"); - die ("Failed to match </$tag>, invalid XML\n") - unless (defined ($end)); - $ret = substr($xml, 0, $end); - $xml = substr($xml, $end + length("</tag>")); - # print ("end $end $xml\n"); - } + my $start = _findfirst($xml, "<$tag>"); + return (undef) unless (defined ($start)); + $xml = substr($xml, $start + length("<$tag>")); + # print ("start $start $xml\n"); + my $end = _findfirst($xml, "</$tag>"); + die ("Failed to match </$tag>, invalid XML\n") + unless (defined ($end)); + $ret = substr($xml, 0, $end); + $xml = substr($xml, $end + length("</tag>")); + # print ("end $end $xml\n"); + } + # print("Result for <$tag> $order: [$ret]\n"); return ($ret); } @@ -870,9 +872,9 @@ sub _findfirst { my ($stack, $needle) = @_; # print ("needle: $needle, stack: $stack\n"); for my $i (0..length($stack)) { - my $sub = substr($stack, $i, length($needle)); - # print ("sub: $sub\n"); - return ($i) if ($sub eq $needle); + my $sub = substr($stack, $i, length($needle)); + # print ("sub: $sub\n"); + return ($i) if ($sub eq $needle); } return (undef); } @@ -880,7 +882,7 @@ sub _findfirst { sub _findlast { my ($stack, $needle) = @_; for (my $i = length($stack); $i >= 0; $i--) { - return ($i) if (substr($stack, $i, length($needle)) eq $needle); + return ($i) if (substr($stack, $i, length($needle)) eq $needle); } return (undef); }