[-]
[+]
|
Changed |
crossroads.spec
|
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/ChangeLog
^
|
@@ -1,3 +1,242 @@
+2.62 [KK 2010-01-08]
+- Fixed possible coredump cause in Balancer::serve(), which might
+ explain some troubles during high load (many really concurrent requests).
+
+2.61 [KK 2009-11-17]
+- Netbuffer::netwrite() will abort after 5 attempts that yield 0
+ written bytes. Probable cause of CPU hogging - thx Franz J. for
+ reporting.
+- Fixed potential cause of crashes in base class for Dispatcher
+ (algorithm handler would not get initialized in the constructors).
+
+2.60 [KK 2009-11-02]
+- Some errors demoted to informational messages to choke the amount of
+ log information that's generated.
+
+2.59 [KK 2009-10-14]
+- Bugfix in logger handling of xrctl, read
+ http://xrforum.org/viewtopic.php?t=495 if you fancy. Thanks Hobbit
+ for reporting this!
+
+2.58 [KK 2009-10-14]
+- Added test/ftp.xml as a sample ftp service configuration.
+- Implemented URL-based dispatching: flag --url-match/-j, present in
+ xrctl, tested in test/urlmatch.xml, added to web interface
+- Usage information now shows long versions of flags first.
+
+2.57 [KK 2009-09-14]
+- Output of "xrctl status" colorized when a service is not running,
+ thanks Frederik D. for the suggestion and the code!
+- Small cleanup of xrctl's usage information.
+
+2.56 [KK 2009-05-26]
+- Bugfix in cleaning of IPStore map
+- Implemented flag --remove-reservations (xrctl: tag: removereservations)
+- Timeouts adjusted: all read time outs set to 30, client-write set to
+ 5, backend-write set to 3
+- Implemented tag prefixtimestamp in system block to force timestamping.
+
+2.55 [KK 2009-05-13]
+- Implemented connection error counting of back ends.
+- Select-handling revised: atomic readability and writeability checks,
+ see xr/fdset/wait.cc and wait_r(), wait_w(), wait_rw().
+- Checks for connect() success can be configured to verify only
+ writeable status of a socket (see xr/backend/connect.cc, macro
+ CONNECTCHECK_ONLY_WRITABLE in xr/etc/Makefile.class).
+- Added test/xr-mysql-connect.
+- Added commands kill and killstart to xrctl. Action restart renamed
+ to stopstart.
+- Added simple benchmarking under test/: xr-http-test (based on an
+ older script) and xr-smtp-test. Plus a simple analyzer of the
+ reported timings, xr-analyze-test.
+- Revamped stored-ip dispatcher, added IPStore class to keep track of clients.
+
+2.54 [KK 2009-04-29]
+- Stamped new STABLE version.
+
+2.53 [KK 2009-04-24]
+- test/xr-client-ping now can live without LWP::UserAgent. When this
+ Perl module is not present, the script will fall back to wget/curl.
+- Upon thread kill, sockets are closed using socketclose()
+ (sys/socketclose.cc), which also shutdown()s the socket.
+- Bugfix in xrctl regarding new format of client/backend timeout.
+
+2.52 [KK 2009-04-21]
+- Bugfix in the roundrobin dispatcher. When only one back end would be
+ up, then the roundrobin dispatcher would incorrectly compute the
+ right back end. This bug only became visible with host-matching in
+ HTTP mode.
+
+2.51 [KK 2009-04-15]
+- Separate "write" timeouts introduced in flags -t and -T
+- Web interface activity overview includes client ip addresses
+- Added test/xr-client-ping (see docs)
+
+2.50 [KK 2009-03-30]
+- Bugfix in activation of the onfail script in the checkup thread.
+- Compilation flag -Werror only passed to the compiler when on the
+ development system Thera.local (see xr/etc/Makefile.class).
+- Onstart, onend and onfail scripts (when present) are invoked with a
+ third argument, the number of connections at the time
+- Set-Cookie header directive (see the HTTP dispatcher) attaches an
+ explicit path "/".
+- Rewired the tcp dispatcher and http dispatcher to use a common
+ buffer inside the tcp dispatcher (instead of 2 separate ones).
+
+2.49 [KK 2009-03-27]
+- Implemented onfail hook (to complement onstart/onend, flag -y).
+
+2.48 [KK 2009-03-26]
+- Implemented onstart/onend hooks (flags -z, -Z).
+- Sticky HTTP mode inspects the URI (parameter XRTarget) when no
+ sticky cookie is present.
+
+2.47 [KK 2009-03-04]
+- Stored-ip dispatching was enhanced to "anticipate" reconnects from
+ previously seen clients. The number of anticipated connections is taken
+ into account when dispatching a new client.
+- Bugfix in command line parsing of dispatch mode lax-stored-ip (would
+ be recognized as strict).
+
+2.46 [KK 2009-02-18]
+- Dispatcher-related classes moved under directory xr/Dispatchers/.
+- UDP balancer implementation started (code stubs in place).
+- xrctl bugfixed: a backend-scope maxconnections statement would
+ confuse the global server block. Thanks Rajeev for reporting.
+
+2.45 [KK 2009-02-18]
+- Usage information formatted.
+- Rubberstamped "stable"!
+
+2.44 [KK 2009-02-11]
+- Reviewed code for "throw"s (edited for consistency).
+- Bug in Webinterface::execute() (during accept()). Thanks Vladimir T.
+ for reporting and testing.
+
+2.43 [KK 2009-02-09]
+- Added Httpbuffer::replaceheader() methods
+- Added flag -I (to replace Host: headers), integrated in xrctl /
+ webinterface
+- Webinterface reports approx. # of open fd's and the limit (in activity)
+
+2.42 [KK 2009-01-28]
+- Bugfix in "xrctl generateconfig". The activity info introduced
+ before (which the web interface now emites), confused xrctl.
+- Bugfix in sys/ipmatch.cc: mask matching corrected for systems where
+ longs are not 4 bytes (64bit systems).
+- Bugfix in hashed-ip dispatch mode: When no back ends were available,
+ the dispatcher would crash (fixed).
+- XSLT stylesheet that transforms web interface slightly changed to
+ improve the looks.
+- Bugfix in xrctl's "rotate" action. Thanks "ncisrael" for reporting
+ it in the forum.
+
+2.41 [KK 2009-01-05]
+- Bugfix in xrctl, thanks Eddie for reporting it.
+
+2.40 [KK 2009-01-03]
+- Netbuffer::read() failures are no longer logged as errors, but as
+ verbose messages.
+- XML configuration now supports "logger" to redefine logger program.
+- Small changes to xrctl: ps command is auto-guessed when not
+ specified in the XML configuration.
+
+2.39 [KK 2008-12-04]
+- Added Httpbuffer::reset()
+- The HTTP dispatcher will show back end error returns (when the HTTP
+ code is in the 400 or 500 range)
+- main() will show runtime limits when invoked with -v
+- The HTTP dispatcher no longer downgrades to HTTP/1.0 and closed
+ connections. Webserver back ends can do this.
+- Exceptions during the sending of an error page in HTTP mode are
+ discarded, no longer logged.
+- Mutex lock added around thread startups (see Thread::start()), when
+ requested soq in sys/sys.
+- Mutex locks around malloc()/realloc(), when requested so in sys/sys.
+- Fixed possible socket leak in TcpDispatcher::execute(). The back end
+ socket might not have been closed when the dispatch phase crashed.
+- Dispatchers are now derived from a new base class Dispatcher, in
+ preparation for UDP handling.
+- Timestamp handling centralized in a class Timestamp.
+- Messaging (msg(), debugmsg(), reportmsg(), warnmsg()) centralized.
+- Started threads are administered in Threadlist. Separate threads are
+ killable from the web interface, or all threads to a given back end.
+
+2.38 [KK 2008-11-19]
+- Bugfix in xrctl: Weights and max connections in back ends are now
+ passed correctly.
+- Added version ID to xrctl.
+
+2.37 [KK 2008-11-17]
+- Main accept-loop in Balancer::serve() doesn't throw an exception
+ when a network connection can't be accepted. The reason is shown in
+ a warnmsg().
+
+2.36 [KK 2008-11-11]
+- Typo bugfix in xrctl, fixed.
+- xrctl will take the environment's PATH if no path is given in the
+ configuration.
+
+2.35 [KK 2008-11-11]
+- The wakeup and checkup thread are always started, even when no
+ wakeups or checkups are defined. Reason is that later these timers
+ can be set in the web interface, so we want the threads running.
+ When no checks/wakeups are applicable, the threads will recheck each
+ 30 secs.
+- Server type (tcp/http) made configurable through the web interface.
+
+2.34 [KK 2008-11-09]
+- Fixed sys/str2parts.cc for 64bit systems.
+
+2.33 [KK 2008-11-07]
+- Solaris portability for gcc 3.4 issues in Tcpdispatcher::execute()
+- Removed warnings about non-found libraries (on systems that don't
+ need them)
+- Support for DESTDIR setting in top Makefile (for debian packaging)
+- Man pages added as doc/xr.1, doc/xrctl.1 and doc/xrctl.xml.5.
+
+2.32 [KK 2008-11-05]
+- Bugfix in "stored-ip" dispatching algorithm.
+
+2.31 [KK 2008-10-30]
+- Changes related to XML-style configuration file support. A
+ new-style xrctl is in provided and during "make install" put in
+ BINDIR (normally: /usr/sbin). See test/sampleconf.xml for an example
+ of a configuration file.
+- Added webinterface URI's to control DOS-protection related settings.
+- Added DOS-protection variables to XML-output of the web interface.
+- Server-wide maxconnection tag output moved into dosprotection block.
+- Added display of such variables to the style sheet that renders the
+ XML in a browser.
+- Added option close-sockets-fast to XML output and to website URI
+ controls.
+- Added allow-from and deny-from lists to XML output and to website
+ URI controls.
+- Added the primary distribution site to the top-level Makefile as
+ macro. This now shows up in "xr -V". The version ID and site also
+ show up at the bottom of the web interface screen.
+- Target "uninstall" added to the top-level Makefile.
+- Bugfix in Netbuffer::netwrite(). When the remote connection would be
+ hung up, XR would be blissfully unaware. SIGPIPE signals are now ignored.
+- Implemented flag -g / --backend-check. Alternatives:
+ connect:ip:port, get:ip:port[/uri], external:program.
+- Added back end check type to the web interface reports, created
+ control at web interface for /backend/NR/backendcheck/VALUE to
+ change it. Added to XML configuration parsing.
+- Docs updated, ofc.
+- Status of balancer shown in web interface. Minor bugfix in xrctl.
+- Added flags -E/-e (hard/soft-maxconn-excess, to call an external
+ program). Also added to web interface with controls and updated docs.
+- Added mutex locks around cerr output catch-blocks of exceptions.
+- Added more checks for memory allocation faults.
+- Implemented DNS caching of back end host names (flag -F,
+ --dns-cache-timeout). Also implemented in web interface output and
+ controls.
+- Implemented adding and/or deleting back ends from the user
+ interface, including scripting URI's.
+- Implemented generation of a new configuration using "xrctl
+ generateconfig".
+
2.30 [KK 2008-10-25]
- Reversioned to 2.30 in prepration for STABLE release.
- Bugfix in Netbuffer::netwrite() (debug output of written data)
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/Makefile
^
|
@@ -1,11 +1,14 @@
# Top-level Makefile for XR
# -------------------------
-VER = 2.30
-BINDIR = /usr/sbin
+VER = 2.62
+PREFIX = $(DESTDIR)/usr
+BINDIR = $(PREFIX)/sbin
+MANDIR = $(PREFIX)/share/man
TAR = /tmp/crossroads-$(VER).tar.gz
AUTHOR = Karel Kubat <karel@kubat.nl>
MAINTAINER = Karel Kubat <karel@kubat.nl>
+DISTSITE = http://crossroads.e-tunity.com
BASE = $(shell pwd)
foo:
@@ -13,7 +16,9 @@
@echo 'Choose:'
@echo ' make local - local program construction'
@echo ' make localprof - local, with profiling info'
+ @echo ' make localmem - local, with memory debugging'
@echo ' make install - installation to $(BINDIR)'
+ @echo ' make uninstall - removes installed programs'
@echo ' make clean - removal after local/install'
@echo ' make tar - pack sources in an archive'
@echo ' make commit - commit to repository (maintainer only)'
@@ -24,22 +29,60 @@
xr/etc/gettools /usr/local/bin xr/etc c-conf e-ver
xr/etc/e-ver ChangeLog $(VER)
BASE=$(BASE) AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
+ DISTSITE='$(DISTSITE)' MEMDEBUG=$(MEMDEBUG)\
VER='$(VER)' PROF=$(PROF) PROFILER=$(PROFILER) $(MAKE) -C xr
localprof:
PROF=-pg PROFILER=-DPROFILER make local
-install: local
+localmem:
+ MEMDEBUG=-DMEMDEBUG make local
+
+install: local $(BINDIR)/xrctl install-manpages
mkdir -p $(BINDIR)
BASE=$(BASE) AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
+ DISTSITE='$(DISTSITE)' \
VER='$(VER)' BINDIR=$(BINDIR) $(MAKE) -C xr install
@echo
@echo ' The balancer program xr is now installed to $(BINDIR).'
- @echo ' Consider configuring xrctl/xrctl and copying it to $(BINDIR).'
- @echo ' The helper xrctl is not installed automatically!!'
+ @echo ' The control script xrctl is installed there too. In order to'
+ @echo ' use it, you will have to create /etc/xrctl.xml (if you have'
+ @echo ' not done so yet). See "man xrctl.xml" for an example.'
+ @echo
@echo ' Have fun with Crossroads $(VER),'
@echo ' -- $(MAINTAINER)'
@echo
+$(BINDIR)/xrctl: xrctl/xrctl Makefile
+ sed 's:__VER__:$(VER):' < xrctl/xrctl > $(BINDIR)/xrctl
+ chmod +x $(BINDIR)/xrctl
+install-manpages: $(MANDIR)/man1/xr.1 $(MANDIR)/man1/xrctl.1 \
+ $(MANDIR)/man5/xrctl.xml.5
+$(MANDIR)/man1/xr.1: doc/xr.1
+ mkdir -p $(MANDIR)/man1
+ cp $< $@
+$(MANDIR)/man1/xrctl.1: doc/xrctl.1
+ mkdir -p $(MANDIR)/man1
+ cp $< $@
+$(MANDIR)/man5/xrctl.xml.5: doc/xrctl.xml.5
+ mkdir -p $(MANDIR)/man5
+ cp $< $@
+
+uninstall:
+ rm -f $(BINDIR)/xr $(BINDIR)/xrctl
+ @echo
+ @echo 'The balancer binary xr and the control script xrctl have been'
+ @echo 'removed from $(BINDIR).'
+ @echo
+ @if [ -f /etc/xrctl.xml ] ; then \
+ echo 'The configuration /etc/xrctl.xml still exists. Remove this' ; \
+ echo 'by hand if you are sure you will not be needing it.'; \
+ else \
+ echo 'Configuration /etc/xrctl.xml was not found. Maybe you have'; \
+ echo 'it in a different location or under a different name.'; \
+ echo 'If so, consider removing it by hand.'; \
+ fi;
+ @echo
+ @echo 'XR was uninstalled!'
clean:
rm -rf xr/build/*
@@ -59,4 +102,4 @@
test `svn status | grep '^\?' | wc -l` -eq 0 || \
(echo 'SVN not fully up to date: run "svn status"' && exit 1)
perl -c xrctl/xrctl
- svn commit
+ svn -m $(VER) commit
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/doc/xr.1
^
|
@@ -0,0 +1,57 @@
+.TH "XR" "1" "Nov 6, 2008" "Crossroads" "Man Page"
+
+.SH "NAME"
+xr \- Crossroads Load Balancer & Fail Over Utility
+
+.SH "SYNOPSIS"
+\fBxr [--verbose] [--web-interface XRSERVER:PORT] --server
+tcp:XRSERVER:PORT --backend BACKEND:PORT [--backend BACKEND:PORT] ...\fR
+
+.SH "DESCRIPTION"
+This manual page briefly documents XR, the Crossroads Load Balancer &
+Fail Over Utility.
+
+XR is an open source load balancer and fail over utility for TCP based
+services. It is a dae mon running in user space, and features
+extensive configurability, polling of back ends using wake up calls,
+status reporting, many algorithms to select the 'right' back end for a
+reques t (and user-defined algorithms for very special cases), and
+much more.
+
+XR is service-independent: it is usable for any TCP service, such as
+HTTP(S), SSH, SMTP, dat abase connections. In the case of HTTP
+balancing, XR handles multiple host balancing, and can provide session
+stickiness for back end processes that need sessions, but aren't
+session-awa re of other back ends.
+
+XR furthermore features a management web interface and can be run as a
+stand-alone daemon, or via inetd.
+
+Execute 'xr -h' to get a complete list of available command-line parameters.
+
+.SH "EXAMPLE"
+xr --verbose --server tcp:0:80 --backend 10.1.1.1:80 --backend
+10.1.1.2:80 --backend 10.1.1. 3:80 --web-interface 0:8001
+
+This instructs XR to listen to port 80 and to dispatch traffic to the
+servers 10.1.1.1, 10.1.1.2 and 10.1.1.2, port 80. A web interface for
+the balancer is started on port 8001.
+
+Direct your browser to the server running XR. You will see the pages
+served by one of the three back ends. The console where XR is
+started, will show what's going on (due to the presence of
+--verbose).
+
+Direct your browser to the server running XR, but port 8001. You will
+see the web interface, which shows the status, and where you can
+alter some settings.
+
+.SH "SEE ALSO"
+xrctl(1)
+
+.SH "AUTHOR"
+XR was written by Karel Kubat <karel@kubat.nl>. Web page:
+http://crossroads.e-tunity.com
+
+.P
+This man page was written by Frederik Dannemare <frederik@dannemare.net>.
|
|
Changed |
crossroads-stable.tar.gz/doc/xr.odt
^
|
|
Changed |
crossroads-stable.tar.gz/doc/xr.pdf
^
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/doc/xrctl.1
^
|
@@ -0,0 +1,41 @@
+.TH "XRCTL" "1" "2008,2009" "Crossroads" "Man Page"
+
+.SH "NAME"
+xrctl \- Crossroads control-script
+
+.SH "SYNOPSIS"
+\fBxrctl [OPTION]...\fR
+
+.SH "DESCRIPTION"
+This manual page briefly documents xrctl, the Crossroads
+control-script. Instead of starting XR by hand, consider using xrctl.
+
+Edit /etc/xrctl.xml, which is the configuration file, and configure
+your service(s), all their options, and back ends. Then type xrctl
+start to start all your services, or xrctl stop to stop them.
+
+.SH "OPTIONS"
+.nf
+list [SERVICE] - show configuration of a service, or of all
+start [SERVICE] - start a service, or all configured services
+stop [SERVICE] - stop a service, or all configured services
+kill [SERVICE] - brutal stop, interrupts connections
+force [SERVICE] - start a service (or all) if not running
+stopstart [SERVICE] - stop and start a service, or all
+killstart [SERVICE] - kill and start a service, or all
+status [SERVICE] - show running status of a service, or of all
+rotate [SERVICE] - rotate logs of a service or of all
+generateconfig [SERVICE] - queries running XR's for the configuration
+ and shows it in the format of /etc/xrctl.xml
+.fi
+
+.SH "SEE ALSO"
+xr(1), xrctl.xml(5)
+
+.SH "AUTHOR"
+xrctl was written by Karel Kubat <karel@kubat.nl>. Web page:
+http://crossroads.e-tunity.com
+
+.P
+This man page was written by Frederik Dannemare
+<frederik@dannemare.net> and updated by Karel Kubat <karel@kubat.nl>.
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/doc/xrctl.xml.5
^
|
@@ -0,0 +1,246 @@
+.TH "XRCTL.XML" "5" "2008,2009" "Crossroads" "Man Page"
+
+.SH "NAME"
+xrctl.xml \- Crossroads control-script configuration file
+
+.SH "SYNOPSIS"
+The file /etc/xrctl.xml is xrctl's configuration. It defines how xrctl
+will start the balancer xr. If you wish to use xrctl to control the
+balancer, then you must configure all services, options and back ends
+in xrctl.xml.
+
+.SH "EXAMPLE"
+The following is a configuration example. See the file xr.pdf which is
+distributed with the sources for a full description.
+
+.nf
+<?xml version="1.0" encoding="UTF-8">
+
+<configuration>
+
+ <!-- General system configuration section -->
+
+ <system>
+ <!-- Path where the "xr" binary is searched, and zippers as "gzip"
+ and "bzip2", and the "ps" command. Default is that xrctl
+ uses $PATH. -->
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ <!-- "ps" command that shows the PID and command. On Solaris, use
+ /usr/bin/ps -ef "pid comm" and on Linux/MacOSX use
+ /bin/ps -ax -o pid,command. Default is that xrctl guesses
+ the right command. Example:
+ <pscmd>/bin/ps ax -o pid,command</pscmd> -->
+ <!-- Use "logger" to add output to syslog or not? Logger will be
+ used if the binary can be found, and if uselogger is true. -->
+ <uselogger>true</uselogger>
+ <!-- The default logger is the program "logger". Redefine here if
+ you like, for example to a piping logrotate program. Example:
+ <logger>clpipe /var/log/xr.clog</logger>
+ The default <logger> command is: logger -t xr.{service} -->
+ <!-- If logger is NOT used, xrctl will manage log output. In that
+ case, specify the following:
+ - Where do logs get written?
+ <logdir>/var/log</logdir>
+ - How big may the logs become? Manipulated during "xrctl rotate".
+ <maxlogsize>100000</maxlogsize>
+ - How many history logs to keep?
+ <loghistory>10</loghistory> -->
+ <!-- To force log line timestamping, add:
+ <prefixtimestamp>true</prefixtimestamp>
+ Or turn it off with value "false". -->
+ </system>
+
+ <!-- Service descriptions: This section defines all balancing
+ services that you want to start. Each service will lead to one
+ invocation of "xr". -->
+
+ <!-- Very simple TCP service that dispatches SSH connections on
+ port 20.000 to three back ends. Most options are left to
+ their defaults. -->
+ <service>
+ <!-- Service name, must be unique -->
+ <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. -->
+ <type>tcp</type>
+ <address>0:20000</address>
+ <webinterface>0:20001</webinterface>
+ <!-- Clients may be idle for 30 minutes, then they are logged
+ out. But they must accept data from XR within 3 seconds,
+ the latter is the write timeout: -->
+ <clienttimeout>1800:3</clienttimeout>
+
+ <!-- DNS entries are by default cached for 3600 sec (1 hour).
+ Change if you like, for example to half an hour: -->
+ <dnscachetimeout>1800</dnscachetimeout>
+ </server>
+
+ <!-- Back ends for the service. -->
+ <backend>
+ <!-- IP:port to dispatch to. -->
+ <address>server1:22</address>
+ </backend>
+ <backend>
+ <address>server2:22</address>
+ </backend>
+ <backend>
+ <address>server2:22</address>
+ </backend>
+ </service>
+
+ <!-- Here is an HTTP service for web balancing. It shows more
+ advanced features. -->
+ <service>
+ <name>webone</name>
+
+ <!-- Balancer server description -->
+ <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 and bound only to the local host address. -->
+ <address>0:20010</address>
+ <type>http</type>
+ <webinterface>127.0.0.1:20011</webinterface>
+
+ <!-- A non-default dispatch mode, here: by client IP.-->
+ <dispatchmode>lax-hashed-ip</dispatchmode>
+
+ <!-- Checks. Dead back ends are checked each 3 seconds. There is
+ no checking of dead and live back ends (checkupinterval 0). -->
+ <checks>
+ <wakeupinterval>3</wakeupinterval>
+ <checkupinterval>0</checkupinterval>
+ </checks>
+
+ <debugging>
+ <!-- Let's go with full messaging: verbose, debug, and logging
+ of transmitted messages. -->
+ <verbose>yes</verbose>
+ <debug>yes</debug>
+ <logtrafficdir>/tmp</logtrafficdir>
+ </debugging>
+
+ <!-- If the balancer runs out of sockets because too many
+ closing connections are in TIME_WAIT state, use: -->
+ <closesocketsfast>yes</closesocketsfast>
+
+ <!-- XR can run specific scripts when client activity starts or
+ ends. When given, the scripts are run with the arguments:
+ the client IP, and the back end (server:port), and the
+ number of connections to that back end. A very simple script
+ /where/ever/activitystart might e.g. do:
+ echo Client $1 going to back end $2, $3 connections so far \
+ >> /tmp/activity.log
+ A very simple script /where/ever/activityend might e.g. do:
+ echo Client $1 is done with back end $2 >> /tmp/activity.log
+ - onstart: is run when a client is about to be handled at a
+ back end
+ - onend: is run after succesful termination
+ - onfail: is run after unsuccesful termination
+ -->
+ <onstart>/where/ever/activitystart</onstart>
+ <onend>/where/ever/activityend</onend>
+ <onfail>/where/ever/activityaborted</onfail>
+
+ <!-- Access restrictions: we allow from two IP ranges, and deny
+ from one IP address. The overall results:will be:
+ - Access will be allowed from 10.*.*.*
+ - And allowed from 192.168.1.*, but not from 192.168.1.100 -->
+ <acl>
+ <allowfrom>10.255.255.255</allowfrom>
+ <allowfrom>192.168.1.255</allowfrom>
+ <denyfrom>192.168.1.100</denyfrom>
+ </acl>
+
+ <!-- For a nonstandard buffer size (default is 2k), use: -->
+ <buffersize>4096</buffersize>
+
+ <dosprotection>
+ <!-- Here is some basic DOS protection. Connections from IP's
+ are counted over timeinterval seconds (here: 2 sec). When a
+ client exceeds the hard limit hardmaxconnrate (here: 200),
+ then it is denied access. When it exceeds the soft limit
+ softmaxconnrate (here: 150), then each connection is
+ delayed for defertime microsecs (here: 1.000.000, one
+ sec).
+ Finally, the entire balancer will be allowed to serve up
+ to 400 simultaneous connections. -->
+ <timeinterval>2</timeinterval>
+ <hardmaxconnrate>200</hardmaxconnrate>
+ <softmaxconnrate>150</softmaxconnrate>
+ <defertime>1000000</defertime>
+ <maxconnections>400</maxconnections>
+
+ <!-- Let's add some more protection. When a user exceeds their
+ hard maxconn rate, "/path/to/program" will be invoked
+ with the IP as argument. That program may eg. call
+ iptables to block the client. There is also a tag
+ softmaxconnexcess (not shown here). -->
+ <hardmaxconnexcess>/path/to/program</hardmaxconnexcess>
+
+ </dosprotection>
+
+ <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
+ two serverheaders to insert -->
+ <addxrversion>off</addxrversion>
+ <addxforwardedfor>on</addxforwardedfor>
+ <stickyhttp>off</stickyhttp>
+ <serverheaders>
+ <header>MyFirstHeader: Whatever</header>
+ <header>MySecondHeader: WhateverElse</header>
+ </serverheaders>
+ </http>
+ </server>
+
+ <!-- Back end definitions -->
+ <backend>
+ <!-- Backend lives on server1:80 and is very big (weight 2).
+ XR will forward up to 300 connections to it. The back end
+ checking is left to the default, which is: connect to the
+ IP and port of the back end. Requests for host
+ www.mysite.org will be serviced here. -->
+ <address>server1:80</address>
+ <weight>2</weight>
+ <maxconnections>300</maxconnections>
+ <hostmatch>www.mysite.org</hostmatch>
+ </backend>
+ <backend>
+ <!-- Backend lives on server2:80, has the default weight 1.
+ XR will forward up to 100 connections to it. The back end
+ checking is done by connecting to an alternative port 81.
+ This back end will be eligible for requests for the site
+ www.myothersite.org. -->
+ <address>server2:80</address>
+ <maxconnections>100</maxconnections>
+ <backendcheck>connect::81</backendcheck>
+ <hostmatch>www.myothersite.org</hostmatch>
+ </backend>
+ <backend>
+ <!-- Backend lives on server3:80, has the standard weight and no
+ limitations for the max nr. of connections. Back end
+ checking is done by retrieving /healthcheck.cgi from the
+ server. The back end is eligible for www.myothersite.org. -->
+ <address>server3:80</address>
+ <backendcheck>get:server3:80/healthcheck.cgi</backendcheck>
+ <hostmatch>www.myothersite.org</hostmatch>
+ </backend>
+ </service>
+
+</configuration>
+.fi
+
+.SH "SEE ALSO"
+xr(1), xrctl(1)
+
+.SH "AUTHOR"
+xrctl and the corresponding configuration file format were written by
+Karel Kubat <karel@kubat.nl>. Web page: http://crossroads.e-tunity.com
+
+.P
+This man page was written by Karel Kubat <karel@kubat.nl>.
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/ftp.xml
^
|
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration>
+ <system>
+ <uselogger>false</uselogger>
+ <logdir>/tmp</logdir>
+ </system>
+
+ <service>
+ <name>ftp</name>
+ <server>
+ <type>tcp</type>
+ <address>127.0.0.1:21</address>
+ <dispatchmode>first-available</dispatchmode>
+ <verbose>true</verbose>
+ </server>
+ <backend>
+ <address>172.16.238.1:21</address>
+ <debug>true</debug>
+ </backend>
+ </service>
+
+ <service>
+ <name>ftpdata</name>
+ <server>
+ <type>tcp</type>
+ <address>127.0.0.1:20</address>
+ <dispatchmode>first-available</dispatchmode>
+ <verbose>true</verbose>
+ <debug>true</debug>
+ </server>
+ <backend>
+ <address>172.16.238.1:20</address>
+ </backend>
+ </service>
+
+</configuration>
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/multi-ip.xml
^
|
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+ <system>
+ <uselogger>true</uselogger>
+ <logger>clpipe /var/log/xr.clog</logger>
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ </system>
+
+ <service>
+ <name>first</name>
+ <server>
+ <type>tcp</type>
+ <address>192.168.2.5:10000</address>
+ <dispatchmode>first-available</dispatchmode>
+ <webinterface>192.168.2.5:10001</webinterface>
+ <verbose>yes</verbose>
+ <debug>yes</debug>
+ </server>
+ <backend>
+ <address>www.google.nl:80</address>
+ </backend>
+ <backend>
+ <address>www.thedailywtf.com:80</address>
+ </backend>
+ </service>
+
+ <service>
+ <name>second</name>
+ <server>
+ <type>tcp</type>
+ <address>172.16.89.1:10000</address>
+ <dispatchmode>first-available</dispatchmode>
+ <webinterface>172.16.89.1:10001</webinterface>
+ <verbose>yes</verbose>
+ <debug>yes</debug>
+ </server>
+ <backend>
+ <address>kubat.nl:80</address>
+ </backend>
+ <backend>
+ <address>slashdot.org:80</address>
+ </backend>
+ </service>
+
+</configuration>
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/ntimes
^
|
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+# ntimes <command> - fork and run it
+
+die ("Usage: ntimes TIMES COMMAND\n",
+ "Forks TIMES and each fork runs the COMMAND.\n") if ($#ARGV != 1);
+for my $i (1..$ARGV[0]) {
+ my $pid = fork();
+ die ("$0: cannot fork, $!\n") unless (defined($pid));
+ if (!$pid) {
+ system($ARGV[1]);
+ exit();
+ }
+}
+
+while (1) {
+ my $kid = wait();
+ last if ($kid < 1);
+ print ("$0: Child $kid terminated\n");
+}
+print ("All forks have finished, done.\n");
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/onend
^
|
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo Client $1 was handled by $2 and is now stopping, $3 connections remain \
+ >> /tmp/activity.log
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/onfail
^
|
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo Back end $2 with $3 connections failed for client $1 \
+ >> /tmp/activity.log
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/onstart
^
|
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo Client $1 will be handled by $2, $3 connections so far \
+ >> /tmp/activity.log
+
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/restricted-webinterface.xml
^
|
@@ -0,0 +1,50 @@
+<!-- This is an example of an XR configuration that limits the access
+ to the web interface to one address. -->
+
+<?xml version="1.0" encoding="UTF-8">
+
+<configuration>
+ <system>
+ <uselogger>false</uselogger>
+ <logdir>/tmp</logdir>
+ </system>
+
+ <service>
+ <!-- This is the main service that XR is responsible for.
+ The local web interface runs on port 11.001 but is accessible
+ only from localhost (127.0.0.1). The sample service runs on
+ port 11.000 and is accessible from wherever you like. -->
+ <name>web</name>
+ <server>
+ <type>tcp</type>
+ <address>0:11000</address>
+ <webinterface>127.0.0.1:11001</webinterface>
+ <verbose>yes</verbose>
+ </server>
+
+ <backend>
+ <address>server1:8000</address>
+ </backend>
+ </service>
+
+ <service>
+ <!-- Here's a forwarder that runs on port 11.002 and only accepts
+ traffic from IP address 10.50.45.189. The calls are
+ dispatched to localhost:11.001, so that this way, the web
+ interface is only accessible via:
+ a. port 11.001 from localhost, or
+ b. port 11.002 from 10.50.45.189 -->
+ <name>webinterface</name>
+ <server>
+ <type>tcp</type>
+ <address>0:11002</address>
+ <acl>
+ <allowfrom>10.50.45.189</allowfrom>
+ </acl>
+ </server>
+ <backend>
+ <address>localhost:11001</address>
+ </backend>
+ </service>
+
+</configuration>
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/test/sampleconf.xml
^
|
@@ -1,53 +1,193 @@
-<!-- Sample XR Configuration.
- Just doodling around here for an XML format for the config.
- No relevance (yet). -->
+<?xml version="1.0" encoding="UTF-8">
<configuration>
-
- <!-- Global settings, applicable to all services -->
-
- <piddir>/var/run</piddir>
- <uselogger>true</uselogger>
- <logdir>/var/log</logdir>
- <maxlogsize>100000</maxlogsize>
- <path>
- <dir>/bin</dir>
- <dir>/sbin</dir>
- <dir>/usr/bin</dir>
- <dir>/usr/sbin</dir>
- <dir>/usr/local/bin</dir>
- <dir>/usr/local/sbin</dir>
- <dir>/opt/local/bin</dir>
- <dir>/opt/local/sbin</dir>
- </path>
-
- <!-- Service descriptors -->
-
- <service name="web">
- <!-- Multi-host balancing. "www.onesite.org" or anything matching
- "onesite" goes to the 10.1.1 back ends. Anything matching
- "othersite" goes to the 10.1.9 back ends. -->
- <server>http:0:81</server>
- <dispatchmode>least-connections</dispatchmode>
- <backends hostmatch="onesite">
- <backend>
- <address>10.1.1.1:80</address>
- <weight>5</weight>
- </backend>
- <backend>
- <address>10.1.1.2:80</address>
- <maxconnections>10</maxconnections>
- </backend>
- </backends>
- <backends hostmatch="othersite">
- <backend>
- <address>10.1.9.1:80</address>
- </backend>
- <backend>
- <address>10.1.9.2:80</address>
- </backend>
- </backends>
- <verbose>true</verbose>
+
+ <!-- General system configuration section -->
+
+ <system>
+ <!-- Path where the "xr" binary is searched, and zippers as "gzip"
+ and "bzip2", and the "ps" command. Default is that xrctl
+ uses $PATH. -->
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ <!-- "ps" command that shows the PID and command. On Solaris, use
+ /usr/bin/ps -ef "pid comm" and on Linux/MacOSX use
+ /bin/ps -ax -o pid,command. Default is that xrctl guesses
+ the right command. Example:
+ <pscmd>/bin/ps ax -o pid,command</pscmd> -->
+ <!-- Use "logger" to add output to syslog or not? Logger will be
+ used if the binary can be found, and if uselogger is true. -->
+ <uselogger>true</uselogger>
+ <!-- The default logger is the program "logger". Redefine here if
+ you like, for example to a piping logrotate program. Example:
+ <logger>clpipe /var/log/xr.clog</logger>
+ The default <logger> command is: logger -t xr.{service} -->
+ <!-- If logger is NOT used, xrctl will manage log output. In that
+ case, specify the following:
+ - Where do logs get written?
+ <logdir>/var/log</logdir>
+ - How big may the logs become? Manipulated during "xrctl rotate".
+ <maxlogsize>100000</maxlogsize>
+ - How many history logs to keep?
+ <loghistory>10</loghistory> -->
+ </system>
+
+ <!-- Service descriptions: This section defines all balancing
+ services that you want to start. Each service will lead to one
+ invocation of "xr". -->
+
+ <!-- Very simple TCP service that dispatches SSH connections on
+ port 20.000 to three back ends. Most options are left to
+ their defaults. -->
+ <service>
+ <!-- Service name, must be unique -->
+ <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. -->
+ <type>tcp</type>
+ <address>0:20000</address>
+ <webinterface>0:20001</webinterface>
+ <!-- Clients may be idle for 30 minutes, then they are logged
+ out. -->
+ <clienttimeout>1800</clienttimeout>
+ </server>
+
+ <!-- Back ends for the service. -->
+ <backend>
+ <!-- IP:port to dispatch to. -->
+ <address>server1:22</address>
+ </backend>
+ <backend>
+ <address>server2:22</address>
+ </backend>
+ <backend>
+ <address>server2:22</address>
+ </backend>
</service>
-
+
+ <!-- Here is an HTTP service for web balancing. It shows more
+ advanced features. -->
+ <service>
+ <name>webone</name>
+
+ <!-- Balancer server description -->
+ <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. -->
+ <address>0:20010</address>
+ <type>http</type>
+ <webinterface>127.0.0.1:20011</webinterface>
+
+ <!-- A non-default dispatch mode, here: by client IP.-->
+ <dispatchmode>lax-hashed-ip</dispatchmode>
+
+ <!-- Checks. Dead back ends are checked each 3 seconds. There is
+ no checking of dead and live back ends (checkupinterval 0). -->
+ <checks>
+ <wakeupinterval>3</wakeupinterval>
+ <checkupinterval>0</checkupinterval>
+ </checks>
+
+ <debugging>
+ <!-- Let's go with full messaging: verbose, debug, and logging
+ of transmitted messages. -->
+ <verbose>yes</verbose>
+ <debug>yes</debug>
+ <logtrafficdir>/tmp</logtrafficdir>
+ </debugging>
+
+ <!-- If the balancer runs out of sockets because too many
+ closing connections are in TIME_WAIT state, use: -->
+ <closesocketsfast>yes</closesocketsfast>
+
+ <!-- Access restrictions: we allow from two IP ranges, and deny
+ from one IP address. The overall results:will be:
+ - Access will be allowed from 10.*.*.*
+ - And allowed from 192.168.1.*, but not from 192.168.1.100 -->
+ <acl>
+ <allowfrom>10.255.255.255</allowfrom>
+ <allowfrom>192.168.1.255</allowfrom>
+ <denyfrom>192.168.1.100</denyfrom>
+ </acl>
+
+ <dosprotection>
+ <!-- Here is some basic DOS protection. Connections from IP's
+ are counted over timeinterval seconds (here: 2 sec). When a
+ client exceeds the hard limit hardmaxconnrate (here: 200),
+ then it is denied access. When it exceeds the soft limit
+ softmaxconnrate (here: 150), then each connection is
+ delayed for defertime microsecs (here: 1.000.000, one
+ sec).
+ Finally, the entire balancer will be allowed to serve up
+ to 400 simultaneous connections.
+ -->
+ <timeinterval>2</timeinterval>
+ <hardmaxconnrate>200</hardmaxconnrate>
+ <softmaxconnrate>150</softmaxconnrate>
+ <defertime>1000000</defertime>
+ <maxconnections>400</maxconnections>
+
+ <!-- Let's add some more protection. When a user exceeds their
+ hard maxconn rate, "/path/to/program" will be invoked
+ with the IP as argument. That program may eg. call
+ iptables to block the client. There is also a tag
+ softmaxconnexcess (not shown here). -->
+ <hardmaxconnexcess>/path/to/program</hardmaxconnexcess>
+
+ </dosprotection>
+
+ <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 -->
+ <addxrversion>off</addxrversion>
+ <addxforwardedfor>on</addxforwardedfor>
+ <stickyhttp>off</stickyhttp>
+ <replacehostheader>on</replacehostheader>
+ <serverheaders>
+ <header>MyFirstHeader: Whatever</header>
+ <header>MySecondHeader: WhateverElse</header>
+ </serverheaders>
+ </http>
+ </server>
+
+ <!-- Back end definitions -->
+ <backend>
+ <!-- Backend lives on server1:80 and is very big (weight 2).
+ XR will forward up to 300 connections to it. The back end
+ checking is left to the default, which is: connect to the
+ IP and port of the back end. Requests for host
+ www.mysite.org will be serviced here. -->
+ <address>server1:80</address>
+ <weight>2</weight>
+ <maxconnections>300</maxconnections>
+ <hostmatch>www.mysite.org</hostmatch>
+ </backend>
+ <backend>
+ <!-- Backend lives on server2:80, has the default weight 1.
+ XR will forward up to 100 connections to it. The back end
+ checking is done by connecting to an alternative port 81.
+ This back end will be eligible for requests for the site
+ www.myothersite.org. -->
+ <address>server2:80</address>
+ <maxconnections>100</maxconnections>
+ <backendcheck>connect::81</backendcheck>
+ <hostmatch>www.myothersite.org</hostmatch>
+ </backend>
+ <backend>
+ <!-- Backend lives on server3:80, has the standard weight and no
+ limitations for the max nr. of connections. Back end
+ checking is done by retrieving /healthcheck.cgi from the
+ server. The back end is eligible for www.myothersite.org. -->
+ <address>server3:80</address>
+ <backendcheck>get:server3:80/healthcheck.cgi</backendcheck>
+ <hostmatch>www.myothersite.org</hostmatch>
+ </backend>
+ </service>
+
</configuration>
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/test.cgi
^
|
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+# Simple script for benchmarking purposes. Invoke as:
+# http://whereever/test.cgi?bytes=XYZZY&usec=PLUGH
+# Will spam XYZZY bytes as payload, and delay for PLUGH microsecs.
+# The payload files are created in $tmpdir if they don't yet exist
+# so that CPU looping is avoided.
+
+use strict;
+use Time::HiRes qw(usleep);
+use CGI qw(:standard);
+
+my $tmpdir = '/tmp';
+
+# CGI Header
+print ("Content-Type: text/plain\r\n\r\n");
+
+# Delay for 'usec' microsecs.
+my $usec = param('usec') or 0;
+usleep($usec);
+
+# Check that we have a file for the payload. If not, create it.
+my $bytes = param('bytes');
+my $file = "$tmpdir/test.cgi.$bytes";
+if (! -f $file) {
+ open (my $of, ">$file") or die ("Cannot write $file: $!\n");
+ for (my $i = 0; $i < $bytes; $i++) {
+ print $of ('X');
+ }
+ close ($of);
+}
+# Send the file to the browser.
+my $buf;
+open (my $if, $file) or die ("Cannot read $file: $!\n");
+while (sysread($if, $buf, 2048)) {
+ print ($buf);
+}
+
+# All done. Return control to the web server.
+
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/urlmatch.xml
^
|
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+ <system>
+ <uselogger>false</uselogger>
+ <logdir>/tmp</logdir>
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ </system>
+
+ <service>
+ <name>urlmatch</name>
+ <server>
+ <type>http</type>
+ <address>0:20000</address>
+ <webinterface>0:20001</webinterface>
+ <verbose>yes</verbose>
+ <debug>yes</debug>
+ </server>
+ <backend>
+ <address>server1:10000</address>
+ <urlmatch>/$</urlmatch>
+ </backend>
+ <backend>
+ <address>server2:10000</address>
+ <urlmatch>/pages/fch</urlmatch>
+ </backend>
+ <backend>
+ <address>server3:10000</address>
+ <urlmatch>/pages/[^f][^c][^h]</urlmatch>
+ </backend>
+ </service>
+</configuration>
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-analyze-test
^
|
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+
+my $n = 0;
+my $tot = 0;
+while (my $line = <STDIN>) {
+ next unless ($line =~ /^\d/);
+ $tot += $line;
+ $n++;
+}
+exit(1) unless ($n);
+
+print ("Total : $n\n",
+ "Average : ", $tot / $n, "\n");
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-client-ping
^
|
@@ -0,0 +1,150 @@
+#!/usr/bin/perl
+
+use POSIX ':sys_wait_h';
+use strict;
+
+# Main
+my $quiet = 0;
+while($ARGV[0] eq '-q') {
+ $quiet++;
+ shift(@ARGV);
+}
+
+usage() if ($#ARGV != 1);
+my $sleeptime = sprintf('%d', $ARGV[1]);
+die("$0: bad interval $ARGV[1]\n") if ($sleeptime < 2);
+while (1) {
+ # Clean up any zombies
+ while (waitpid(-1, WNOHANG) > 0) { }
+
+ # Run the test
+ do_test();
+
+ # Sleep for the duration of the interval
+ my $slept = 0;
+ while ($slept < $sleeptime) {
+ $slept += sleep($sleeptime - $slept);
+ }
+}
+
+# Show usage and croak
+sub usage() {
+ die <<"ENDUSAGE";
+
+Usage: xr-client-ping [-q] WEBINTERFACE-URL INTERVAL
+The web interface is queried for clients. Connections to non-pingable clients
+are killed. The process is repeated each interval.
+
+The arguments:
+ -q: quiet mode, suppresses verbose messaging
+ WEBINTERFACE-URL: the URL of XR's web interface, include http://
+ INTERVAL: number of seconds
+
+ENDUSAGE
+}
+
+# Start a single test
+my $_tries = 0;
+sub do_test() {
+ msg ("-----------------------------------------------------------------\n");
+ msg ("Starting check run\n");
+ my $xml;
+ eval {
+ $xml = http_get($ARGV[0]);
+ };
+ if ($@) {
+ msg ("Could not access web interface: $@\n");
+ die ("Too many tries now, giving up...\n") if ($_tries++ > 5);
+ return;
+ }
+ $_tries = 0;
+
+ my $active = 0;
+ my ($id, $clientip);
+ for my $line (split(/\n/, $xml)) {
+ $active = 1 if ($line =~ /<thread>/);
+ $active = 0 if ($line =~ /<\/thread>/);
+
+ if ($active) {
+ if ($line =~ /<id>/) {
+ $id = $line;
+ $id =~ s/\s*<id>//;
+ $id =~ s/<\/id>.*//;
+ } elsif ($line =~ /<clientip>/) {
+ $clientip = $line;
+ $clientip =~ s/\s*<clientip>//;
+ $clientip =~ s/<\/clientip>//;
+ check_client($id, $clientip) if ($clientip ne '0.0.0.0');
+ }
+ }
+ }
+}
+
+# Check one thread ID and client IP
+sub check_client($$) {
+ my ($id, $clientip) = @_;
+
+
+ msg ("Checking connection for client $clientip (XR thread $id)\n");
+ return if (fork());
+
+ my $cmd = "ping -c3 -t3 $clientip >/dev/null";
+ msg ("$clientip: pinging (external '$cmd')\n");
+ my $status = system($cmd);
+ if ($status != 0) {
+ msg ("$clientip: ping status '$status' $!\n");
+ msg ("$clientip: not reachable, stopping XR thread $id\n");
+ eval {
+ http_get("$ARGV[0]/thread/kill/$id");
+ };
+ msg ("Failed to stop thread $id\n") if ($@);
+ } else {
+ msg ("$clientip: reachable, connection assumed valid\n");
+ }
+ exit(0);
+}
+
+# Do a HTTP GET. Try LWP::UserAgent if available, else try wget.
+sub http_get($) {
+ my $url = shift;
+ my $ua;
+
+ # Try LWP::UserAgent
+ eval {
+ require LWP::UserAgent;
+ };
+ if (! $@) {
+ $ua = LWP::UserAgent->new();
+ $ua->timeout(3);
+ my $res = $ua->get($url);
+ die ("Could not access url '$url'\n")
+ unless ($res->is_success());
+ return $res->content();
+ }
+
+ # Try wget or curl, or any other command (can be put in here)
+ for my $cmd ("wget -q -O- -T3 '$url'",
+ "curl --connect-timeout 3 -s '$url'") {
+ msg ("Running: $cmd\n");
+ open (my $if, "$cmd |");
+ if ($if) {
+ my $cont = '';
+ while (my $line = <$if>) {
+ $cont .= $line;
+ }
+ if (close($if)) {
+ return $cont;
+ } else {
+ msg("$cmd failed: $!\n");
+ }
+ }
+ }
+
+ # All failed, now what?
+ die ("No method to access url '$url'\n");
+}
+
+# Verbose messaging
+sub msg {
+ print ($$, ' ', scalar(localtime()), ' ', @_) unless ($quiet);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-http-test
^
|
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+use strict;
+use LWP::UserAgent;
+use Time::HiRes qw(sleep gettimeofday tv_interval);
+
+$|++;
+
+die <<"ENDUSAGE" if ($#ARGV != 2);
+
+Usage: $0 URL THREADS DURATION
+Will start THREADS to get URL. The entire test will run for DURATION
+seconds.
+
+ENDUSAGE
+
+my ($url, $threads, $duration) = @ARGV;
+for my $i (1..$threads) {
+ next if (fork());
+
+ my $t_start = [gettimeofday()];
+ my $runs = 0;
+ while (tv_interval($t_start) < $duration) {
+ $runs++;
+ my $t_run = [gettimeofday()];
+ my $ua = LWP::UserAgent->new();
+ $ua->timeout(5);
+ my $resp = $ua->get($url);
+ print(tv_interval($t_run), "\n")
+ if ($resp->is_success());
+ }
+ exit(0);
+}
+
+while(wait() != -1) {
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-mysql-connect
^
|
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# Where does your mysql live?
+mysql=/usr/local/mysql/bin/mysql
+
+# Get the host and port from the argument (which is the back end,
+# as host:port)
+host=`echo $1 | sed 's/:.*//'`
+port=`echo $1 | sed 's/.*://'`
+
+# Try to connect to the host and port. Print the result on stdout.
+echo quit | $mysql -h $host -P $port --protocol=tcp --connect-timeout=3
+echo $?
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-smtp-test
^
|
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+use strict;
+use Net::SMTP;
+use Time::HiRes qw(sleep gettimeofday tv_interval);
+
+$|++;
+
+die <<"ENDUSAGE" if ($#ARGV != 4);
+
+Usage: $0 ENDPOINT FROM-ADDRESS TO-ADDRESS THREADS DURATION
+Will start THREADS to send dummy e-mails to TO-ADDRESS. The test will run
+for DURATION seconds. ENDPOINT is the IP address to connect to.
+
+ENDUSAGE
+
+my ($endpoint, $from, $address, $threads, $duration) = @ARGV;
+for my $i (1..$threads) {
+ next if (fork());
+
+ my $t_start = [gettimeofday()];
+ my $runs = 0;
+ while (tv_interval($t_start) < $duration) {
+ $runs++;
+ my $t_run = [gettimeofday()];
+ my $smtp = Net::SMTP->new($endpoint, Timeout => 5)
+ or die ("Cannot start SMTP\n");
+ $smtp->mail($from);
+ $smtp->to($address);
+ $smtp->data();
+ $smtp->datasend("To: $address\n");
+ $smtp->datasend("Subject: Testing mail\n");
+ $smtp->datasend("\n");
+ $smtp->datasend("This is just a test message.\n");
+ $smtp->dataend();
+ $smtp->quit();
+ print(tv_interval($t_run), "\n");
+ }
+ exit(0);
+}
+
+while(wait() != -1) {
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-stdby
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/test/xr-stdby/xr-is-live
^
|
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+
+use strict;
+
+die <<"ENDUSAGE" if ($#ARGV < 0);
+
+Usage: xr-is-live HOST [HOST...]
+
+Polls stated host(s) for live status. Exits with the number of unreachable
+hosts.
+
+Sample usage:
+ xr-is-live onehost - checks if the one host is down
+ xr-is-live h1 h2 h3 h3 h5 - checks if this network is down (this can be
+ assumed when exit status is larger than 3)
+
+ENDUSAGE
+
+for my $h (@ARGV) {
+ next if fork();
+ if (!testlive($h)) {
+ print ("$h is not reachable\n");
+ exit (1);
+ }
+ exit (0);
+}
+my $ret = 0;
+while (1) {
+ last if (wait() == -1);
+ $ret++ if ($?);
+}
+
+print ("total $ret not reachable host(s)\n") if ($ret);
+exit ($ret);
+
+sub testlive($) {
+ my $h = shift;
+
+ system("ping -c3 -t1 '$h' >/dev/null") and return undef;
+ return 1;
+}
+
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/test/xr-test
^
|
@@ -1,36 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use LWP::UserAgent;
-use Time::HiRes qw(sleep gettimeofday tv_interval);
-
-die ("Usage: $0 URL TRIALS\n") if ($#ARGV != 1);
-$|++;
-
-my $n = 0;
-my $totaltime = 0;
-
-my $ua = LWP::UserAgent->new();
-$ua->timeout(5);
-while ($n < $ARGV[1]) {
- my $success = 0;
- for my $i (1..10) {
- my $t0 = [ gettimeofday() ];
- my $resp = $ua->get($ARGV[0]);
- if ($resp->is_success()) {
- $success++;
- $n++;
- $totaltime += tv_interval ($t0);
- last;
- }
- if ($i == 3) {
- die ($resp->status_line(), "\n");
- }
- sleep (1);
- }
- sleep (0.1);
- printf ("\rN: %5d Tot:%8.3g Avg:%8.3g", $n, $totaltime, $totaltime / $n)
- if (! ($n % 10));
-}
-
-print ("\n");
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Checkers/checkupthread/checkupthread
^
|
@@ -3,6 +3,7 @@
#include "sys/sys"
#include "ThreadsAndMutexes/thread/thread"
+#include "ThreadsAndMutexes/threadlist/threadlist"
#include "balancer/balancer"
#include "error/error"
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Checkers/checkupthread/execute.cc
^
|
@@ -1,23 +1,40 @@
#include "checkupthread"
void Checkupthread::execute() {
+
+ Threadlist::desc("Checkup thread");
+
while (1) {
- for (unsigned i = 0; i < balancer.nbackends(); i++) {
- Backend target(balancer.backend(i).backenddef());
- target.check();
- if (! balancer.backend(i).live() &&
- target.live() ) {
- balancer.backend(i).live(true);
- msg ("Checkup call: backend " + target.description() +
- " has awoken\n");
- } else if (balancer.backend(i).live() &&
- ! target.live()) {
- balancer.backend(i).live(false);
- msg ("Checkup call: backend " + target.description() +
- " has gone asleep\n");
+ if (config.checkupsec()) {
+ 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");
+ } else {
+ balancer.backend(i).live(false);
+ balancer.backend(i).markconnecterror();
+ msg ("Checkup call: backend " + target.description() +
+ " is unavailable\n");
+ if (config.onfail().length()) {
+ ostringstream o;
+ o << config.onfail() << " 0.0.0.0 "
+ << target.description() << ' '
+ << balancer.backend(i).connections();
+ sysrun(o.str());
+ }
+ }
+ } catch (...) {
+ socketclose(target.sock());
+ }
}
- }
- sleep (config.checkupsec());
+ sleep (config.checkupsec());
+ } else
+ sleep(30);
}
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Checkers/wakeupthread/execute.cc
^
|
@@ -1,19 +1,30 @@
#include "wakeupthread"
void Wakeupthread::execute() {
+ Threadlist::desc("Wakeup thread");
+
while (1) {
- for (unsigned i = 0; i < balancer.nbackends(); i++) {
- if (! balancer.backend(i).live()) {
- Backend target(balancer.backend(i).backenddef());
- target.check();
- if (target.live()) {
- msg ("Wakeup call: backend " + target.description() +
- " has awoken\n");
- balancer.backend(i).live(true);
+ if (config.wakeupsec()) {
+ for (unsigned i = 0; i < balancer.nbackends(); i++) {
+ if (! balancer.backend(i).live()) {
+ Backend target(balancer.backend(i).backenddef());
+ try {
+ target.check();
+ if (target.live()) {
+ msg ("Wakeup call: backend " +
+ target.description() +
+ " has awoken\n");
+ balancer.backend(i).live(true);
+ } else
+ balancer.backend(i).markconnecterror();
+ } catch (...) {
+ socketclose(target.sock());
+ }
}
}
- }
- sleep (config.wakeupsec());
+ sleep (config.wakeupsec());
+ } else
+ sleep(30);
}
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Checkers/wakeupthread/wakeupthread
^
|
@@ -3,6 +3,7 @@
#include "sys/sys"
#include "ThreadsAndMutexes/thread/thread"
+#include "ThreadsAndMutexes/threadlist/threadlist"
#include "balancer/balancer"
#include "error/error"
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/external/target.cc
^
|
@@ -14,21 +14,23 @@
FILE *f;
if (! (f = popen (o.str().c_str(), "r")) )
- throw static_cast<Error>("Cannot start '") + o.str() + "': " +
- strerror(errno);
+ throw Error("Cannot start '" + o.str() + ": " + strerror(errno));
unsigned i;
if (fscanf (f, "%u", &i) < 1)
- throw static_cast<Error>("External algorithm '") + o.str() +
- "' did not reply with a number";
+ throw Error("External algorithm '" + o.str() +
+ "' did not reply with a number");
msg ((Mstr("External algorithm says:) ") + i) + "\n");
- if (i >= balancer.nbackends())
- throw static_cast<Error>("External algorithm '") + o.str() +
- "': answer " + i + " out of bounds";
+ if (i >= balancer.nbackends()) {
+ ostringstream o;
+ o << "External algorithm '" << o.str() << "': answer "
+ << i << " out of bounds";
+ throw Error(o.str());
+ }
if (pclose (f))
- throw static_cast<Error>("External algorithm '") + o.str() +
- "' terminated with error";
+ throw Error("External algorithm '" + o.str() +
+ "' terminated with error");
return (i);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/firstactive/target.cc
^
|
@@ -4,8 +4,7 @@
BackendVector const &targetlist) {
if ( targetlist.size() == 0 ||
! balancer.backend(targetlist[0]).available() )
- throw static_cast<Error>("First-active algorithm: "
- "no available back ends");
+ throw Error("First-active algorithm: no available back ends");
return (targetlist[0]);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/hashedip/target.cc
^
|
@@ -2,6 +2,11 @@
unsigned HashedIp::target(struct in_addr clientip,
BackendVector const &targetlist) {
+
+ // Nothing to do if we don't have targets.
+ if (!targetlist.size())
+ throw Error("Hashed-ip algorithm: no back ends to dispatch to");
+
// Hash the client's IP into an index
unsigned h = 0;
for (char *cp = (char*)&clientip;
@@ -21,8 +26,9 @@
// In lax mode, fall back to least-connections dispatching
if (! balancer.backend(index).available()) {
if (config.dispatchmode() == Dispatchmode::m_strict_hashed_ip)
- throw static_cast<Error>("Hashed-IP algorithm: target back end ") +
- balancer.backend(index).description() + " unavailable";
+ throw Error("Hashed-IP algorithm: target back end " +
+ balancer.backend(index).description() +
+ " unavailable");
else {
msg ("Hashed-IP algorithm: target back end " +
balancer.backend(index).description() + " unavailable, "
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/leastconn/leastconn
^
|
@@ -5,6 +5,7 @@
#include "error/error"
#include "balancer/balancer"
#include "DispatchAlgorithms/algorithm/algorithm"
+#include "ipstore/ipstore"
class Leastconn: public Algorithm {
public:
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/leastconn/target.cc
^
|
@@ -4,34 +4,48 @@
BackendVector const &targetlist) {
PROFILE("Leastconn::target");
+ msg (Mstr("Starting least-connections dispatcher\n"));
+
+ if (config.debug()) {
+ ostringstream o;
+ o << "Back end target list:";
+ for (unsigned i = 0; i < targetlist.size(); i++)
+ o << ' ' << targetlist[i];
+ o << '\n';
+ _debugmsg(o.str());
+ }
bool found = false;
- unsigned nconn = 0, t = 0;
+ unsigned best_weighted = 0, t = 0;
for (unsigned i = 0; i < targetlist.size(); i++) {
- if (! balancer.backend(targetlist[i]).available())
+ if (! balancer.backend(targetlist[i]).available()) {
+ debugmsg(Mstr("Back end ") +
+ Mstr(balancer.backend(targetlist[i]).description()) +
+ " is NOT available\n");
continue;
- unsigned weighted_conn =
- balancer.backend(targetlist[i]).connections() *
- balancer.backend(targetlist[i]).adjustedweight();
- msg ("Back end " + balancer.backend(targetlist[i]).description() +
- (Mstr(": connections ")
- + balancer.backend(targetlist[i]).connections()) +
- (Mstr(", adjusted weight ") +
- balancer.backend(targetlist[i]).adjustedweight()) +
- (Mstr(", weighted connections ") + weighted_conn) +
- "\n");
+ }
+ unsigned connections = balancer.backend(targetlist[i]).connections();
+ unsigned anticipated = IPStore::anticipated(targetlist[i]);
+ unsigned adjweight = balancer.backend(targetlist[i]).adjustedweight();
+ unsigned this_weight = (connections + anticipated) * adjweight;
+ ostringstream o;
+
+ if (config.debug())
+ o << "Back end " << balancer.backend(targetlist[i]).description()
+ << ": connections=" << connections << ", anticipated=" << anticipated
+ << ", adjweight=" << adjweight << ", thisweight=" << this_weight;
- if (!found || weighted_conn < nconn) {
+ if (!found || this_weight < best_weighted) {
t = targetlist[i];
- nconn = balancer.backend(t).connections();
+ best_weighted = this_weight;
found = true;
- }
+ debugmsg(Mstr(o.str()) + Mstr(" is best so far\n"));
+ } else
+ debugmsg(Mstr(o.str()) + Mstr(" skipped, got a better one\n"));
}
-
if (!found)
- throw static_cast<Error>
- ("Least-connections algorithm: no available back ends");
+ throw Error("Least-connections algorithm: no available back ends");
return (t);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/roundrobin/target.cc
^
|
@@ -4,9 +4,18 @@
BackendVector const &targetlist) {
// No back ends? Don't even try. One back end? Always that one.
if (targetlist.size() == 0)
- throw static_cast<Error>("Round robin dispatcher: no backends\n");
+ throw Error("Round robin dispatcher: no backends\n");
+
+ if (config.debug())
+ for (unsigned i = 0; i < targetlist.size(); i++) {
+ unsigned backend_index = targetlist[i];
+ _debugmsg(Mstr("Round robin dispatcher: target ") +
+ Mstr(i) + Mstr(" is ") +
+ balancer.backend(backend_index).description() + "\n");
+ }
+
if (targetlist.size() == 1)
- return (0);
+ return (targetlist[0]);
static int prev_run_index = -1;
@@ -26,8 +35,7 @@
cur_val = prev_run_index + 1;
cur_val %= targetlist.size();
if (cur_val == first_try_val)
- throw static_cast<Error>("Round robin dispatcher: "
- "no backends\n");
+ throw Error("Round robin dispatcher: no backends\n");
}
// Store for next time 'round
@@ -42,4 +50,3 @@
return (targetlist[cur_val]);
}
}
-
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/storedip/storedip
^
|
@@ -5,8 +5,10 @@
#include "error/error"
#include "balancer/balancer"
#include "config/config"
+#include "timestamp/timestamp"
#include "DispatchAlgorithms/algorithm/algorithm"
#include "DispatchAlgorithms/leastconn/leastconn"
+#include "ipstore/ipstore"
class StoredIp: public Algorithm {
public:
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/storedip/target.cc
^
|
@@ -1,89 +1,56 @@
#include "storedip"
-struct ClientData {
- int targetbackend;
- time_t lastaccess;
-};
-
-struct ClientDataCmp {
- bool operator() (struct in_addr a, struct in_addr b) const {
- long la, lb;
- memcpy (&la, &a, sizeof(long));
- memcpy (&lb, &b, sizeof(long));
- return (la - lb) < 0;
- }
-};
-
-static map<struct in_addr, ClientData, ClientDataCmp> store;
-
unsigned StoredIp::target(struct in_addr clientip,
BackendVector const &targetlist) {
- unsigned target;
- time_t now = time(0);
+ PROFILE("StoredIP::target");
+ IPStore::on();
- // Is the client already known in the map, and not timed out?
- map<struct in_addr, ClientData, ClientDataCmp>::iterator hit =
- store.find(clientip);
- if (store.count(clientip) > 0) {
- time_t diff = now - store[clientip].lastaccess;
-
- msg ("Client IP " + static_cast<string>(inet_ntoa(clientip)) +
- (Mstr(" last visited on ")
- + timestamp(store[clientip].lastaccess)) +
- (Mstr(", ") + diff) +
- " sec ago, and went to " +
- balancer.backend(store[clientip].targetbackend).description() +
- "\n");
-
- if (diff <= config.ipstoretimeout()) {
- // Recent 'nuff
- target = store[clientip].targetbackend;
- if (! balancer.backend(target).available()) {
- // Historical target down - get new one if in lax mode
- if (config.dispatchmode() == Dispatchmode::m_strict_stored_ip)
- throw static_cast<Error>
- ("Stored-IP algorithm: target back end ") +
- balancer.backend(target).description() +
- "unavailable";
- else {
- msg ("Stored IP algorithm: target back end " +
- balancer.backend(target).description() +
- " unavailable, falling back to least-connections\n");
- Leastconn l;
- target = l.target(clientip, targetlist);
- }
- }
- } else {
- // Not recent anymore
- msg ("Visit too long ago, re-dispatching with least-connections\n");
- Leastconn l;
- target = l.target(clientip, targetlist);
+ msg(Mstr("Starting stored-ip dispatcher\n"));
+
+ int tb;
+ if ( (tb = IPStore::target(clientip)) >= 0 ) {
+ unsigned target = tb;
+ IPStore::clear(clientip);
+ if (balancer.backend(target).available()) {
+ // Historical target is up, go there!
+ msg(Mstr("Sending ") + Mstr(inet_ntoa(clientip)) + " to " +
+ balancer.backend(target).description() + "\n");
+ return target;
}
- } else {
- // Historical target unknown, fetch new one
- msg ("New visit from " + static_cast<string>(inet_ntoa(clientip)) +
- "\n");
- Leastconn l;
- target = l.target(clientip, targetlist);
+ msg (Mstr("Historical target ") +
+ balancer.backend(target).description() + " unavailable\n");
+ if (config.dispatchmode() == Dispatchmode::m_strict_stored_ip)
+ throw Error("Stored-IP algorithm: target back end " +
+ balancer.backend(target).description() +
+ " unavailable");
}
- // Update the info.
- ClientData entry = {target, now};
- store[clientip] = entry;
-
- // Weed out store
- map<struct in_addr, ClientData, ClientDataCmp>::iterator
- iter = store.begin();
- while (iter != store.end()) {
- debugmsg ("Stored-IP: " +
- (string)inet_ntoa((*iter).first) + " visited on " +
- timestamp((*iter).second.lastaccess) + "\n");
- if (now - ((*iter).second.lastaccess) > config.ipstoretimeout())
- store.erase(iter);
- else
- iter++;
+ // Client is seen for the first time, or after the timout period, or
+ // their preferred back end is down (and we're in lax mode ofc).
+ // Treat as new connection.
+ Leastconn l;
+
+ if (!config.removereservations())
+ return l.target(clientip, targetlist);
+ else {
+ BackendVector tlist = targetlist;
+ while (true) {
+ try {
+ return l.target(clientip, tlist);
+ } catch (...) {
+ // We're out of back ends and need to clear up the IP store to retry.
+ // We give it a sec, remove the oldest entry, and rebuild the
+ // target list for the least-connections dispatch algorithm.
+ warnmsg("Out of back ends, releasing oldest client entry and retrying\n");
+ IPStore::clearoldest();
+ sleep(1);
+ BackendVector newlist;
+ for (unsigned int i = 0; i < balancer.nbackends(); i++)
+ if (balancer.backend(i).available())
+ newlist.add(i);
+ tlist = newlist;
+ }
+ }
}
-
- // Return target to caller
- return (target);
}
+
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/DispatchAlgorithms/weightedload/target.cc
^
|
@@ -39,6 +39,6 @@
}
- throw static_cast<Error>("Weighted-load algorithm: no available back ends ");
+ throw Error("Weighted-load algorithm: no available back ends ");
return targetlist[0]; // We need some kind of default...
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/checkacl.cc
^
|
@@ -0,0 +1,26 @@
+#include "dispatcher"
+
+bool Dispatcher::check_acl() {
+ if (config.nallow()) {
+ bool allowed = false;
+ for (unsigned n = 0; n < config.nallow(); n++)
+ if (ipmatch(clientip(), config.allow(n))) {
+ allowed = true;
+ break;
+ }
+ if (!allowed) {
+ msg("Not serving client IP " + clientipstr() +
+ ": 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() +
+ ": match in deny list\n");
+ return false;
+ }
+ }
+ return true;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/checkdos.cc
^
|
@@ -0,0 +1,113 @@
+#include "dispatcher"
+
+typedef map < unsigned long, std::queue<time_t> > AccessMap;
+static AccessMap accesslog;
+static time_t accesslog_lastclean = 0;
+
+// Execute an external program upon excess of hard/soft rates
+static void run_excess(string const &prog, char const *ip) {
+ ostringstream o;
+ o << prog << ' ' << ip;
+ msg ((Mstr("Max connection rate exceeded, invoking '") + o.str()) +
+ "'\n");
+ int ret = sysrun(o.str());
+ if (ret == -1)
+ throw Error(string("Failed to start system call: ") +
+ strerror(errno));
+ else if (ret)
+ warnmsg((Mstr("Program '") + o.str()) +
+ (Mstr("' exited with exit status ") + ret) + "\n");
+ else
+ msg((Mstr("Program '") + o.str()) + "' finished.\n");
+}
+
+bool Dispatcher::check_dos() {
+ msg ("Verifying DOS protection\n");
+ Threadlist::desc("Verifying");
+
+ // Check 'softmaxconnrate' and 'hardmaxconnrate' now!
+ // Descend into this block if connrate_time() is set, AND
+ // either hardmaxconnrate() is set,
+ // or both softmaxconnrate() and defertime() are set.
+ if (config.connrate_time() &&
+ (config.hardmaxconnrate() ||
+ (config.softmaxconnrate() && config.defertime()))) {
+ time_t now, min_ts;
+ now = time(0);
+ min_ts = now - config.connrate_time();
+ unsigned max_conns = max(config.hardmaxconnrate(),
+ config.softmaxconnrate());
+
+ Mutex::lock (&accesslog[clientip().s_addr]);
+ accesslog[clientip().s_addr].push(now);
+ Mutex::unlock (&accesslog[clientip().s_addr]);
+
+ if (accesslog_lastclean < min_ts) {
+ // Clean the entire access log, it's been a while...
+
+ Mutex::lock(&accesslog_lastclean);
+ accesslog_lastclean = now;
+ Mutex::unlock(&accesslog_lastclean);
+
+ for (AccessMap::iterator i = accesslog.begin();
+ i != accesslog.end();
+ i++ ) {
+ if (accesslog[i->first].back() < min_ts) {
+ // This IP hasn't made ANY connections in a while -- erase!
+ accesslog.erase(i);
+ } else {
+ // Keep popping off this IP's oldest connection until we
+ // have only "recent" connections left.
+ Mutex::lock(&accesslog[i->first]);
+ while ( accesslog[i->first].front() < min_ts
+ || accesslog[i->first].size() > max_conns ) {
+ accesslog[i->first].pop();
+ }
+ Mutex::unlock(&accesslog[i->first]);
+ }
+ }
+ } else {
+ // The "big log" doesn't need to be fully cleaned,
+ // but this particular IP should be!
+ Mutex::lock(&accesslog[clientip().s_addr]);
+ while ( accesslog[clientip().s_addr].front() < min_ts
+ || accesslog[clientip().s_addr].size() > max_conns ) {
+ accesslog[clientip().s_addr].pop();
+ }
+ Mutex::unlock(&accesslog[clientip().s_addr]);
+ }
+
+ if (config.hardmaxconnrate() &&
+ accesslog[clientip().s_addr].size() >= config.hardmaxconnrate() ) {
+ // This IP has violated the "HARD" limit! Reject the connection
+ ostringstream o;
+ o << "Client " << clientipstr()
+ << " has hit the HARD maximum number of connections ("
+ << config.hardmaxconnrate() << " conections in "
+ << config.connrate_time() << " seconds; "
+ << accesslog[clientip().s_addr].size()
+ << " connections recorded). Client is refused.\n";
+ warnmsg (o.str());
+ socketclose(clientfd());
+ run_excess(config.hardmaxconnexcess(), clientipstr().c_str());
+ return false;
+ } else if (config.softmaxconnrate() &&
+ (accesslog[clientip().s_addr].size() >=
+ config.softmaxconnrate())) {
+ // This IP has violated the "SOFT" Limit. Go to sleep for a while.
+ ostringstream o;
+ o << "Client " << clientipstr()
+ << " has hit the SOFT maximum number of connections ("
+ << config.softmaxconnrate() << " connections in "
+ << config.connrate_time() << " sedonds; "
+ << accesslog[clientip().s_addr].size()
+ << " connections recorded). Client is deferred for "
+ << config.defertime() << " microseconds.\n";
+ warnmsg (o.str());
+ run_excess(config.softmaxconnexcess(), clientipstr().c_str());
+ usleep(config.defertime());
+ }
+ }
+
+ return true;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/clientipstr.cc
^
|
@@ -0,0 +1,10 @@
+#include "dispatcher"
+
+string const &Dispatcher::clientipstr() {
+ if (clientip_str == "") {
+ Mutex::lock( (void*)inet_ntoa );
+ clientip_str = inet_ntoa(clientip());
+ Mutex::unlock( (void*)inet_ntoa );
+ }
+ return clientip_str;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/dispatcher
^
|
@@ -0,0 +1,67 @@
+#ifndef _DISPATCHER_
+#define _DISPATCHER_
+
+#include "sys/sys"
+#include "memory/memory"
+
+#include "balancer/balancer"
+#include "config/config"
+#include "ThreadsAndMutexes/thread/thread"
+#include "ThreadsAndMutexes/threadlist/threadlist"
+#include "backendvector/backendvector"
+#include "netbuffer/netbuffer"
+
+// Dispatching algorithm workers
+#include "DispatchAlgorithms/algorithm/algorithm"
+#include "DispatchAlgorithms/roundrobin/roundrobin"
+#include "DispatchAlgorithms/firstactive/firstactive"
+#include "DispatchAlgorithms/leastconn/leastconn"
+#include "DispatchAlgorithms/external/external"
+#include "DispatchAlgorithms/hashedip/hashedip"
+#include "DispatchAlgorithms/storedip/storedip"
+#include "DispatchAlgorithms/weightedload/weightedload"
+
+#ifdef MEMDEBUG
+class Dispatcher: public Thread, public Memory
+#else
+class Dispatcher: public Thread
+#endif
+{
+public:
+
+ Dispatcher(int fd, struct in_addr ip);
+ Dispatcher(int fd);
+ virtual ~Dispatcher();
+
+ virtual void execute() = 0;
+ virtual void dispatch() = 0;
+ virtual void handle() = 0;
+
+ bool check_dos();
+ bool check_acl();
+
+ 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; }
+ Algorithm *algorithm() const { return algo; }
+
+ BackendVector &targetlist() { return target_list; }
+ void targetlist (BackendVector t) { target_list = t; }
+
+private:
+ void start_dispatcher();
+ struct in_addr client_ip;
+ int target_backend, client_fd, backend_fd;
+ Algorithm *algo;
+ BackendVector target_list;
+ string clientip_str;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/dispatcher1.cc
^
|
@@ -0,0 +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() {
+
+ start_dispatcher();
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/dispatcher2.cc
^
|
@@ -0,0 +1,6 @@
+#include "dispatcher"
+
+Dispatcher::~Dispatcher() {
+ delete algo;
+ debugmsg ("Dispatcher finished\n");
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/dispatcher3.cc
^
|
@@ -0,0 +1,8 @@
+#include "dispatcher"
+
+Dispatcher::Dispatcher(int fd):
+ Thread(), target_backend(-1), client_fd(fd),
+ backend_fd(-1), algo(0), target_list(), clientip_str() {
+
+ start_dispatcher();
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/dispatcher/startdispatcher.cc
^
|
@@ -0,0 +1,37 @@
+#include "dispatcher"
+
+void Dispatcher::start_dispatcher() {
+ // Instantiate dispatchmode algorithm
+ switch (config.dispatchmode()) {
+ case Dispatchmode::m_roundrobin:
+ algo = new Roundrobin;
+ break;
+ case Dispatchmode::m_firstactive:
+ algo = new Firstactive;
+ break;
+ case Dispatchmode::m_external:
+ algo = new External;
+ break;
+ case Dispatchmode::m_strict_hashed_ip:
+ case Dispatchmode::m_lax_hashed_ip:
+ algo = new HashedIp;
+ break;
+ case Dispatchmode::m_strict_stored_ip:
+ case Dispatchmode::m_lax_stored_ip:
+ algo = new StoredIp;
+ break;
+ case Dispatchmode::m_weighted_load:
+ algo = new Weightedload;
+ break;
+ case Dispatchmode::m_leastconn:
+ default:
+ algo = new Leastconn;
+ break;
+ }
+
+ // NOTE: Memory errors for algorithm pointer are not handled here,
+ // but in dispatch() (don't want to throw up in the constructor)
+
+ debugmsg("Dispatcher instantiated.\n");
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher/dispatch.cc
^
|
@@ -0,0 +1,107 @@
+#include "httpdispatcher"
+
+void HttpDispatcher::dispatch() {
+ PROFILE("HttpDispatcher::dispatch");
+
+ unsigned stickytarget;
+ string host_header = "";
+ string url = "";
+
+ // Try to dispatch. Since we're in HTTP mode, we must return an
+ // error page when dispatching fails.
+ try {
+
+ // Get the client's request. May need for cookie inspection or for the
+ // host header.
+ while (!buf().headersreceived())
+ if (!buf().netread(clientfd(), config.client_read_timeout())) {
+ msg ("Didn't receive a valid client request, stopping");
+ return;
+ }
+ msg ("Received client request: '" + buf().firstline() + "'\n");
+
+ // See if hostmatching or urlmatching is used.
+ // This is true when hosts or urls are matched against non-dot.
+ bool hostmatchused = false;
+ bool urlmatchused = false;
+ for (unsigned i = 0; i < balancer.nbackends(); i++) {
+ if (balancer.backend(i).hostmatch() != ".")
+ hostmatchused = true;
+ if (balancer.backend(i).urlmatch() != ".")
+ urlmatchused = true;
+ }
+
+ // Build new target list if host- or url matching applies.
+ if (hostmatchused || urlmatchused) {
+ msg ("Creating matched target list for the HTTP dispatcher\n");
+
+ if (hostmatchused)
+ host_header = buf().headerval("Host");
+ if (urlmatchused)
+ url = buf().url();
+
+ BackendVector v;
+ v.isdefined(true);
+
+ for (unsigned i = 0; i < balancer.nbackends(); i++) {
+ if (! balancer.backend(i).available())
+ continue;
+ bool host_allowed = true, url_allowed = true;
+ if (hostmatchused &&
+ regexec(&(balancer.backend(i).hostregex()),
+ host_header.c_str(), 0, 0, 0)) {
+ debugmsg("Back end " + balancer.backend(i).description() +
+ " forbidden due to hostmatch\n");
+ host_allowed = false;
+ }
+ if (urlmatchused &&
+ regexec(&(balancer.backend(i).urlregex()),
+ url.c_str(), 0, 0, 0)) {
+ debugmsg("Back end " + balancer.backend(i).description() +
+ " forbidden due to urlmatch\n");
+ url_allowed = false;
+ }
+ if (host_allowed && url_allowed) {
+ v.add(i);
+ msg("Candidate target: " +
+ balancer.backend(i).description() + "\n");
+ }
+ }
+ targetlist(v);
+ }
+
+ // Dispatch as a normal backend if sticky HTTP is off, or if the
+ // sticky target is badly specified.
+ if (!config.stickyhttp() ||
+ (sscanf (buf().cookievalue ("XRTarget").c_str(),
+ "%d", &stickytarget) < 1 &&
+ sscanf (buf().paramvalue ("XRTarget").c_str(),
+ "%d", &stickytarget) < 1) ||
+ stickytarget >= balancer.nbackends()) {
+ issticky(false);
+ TcpDispatcher::dispatch();
+ } else {
+ // Got a sticky target. Try to connect. If that fails, fallback
+ // to non-sticky dispatching.
+ targetbackend(stickytarget);
+ Backend tb = balancer.backend(stickytarget);
+ msg ("Sticky HTTP request for " + tb.description() + "\n");
+ if (! tb.connect()) {
+ balancer.backend(stickytarget).live(false);
+ msg ("Failed to connect to back end " + tb.description() +
+ ", trying to dispatch to other\n");
+ issticky(false);
+ TcpDispatcher::dispatch();
+ } else {
+ backendfd(tb.sock());
+ issticky(true);
+ }
+ }
+
+
+ } catch (Error const &e) {
+ senderrorpage(e.what());
+ throw e;
+ }
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher/handle.cc
^
|
@@ -0,0 +1,105 @@
+#include "httpdispatcher"
+
+void HttpDispatcher::handle() {
+ PROFILE("HttpDispatcher::handle");
+
+ // The client request was already retrieved before starting the
+ // dispatcher. We can continue by applying server-directed headers.
+ if (config.addxrversion())
+ buf().setheader ("XR", VER);
+ if (config.addxforwardedfor())
+ buf().addheader ("X-Forwarded-For", clientipstr());
+ for (unsigned n = 0; n < config.nserverheaders(); n++)
+ buf().setheader (config.serverheader(n));
+
+ // Patch up the Host: header if requested so.
+ 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());
+
+ // Let's see if we will need to modify the server headers.
+ bool modify_serverheaders = false;
+ if (config.addxrversion() ||
+ (config.stickyhttp() && !issticky()))
+ modify_serverheaders = true;
+
+ // Store the client request. May want to log it later.
+ string client_request = buf().firstline();
+
+ // Go into copy-thru mode. If required, catch the server headers on
+ // their first appearance and modify them.
+ bool backend_response_checked = false;
+ while (1) {
+ Fdset readset (maxtimeout(config.client_read_timeout(),
+ config.backend_read_timeout()));
+ readset.add(clientfd());
+ readset.add(backendfd());
+ readset.wait_r();
+
+ int sock;
+ if (readset.readable(clientfd()))
+ sock = clientfd();
+ else if (readset.readable(backendfd()))
+ sock = backendfd();
+ else
+ break;
+
+ buf().reset();
+
+ if (!buf().netread(sock))
+ break;
+
+ if (sock == backendfd() && modify_serverheaders) {
+ debugmsg("Back end response seen, applying modifications\n");
+ modify_serverheaders = false;
+ while (! buf().headersreceived())
+ if (!buf().netread (sock, config.backend_read_timeout()))
+ throw Error("Failed to get headers from back end");
+ if (config.addxrversion())
+ buf().setheader("XR", VER);
+ if (config.stickyhttp() && !issticky()) {
+ ostringstream o;
+ o << "XRTarget=" << targetbackend() << "; path=/";
+ buf().setheader("Set-Cookie", o.str());
+ }
+ }
+
+ // The back end response may now get flushed to the client.
+ // If the response code is 4** or 5**, log it as a warning.
+ if (!backend_response_checked &&
+ sock == backendfd() && buf().headersreceived()) {
+ string respcode = buf().stringat(9, 3);
+ if (respcode[0] == '4' || respcode[0] == '5')
+ warnmsg("HTTP back end indicates fault: '" +
+ buf().firstline() + "' as response to '" +
+ client_request + "'\n");
+ backend_response_checked = true;
+ }
+
+ // Flush info to the other connected side.
+ int othersock, 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());
+ } else {
+ othersock = clientfd();
+ timeout = config.client_read_timeout();
+ }
+
+ debugmsg (Mstr("Had data on ") + sock +
+ (Mstr(", sending to ") + othersock) + "\n");
+
+ buf().netwrite(othersock, timeout);
+ if (sock == backendfd())
+ balancer.backend(targetbackend()).addbytes(buf().bufsz());
+ }
+
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher/httpdispatcher
^
|
@@ -0,0 +1,22 @@
+#ifndef _HTTPDISPATCHER_
+#define _HTTPDISPATCHER_
+
+#include "sys/sys"
+#include "Dispatchers/tcpdispatcher/tcpdispatcher"
+#include "httpbuffer/httpbuffer"
+
+class HttpDispatcher: public TcpDispatcher {
+public:
+ HttpDispatcher (int fd, struct in_addr ip);
+
+ void dispatch();
+ void handle();
+ bool issticky() const { return (is_sticky); }
+ void issticky (bool s) { is_sticky = s; }
+
+private:
+ void senderrorpage(string const &desc);
+ bool is_sticky;
+};
+
+#endif
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher/httpdispatcher1.cc
^
|
(renamed to xr/Dispatchers/httpdispatcher/httpdispatcher1.cc)
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher/httpdispatcher1.cc
^
|
(renamed to xr/Dispatchers/httpdispatcher/httpdispatcher1.cc)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/httpdispatcher/senderrorpage.cc
^
|
@@ -0,0 +1,32 @@
+#include "httpdispatcher"
+
+void HttpDispatcher::senderrorpage(string const &reason) {
+ PROFILE("HttpDispatcher::senderrorpage");
+
+ msg ("Sending error page to client: '" + reason + "'\n");
+ try {
+ string txt =
+ "<html>\n"
+ " <head>\n"
+ " <title>Internal Server Error</title>\n"
+ " </head>\n"
+ " <body>\n"
+ " <h1>Internal Server Error</h1>\n"
+ " You request could not be completed. Please retry later.\n"
+ " </body>\n"
+ "</html>\n";
+ ostringstream mess;
+ mess <<
+ "HTTP/1.0 502 Internal Server Error\r\n"
+ "Content-Length: " << txt.size() << "\r\n"
+ "XR-Reason: " << reason << "\r\n"
+ "\r\n" <<
+ txt;
+ Netbuffer buf(mess.str());
+ buf.netwrite(clientfd(), config.client_write_timeout());
+ } catch (Error const &e) {
+ // Silently discard, we are not interested in errors
+ // that ocur when an error page is being sent
+ msg (Mstr(e.what()) + Mstr(" (while sending error page)\n"));
+ }
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/tcpdispatcher
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/tcpdispatcher/dispatch.cc
^
|
@@ -0,0 +1,44 @@
+#include "tcpdispatcher"
+
+void TcpDispatcher::dispatch() {
+ // Check that a working algorithm is available. May be missing if
+ // constructor's "new" failed.
+ if (!algorithm())
+ throw Error("No algorithm in Tcpdispatcher::dispatch");
+
+ bool connected = false;
+
+ // Build up the target list, if not yet done so. The HTTP dispatcher
+ // might've created it already for host-based matching (in which case
+ // we won't bother here).
+ if (! targetlist().isdefined()) {
+ msg ("Creating target list for the TCP dispatcher\n");
+ for (unsigned i = 0; i < balancer.nbackends(); i++)
+ if (balancer.backend(i).available()) {
+ targetlist().add(i);
+ if (config.verbose())
+ msg (" Candidate target: " +
+ balancer.backend(i).description() + "\n");
+ }
+ }
+
+ // 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());
+ if (!tb.connect()) {
+ balancer.backend(targetbackend()).live(false);
+ if (config.verbose())
+ msg ("Failed to connect to back end " + tb.description() +
+ ", trying to dispatch to other\n");
+ } else {
+ connected = true;
+ backendfd(tb.sock());
+ msg ((Mstr("Will dispatch client to back end ") +
+ tb.description()) +
+ (Mstr(" on fd ") + tb.sock()) + "\n");
+ break;
+ }
+ }
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/tcpdispatcher/execute.cc
^
|
@@ -0,0 +1,89 @@
+#include "tcpdispatcher"
+
+void TcpDispatcher::execute() {
+ Threadlist::clientfd(clientfd());
+ Threadlist::clientip(clientip());
+
+ if (!check_dos() ||
+ !check_acl())
+ return;
+
+ msg ((Mstr("Dispatch request for client fd ") + clientfd()) + "\n");
+
+ // Try to determine the back end.
+ try {
+ Threadlist::desc("Dispatching");
+ dispatch();
+ } catch (Error const &e) {
+ Mutex::lock(&cerr);
+ cerr << e.what() << "\n";
+ Mutex::unlock(&cerr);
+ socketclose(clientfd());
+ socketclose(backendfd());
+ return;
+ }
+
+ // Verify that the target is within the allowed set.
+ if (targetbackend() < 0 || targetbackend() >= (int)balancer.nbackends()) {
+ cerr << "WARNING: target back end " << targetbackend()
+ << " out of range\n";
+ socketclose(clientfd());
+ socketclose(backendfd());
+ return;
+ }
+
+ // Dispatch!
+ msg ((Mstr("Dispatching client fd ") + clientfd()) +
+ (Mstr(" to ") + balancer.backend(targetbackend()).description()) +
+ (Mstr(", fd ") + backendfd()) + "\n");
+
+
+ Threadlist::desc("Serving");
+ Threadlist::backend(targetbackend());
+ Threadlist::backendfd(backendfd());
+
+ balancer.backend(targetbackend()).startconnection();
+ if (config.onstart().length()) {
+ ostringstream o;
+ o << config.onstart() << ' ' << clientipstr() << ' '
+ << balancer.backend(targetbackend()).description()
+ << ' ' << balancer.backend(targetbackend()).connections();
+ msg (Mstr("Running onstart script: ") + o.str() + "\n");
+ sysrun(o.str());
+ }
+
+ bool failed = false;
+ try {
+ handle();
+ } catch (Error const &e) {
+ Mutex::lock(&cerr);
+ cerr << e.what() << "\n";
+ Mutex::unlock(&cerr);
+ failed = true;
+ if (config.onfail().length()) {
+ ostringstream o;
+ o << config.onfail() << ' ' << clientipstr() << ' '
+ << balancer.backend(targetbackend()).description() << ' '
+ << balancer.backend(targetbackend()).connections();
+ msg(Mstr("Running onfail script: ") + o.str() + "\n");
+ sysrun(o.str());
+ }
+ }
+
+ socketclose (clientfd());
+ socketclose (backendfd());
+
+ balancer.backend(targetbackend()).endconnection();
+ if (!failed && config.onend().length()) {
+ ostringstream o;
+ o << config.onend() << ' ' << clientipstr() << ' '
+ << balancer.backend(targetbackend()).description() << ' '
+ << balancer.backend(targetbackend()).connections();
+ msg (Mstr("Running onend script: ") + o.str() + "\n");
+ sysrun(o.str());
+ }
+
+ msg ((Mstr("Done dispatching to back end fd ") + backendfd()) +
+ (Mstr(" at ") + balancer.backend(targetbackend()).description()) +
+ "\n");
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/tcpdispatcher/handle.cc
^
|
@@ -0,0 +1,40 @@
+#include "tcpdispatcher"
+
+void TcpDispatcher::handle() {
+
+ debugmsg (Mstr("TCP dispatcher: About to shuttle between client fd ") +
+ clientfd() +
+ (Mstr(" and backend fd ") + backendfd()) + "\n");
+
+ while (1) {
+ Fdset readset(maxtimeout(config.client_read_timeout(),
+ config.backend_read_timeout()));
+ readset.add(clientfd());
+ readset.add(backendfd());
+ readset.wait_r();
+
+ int sock, othersock, timeout;
+ if (readset.readable(clientfd())) {
+ sock = clientfd();
+ othersock = backendfd();
+ timeout = config.backend_write_timeout();
+ } else if (readset.readable(backendfd())) {
+ sock = backendfd();
+ othersock = clientfd();
+ timeout = config.client_write_timeout();
+ } else
+ break;
+
+ if (!netbuffer.netread(sock))
+ break;
+ debugmsg (Mstr("Had data on ") + sock +
+ (Mstr(", sending to ") + othersock) + "\n");
+ netbuffer.netwrite (othersock, timeout);
+ if (sock == backendfd())
+ balancer.backend(targetbackend()).addbytes(netbuffer.bufsz());
+ else
+ IPStore::activity(clientip(), targetbackend());
+
+ netbuffer.reset();
+ }
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/tcpdispatcher/tcpdispatcher
^
|
@@ -0,0 +1,27 @@
+#ifndef _TCPDISPATCHER_
+#define _TCPDISPATCHER_
+
+#include "Dispatchers/dispatcher/dispatcher"
+#include "netbuffer/netbuffer"
+#include "httpbuffer/httpbuffer"
+#include "ipstore/ipstore"
+
+class TcpDispatcher: public Dispatcher {
+public:
+
+ TcpDispatcher (int fd, struct in_addr ip);
+
+ virtual void execute();
+ virtual void dispatch();
+ virtual void handle();
+
+ unsigned readchunk (int src);
+
+ Httpbuffer &buf() { return netbuffer; }
+
+private:
+ Httpbuffer netbuffer; // same as netbuffer, but
+ // httpdispatcher reuses it
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/tcpdispatcher/tcpdispatcher1.cc
^
|
@@ -0,0 +1,5 @@
+#include "tcpdispatcher"
+
+TcpDispatcher::TcpDispatcher(int cfd, struct in_addr cip):
+ Dispatcher(cfd, cip), netbuffer() {
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/udpdispatcher
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/udpdispatcher/dispatch.cc
^
|
@@ -0,0 +1,5 @@
+#include "udpdispatcher"
+
+void UdpDispatcher::dispatch() {
+ throw Error("UDP dispatcher: dispatch not yet implemented");
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/udpdispatcher/execute.cc
^
|
@@ -0,0 +1,5 @@
+#include "udpdispatcher"
+
+void UdpDispatcher::execute() {
+ throw Error("UDP dispatcher: execute not yet implemented");
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/udpdispatcher/handle.cc
^
|
@@ -0,0 +1,5 @@
+#include "udpdispatcher"
+
+void UdpDispatcher::handle() {
+ throw Error("UDP dispatcher: handle not yet implemented");
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/udpdispatcher/udpdispatcher
^
|
@@ -0,0 +1,16 @@
+#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
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/Dispatchers/udpdispatcher/udpdispatcher1.cc
^
|
@@ -0,0 +1,4 @@
+#include "udpdispatcher"
+
+UdpDispatcher::UdpDispatcher(int fd) : Dispatcher(fd), netbuffer() {
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/Makefile
^
|
@@ -5,17 +5,17 @@
BIN = $(BUILDDIR)/xr
LIB = $(BUILDDIR)/libxr.a
TMPXR = /tmp/xr-$(shell whoami)
-CONF_CC = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache c++-compiler)
-CONF_OPTFLAGS = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache optflags)
-CONF_LIB = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
+CONF_CC = $(shell etc/c-conf -c $(BUILDDIR)/config.cache c++-compiler)
+CONF_OPTFLAGS = $(shell etc/c-conf -c $(BUILDDIR)/config.cache optflags)
+CONF_LIB = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \
lib ucb nsl pthread socket m alf)
-CONF_GETOPT = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
+CONF_GETOPT = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \
ifheader getopt.h HAVE_GETOPT_H)
-CONF_GETOPT_LONG = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
+CONF_GETOPT_LONG = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \
libfunction getopt_long HAVE_GETOPT_LONG)
-CONF_INET_ATON = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
+CONF_INET_ATON = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \
libfunction inet_aton HAVE_INET_ATON)
-CONF_STRNSTR = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
+CONF_STRNSTR = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \
libfunction strnstr HAVE_STRNSTR)
foo:
@@ -35,6 +35,7 @@
echo "Making: $$f"; \
BASE=$(BASE) CC=$(CONF_CC) BUILDDIR=$(BUILDDIR) VER='$(VER)' \
AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
+ 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)' \
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/mutex/plock.cc
^
|
@@ -6,12 +6,12 @@
if (iter == s_lock.end()) {
// No such lock yet, create the mutex
if (int res = pthread_mutex_init(&s_lock[target], 0))
- throw static_cast<Error>("Failed to initialize static mutex: ") +
- strerror(res);
+ throw Error(string("Failed to initialize static mutex: ") +
+ strerror(res));
}
if (int res = pthread_mutex_lock(&s_lock[target]))
- throw static_cast<Error>("Failed to obtain mutex lock: ") +
- strerror(res);
+ throw Error(string("Failed to obtain mutex lock: ") +
+ strerror(res));
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/mutex/unlock.cc
^
|
@@ -6,6 +6,6 @@
PROFILE("Mutex::unlock");
if (int res = pthread_mutex_unlock(&s_lock[target]))
- throw static_cast<Error>("Failed to release mutex lock: ") +
- strerror(res);
+ throw Error(string("Failed to release mutex lock: ") +
+ strerror(res));
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/thread/run.cc
^
|
@@ -0,0 +1,25 @@
+#include "thread"
+#include "sys/sys"
+
+void *Thread::_run (void *data) {
+ Thread *t = (Thread*) data;
+
+ debugmsg(Mstr("Thread: starting run\n"));
+ Threadlist::enregister();
+ try {
+ t->execute();
+ } catch (Error const &e) {
+ lock(&cerr);
+ cerr << e.what() << "\n";
+ unlock(&cerr);
+ }
+ Threadlist::deregister();
+ debugmsg(Mstr("Thread: ending run\n"));
+
+ // Cleanups
+ delete (t);
+
+ // To satisfy the prototype
+ return (0);
+}
+
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/thread/start.cc
^
|
@@ -1,22 +1,6 @@
#include "thread"
#include "profiler/profiler"
-static void *_run (void *data) {
- Thread *t = (Thread*) data;
-
- try {
- t->execute();
- } catch (Error const &e) {
- cerr << e.what() << "\n";
- }
-
- // Cleanups
- delete (t);
-
- // To satisfy the prototype
- return (0);
-}
-
void Thread::start() {
PROFILE ("Thread::start");
@@ -28,11 +12,17 @@
int res;
if (pthread_attr_init (&attr))
- throw static_cast<Error>("Cannot initialize thread attributes");
+ throw Error("Cannot initialize thread attributes");
if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED))
- throw static_cast<Error>("Cannot set thread state as detached");
+ throw Error("Cannot set thread state as detached");
for (int i = 0; i < 3; i++) {
+# ifdef MISTRUST_THREAD_CREATE_THREADSAFE
+ lock((void*)_run);
+# endif
res = pthread_create (&th, &attr, _run, (void*) this);
+# ifdef MISTRUST_THREAD_CREATE_THREADSAFE
+ unlock((void*)_run);
+# endif
if (!res) {
pthread_attr_destroy (&attr);
return;
@@ -44,13 +34,12 @@
continue;
} else {
pthread_attr_destroy (&attr);
- throw static_cast<Error>("Failed to start thread: ") +
- strerror(res);
+ throw Error(string("Failed to start thread: ") +
+ strerror(res));
}
}
- throw static_cast<Error>("Failed to start thread: "
- "Resources unavailable after 3 tries, "
- "giving up");
+ throw Error("Failed to start thread: "
+ "Resources unavailable after 3 tries, giving up");
}
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/thread/thread
^
|
@@ -2,17 +2,28 @@
#define _THREAD_
#include "sys/sys"
+#include "memory/memory"
#include "error/error"
#include "config/config"
+#include "timestamp/timestamp"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "ThreadsAndMutexes/threadlist/threadlist"
using namespace std;
-class Thread: public Mutex {
+#ifdef MEMDEBUG
+class Thread: public Mutex, public Memory
+#else
+class Thread: public Mutex
+#endif
+{
public:
virtual ~Thread();
void start();
virtual void execute();
+
+private:
+ static void *_run(void *data);
};
#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadinfo
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadinfo/threadinfo
^
|
@@ -0,0 +1,40 @@
+#ifndef _THREADINFO_
+#define _THREADINFO_
+
+#include "sys/sys"
+#include "timestamp/timestamp"
+#include "ThreadsAndMutexes/mutex/mutex"
+
+class Threadinfo {
+public:
+ Threadinfo():
+ th_desc(), th_tm(), th_backend(-1), th_backendfd(-1), th_clientfd(-1)
+ {
+ memset(&th_clientip, 0, sizeof(th_clientip));
+ }
+
+ void desc(string s) { th_desc = s; }
+ string const &desc() const { return th_desc; }
+
+ Timestamp const ×tamp() const { return th_tm; }
+
+ void backend(int b) { th_backend = b; }
+ int backend() const { return th_backend; }
+
+ void backendfd(int f) { th_backendfd = f; }
+ int backendfd() const { return th_backendfd; }
+
+ void clientfd(int f) { th_clientfd = f; }
+ int clientfd() const { return th_clientfd; }
+
+ void clientip(struct in_addr c) { th_clientip = c; }
+ struct in_addr clientip() const { return th_clientip; }
+
+private:
+ string th_desc;
+ Timestamp th_tm;
+ int th_backend, th_backendfd, th_clientfd;
+ struct in_addr th_clientip;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/backend.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+void Threadlist::backend(int b) {
+ th_map[pthread_self()].backend(b);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/backendfd.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+void Threadlist::backendfd(int f) {
+ th_map[pthread_self()].backendfd(f);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/clientfd.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+void Threadlist::clientfd(int f) {
+ th_map[pthread_self()].clientfd(f);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/clientip.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+void Threadlist::clientip(struct in_addr adr) {
+ th_map[pthread_self()].clientip(adr);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/deregister1.cc
^
|
@@ -0,0 +1,7 @@
+#include "threadlist"
+
+void Threadlist::deregister(pthread_t id) {
+ Mutex::lock(&th_map);
+ th_map.erase(id);
+ Mutex::unlock(&th_map);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/deregister2.cc
^
|
@@ -0,0 +1,6 @@
+#include "threadlist"
+
+void Threadlist::deregister() {
+ deregister(pthread_self());
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/desc.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+void Threadlist::desc(string const &s) {
+ th_map[pthread_self()].desc(s);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/enregister.cc
^
|
@@ -0,0 +1,10 @@
+#include "threadlist"
+
+Threadmap Threadlist::th_map;
+
+void Threadlist::enregister() {
+ Threadinfo n;
+ Mutex::lock(&th_map);
+ th_map[pthread_self()] = n;
+ Mutex::unlock(&th_map);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/info.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+Threadinfo Threadlist::info(pthread_t id) {
+ return th_map[id];
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/map.cc
^
|
@@ -0,0 +1,5 @@
+#include "threadlist"
+
+Threadmap &Threadlist::map() {
+ return th_map;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ThreadsAndMutexes/threadlist/threadlist
^
|
@@ -0,0 +1,26 @@
+#ifndef _THREADLIST_
+#define _THREADLIST_
+
+#include "sys/sys"
+#include "ThreadsAndMutexes/threadinfo/threadinfo"
+
+typedef map<pthread_t, Threadinfo> Threadmap;
+
+class Threadlist {
+public:
+ static void enregister();
+ static void deregister(pthread_t id);
+ static void deregister();
+ static Threadmap &map();
+ 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 clientip(struct in_addr adr);
+
+private:
+ static Threadmap th_map;
+};
+
+#endif
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backend/available.cc
^
|
@@ -1,16 +1,23 @@
#include "backend"
#include "profiler/profiler"
+#include "ipstore/ipstore"
bool Backend::available() const {
PROFILE("Backend::available");
- debugmsg((Mstr("Back end ") + description()) +
- (Mstr(": ") + livestr()) +
- (Mstr(", ") + upstr()) +
- (Mstr(", ") + connections()) +
- (Mstr(" connections of ") + maxconn()) +
- " max\n");
+ if (config.debug()) {
+ ostringstream o;
+ o << "Back end " << description() << ": "
+ << livestr() << ", " << upstr() << ", "
+ << connections() << " connections, "
+ << IPStore::anticipated(balancerindex()) << " anticipated, "
+ << maxconn() << " max\n";
+ _debugmsg(o.str());
+ }
if (!maxconn())
return (islive && isup);
- return (islive && isup && connections() < maxconn());
+ return (islive &&
+ isup &&
+ (connections() + IPStore::anticipated(balancerindex()) <
+ maxconn()));
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backend/backend
^
|
@@ -7,6 +7,9 @@
#include "error/error"
#include "ThreadsAndMutexes/mutex/mutex"
#include "profiler/profiler"
+#include "backendcheck/backendcheck"
+#include "httpbuffer/httpbuffer"
+#include "dnsentry/dnsentry"
using namespace std;
@@ -16,50 +19,83 @@
Backend (BackendDef const &b);
virtual ~Backend();
bool connect();
+ void markconnecterror();
+
+ int sock() const { return clsocket; }
+
void check();
string description() const;
+
bool available() const;
string availablestr() const;
- void live (bool state);
+
+ bool live() const { return islive; };
+ void live (bool state);
string livestr() const;
+
void up (bool state);
+ bool up() const { return isup; }
string upstr() const;
+
+ string const &server() const { return bdef.server(); }
+ void server(string s) { bdef.server(s); }
- bool live() const { return (islive); };
- bool up() const { return (isup); }
- int sock() const { return (clsocket); }
- string const &server() const { return (bdef.server()); }
- int port() const { return (bdef.port()); }
- unsigned maxconn() const { return (bdef.maxconn()); }
+ int port() const { return bdef.port(); }
+ void port(int p) { bdef.port(p); }
+
+ unsigned maxconn() const { return bdef.maxconn(); }
void maxconn (unsigned m) { bdef.maxconn(m); }
- string const &hostmatch() const { return (bdef.hostmatch()); }
+
+ string const &hostmatch() const { return bdef.hostmatch(); }
void hostmatch(string const &s) { bdef.hostmatch(s); }
- regex_t const &hostregex() const { return (bdef.hostregex()); }
- unsigned weight() const { return (bdef.weight()); }
+ regex_t const &hostregex() const { return bdef.hostregex(); }
+
+ string const &urlmatch() const { return bdef.urlmatch(); }
+ void urlmatch(string const &u) { bdef.urlmatch(u); }
+ regex_t const &urlregex() const { return bdef.urlregex(); }
+
+ unsigned weight() const { return bdef.weight(); }
void weight (unsigned w) { bdef.weight(w); }
- unsigned adjustedweight() const { return (bdef.adjustedweight()); }
- unsigned connections() const { return (nconn); }
- double bytesserved() const { return (bytes_served); }
- unsigned clientsserved() const { return (totconn); }
- double loadavg() const { return (loadaverage); }
+ unsigned adjustedweight() const { return bdef.adjustedweight(); }
+
+ unsigned connections() const { return nconn; }
+ unsigned connecterrors() const { return nconnerr; }
+
+ double bytesserved() const { return bytes_served; }
+ unsigned clientsserved() const { return totconn; }
+
+ double loadavg() const { return loadaverage; }
void loadavg(double l) { loadaverage = l; }
void addbytes (unsigned n);
void startconnection();
- void endconnection();
+ void endconnection();
BackendDef const &backenddef() const {
- return (bdef);
+ return bdef;
+ }
+
+ BackendCheck const &backendcheck() {
+ return bdef.backendcheck();
}
+ void backendcheck(BackendCheck const &b) {
+ bdef.backendcheck(b);
+ }
+
+ void balancerindex(int i) { index = i; }
+ int balancerindex() const { return index; }
+
private:
BackendDef bdef;
bool islive;
bool isup;
int clsocket;
- unsigned nconn, totconn;
+ unsigned nconn, totconn, nconnerr;
double bytes_served;
double loadaverage;
+ DNSEntry dnsentry;
+ int index;
};
#endif
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backend/backend1.cc
^
|
@@ -1,7 +1,8 @@
#include "backend"
Backend::Backend () :
- islive(true), isup(true), clsocket(-1),
- nconn(0), totconn(0), bytes_served(0),
- loadaverage(0.1) {
+ bdef(), islive(true), isup(true), clsocket(-1),
+ nconn(0), totconn(0), nconnerr(0),
+ bytes_served(0),
+ loadaverage(0.1), dnsentry(), index(-1) {
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backend/backend2.cc
^
|
@@ -1,6 +1,7 @@
#include "backend"
Backend::Backend (BackendDef const &b) :
- bdef(b), islive(true), clsocket(-1), nconn(0), totconn(0),
- bytes_served(0), loadaverage(0.1) {
+ bdef(b), islive(true), isup(true), clsocket(-1), nconn(0), totconn(0),
+ nconnerr(0),
+ bytes_served(0), loadaverage(0.1), dnsentry(), index(-1) {
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backend/check.cc
^
|
@@ -1,6 +1,98 @@
#include "backend"
void Backend::check() {
- connect();
- socketclose (clsocket);
+ debugmsg(Mstr("About to check back end ") + description() + ". " +
+ Mstr(backendcheck().description()) + "\n");
+
+ ostringstream o;
+ Backend tester;
+ Httpbuffer httpbuffer;
+
+ switch (backendcheck().checktype()) {
+ case BackendCheck::c_connect:
+ if (backendcheck().server() == "" && backendcheck().port() == 0) {
+ // Most common: TCP connect to the actual back end
+ 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());
+ tester.connect();
+ socketclose (tester.sock());
+ live(tester.live());
+ msg (Mstr("Alternative back end for testing ") +
+ tester.description() + " is " + livestr() + "\n");
+ }
+ break;
+
+ case BackendCheck::c_get:
+ // HTTP GET to stated server, port, uri
+ tester.server(backendcheck().server());
+ tester.port(backendcheck().port());
+ tester.connect();
+ if (! tester.live()) {
+ warnmsg((Mstr("HTTP GET checker: host ") +
+ backendcheck().server()) +
+ (Mstr(", port ") + backendcheck().port()) +
+ " not responding\n");
+ live(false);
+ } else {
+ o << "GET " << backendcheck().uri() << " HTTP/1.0\r\n"
+ "Host: " << backendcheck().server() << "\r\n"
+ "Connection: close\r\n"
+ "\r\n";
+ httpbuffer.setstring (o.str());
+ httpbuffer.netwrite(tester.sock(), config.backend_write_timeout());
+ httpbuffer.reset();
+ while (!httpbuffer.headersreceived())
+ httpbuffer.netread(tester.sock(),
+ config.backend_read_timeout());
+ msg((Mstr("HTTP GET checker got answer: '") +
+ httpbuffer.firstline()) + "'\n");
+ if (httpbuffer.stringat(9, 3) == "200")
+ live(true);
+ else
+ debugmsg("Back end assumed dead.\n");
+ }
+ socketclose(tester.sock());
+ break;
+
+ case BackendCheck::c_external:
+ // External program to be called, with arguments:
+ // IP:PORT availability current-connections
+ o << backendcheck().program() << ' ' << description() << ' '
+ << availablestr() << ' ' << connections();
+ FILE *f;
+ int result;
+ if (! (f = popen(o.str().c_str(), "r")) ) {
+ live(false);
+ warnmsg(Mstr("Failed to start external checker '") + o.str() +
+ "': " + strerror(errno) + "\n");
+ } else {
+ if (fscanf(f, "%d", &result) < 1) {
+ live(false);
+ warnmsg(Mstr("External checker '") + o.str() +
+ Mstr("' did not reply with a number\n"));
+ } else {
+ msg((Mstr("External checker '") + o.str()) +
+ (Mstr("' replied: ") + result) + '\n');
+ live(result == 0);
+ }
+ if (pclose(f)) {
+ warnmsg((Mstr("External checker '") + o.str()) +
+ "' terminated with error\n");
+ live(false);
+ }
+ }
+ break;
+
+ default:
+ throw Error("Internal fry in Backend::check()");
+ }
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backend/connect.cc
^
|
@@ -10,59 +10,63 @@
// Create client socket
if ( (clsocket = socket (PF_INET, SOCK_STREAM, 0)) < 0 )
- throw static_cast<Error>("Failed to create client socket: ") +
- strerror(errno);
+ throw Error(string("Failed to create client socket: ") +
+ strerror(errno));
// Resolve hostname, prepare binding
struct sockaddr_in backendaddr;
+
backendaddr.sin_family = AF_INET;
backendaddr.sin_port = htons(bdef.port());
- struct hostent *hostaddr;
- static int locker;
-
- Mutex::lock(&locker);
- if ( (hostaddr = gethostbyname(bdef.server().c_str())) )
- memcpy ((char *) &backendaddr.sin_addr.s_addr,
- hostaddr->h_addr_list[0], hostaddr->h_length);
- Mutex::unlock(&locker);
- if (! hostaddr) {
- socketclose (clsocket);
- throw static_cast<Error>("Failed to resolve backend host '") +
- bdef.server();
- }
+ backendaddr.sin_addr.s_addr = dnsentry.resolve(bdef.server());
// 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 static_cast<Error>("Failed to get fd flags: ") + strerror(errno);
+ throw Error(string("Failed to get fd flags: ") + strerror(errno));
}
if (fcntl (clsocket, F_SETFL, flags | O_NONBLOCK) == -1) {
socketclose (clsocket);
- throw static_cast<Error>("Failed to fd in nonblocking mode: ") +
- strerror(errno);
+ throw Error(string("Failed to fd in nonblocking mode: ") +
+ strerror(errno));
}
// Do the connect
int conres = ::connect (clsocket, (struct sockaddr *)&backendaddr,
sizeof(backendaddr));
int conerrno = errno;
+ debugmsg((Mstr("Connect result: ") + conres) +
+ (Mstr(", errno: ") + conerrno) + "\n");
// Put socket again in blocking mode.
if (fcntl (clsocket, F_SETFL, flags) == -1) {
socketclose (clsocket);
- throw static_cast<Error>("Failed to put fd in blocking mode: ") +
- strerror(errno);
+ throw Error(string("Failed to put fd in blocking mode: ") +
+ strerror(errno));
}
// Check on the outcome of the connect
if (!conres || conerrno == EINPROGRESS) {
// Wait for socket to go writable.
- Fdset fdset (config.backend_timeout());
+ Fdset fdset (config.backend_write_timeout());
fdset.add (clsocket);
- if (fdset.readwriteable() == -1 && fdset.writeable() == clsocket)
+ fdset.wait_rw();
+
+ debugmsg(Mstr("Connectiong to ") + description() + "\n");
+
+# ifdef CONNECTCHECK_ONLY_WRITABLE
+ if (fdset.writeable(clsocket))
+ islive = true;
+ else
+ markconnecterror();
+# else
+ if (fdset.writeable(clsocket) && !fdset.readable(clsocket))
islive = true;
+ else
+ markconnecterror();
+# endif
}
debugmsg ((Mstr("Back end ") + description()) +
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backend/markconnecterror.cc
^
|
@@ -0,0 +1,8 @@
+#include "backend"
+
+void Backend::markconnecterror() {
+ Mutex::lock(&nconnerr);
+ nconnerr++;
+ Mutex::unlock(&nconnerr);
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backendcheck
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backendcheck/backendcheck
^
|
@@ -0,0 +1,45 @@
+#ifndef _BACKENDCHECK_
+#define _BACKENDCHECK_
+
+#include "sys/sys"
+#include "error/error"
+
+class BackendCheck {
+public:
+ enum CheckType {
+ c_connect,
+ c_get,
+ c_external,
+ };
+
+ BackendCheck();
+
+ CheckType checktype() const { return check_type; }
+ void checktype(CheckType t) { check_type = t; }
+
+ string server() const { return srv; }
+ void server(string s) { srv = s; }
+
+ int port() const { return prt; }
+ void port(int p) { prt = p; }
+
+ string uri() const { return geturi; }
+ void uri(string u) { geturi = u; }
+
+ string program() const { return extprog; }
+ void program(string const &p) { extprog = p; }
+
+ void parse(string setting);
+
+ string setting() const;
+ string description() const;
+
+private:
+ CheckType check_type;
+ string srv;
+ int prt;
+ string geturi;
+ string extprog;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backendcheck/backendcheck1.cc
^
|
@@ -0,0 +1,6 @@
+#include "backendcheck"
+
+BackendCheck::BackendCheck() : check_type(c_connect), srv(""), prt(0),
+ geturi(""), extprog("")
+{
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backendcheck/description.cc
^
|
@@ -0,0 +1,39 @@
+#include "backendcheck"
+
+string BackendCheck::description() const {
+ ostringstream o;
+
+ o << "Back end check type: ";
+ switch (check_type) {
+ case c_connect:
+ o << "TCP connect to ";
+ if (srv == "")
+ o << "backend IP, ";
+ else
+ o << "alternative IP '" << srv << "', ";
+ if (prt == 0)
+ o << "backend port";
+ else
+ o << "alternative port '" << prt << "'";
+ break;
+ case c_get:
+ o << "HTTP GET to ";
+ if (srv == "")
+ o << "backend IP, ";
+ else
+ o << "alternative IP '" << srv << "', ";
+ if (prt == 0)
+ o << "backend port";
+ else
+ o << "alternative port '" << prt << "'";
+ break;
+ case c_external:
+ o << "External program " << extprog;
+ break;
+ default:
+ throw Error("Internal jam in BackendCheck::description");
+ }
+
+ return (o.str());
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backendcheck/parse.cc
^
|
@@ -0,0 +1,65 @@
+#include "backendcheck"
+
+static int parse_port(string const &s) {
+ int ret;
+ if (sscanf(s.c_str(), "%d", &ret) < 1)
+ ret = 0;
+ return ret;
+}
+
+static string parse_uri(string const &s) {
+ size_t slash = s.find_first_of('/');
+ if (slash == string::npos)
+ return ("/");
+ return s.substr(slash);
+}
+
+void BackendCheck::parse(string setting) {
+ // Resets to default
+ if (!setting.size()) {
+ check_type = c_connect;
+ srv = "";
+ prt = 0;
+ geturi = "";
+ extprog = "";
+ return;
+ }
+
+ vector<string> parts = str2parts(setting, ':');
+
+ // connect:IP:PORT
+ if (parts.size() == 3 && parts[0] == "connect") {
+ check_type = c_connect;
+ srv = parts[1];
+ prt = parse_port(parts[2]);
+ geturi = "";
+ extprog = "";
+ return;
+ }
+
+ // get:IP:PORT
+ // get:IP:PORT/URI
+ if (parts.size() == 3 && parts[0] == "get") {
+ check_type = c_get;
+ srv = parts[1];
+ prt = parse_port(parts[2]);
+ geturi = parse_uri(parts[2]);
+ extprog = "";
+ return;
+ }
+
+ // external:PROGRAM
+ if (parts.size() == 2 && parts[0] == "external") {
+ check_type = c_external;
+ srv = "";
+ prt = 0;
+ geturi = "";
+ extprog = parts[1];
+ return;
+ }
+
+ // No luck today
+ throw Error("Back end check specifiers must be either an empty string, "
+ "or 'connect:IP:PORT' or 'get:IP:PORT' or 'get'IP:PORT/URI' "
+ "or 'external:PROGRAM'");
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backendcheck/setting.cc
^
|
@@ -0,0 +1,22 @@
+#include "backendcheck"
+
+string BackendCheck::setting() const {
+ ostringstream o;
+
+ if (check_type == c_external)
+ o << "external:" << extprog;
+ else {
+ if (check_type == c_connect)
+ o << "connect:";
+ else
+ o << "get:";
+ if (srv != "")
+ o << srv;
+ o << ':';
+ if (prt)
+ o << prt;
+ if (check_type == c_get)
+ o << geturi;
+ }
+ return o.str();
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backenddef/backenddef
^
|
@@ -1,33 +1,48 @@
#ifndef _BACKENDDEF_
#define _BACKENDDEF_
-#include "../sys/sys"
-#include "../error/error"
+#include "sys/sys"
+#include "error/error"
#include "profiler/profiler"
+#include "backendcheck/backendcheck"
+#include "ThreadsAndMutexes/mutex/mutex"
using namespace std;
class BackendDef {
public:
-BackendDef(): srv(""), prt(-1), max(0), host_match(""), wt(1) {}
- BackendDef (string s, string p, string m = "", string w = "1");
+ BackendDef():
+ srv(""), prt(-1), max(0), host_match(""), url_match(""),
+ wt(1), backend_check() {
+ hostmatch("");
+ urlmatch("");
+ }
+ BackendDef(string s, string p, string m = "", string w = "1");
- void server(string s) { srv = s; }
- string const &server() const { return (srv); }
+ void server(string s) { srv = s; }
+ string const &server() const { return (srv); }
- void port (int p) { prt = p; }
- int port() const { return (prt); }
+ void port (int p) { prt = p; }
+ int port() const { return (prt); }
- unsigned maxconn() const { return (max); }
- void maxconn (unsigned m) { max = m; }
+ unsigned maxconn() const { return (max); }
+ void maxconn (unsigned m) { max = m; }
- unsigned weight() const { return wt; }
+ unsigned weight() const { return wt; }
void weight (unsigned w);
- unsigned adjustedweight() const { return min_wt + max_wt - wt; }
+ unsigned adjustedweight() const { return min_wt +
+ max_wt - wt; }
void hostmatch(string const &s);
- string const &hostmatch() const { return (host_match); }
- regex_t const &hostregex() const { return (host_regex); }
+ string const &hostmatch() const { return (host_match); }
+ regex_t const &hostregex() const { return (host_regex); }
+
+ void urlmatch(string const &u);
+ string const &urlmatch() const { return (url_match); }
+ regex_t const &urlregex() const { return (url_regex); }
+
+ BackendCheck const &backendcheck() { return backend_check; }
+ void backendcheck(BackendCheck const &b) { backend_check = b; }
private:
string srv;
@@ -35,9 +50,12 @@
unsigned max;
string host_match;
regex_t host_regex;
+ string url_match;
+ regex_t url_regex;
unsigned wt;
static unsigned min_wt, max_wt;
static bool minmax_wt_set;
+ BackendCheck backend_check;
};
#endif
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backenddef/backenddef1.cc
^
|
@@ -6,21 +6,22 @@
BackendDef::BackendDef (string server, string port,
string maxclients, string w) :
- srv(server), prt(-1), max(0), host_match(""), wt(1) {
+ srv(server), prt(-1), max(0), host_match(""), url_match(""),
+ wt(1), backend_check() {
if (sscanf (port.c_str(), "%d", &prt) < 1)
- throw static_cast<Error>("Bad backend port specifier: '") + port +
- "' is not a number";
+ throw Error("Bad backend port specifier: '" + port +
+ "' is not a number");
if (maxclients.size() &&
sscanf (maxclients.c_str(), "%u", &max) < 1)
- throw static_cast<Error>("Bad maximum connections specifier: '") +
- maxclients + "' is not a number";
+ throw Error("Bad maximum connections specifier: '" +
+ maxclients + "' is not a number");
unsigned ww;
if (sscanf(w.c_str(), "%u", &ww) < 1)
- throw static_cast<Error>("Bad backend weight specifier: '") + w +
- "' is not a number";
+ throw Error("Bad backend weight specifier: '" + w +
+ "' is not a number");
if (ww < 1)
- throw static_cast<Error>("Weights less than 1 are not supported");
+ throw Error("Weights less than 1 are not supported");
weight(ww);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backenddef/hostmatch.cc
^
|
@@ -2,12 +2,10 @@
void BackendDef::hostmatch (string const &s) {
PROFILE("BackendDef::hostmatch");
-
- host_match = s;
- if (host_match == "")
- host_match = ".";
+
+ host_match = (s == "" ? "." : s);
if (regcomp (&host_regex, host_match.c_str(),
REG_EXTENDED | REG_ICASE | REG_NOSUB))
- throw static_cast<Error>("Host match specifier '") +
- host_match + "' isn't a valid regular expression";
+ throw Error("Host match specifier '" +
+ host_match + "' isn't a valid regular expression");
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/backenddef/urlmatch.cc
^
|
@@ -0,0 +1,11 @@
+#include "backenddef"
+
+void BackendDef::urlmatch (string const &s) {
+ PROFILE("BackendDef::urlmatch");
+
+ url_match = (s == "" ? "." : s);
+ if (regcomp (&url_regex, url_match.c_str(),
+ REG_EXTENDED | REG_ICASE | REG_NOSUB))
+ throw Error("Url match specifier '" +
+ url_match + "' isn't a valid regular expression");
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/backenddef/weight.cc
^
|
@@ -3,6 +3,7 @@
void BackendDef::weight(unsigned w) {
wt = w;
+ Mutex::lock(&minmax_wt_set);
if (!minmax_wt_set) {
min_wt = w;
max_wt = w;
@@ -13,4 +14,5 @@
if (max_wt > w)
max_wt = w;
}
+ Mutex::unlock(&minmax_wt_set);
}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/balancer/addbackend.cc
^
|
@@ -1,7 +0,0 @@
-#include "balancer"
-
-void Balancer::addbackend (BackendDef const &b) {
- Backend newb (b);
- backends.push_back (newb);
- backends[backends.size() - 1].check();
-}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/balancer/addbackend1.cc
^
|
@@ -0,0 +1,6 @@
+#include "balancer"
+
+void Balancer::addbackend (BackendDef const &b) {
+ Backend newb (b);
+ addbackend(newb);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/balancer/addbackend2.cc
^
|
@@ -0,0 +1,20 @@
+#include "balancer"
+
+void Balancer::addbackend (Backend const &b,
+ bool is_up, bool is_live, bool do_check) {
+ debugmsg(Mstr("Adding back end ") + b.description() + " to list\n");
+
+ Mutex::lock(&backends);
+ backends.push_back (b);
+ backends[backends.size() - 1].up(is_up);
+ backends[backends.size() - 1].live(is_live);
+ backends[backends.size() - 1].balancerindex(backends.size() - 1);
+ Mutex::unlock(&backends);
+
+ if (do_check) {
+ debugmsg(Mstr("Verifying configured back end\n"));
+ backends[backends.size() - 1].check();
+ }
+
+ debugmsg(Mstr("Back end ") + b.description() + " added to list\n");
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/balancer/balancer
^
|
@@ -2,6 +2,7 @@
#define _BALANCER_
#include "sys/sys"
+#include "memory/memory"
#include "backend/backend"
#include "backenddef/backenddef"
@@ -20,16 +21,22 @@
public:
Balancer ();
void init();
- void addbackend (BackendDef const &b);
+ void addbackend(BackendDef const &b);
+ void addbackend(Backend const &b,
+ bool is_up = true, bool is_live = true,
+ bool do_check = true);
+ void deletebackend(unsigned i);
void serve();
- unsigned nbackends() { return (backends.size()); }
- Backend &backend (unsigned i) { return (backends[i]); }
- bool terminate() const { return (term); }
+ unsigned nbackends() { return backends.size(); }
+ Backend &backend (unsigned i) { return backends[i]; }
+ bool terminate() const { return term; }
void terminate (bool t) { term = t; }
- bool report() const { return (rep); }
+ bool report() const { return rep; }
void report (bool r) { rep = r; }
- long requestnr() const { return (request_nr); }
+ void restart(bool t) { rest = t; }
+ bool restart() const { return rest; }
+ long requestnr() const { return request_nr; }
unsigned connections();
@@ -42,6 +49,7 @@
vector<Backend> backends;
bool term;
bool rep;
+ bool rest;
};
extern Balancer balancer;
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/balancer/balancer1.cc
^
|
@@ -1,5 +1,6 @@
#include "balancer"
Balancer::Balancer () :
- server_fd(-1), request_nr(0), backends(), term(false), rep(false) {
+ server_fd(-1), request_nr(0), backends(),
+ term(false), rep(false), rest(false) {
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/balancer/deletebackend.cc
^
|
@@ -0,0 +1,17 @@
+#include "balancer"
+
+void Balancer::deletebackend(unsigned i) {
+ if (backend(i).up())
+ throw Error("Only 'down' back ends can be deleted.");
+ if (backend(i).connections()) {
+ ostringstream o;
+ o << "Back end cannot be deleted, there are still "
+ << backend(i).connections() << " connections";
+ throw Error(o.str());
+ }
+
+ Mutex::lock(&backends);
+ backends.erase(backends.begin() + i,
+ backends.begin() + i + 1);
+ Mutex::unlock(&backends);
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/balancer/init.cc
^
|
@@ -2,15 +2,22 @@
void Balancer::init() {
// Set the listening socket.
- if (config.sport())
- server_fd = serversocket (config.sipaddr(), config.sport(),
- "balancer");
- else
- server_fd = 0;
-
+ if (config.stype() != Servertype::t_udp) {
+ if (config.sport())
+ server_fd = serversocket(config.sipaddr(), config.sport(),
+ "balancer", Servertype::t_tcp);
+ else
+ server_fd = 0;
+ } else {
+ server_fd = serversocket(config.sipaddr(), config.sport(),
+ "balancer", Servertype::t_udp);
+ }
+
// Start the web interface if requested.
- if (config.usewebinterface()) {
+ if (config.usewebinterface() && !config.foregroundmode()) {
Webinterface *w = new Webinterface();
+ if (! w)
+ throw Error("Memory fault in Balancer::init");
w->start();
}
@@ -18,7 +25,9 @@
for (int i = 0; i < config.backends(); i++)
addbackend (config.backend(i));
- for (unsigned i = 0; i < nbackends(); i++)
- msg ("Initial backend state: " + backend(i).description() + " is " +
- backend(i).availablestr() + "\n");
+ if (config.verbose()) {
+ for (unsigned i = 0; i < nbackends(); i++)
+ _msg ("Initial backend state: " + backend(i).description() +
+ " is " + backend(i).availablestr() + "\n");
+ }
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/balancer/serve.cc
^
|
@@ -1,19 +1,28 @@
#include "balancer"
-#include "../tcpdispatcher/tcpdispatcher"
-#include "../httpdispatcher/httpdispatcher"
+#include "Dispatchers/tcpdispatcher/tcpdispatcher"
+#include "Dispatchers/httpdispatcher/httpdispatcher"
+#include "Dispatchers/udpdispatcher/udpdispatcher"
+
+// #define SHOWDEBUG
void Balancer::serve() {
- int clsock;
+ int clsock = -1;
- // Start up wakeup/checkup handlers.
- if (config.wakeupsec() && !config.foregroundmode() && config.sport()) {
+ // 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");
Wakeupthread *wt = new Wakeupthread();
+ if (!wt)
+ throw Error("Memory fault in Balancer::serve");
wt->start();
- }
- if (config.checkupsec() && !config.foregroundmode() && config.sport()) {
+
msg ("Starting checkup thread.\n");
Checkupthread *ct = new Checkupthread();
+ if (!ct)
+ throw Error("Memory fault in Balancer::serve");
ct->start();
}
@@ -21,18 +30,23 @@
if (config.pidfile() != "") {
FILE *f;
if (! (f = fopen (config.pidfile().c_str(), "w")) )
- throw static_cast<Error>("Cannot write pid file ") +
- config.pidfile() + ": " + strerror(errno);
+ throw Error(string("Cannot write pid file ") +
+ config.pidfile() + ": " + strerror(errno));
fprintf (f, "%u\n", getpid());
fclose (f);
}
// Wait for activity, serve it.
msg ((Mstr("Awaiting activity on fd ") + server_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);
- if (fdset.readable() < 0) {
+ 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");
@@ -58,97 +72,74 @@
report (false);
reportmsg ("*** XR STATUS REPORT ENDS ***\n");
continue;
+ } else if (restart()) {
+ msg ("Restart requested\n");
+ config.restart();
} else {
- msg ("Non-meaningful interrupt, resuming\n");
+ msg ("Non-meaningful interrupt or select timeout, resuming\n");
continue;
}
}
- // Got activity!
+ // Got activity! Check total # of connections.
+ msg ((Mstr("Got activity on fd ") + server_fd) + "\n");
request_nr++;
-
+ if (config.maxconn() && connections() >= config.maxconn()) {
+ msg ((Mstr("Not serving connection: already ") + connections()) +
+ (Mstr(" connection(s) (max ") + config.maxconn()) + ")\n");
+ continue;
+ }
+
if (server_fd) {
- // If tcp-serving: server_fd > 0; serve and loop again
- int size;
+ // In daemon mode (server_fd > 0): serve and loop again
struct sockaddr_in clname;
-
- size = sizeof(clname);
- if ( (clsock = accept (server_fd, (struct sockaddr *) &clname,
- (socklen_t*) &size)) < 0 )
- throw static_cast<Error>("Failed to accept network connection");
+ int size = sizeof(clname);
- string clientip = inet_ntoa(clname.sin_addr);
-
- // If there is an allow list, the client must match it.
- if (config.nallow()) {
- if (config.debug())
- debugmsg ("Matching " + clientip + " against allow list\n");
- bool allowed = false;
- for (unsigned n = 0; n < config.nallow(); n++) {
- if (ipmatch (clname.sin_addr, config.allow(n))) {
- allowed = true;
- break;
- }
- }
- if (!allowed) {
- msg ("Not serving client " + clientip +
- ": no match in allow list\n");
- socketclose (clsock);
+ // 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(Mstr("Failed to accept network connection: ") +
+ Mstr(strerror(errno)) + "\n");
continue;
}
- }
- // If the client is in the deny list, deny it.
- if (config.debug())
- debugmsg ("Matching " + clientip + " against deny list\n");
- bool denied = false;
- for (unsigned n = 0; n < config.ndeny(); n++)
- if (ipmatch (clname.sin_addr, config.deny(n))) {
- denied = true;
- break;
- }
- if (denied) {
- msg ("Not serving client " + clientip +
- ": match in deny list\n");
- socketclose (clsock);
- continue;
+ string clientip = inet_ntoa(clname.sin_addr);
+ msg ((Mstr("Accepted connection from ") + clientip) +
+ (Mstr(" as client fd ") + clsock) + "\n");
}
- // Show whom we've accepted
+ // Show how we look
if (config.verbose()) {
- msg ((Mstr("Accepted connection from ") + clientip) +
- (Mstr(" as client fd ") + clsock) + "\n");
+ ostringstream o;
msg ((Mstr("Balancer is serving ") + connections()) +
" clients\n");
msg ("Current back end states:\n");
for (unsigned i = 0; i < nbackends(); i++)
msg((Mstr(" Back end ") + backend(i).description()) +
(Mstr(": ") + backend(i).connections()) +
- (Mstr(" connections , max ") + backend(i).maxconn()) +
- (Mstr(", status ") + backend(i).availablestr()) + "\n");
- }
-
- // We got action! Check if the total connections to the
- // balancer doesn't exceed the max.
- if (config.maxconn() && connections() >= config.maxconn()) {
- msg ((Mstr("Not serving client: already ") + connections()) +
- (Mstr(" (max ") + config.maxconn()) + "\n");
- socketclose (clsock);
- continue;
+ (Mstr(" connections, max ") + backend(i).maxconn()) +
+ (Mstr(", status ") + backend(i).availablestr()) +
+ (Mstr(", anticipated ") + IPStore::anticipated(i)) +
+ "\n");
}
- TcpDispatcher *d;
+ Dispatcher *d;
switch (config.stype()) {
case Servertype::t_tcp:
- d = new TcpDispatcher (clsock, clname.sin_addr);
+ d = new TcpDispatcher(clsock, clname.sin_addr);
break;
case Servertype::t_http:
- d = new HttpDispatcher (clsock, clname.sin_addr);
+ d = new HttpDispatcher(clsock, clname.sin_addr);
+ break;
+ case Servertype::t_udp:
+ d = new UdpDispatcher(server_fd);
break;
default:
- throw static_cast<Error>("Internal error, "
- "can't choose dispatcher");
+ throw Error("Internal error, can't choose dispatcher");
break;
}
+ if (!d)
+ throw Error("Memory fault in Balancer::serve");
// Allocation boundary printout
if (config.debug()) {
@@ -157,14 +148,19 @@
_debugmsg (Mstr("Allocation boundary at dispatcher start: ") +
mem + "\n");
}
-
+ #ifdef SHOWDEBUG
+ void *mem = malloc(16);
+ free (mem);
+ cout << "XR allocation at dispatcher start: " << mem << '\n';
+ #endif
+
d->start();
} else {
// If fd-serving, serve and close. Don't thread it up.
- TcpDispatcher *d;
+ 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);
@@ -172,15 +168,19 @@
case Servertype::t_http:
d = new HttpDispatcher (server_fd, dummy);
break;
+ case Servertype::t_udp:
+ throw Error("UDP dispatching not allowed in inetd-mode");
+ break;
default:
- throw static_cast<Error>("Internal error, "
- "can't choose dispatcher");
+ throw Error("Internal error, can't choose dispatcher");
break;
}
+ if (!d)
+ throw Error("Memory fault in Balancer::serve");
d->execute();
break;
}
-
+
// If we exceed the max # of requests, stop..
if (config.quitafter()) {
msg ((Mstr("Request ") + requestnr()) +
@@ -192,10 +192,12 @@
}
}
+ // We're stopping now. If a PID stamp was created, remove it.
+ if (config.pidfile() != "")
+ unlink (config.pidfile().c_str());
- // We're stopping XR now. Wait for running threads to die off.
+ // Wait for running threads to die off.
socketclose (server_fd);
- shutdown (server_fd, SHUT_RDWR);
unsigned prev_conn = 0x19081962;
while (1) {
unsigned curr_conn = balancer.connections();
@@ -208,8 +210,4 @@
sleep (1);
}
msg ("XR is idle, stopping.\n");
-
- // If a PID stamp was created, remove it now.
- if (config.pidfile() != "")
- unlink (config.pidfile().c_str());
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/addallow.cc
^
|
@@ -3,6 +3,6 @@
void Config::addallow (string a) {
struct in_addr in;
if (!inet_aton (a.c_str(), &in))
- throw static_cast<Error>("Bad allow-from specfier '") + a + "'";
+ throw Error("Bad allow-from specfier '" + a + "'");
allowlist.push_back (in);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/adddeny.cc
^
|
@@ -3,6 +3,6 @@
void Config::adddeny (string d) {
struct in_addr in;
if (!inet_aton (d.c_str(), &in))
- throw static_cast<Error>("Bad deny-from specfier '") + d + "'";
+ throw Error("Bad deny-from specfier '" + d + "'");
denylist.push_back (in);
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/config/changeallow.cc
^
|
@@ -0,0 +1,12 @@
+#include "config"
+
+void Config::changeallow (string &a, unsigned index) {
+ if (index >= allowlist.size())
+ throw Error("No such allow-from specifier");
+
+ struct in_addr in;
+ if (!inet_aton (a.c_str(), &in))
+ throw Error("Bad allow-from specfier '" + a + "'");
+ allowlist[index] = (in);
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/config/changedeny.cc
^
|
@@ -0,0 +1,12 @@
+#include "config"
+
+void Config::changedeny (string &a, unsigned index) {
+ if (index >= denylist.size())
+ throw Error("No such deny-from specifier");
+
+ struct in_addr in;
+ if (!inet_aton (a.c_str(), &in))
+ throw Error("Bad deny-from specfier '" + a + "'");
+ denylist[index] = (in);
+}
+
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/config
^
|
@@ -7,6 +7,7 @@
#include "dispatchmode/dispatchmode"
#include "error/error"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "backendcheck/backendcheck"
using namespace std;
@@ -27,17 +28,46 @@
unsigned quitafter() const { return (quit_after); }
Servertype::Type stype() const { return (styp.type()); }
+ void stype(string const &s) { styp.type(s); }
string stypestr() const { return (styp.typestr()); }
- string sipaddr() const { return (sip); }
+ string const &sipaddr() const { return (sip); }
int sport() const { return (lport); }
int backends() const { return (blist.size()); }
- unsigned client_timeout() const { return (c_timeout); }
- void client_timeout (unsigned c) { c_timeout = c; }
+ /* PID file */
+ void pidfile(string const &s);
+ string const &pidfile() {
+ return pid_file;
+ }
- unsigned backend_timeout() const { return (b_timeout); }
- void backend_timeout (unsigned b) { b_timeout = b; }
+ /* Client timeouts */
+ unsigned client_read_timeout() const {
+ return (c_timeout);
+ }
+ void client_read_timeout (unsigned c) {
+ c_timeout = c;
+ }
+ unsigned client_write_timeout() const {
+ return c_write_timeout;
+ }
+ void client_write_timeout(unsigned c) {
+ c_write_timeout = c;
+ }
+
+ /* Back end timeouts */
+ unsigned backend_read_timeout() const {
+ return (b_timeout);
+ }
+ void backend_read_timeout (unsigned b) {
+ b_timeout = b;
+ }
+ unsigned backend_write_timeout() const {
+ return b_write_timeout;
+ }
+ void backend_write_timeout(unsigned b) {
+ b_write_timeout = b;
+ }
unsigned wakeupsec() const { return (wakeup); }
void wakeupsec (unsigned w) { wakeup = w; }
@@ -59,13 +89,15 @@
bool stickyhttp() const { return (sticky_http); }
void stickyhttp(bool b);
+ bool replacehostheader() const { return replace_host_header; }
+ void replacehostheader(bool s) { replace_host_header = s; }
+
unsigned maxconn() const { return (max_conn); }
void maxconn (unsigned m);
- string externalalgorithm() const { return (external_algorithm); }
-
- string pidfile() const { return (pid_file); }
- void pidfile (string const &p);
+ string const &externalalgorithm() const {
+ return (external_algorithm);
+ }
bool prefixtimestamp() const { return (prefix_timestamp); }
void prefixtimestamp (bool p);
@@ -74,16 +106,20 @@
void fastclose (bool f);
bool usewebinterface() const { return use_webinterface; }
- string webinterfaceip() const { return webinterface_ip; }
+ string const &webinterfaceip() const {
+ return webinterface_ip;
+ }
int webinterfaceport() const { return webinterface_port; }
unsigned nserverheaders() const { return (serverheaders.size()); }
- string serverheader (unsigned n) { return (serverheaders[n]); }
+ string const &serverheader (unsigned n) {
+ return (serverheaders[n]);
+ }
void addserverheader (string const &s);
void removeserverheader (unsigned i);
void changeserverheader (unsigned i, string const &s);
- string dumpdir() const { return (dump_dir); }
+ string const &dumpdir() const { return (dump_dir); }
void dumpdir (string s) { dump_dir = s; }
unsigned softmaxconnrate() const { return soft_maxconnrate; }
@@ -95,11 +131,21 @@
unsigned connrate_time() const { return connrate_timeinterval; }
void connrate_time(unsigned n) { connrate_timeinterval = n; }
+ unsigned dnscachetimeout() const { return dns_cache_timeout; }
+ void dnscachetimeout(unsigned t) { dns_cache_timeout = t; }
+
unsigned nallow() const { return (allowlist.size()); }
unsigned ndeny() const { return (denylist.size()); }
+ void addallow (string a);
+ void adddeny (string d);
+ void changeallow(string &a, unsigned index);
+ void changedeny(string &a, unsigned index);
+ void deleteallow(unsigned index);
+ void deletedeny(unsigned index);
+
int ipstoretimeout() const { return (ipstore_timeout); }
void ipstoretimeout(int t);
- struct in_addr allow(unsigned n) const {
+ struct in_addr allow(unsigned n) const {
return (allowlist[n]);
}
struct in_addr deny(unsigned n) const {
@@ -115,24 +161,54 @@
string dispatchmodestr() const {
return (dmode.modestr());
}
+ bool removereservations() const {
+ return remove_reservations;
+ }
+ void removereservations(bool b) {
+ remove_reservations = b;
+ }
+
+ string const &softmaxconnexcess() const {
+ return soft_maxconn_excess_prog;
+ }
+ void softmaxconnexcess(string const &s) {
+ soft_maxconn_excess_prog = s;
+ }
+ string const &hardmaxconnexcess() const {
+ return hard_maxconn_excess_prog;
+ }
+ void hardmaxconnexcess(string const &s) {
+ hard_maxconn_excess_prog = s;
+ }
+
+ void onstart(string s) { on_start = s; }
+ string const &onstart() const { return on_start; }
+ void onend(string s) { on_end = s; }
+ string const &onend() const { return on_end; }
+ void onfail(string s) { on_fail = s; }
+ string const &onfail() const { return on_fail; }
+
+ /* Restart of program */
+ void restart();
private:
- void setbackend (string s, string hostmatch);
+ void setbackend (string const &s, string const &hostmatch,
+ string const &urlmatch,
+ BackendCheck const &bc);
void setwebinterface (string s);
void setserver (string s);
void setdispatchmode (string s);
int setinteger (string s) const;
- void addallow (string a);
- void adddeny (string d);
+ static string pid_file;
static bool verbose_flag;
static int lport;
static Servertype styp;
static string sip;
static vector<BackendDef> blist;
static Dispatchmode dmode;
- static unsigned c_timeout;
- static unsigned b_timeout;
+ static unsigned c_timeout, c_write_timeout;
+ static unsigned b_timeout, b_write_timeout;
static unsigned wakeup;
static unsigned checkup;
static unsigned bufsize;
@@ -141,9 +217,9 @@
static bool debug_flag;
static bool add_x_forwarded_for;
static bool sticky_http;
+ static bool replace_host_header;
static unsigned max_conn;
static string external_algorithm;
- static string pid_file;
static bool prefix_timestamp;
static vector<string> serverheaders;
static vector<struct in_addr> allowlist;
@@ -159,6 +235,12 @@
static unsigned defer_time;
static unsigned connrate_timeinterval;
static unsigned quit_after;
+ static string soft_maxconn_excess_prog;
+ static string hard_maxconn_excess_prog;
+ static unsigned dns_cache_timeout;
+ static string on_start, on_end, on_fail;
+ static bool remove_reservations;
+ static char **org_argv;
};
extern Config config;
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/config1.cc
^
|
@@ -7,7 +7,9 @@
vector<BackendDef> Config::blist;
Dispatchmode Config::dmode;
unsigned Config::c_timeout = 30;
+unsigned Config::c_write_timeout = 5;
unsigned Config::b_timeout = 30;
+unsigned Config::b_write_timeout = 3;
unsigned Config::wakeup = 5;
unsigned Config::checkup = 0;
unsigned Config::bufsize = 2048;
@@ -16,6 +18,7 @@
bool Config::debug_flag = false;
bool Config::add_x_forwarded_for = false;
bool Config::sticky_http = false;
+bool Config::replace_host_header = false;
unsigned Config::max_conn = 0;
string Config::external_algorithm = "";
string Config::pid_file = "";
@@ -34,6 +37,14 @@
unsigned Config::defer_time = 500000;
unsigned Config::connrate_timeinterval = 1;
unsigned Config::quit_after = 0;
+string Config::soft_maxconn_excess_prog = "";
+string Config::hard_maxconn_excess_prog = "";
+unsigned Config::dns_cache_timeout = 3600;
+string Config::on_start = "";
+string Config::on_end = "";
+string Config::on_fail = "";
+bool Config::remove_reservations = false;
+char **Config::org_argv = 0;
Config::Config () {
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/config/deleteallow.cc
^
|
@@ -0,0 +1,9 @@
+#include "config"
+
+void Config::deleteallow(unsigned index) {
+ if (index >= allowlist.size())
+ throw Error("Index out of range, cannot delete allow-from");
+ allowlist.erase(allowlist.begin() + index,
+ allowlist.begin() + index + 1);
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/config/deletedeny.cc
^
|
@@ -0,0 +1,9 @@
+#include "config"
+
+void Config::deletedeny(unsigned index) {
+ if (index >= denylist.size())
+ throw Error("Index out of range, cannot delete deny-from");
+ denylist.erase(denylist.begin() + index,
+ denylist.begin() + index + 1);
+}
+
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/parsecmdline.cc
^
|
@@ -4,6 +4,9 @@
using namespace std;
void Config::parsecmdline (int ac, char **av) {
+ // Remember original argv.
+ org_argv = av;
+
// Prepare invoking command line.
string cmdline;
for (int i = 0; i < ac; i++) {
@@ -13,45 +16,55 @@
}
// Not a single argument? Usage.
if (ac == 1)
- throw static_cast<Error>("Bad command line '") +
- cmdline + "'\n" + USAGE;
+ throw Error("Bad command line '" + cmdline + "'\n" + USAGE);
-# define OPTSTRING "?a:A:B:b:c:CDd:fhH:l:m:M:nPp:Q:r:R:Ss:t:T:u:U:vVW:w:xX"
+# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:Gg:hH:Ij:l:" \
+ "m:M:nPQ:r:R:Ss:t:T:u:U:vVW:w:xXy:z:Z:"
# ifdef HAVE_GETOPT_LONG
static struct option longopts[] = {
- { "allow-from", required_argument, 0, 'a' },
- { "deny-from", required_argument, 0, 'A' },
- { "backend", required_argument, 0, 'b' },
- { "buffer-size", required_argument, 0, 'B' },
- { "checkup-interval", required_argument, 0, 'c' },
- { "close-sockets-fast", no_argument, 0, 'C' },
- { "debug", no_argument, 0, 'D' },
- { "dispatch-mode", required_argument, 0, 'd' },
- { "foreground", no_argument, 0, 'f' },
- { "help", no_argument, 0, 'h' },
- { "add-server-header", required_argument, 0, 'H' },
- { "log-traffic-dir", required_argument, 0, 'l' },
- { "max-connections", required_argument, 0, 'm' },
- { "host-match", required_argument, 0, 'M' },
- { "tryout", no_argument, 0, 'n' },
- { "prefix-timestamp", no_argument, 0, 'P' },
- { "pidfile", required_argument, 0, 'p' },
- { "soft-maxconnrate", required_argument, 0, 'r' },
- { "quit-after", required_argument, 0, 'Q' },
- { "hard-maxconnrate", required_argument, 0, 'R' },
- { "server", required_argument, 0, 's' },
- { "sticky-http", no_argument, 0, 'S' },
- { "backend-timeout", required_argument, 0, 't' },
- { "client-timeout", required_argument, 0, 'T' },
- { "time-interval", required_argument, 0, 'u' },
- { "defer-time", required_argument, 0, 'U' },
- { "verbose", no_argument, 0, 'v' },
- { "version", no_argument, 0, 'V' },
- { "wakeup-interval", required_argument, 0, 'w' },
- { "web-interface", required_argument, 0, 'W' },
- { "add-xr-version", no_argument, 0, 'X' },
- { "add-x-forwarded-for", no_argument, 0, 'x' },
- { 0, 0, 0, 0 }
+ { "allow-from", required_argument, 0, 'a' },
+ { "deny-from", required_argument, 0, 'A' },
+ { "backend", required_argument, 0, 'b' },
+ { "buffer-size", required_argument, 0, 'B' },
+ { "checkup-interval", required_argument, 0, 'c' },
+ { "close-sockets-fast", no_argument, 0, 'C' },
+ { "debug", no_argument, 0, 'D' },
+ { "dispatch-mode", required_argument, 0, 'd' },
+ { "hard-maxconn-excess", required_argument, 0, 'E' },
+ { "soft-maxconn-excess", required_argument, 0, 'e' },
+ { "dns-cache-timeout", required_argument, 0, 'F' },
+ { "foreground", no_argument, 0, 'f' },
+ { "remove-reservations", no_argument, 0, 'G' },
+ { "backend-check", required_argument, 0, 'g' },
+ { "help", no_argument, 0, 'h' },
+ { "add-server-header", required_argument, 0, 'H' },
+ { "replace-host-header", no_argument, 0, 'I' },
+ { "url-match", required_argument, 0, 'j' },
+ { "log-traffic-dir", required_argument, 0, 'l' },
+ { "max-connections", required_argument, 0, 'm' },
+ { "host-match", required_argument, 0, 'M' },
+ { "tryout", no_argument, 0, 'n' },
+ { "pidfile", required_argument, 0, 'p' },
+ { "prefix-timestamp", no_argument, 0, 'P' },
+ { "soft-maxconnrate", required_argument, 0, 'r' },
+ { "quit-after", required_argument, 0, 'Q' },
+ { "hard-maxconnrate", required_argument, 0, 'R' },
+ { "server", required_argument, 0, 's' },
+ { "sticky-http", no_argument, 0, 'S' },
+ { "backend-timeout", required_argument, 0, 't' },
+ { "client-timeout", required_argument, 0, 'T' },
+ { "time-interval", required_argument, 0, 'u' },
+ { "defer-time", required_argument, 0, 'U' },
+ { "verbose", no_argument, 0, 'v' },
+ { "version", no_argument, 0, 'V' },
+ { "wakeup-interval", required_argument, 0, 'w' },
+ { "web-interface", required_argument, 0, 'W' },
+ { "add-xr-version", no_argument, 0, 'X' },
+ { "add-x-forwarded-for", no_argument, 0, 'x' },
+ { "onfail", required_argument, 0, 'y' },
+ { "onstart", required_argument, 0, 'z' },
+ { "onend", required_argument, 0, 'Z' },
+ { 0, 0, 0, 0 }
};
# endif
@@ -59,6 +72,9 @@
bool backend_set = false;
bool tryout = false, wakeup_used = false;
string current_hostmatch = "";
+ string current_urlmatch = "";
+ BackendCheck current_backendcheck;
+ vector<string> parts;
# ifdef HAVE_GETOPT_LONG
while ( (opt = getopt_long (ac, av, OPTSTRING, longopts, 0)) > 0)
@@ -74,7 +90,8 @@
adddeny (optarg);
break;
case 'b':
- setbackend (optarg, current_hostmatch);
+ setbackend (optarg, current_hostmatch, current_urlmatch,
+ current_backendcheck);
backend_set = true;
break;
case 'B':
@@ -93,16 +110,37 @@
case 'd':
setdispatchmode (optarg);
break;
+ case 'E':
+ hard_maxconn_excess_prog = optarg;
+ break;
+ case 'e':
+ soft_maxconn_excess_prog = optarg;
+ break;
+ case 'F':
+ dns_cache_timeout = (unsigned)setinteger(optarg);
+ break;
case 'f':
foreground_mode = true;
break;
+ case 'G':
+ removereservations(true);
+ break;
+ case 'g':
+ current_backendcheck.parse(optarg);
+ break;
case 'h':
case '?':
- throw static_cast<Error>(USAGE);
+ throw Error(USAGE);
break;
case 'H':
addserverheader (optarg);
break;
+ case 'I':
+ replacehostheader(true);
+ break;
+ case 'j':
+ current_urlmatch = optarg;
+ break;
case 'l':
dumpdir (optarg);
break;
@@ -118,9 +156,6 @@
case 'P':
prefix_timestamp = true;
break;
- case 'p':
- pid_file = optarg;
- break;
case 'Q':
quit_after = (unsigned) setinteger(optarg);
break;
@@ -137,10 +172,28 @@
sticky_http = true;
break;
case 't':
- b_timeout = setinteger (optarg);
+ parts = str2parts(optarg, ':');
+ if (parts.size() == 1) {
+ backend_read_timeout(atoi(parts[0].c_str()));
+ backend_write_timeout(atoi(parts[0].c_str()));
+ } else if (parts.size() == 2) {
+ backend_read_timeout(atoi(parts[0].c_str()));
+ backend_write_timeout(atoi(parts[1].c_str()));
+ } else
+ throw Error("Bad backend timeout specifier, "
+ "expected SEC or RSEC:WSEC");
break;
case 'T':
- c_timeout = setinteger (optarg);
+ parts = str2parts(optarg, ':');
+ if (parts.size() == 1) {
+ client_read_timeout(atoi(parts[0].c_str()));
+ client_write_timeout(atoi(parts[0].c_str()));
+ } else if (parts.size() == 2) {
+ client_read_timeout(atoi(parts[0].c_str()));
+ client_write_timeout(atoi(parts[1].c_str()));
+ } else
+ throw Error("Bad backend timeout specifier, "
+ "expected SEC or RSEC:WSEC");
break;
case 'U':
defer_time = (unsigned) setinteger(optarg);
@@ -155,6 +208,7 @@
cout << "XR version : " << VER << "\n"
<< "Written by : " << AUTHOR << "\n"
<< "Maintained by : " << MAINTAINER << "\n"
+ << "Primary site : " << DISTSITE << "\n"
<< "Compiled with : " << CONF_CC << "\n"
<< "Optimization : " << CONF_OPTFLAGS << "\n"
<< "System : " << SYS << "\n"
@@ -205,28 +259,34 @@
case 'x':
add_x_forwarded_for = true;
break;
+ case 'y':
+ onfail(optarg);
+ break;
+ case 'z':
+ onstart(optarg);
+ break;
+ case 'Z':
+ onend(optarg);
+ break;
default:
- throw static_cast<Error>("Unknown flag, try 'xr -h' for usage");
+ throw Error("Unknown flag, try 'xr -h' for usage");
break;
}
}
// Sanity checks.
if (ac != optind)
- throw static_cast<Error>("Bad command line '") + cmdline + "'\n" +
- USAGE;
+ throw Error("Bad command line '" + cmdline + "'\n" + USAGE);
if (!backend_set)
- throw static_cast<Error>
- ("No backend defined, use '-b...' at least once, "
- "or try 'xr -h' for usage");
+ throw Error("No backend defined, use '-b...' at least once, "
+ "or try 'xr -h' for usage");
if (checkup && wakeup) {
if (!wakeup_used)
wakeup = 0;
else
- throw static_cast<Error>
- ("Use either --checkup-interval or --wakeup-interval, "
- "but not both");
+ throw Error("Use either --checkup-interval or --wakeup-interval, "
+ "but not both");
}
// In tryout mode, stop now.
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/config/restart.cc
^
|
@@ -0,0 +1,11 @@
+#include "config"
+
+void Config::restart() {
+ for (int i = 0; org_argv[i]; i++)
+ cout << "Arg " << i << ": " << org_argv[i] << '\n';
+ execvp("xr", org_argv);
+ ostringstream o;
+ o << "Failed to restart: errno=" << errno << ", "
+ << strerror(errno);
+ throw Error(o.str());
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/setbackend.cc
^
|
@@ -1,11 +1,13 @@
#include "config"
-void Config::setbackend (string str, string host) {
+void Config::setbackend (string const &str, string const &host,
+ string const &url,
+ BackendCheck const &backend_check) {
vector<string> parts = str2parts (str, ':');
if (parts.size() < 2 || parts.size() > 4)
- throw static_cast<Error>("Bad back end specifier in '-b") + str +
- "', expected: SERVER:PORT or SERVER:PORT:MAXCONNECTIONS or "
- "SERVER:PORT:MAXCONNECTIONS:WEIGHT";
+ throw Error("Bad back end specifier in '-b" + str +
+ "', expected: SERVER:PORT or SERVER:PORT:MAXCONNECTIONS"
+ " or SERVER:PORT:MAXCONNECTIONS:WEIGHT");
BackendDef *bdp = 0;
if (parts.size() == 2)
@@ -14,7 +16,11 @@
bdp = new BackendDef(parts[0], parts[1], parts[2]);
else if (parts.size() == 4)
bdp = new BackendDef(parts[0], parts[1], parts[2], parts[3]);
+ if (!bdp)
+ throw Error("Memory fault in Config::setbackend");
bdp->hostmatch(host);
+ bdp->urlmatch(url);
+ bdp->backendcheck(backend_check);
blist.push_back (*bdp);
delete bdp;
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/setdispatcmode.cc
^
|
@@ -33,12 +33,12 @@
dmode.mode (Dispatchmode::m_lax_stored_ip);
ipstoretimeout(setinteger(s.substr(2)));
} else if (s.substr(0, 14) == "lax-stored-ip:") {
- dmode.mode (Dispatchmode::m_strict_stored_ip);
+ dmode.mode (Dispatchmode::m_lax_stored_ip);
ipstoretimeout(setinteger(s.substr(14)));
} else
- throw static_cast<Error>("Bad dispatch mode -d") + s;
+ throw Error("Bad dispatch mode -d" + s);
if (dmode.mode() == Dispatchmode::m_external &&
external_algorithm.size() < 1)
- throw static_cast<Error>("External algorithm handler missing");
+ throw Error("External algorithm handler missing");
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/setinteger.cc
^
|
@@ -3,7 +3,6 @@
int Config::setinteger (string s) const {
int ret;
if (sscanf (s.c_str(), "%d", &ret) < 1)
- throw static_cast<Error>("Bad numeric specifier in '") + s +
- ": not a number";
+ throw Error("Bad numeric specifier in '" + s + ": not a number");
return (ret);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/setserver.cc
^
|
@@ -4,8 +4,7 @@
// Split into 3 parts
vector<string> parts = str2parts (str, ':');
if (parts.size() != 3)
- throw static_cast<Error>
- ("Bad server specifier, expected: TYPE:IPADDRESS:PORT");
+ throw Error("Bad server specifier, expected: TYPE:IPADDRESS:PORT");
// Store type, IP and port
styp.type (parts[0]);
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/config/setwebinterface.cc
^
|
@@ -3,8 +3,8 @@
void Config::setwebinterface (string str) {
vector<string> parts = str2parts (str, ':');
if (parts.size() != 2)
- throw static_cast<Error>("Bad webinterface specifier in '-W") +
- str + "', expected: IP:PORT";
+ throw Error("Bad webinterface specifier in '-W" +
+ str + "', expected: IP:PORT");
use_webinterface = true;
webinterface_ip = parts[0];
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/dnsentry
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/dnsentry/dnsentry
^
|
@@ -0,0 +1,19 @@
+#ifndef _DNSENTRY_
+#define _DNSENTRY_
+
+#include "config/config"
+#include "error/error"
+#include "timestamp/timestamp"
+#include "ThreadsAndMutexes/mutex/mutex"
+
+class DNSEntry {
+public:
+ DNSEntry(): result(0), timestamp() {}
+ in_addr_t &resolve(string const &str);
+
+private:
+ in_addr_t result;
+ Timestamp timestamp;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/dnsentry/resolve.cc
^
|
@@ -0,0 +1,20 @@
+#include "dnsentry"
+
+in_addr_t &DNSEntry::resolve (string const &h) {
+ // If the entry is there and if it's up to date, run with it
+ if (result && timestamp.elapsed() <= (double)config.dnscachetimeout())
+ return result;
+
+ // Resolve now.
+ struct hostent *hostaddr;
+ Mutex::lock((void*)gethostbyname);
+ if ( (hostaddr = gethostbyname(h.c_str())) )
+ memcpy (&result, hostaddr->h_addr_list[0], hostaddr->h_length);
+ Mutex::unlock((void*)gethostbyname);
+
+ if (!hostaddr)
+ throw Error("Failed to resolve host '" + h + "'");
+
+ debugmsg(Mstr("Host ") + h + " resolved\n");
+ return result;
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/error/error
^
|
@@ -1,7 +1,8 @@
#ifndef _ERROR_
#define _ERROR_
-#include "../sys/sys"
+#include "sys/sys"
+#include "timestamp/timestamp"
using namespace std;
@@ -10,7 +11,7 @@
Error (string s);
Error (int i);
Error &operator+ (Error const &other);
- Error &operator+ (string const s);
+ Error &operator+ (string const &s);
Error &operator+ (int i);
char const *what() const throw ();
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/error/opplus2.cc
^
|
@@ -1,6 +1,6 @@
#include "error"
-Error &Error::operator+ (string s) {
+Error &Error::operator+ (string const &s) {
desc += s;
return (*this);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/error/what.cc
^
|
@@ -3,8 +3,10 @@
char const *Error::what() const throw (){
ostringstream o;
- if (config.prefixtimestamp())
- o << timestamp() << ' ';
+ if (config.prefixtimestamp()) {
+ Timestamp tm;
+ o << tm.desc() << ' ';
+ }
o << pthread_self() << " ERROR: " << desc;
return (o.str().c_str());
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/etc/Makefile.class
^
|
@@ -2,16 +2,23 @@
OBJ = $(patsubst %.cc, $(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o, $(SRC))
DIR = $(shell pwd | sed 's:.*/::')
SYS = $(shell uname)
+HST = $(shell hostname)
+# CCC = -DCONNECTCHECK_ONLY_WRITABLE
+
+ifeq ($(HST), Thera.local)
+ ERRFLAG = -Werror
+endif
class-compile: $(OBJ)
$(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o: %.cc
@echo "Compiling: " `pwd` $<
- @$(CONF_CC) $(PROF) $(PROFILER) $(CONF_OPTFLAGS) \
- -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \
- -DMAINTAINER='"$(MAINTAINER)"' -DSYS='"$(SYS)"' -D$(SYS) \
+ $(CONF_CC) $(PROF) $(PROFILER) $(CONF_OPTFLAGS) \
+ -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' -DHST='"$(HST)"' \
+ -DMAINTAINER='"$(MAINTAINER)"' -DDISTSITE='"$(DISTSITE)"' \
+ -DSYS='"$(SYS)"' -D$(SYS) $(MEMDEBUG) $(CCC) \
-DCONF_CC='"$(CONF_CC)"' -DCONF_LIB='"$(CONF_LIB)"' \
-DCONF_OPTFLAGS='"$(CONF_OPTFLAGS)"' $(CONF_STRNSTR) \
$(CONF_GETOPT) $(CONF_GETOPT_LONG) $(CONF_INET_ATON) \
-I$(BASE)/xr \
- -c -g -Wall -o $@ $<
+ -c -g -Wall $(ERRFLAG) -o $@ $<
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/etc/c-conf
^
|
@@ -157,18 +157,21 @@
sub findbin($) {
my $bin = shift;
my $bestx = undef;
- my $bestver = 0;
+ my $bestver = -1;
foreach my $d (split (/:/, $ENV{PATH})) {
- for my $x (glob("$d/$bin"), glob("$d/$bin.exe"),
- glob("$d/$bin-*"), glob("$d/$bin-*.exe")) {
+ my @cand = (glob("$d/$bin"), glob("$d/$bin.exe"),
+ glob("$d/$bin-*"), glob("$d/$bin-*.exe"));
+ msg ("Candidates for '$bin' in '$d': [@cand]\n");
+ for my $x (@cand) {
if (-x $x) {
my $ver = $x;
$ver =~ s{^.*/[^\d]*}{};
$ver = sprintf("%g", $ver);
- # msg ("Candidate for '$bin': $x, version $ver\n");
+ msg ("Version of $x: $ver\n");
if ($bestver < $ver or !$bestx) {
$bestver = $ver;
$bestx = $x;
+ msg (" .. best so far\n");
}
}
}
@@ -362,8 +365,8 @@
output ("-l$lib");
}
}
- warning ("Library '$lib' not found\n")
- unless ($found);
+ #warning ("Library '$lib' not found\n")
+ # unless ($found);
}
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/etc/status-nosavebutton.xslt
^
|
@@ -0,0 +1,897 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="html"
+ encoding="UTF-8"
+ doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
+
+<xsl:template match="/">
+ <html>
+ <head>
+ <title>XR Status Overview</title>
+ <style type="text/css">
+ h1 {
+ font-family: Verdana,Helvetica;
+ font-size: 12pt;
+ color: blue;
+ }
+ body {
+ font-family: Verdana,Helvetica;
+ font-size: 8pt;
+ }
+ table {
+ border-collapse: collapse;
+ border-style: hidden;
+ }
+ td {
+ font-family: Verdana,Helvetica;
+ font-size: 8pt;
+ background-color: #ffff99;
+ border: 1px solid #f0f090;
+ }
+ input {
+ font-family: Verdana,Helvetica;
+ font-size: 8pt;
+ }
+ .header { background-color: #f3f399; }
+ .footer { color: gray; }
+ </style>
+ <script type="text/javascript">
+ function goto(uri, input) {
+ if (input == '')
+ document.location = uri;
+ else {
+ var el = document.getElementById(input);
+ if (el) {
+ var value = el.value;
+ if (value != "")
+ document.location = uri + encodeURIComponent(value);
+ else
+ document.location = uri;
+ }
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <h1>XR Status Overview</h1>
+ <hr/>
+ <xsl:apply-templates/>
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template match="/status">
+ <table>
+ <tr>
+ <td valign="top">
+ <!-- This is the left hand detailed status view -->
+ <table>
+ <tr>
+ <td colspan="4"><b>Detailed Status</b></td>
+ </tr>
+ <tr>
+ <td colspan="4"><hr/></td>
+ </tr>
+ <xsl:apply-templates select="/status/server"/>
+ <xsl:apply-templates select="/status/backend"/>
+
+ <tr> <td colspan="4"><hr/></td></tr>
+ <tr>
+ <td class="header" colspan="2">
+ <b>Add back end ip:port</b>
+ </td>
+ <td class="header" colspan="2" align="right">
+ <input type="text" size="30" name="addbackend" id="addbackend"
+ onchange="goto('/server/addbackend/', 'addbackend');"/>
+ </td>
+ </tr>
+ <tr> <td colspan="4"><hr/></td></tr>
+
+ </table>
+ <xsl:apply-templates select="/status/id"/>
+ </td>
+ <td valign="top">
+ <!-- This is the right-hand overview -->
+ <table width="100%">
+ <tr>
+ <td colspan="2"><b>Quick Overview</b></td>
+ </tr>
+ <tr>
+ <td colspan="2"><hr/></td>
+ </tr>
+ <xsl:for-each select="/status/backend">
+ <tr>
+ <td>
+ <b>Back end
+ <a href="#{nr}"><xsl:value-of select="address"/></a>
+ </b>
+ </td>
+ <td>
+ <xsl:value-of select="up"/>,
+ <xsl:value-of select="live"/>,
+ <xsl:value-of select="available"/>,
+ <xsl:value-of select="connections"/> connections
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <!-- This is the activity overview -->
+ <table width="100%">
+ <tr>
+ <td colspan="5"><hr/></td>
+ </tr>
+ <tr>
+ <td colspan="5"><b>Activity</b></td>
+ </tr>
+ <tr>
+ <td colspan="5"><hr/></td>
+ </tr>
+ <tr>
+ <td colspan="3">Number of threads</td>
+ <td><xsl:value-of select="/status/activity/threadcount"/></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="3">Used file descriptors (approx.)</td>
+ <td><xsl:value-of select="/status/activity/openfiles"/></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="3">File descriptor limit</td>
+ <td><xsl:value-of select="/status/activity/maxopenfiles"/></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td><b>Thread</b></td>
+ <td><b>Description</b></td>
+ <td><b>Back end</b></td>
+ <td><b>Duration</b></td>
+ <td></td>
+ </tr>
+ <xsl:apply-templates select="/status/activity/threadlist/thread">
+ <xsl:sort select="duration" data-type="number"/>
+ </xsl:apply-templates>
+ </table>
+ </td>
+ </tr>
+ </table>
+</xsl:template>
+
+<xsl:template match="/status/activity/threadlist/thread">
+ <tr>
+ <td><xsl:value-of select="id"/></td>
+ <td>
+ <xsl:value-of select="description"/>
+ <xsl:if test="clientip != '0.0.0.0'">
+ (client <xsl:value-of select="clientip"/>)
+ </xsl:if>
+ </td>
+ <xsl:choose>
+ <xsl:when test="backend = -1">
+ <td></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td><xsl:value-of select="address"/></td>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td><xsl:value-of select="duration"/></td>
+ <xsl:choose>
+ <xsl:when test="backend = -1">
+ <td></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td><input type="button" value="Kill"
+ onclick="goto('/thread/kill/{id}', '');"/>
+ </td>
+ </xsl:otherwise>
+ </xsl:choose>
+ </tr>
+</xsl:template>
+
+<xsl:template match="/status/id">
+ <i>
+ Powered by Crossroads V<xsl:value-of select="version"/>.
+ Visit
+ <a href="{distsite}"
+ target="_blank"><xsl:value-of select="distsite"/></a>
+ for more info.
+ </i>
+</xsl:template>
+
+<xsl:template match="/status/server">
+ <tr>
+ <td class="header" colspan="3">
+ <b>Server <xsl:value-of select="address"/> </b>
+ </td>
+ <td class="header">
+ <input type="button" onclick="goto('/', '');" value="Refresh"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Status</td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="terminating = 0">
+ Accepting connections,
+ <xsl:value-of select="connections"/> concurrent client(s),
+ </xsl:when>
+ <xsl:otherwise>
+ <font color="red">
+ Terminating, still serving
+ <xsl:value-of select="connections"/> connections,
+ </font>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="backends"/> defined back ends
+ </td>
+ </tr>
+ <tr>
+ <td>Dispatch mode</td>
+ <td colspan="3"> <xsl:value-of select="dispatchmode"/> </td>
+ </tr>
+ <tr>
+ <td>Type</td>
+ <td colspan="2"></td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="type = 'http'">
+ <select onchange="goto('/server/type/tcp', '');">
+ <option value="tcp">tcp</option>
+ <option value="http" selected="1">http</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/type/http', '');">
+ <option value="tcp" selected="1">tcp</option>
+ <option value="http">http</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td>Checks</td>
+ <td>Wakeup interval</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="checks/wakeupinterval = 0">
+ off
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="wakeupinterval"
+ id="wakeupinterval" value="{checks/wakeupinterval}"
+ onchange="goto('/server/wakeupinterval/', 'wakeupinterval');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Checkup interval</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="checks/checkupinterval = 0">
+ off
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="checkupinterval"
+ id="checkupinterval" value="{checks/checkupinterval}"
+ onchange="goto('/server/checkupinterval/', 'checkupinterval');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Timeouts</td>
+ <td>Client read</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="clientreadtimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="clientreadtimeout"
+ id="clientreadtimeout" value="{clientreadtimeout}"
+ onchange="goto('/server/clientreadtimeout/', 'clientreadtimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Client write</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="clientwritetimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="clientwritetimeout"
+ id="clientwritetimeout" value="{clientwritetimeout}"
+ onchange="goto('/server/clientwritetimeout/', 'clientwritetimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Back end read</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="backendreadtimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="backendreadtimeout"
+ id="backendreadtimeout" value="{backendreadtimeout}"
+ onchange="goto('/server/backendreadtimeout/', 'backendreadtimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Back end write</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="backendwritetimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="backendwritetimeout"
+ id="backendwritetimeout" value="{backendwritetimeout}"
+ onchange="goto('/server/backendwritetimeout/', 'backendwritetimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>DNS cache validity</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="dnscachetimeout = 0">
+ unused
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="dnscachetimeout"
+ id="dnscachetimeout" value="{dnscachetimeout}"
+ onchange="goto('/server/dnscachetimeout/', 'dnscachetimeout');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td>Fast sockets closing</td>
+ <td colspan="2">eliminates TIME_WAIT state</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="closesocketsfast = 0">
+ <select onchange="goto('/server/closesocketsfast/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/closesocketsfast/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td>Debugging</td>
+ <td colspan="2">Verbose logging</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="debugging/verbose = 0">
+ <select onchange="goto('/server/verbose/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/verbose/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Debug logging</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="debugging/debug = 0">
+ <select onchange="goto('/server/debug/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/debug/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Traffic log directory</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="logtrafficdir" id="logtrafficdir"
+ value="{debugging/logtrafficdir}"
+ onchange="goto('/server/logtrafficdir/', 'logtrafficdir');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Activity scripts</td>
+ <td>Onstart command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onstart" id="onstart"
+ value="{onstart}"
+ onchange="goto('/server/onstart/', 'onstart');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Onend command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onend" id="onend"
+ value="{onend}"
+ onchange="goto('/server/onend/', 'onend');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Onfail command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onfail" id="onfail"
+ value="{onfail}"
+ onchange="goto('/server/onfail/', 'onfail');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Network buffer size</td>
+ <td colspan="2">bytes</td>
+ <td>
+ <input type="text" size="8" name="serverbufsz" id="serverbufsz"
+ value="{buffersize}"
+ onchange="goto('/server/buffersize/', 'serverbufsz');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>DOS Protection</td>
+ <td>Max. connections </td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/dosprotection/maxconnections = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ maximum value (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="setservermaxcon" class="input"
+ id="setservermaxcon" value="{dosprotection/maxconnections}"
+ onchange="goto('/server/maxconnections/', 'setservermaxcon');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>Sample duration</td>
+ <td>sec</td>
+ <td>
+ <input type="text" size="8" name="timeinterval" class="input"
+ id="timeinterval" value="{dosprotection/timeinterval}"
+ onchange="goto('/server/timeinterval/', 'timeinterval');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Hard max connection rate</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/dosprotection/hardmaxconnrate = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sessions per sample (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="hardmaxconnrate" class="input"
+ id="hardmaxconnrate" value="{dosprotection/hardmaxconnrate}"
+ onchange="goto('/server/hardmaxconnrate/', 'hardmaxconnrate');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Soft max connection rate</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/dosprotection/softmaxconnrate = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sessions per sample (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="softmaxconnrate" class="input"
+ id="softmaxconnrate" value="{dosprotection/softmaxconnrate}"
+ onchange="goto('/server/softmaxconnrate/', 'softmaxconnrate');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Defer time</td>
+ <td>in microsec, 1.000.000 = 1 sec</td>
+ <td>
+ <input type="text" size="8" name="defertime" class="input"
+ id="defertime" value="{dosprotection/defertime}"
+ onchange="goto('/server/defertime/', 'defertime');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>Hard excess signal program</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="hardexcess" class="input"
+ id="hardexcess" value="{dosprotection/hardmaxconnexcess}"
+ onchange="goto('/server/hardmaxconnexcess/', 'hardexcess');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>Soft excess signal program</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="softexcess" class="input"
+ id="softexcess" value="{dosprotection/softmaxconnexcess}"
+ onchange="goto('/server/softmaxconnexcess/', 'softexcess');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td>Access Control Lists</td>
+ <td>New allow-from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="addallowfrom" class="input"
+ id="addallowfrom"
+ onchange="goto('/server/addallowfrom/', 'addallowfrom');"/>
+ </td>
+ </tr>
+ <xsl:apply-templates select="/status/server/acl/allow"/>
+ <tr>
+ <td></td>
+ <td>New deny-from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="adddenyfrom" class="input"
+ id="adddenyfrom"
+ onchange="goto('/server/adddenyfrom/', 'adddenyfrom');"/>
+ </td>
+ </tr>
+ <xsl:apply-templates select="/status/server/acl/deny"/>
+
+ <xsl:if test="/status/server/type = 'http'">
+ <xsl:apply-templates select="/status/server/http"/>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template match="/status/backend">
+ <tr> <td colspan="4"><hr/></td></tr>
+ <tr>
+ <td class="header" colspan="3">
+ <a name="{nr}"/>
+ <b> Back end <xsl:value-of select="address"/> </b>
+ </td>
+ <td class="header">
+ <input type="button" value="Delete"
+ onclick="goto('/server/deletebackend/{nr}', '');"/>
+ </td>
+ </tr>
+ <tr>
+ <td><b>State</b></td>
+ <td>Health</td>
+ <td colspan="2">
+ <xsl:value-of select="live"/>,
+ <xsl:value-of select="available"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Connections</td>
+ <td colspan="2">
+ <xsl:value-of select="connections"/>
+ <xsl:if test="anticipated > 0">
+ (anticipating <xsl:value-of select="anticipated"/>)
+ </xsl:if>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Connect failures</td>
+ <td colspan="2">
+ <xsl:value-of select="connecterrors"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Served</td>
+ <td colspan="2">
+ <xsl:value-of select="bytesserved"/> bytes,
+ <xsl:value-of select="clientsserved"/> clients
+ </td>
+ </tr>
+ <tr>
+ <td><b>Options</b></td>
+ <td colspan="2">Weight</td>
+ <td>
+ <input type="text" size="8" name="setbackendweight{nr}"
+ id="setbackendweight{nr}" value="{weight}"
+ onchange="goto('/backend/{nr}/weight/', 'setbackendweight{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Max. connections</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="maxconnections = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ maximum value (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="setbackendmaxcon{nr}" class="input"
+ id="setbackendmaxcon{nr}" value="{maxconnections}"
+ onchange="goto('/backend/{nr}/maxconnections/', 'setbackendmaxcon{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Load average</td>
+ <td>
+ <input type="text" size="8" name="setloadaverage{nr}"
+ id="setloadaverage{nr}" value="{loadavg}"
+ onchange="goto('/backend/{nr}/loadavg/', 'setloadaverage{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Backend check (. to reset)</td>
+ <td>
+ <input type="text" size="8" name="backendcheck{nr}"
+ id="backendcheck{nr}" value="{backendcheck}"
+ onchange="goto('/backend/{nr}/backendcheck/', 'backendcheck{nr}');"/>
+ </td>
+ </tr>
+ <xsl:if test="/status/server/type = 'http'">
+ <tr>
+ <td></td>
+ <td>Host match</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="hostmatch = '.'">
+ any host request
+ </xsl:when>
+ <xsl:otherwise>
+ (. for any host)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="sethostmatch{nr}"
+ id="sethostmatch{nr}" value="{hostmatch}"
+ onchange="goto('/backend/{nr}/hostmatch/',
+ 'sethostmatch{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>URL match</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="urlmatch = '.'">
+ any url request
+ </xsl:when>
+ <xsl:otherwise>
+ (. for any url)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="seturlmatch{nr}"
+ id="seturlmatch{nr}" value="{urlmatch}"
+ onchange="goto('/backend/{nr}/urlmatch/',
+ 'seturlmatch{nr}');"/>
+ </td>
+ </tr>
+ </xsl:if>
+ <tr>
+ <td></td>
+ <td colspan="2">Up state</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="up = 'up'">
+ <select onchange="goto('/backend/{nr}/up/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/backend/{nr}/up/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Stop all connections</td>
+ <td>
+ <input type="button" value="Stop now"
+ onclick="goto('/backend/{nr}/stopconnections', '');"/>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="/status/server/http">
+ <tr>
+ <td>HTTP Goodies</td>
+ <td colspan="2">Add X-Forwarded-For</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="addxforwardedfor = 0">
+ <select onchange="goto('/server/addxforwardedfor/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/addxforwardedfor/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Sticky HTTP</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="stickyhttp = 0">
+ <select onchange="goto('/server/stickyhttp/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/stickyhttp/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Replace Host: headers</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="replacehostheader = 0">
+ <select onchange="goto('/server/replacehostheader/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/replacehostheader/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <xsl:apply-templates select="/status/server/http/serverheaders"/>
+</xsl:template>
+
+<xsl:template match="/status/server/acl/allow">
+ <xsl:for-each select="allowfrom">
+ <tr>
+ <td></td>
+ <td>Allow from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="allowfrom{nr}"
+ id="allowfrom{nr}" value="{mask}"
+ onchange="goto('/server/allowfrom/{nr}/', 'allowfrom{nr}');"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="/status/server/acl/deny">
+ <xsl:for-each select="denyfrom">
+ <tr>
+ <td></td>
+ <td>Deny from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="denyfrom{nr}"
+ id="denyfrom{nr}" value="{mask}"
+ onchange="goto('/server/denyfrom/{nr}/', 'denyfrom{nr}');"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="/status/server/http/serverheaders">
+ <xsl:for-each select="serverheader">
+ <tr>
+ <td></td>
+ <td>Server header</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="serverheader{nr}"
+ id="serverheader{nr}" value="{header}"
+ onchange="goto('/server/changeheader/{nr}/', 'serverheader{nr}');"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ <tr>
+ <td></td>
+ <td>New server header</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="newserverheader"
+ id="newserverheader"
+ onchange="goto('/server/newheader/', 'newserverheader');"/>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="*"/>
+
+</xsl:stylesheet>
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/etc/status-savebutton.xslt
^
|
@@ -0,0 +1,893 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="html"
+ encoding="UTF-8"
+ doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
+
+<xsl:template match="/">
+ <html>
+ <head>
+ <title>XR Status Overview</title>
+ <style type="text/css">
+ h1 {
+ font-family: Verdana,Helvetica;
+ font-size: 12pt;
+ color: blue;
+ }
+ body {
+ font-family: Verdana,Helvetica;
+ font-size: 8pt;
+ }
+ table {
+ border-collapse: collapse;
+ border-style: hidden;
+ }
+ td {
+ font-family: Verdana,Helvetica;
+ font-size: 8pt;
+ background-color: #ffff99;
+ border: 1px solid #f0f090;
+ }
+ input {
+ font-family: Verdana,Helvetica;
+ font-size: 8pt;
+ }
+ .header { background-color: #f3f399; }
+ .footer { color: gray; }
+ </style>
+ <script type="text/javascript">
+ function goto(uri, input) {
+ if (input == '')
+ document.location = uri;
+ else {
+ var el = document.getElementById(input);
+ if (el) {
+ var value = el.value;
+ if (value != "")
+ document.location = uri + encodeURIComponent(value);
+ else
+ document.location = uri;
+ }
+ }
+ }
+ function save(uri, name) {
+ var elem = document.getElementById(name);
+ var val = elem.value;
+ alert('name: ' + name + ', value: ' + val + ', uri: ' + uri);
+ if (val != "")
+ document.location = uri + encodeURIComponent(val);
+ else
+ document.location = uri;
+ }
+ </script>
+ </head>
+ <body>
+ <h1>XR Status Overview</h1>
+ <hr/>
+ <xsl:apply-templates/>
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template match="/status">
+ <table>
+ <tr>
+ <td valign="top">
+ <!-- This is the left hand detailed status view -->
+ <table>
+ <tr>
+ <td colspan="4"><b>Detailed Status</b></td>
+ </tr>
+ <tr>
+ <td colspan="4"><hr/></td>
+ </tr>
+ <xsl:apply-templates select="/status/server"/>
+ <xsl:apply-templates select="/status/backend"/>
+
+ <tr> <td colspan="4"><hr/></td></tr>
+ <tr>
+ <td class="header" colspan="2">
+ <b>Add back end ip:port</b>
+ </td>
+ <td class="header" colspan="2" align="right">
+ <input type="text" size="30" name="addbackend" id="addbackend"
+ onchange="goto('/server/addbackend/', 'addbackend');"/>
+ </td>
+ </tr>
+ <tr> <td colspan="4"><hr/></td></tr>
+
+ </table>
+ <xsl:apply-templates select="/status/id"/>
+ </td>
+ <td valign="top">
+ <!-- This is the right-hand overview -->
+ <table width="100%">
+ <tr>
+ <td colspan="2"><b>Quick Overview</b></td>
+ </tr>
+ <tr>
+ <td colspan="2"><hr/></td>
+ </tr>
+ <xsl:for-each select="/status/backend">
+ <tr>
+ <td>
+ <b>Back end
+ <a href="#{nr}"><xsl:value-of select="address"/></a>
+ </b>
+ </td>
+ <td>
+ <xsl:value-of select="up"/>,
+ <xsl:value-of select="live"/>,
+ <xsl:value-of select="available"/>,
+ <xsl:value-of select="connections"/> connections
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <!-- This is the activity overview -->
+ <table width="100%">
+ <tr>
+ <td colspan="5"><hr/></td>
+ </tr>
+ <tr>
+ <td colspan="5"><b>Activity</b></td>
+ </tr>
+ <tr>
+ <td colspan="5"><hr/></td>
+ </tr>
+ <tr>
+ <td colspan="3">Number of threads</td>
+ <td><xsl:value-of select="/status/activity/threadcount"/></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="3">Used file descriptors (approx.)</td>
+ <td><xsl:value-of select="/status/activity/openfiles"/></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="3">File descriptor limit</td>
+ <td><xsl:value-of select="/status/activity/maxopenfiles"/></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td><b>Thread</b></td>
+ <td><b>Description</b></td>
+ <td><b>Back end</b></td>
+ <td><b>Duration</b></td>
+ <td></td>
+ </tr>
+ <xsl:apply-templates select="/status/activity/threadlist/thread">
+ <xsl:sort select="duration" data-type="number"/>
+ </xsl:apply-templates>
+ </table>
+ </td>
+ </tr>
+ </table>
+</xsl:template>
+
+<xsl:template match="/status/activity/threadlist/thread">
+ <tr>
+ <td><xsl:value-of select="id"/></td>
+ <td><xsl:value-of select="description"/></td>
+ <xsl:choose>
+ <xsl:when test="backend = -1">
+ <td></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td><xsl:value-of select="address"/></td>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td><xsl:value-of select="duration"/></td>
+ <xsl:choose>
+ <xsl:when test="backend = -1">
+ <td></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td><input type="button" value="Kill"
+ onclick="goto('/thread/kill/{id}', '');"/>
+ </td>
+ </xsl:otherwise>
+ </xsl:choose>
+ </tr>
+</xsl:template>
+
+<xsl:template match="/status/id">
+ <i>
+ Powered by Crossroads V<xsl:value-of select="version"/>.
+ Visit
+ <a href="{distsite}"
+ target="_blank"><xsl:value-of select="distsite"/></a>
+ for more info.
+ </i>
+</xsl:template>
+
+<xsl:template match="/status/server">
+ <tr>
+ <td class="header" colspan="3">
+ <b>Server <xsl:value-of select="address"/> </b>
+ </td>
+ <td class="header" align="right">
+ <input type="button" onclick="goto('/', '');" value="Refresh"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Status</td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="terminating = 0">
+ Accepting connections,
+ <xsl:value-of select="connections"/> concurrent client(s),
+ </xsl:when>
+ <xsl:otherwise>
+ <font color="red">
+ Terminating, still serving
+ <xsl:value-of select="connections"/> connections,
+ </font>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="backends"/> defined back ends
+ </td>
+ </tr>
+ <tr>
+ <td>Dispatch mode</td>
+ <td colspan="3"> <xsl:value-of select="dispatchmode"/> </td>
+ </tr>
+ <tr>
+ <td>Type</td>
+ <td colspan="2"></td>
+ <td align="right">
+ <xsl:choose>
+ <xsl:when test="type = 'http'">
+ <select name="servertype" id="servertype">
+ <option value="tcp">tcp</option>
+ <option value="http" selected="1">http</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select name="servertype" id="servertype">
+ <option value="tcp" selected="1">tcp</option>
+ <option value="http">http</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ <input type="button" value="Save"
+ onclick="save('/server/type/', 'servertype');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Checks</td>
+ <td>Wakeup interval</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="checks/wakeupinterval = 0">
+ off
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="wakeupinterval"
+ id="wakeupinterval" value="{checks/wakeupinterval}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/wakeupinterval/', 'wakeupinterval');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Checkup interval</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="checks/checkupinterval = 0">
+ off
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="checkupinterval"
+ id="checkupinterval" value="{checks/checkupinterval}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/checkupinterval/', 'checkupinterval');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Timeouts</td>
+ <td>Client read</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="clientreadtimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td align="right">
+ <input type="text" size="8" name="clientreadtimeout"
+ id="clientreadtimeout" value="{clientreadtimeout}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/clientreadtimeout/', 'clientreadtimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Client write</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="clientwritetimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td align="right">
+ <input type="text" size="8" name="clientwritetimeout"
+ id="clientwritetimeout" value="{clientwritetimeout}"/>
+ <input type="button" value="Save"
+ onchange="save('/server/clientwritetimeout/', 'clientwritetimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Back end read</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="backendreadtimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td align="right">
+ <input type="text" size="8" name="backendreadtimeout"
+ id="backendreadtimeout" value="{backendreadtimeout}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/backendreadtimeout/', 'backendreadtimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Back end write</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="backendwritetimeout = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="backendwritetimeout"
+ id="backendwritetimeout" value="{backendwritetimeout}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/backendwritetimeout/', 'backendwritetimeout');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>DNS cache validity</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="dnscachetimeout = 0">
+ unused
+ </xsl:when>
+ <xsl:otherwise>
+ sec
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="dnscachetimeout"
+ id="dnscachetimeout" value="{dnscachetimeout}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/dnscachetimeout/', 'dnscachetimeout');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td>Fast sockets closing</td>
+ <td colspan="2">eliminates TIME_WAIT state</td>
+ <td align="right">
+ <xsl:choose>
+ <xsl:when test="closesocketsfast = 0">
+ <select name="closesocketsfast" id="closesocketsfast">
+ <option value="on">yes</option>
+ <option value="off" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select name="closesocketsfast" id="closesocketsfast">
+ <option value="on" selected="1">yes</option>
+ <option value="off">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ <input type="button" value="Save"
+ onclick="save('/server/closesocketsfast/', 'closesocketsfast');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Debugging</td>
+ <td colspan="2">Verbose logging</td>
+ <td align="right">
+ <xsl:choose>
+ <xsl:when test="debugging/verbose = 0">
+ <select name="serververbose" id="serververbose">
+ <option value="on">yes</option>
+ <option value="off" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select name="serververbose" id="serververbose">
+ <option value="on" selected="1">yes</option>
+ <option value="off">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ <input type="button" value="Save"
+ onclick="save('/server/verbose/', 'serververbose');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Debug logging</td>
+ <td align="right">
+ <xsl:choose>
+ <xsl:when test="debugging/debug = 0">
+ <select name="serverdebug" id="serverdebug">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select name="serverdebug" id="serverdebug">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ <input type="button" value="Save"
+ onclick="save('/server/debug/', 'serververbose');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Traffic log directory</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="logtrafficdir" id="logtrafficdir"
+ value="{debugging/logtrafficdir}"/>
+ <input type="button" value="Save"
+ onclick="goto('/server/logtrafficdir/', 'logtrafficdir');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Activity scripts</td>
+ <td>Onstart command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onstart" id="onstart"
+ value="{onstart}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/onstart/', 'onstart');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Onend command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onend" id="onend"
+ value="{onend}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/onend/', 'onend');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Onfail command</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="onfail" id="onfail"
+ value="{onfail}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/onfail/', 'onfail');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Network buffer size</td>
+ <td colspan="2">bytes</td>
+ <td>
+ <input type="text" size="8" name="serverbufsz" id="serverbufsz"
+ value="{buffersize}"/>
+ <input type="button" value="Save"
+ onclick="save('/server/buffersize/', 'serverbufsz');"/>
+ </td>
+ </tr>
+ <tr>
+ <td>DOS Protection</td>
+ <td>Max. connections </td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/dosprotection/maxconnections = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ maximum value (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="setservermaxcon" class="input"
+ id="setservermaxcon" value="{dosprotection/maxconnections}"
+ onchange="goto('/server/maxconnections/', 'setservermaxcon');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>Sample duration</td>
+ <td>sec</td>
+ <td>
+ <input type="text" size="8" name="timeinterval" class="input"
+ id="timeinterval" value="{dosprotection/timeinterval}"
+ onchange="goto('/server/timeinterval/', 'timeinterval');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Hard max connection rate</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/dosprotection/hardmaxconnrate = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sessions per sample (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="hardmaxconnrate" class="input"
+ id="hardmaxconnrate" value="{dosprotection/hardmaxconnrate}"
+ onchange="goto('/server/hardmaxconnrate/', 'hardmaxconnrate');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Soft max connection rate</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/dosprotection/softmaxconnrate = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ sessions per sample (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="softmaxconnrate" class="input"
+ id="softmaxconnrate" value="{dosprotection/softmaxconnrate}"
+ onchange="goto('/server/softmaxconnrate/', 'softmaxconnrate');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Defer time</td>
+ <td>in microsec, 1.000.000 = 1 sec</td>
+ <td>
+ <input type="text" size="8" name="defertime" class="input"
+ id="defertime" value="{dosprotection/defertime}"
+ onchange="goto('/server/defertime/', 'defertime');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>Hard excess signal program</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="hardexcess" class="input"
+ id="hardexcess" value="{dosprotection/hardmaxconnexcess}"
+ onchange="goto('/server/hardmaxconnexcess/', 'hardexcess');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>Soft excess signal program</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="softexcess" class="input"
+ id="softexcess" value="{dosprotection/softmaxconnexcess}"
+ onchange="goto('/server/softmaxconnexcess/', 'softexcess');"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td>Access Control Lists</td>
+ <td>New allow-from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="addallowfrom" class="input"
+ id="addallowfrom"
+ onchange="goto('/server/addallowfrom/', 'addallowfrom');"/>
+ </td>
+ </tr>
+ <xsl:apply-templates select="/status/server/acl/allow"/>
+ <tr>
+ <td></td>
+ <td>New deny-from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="adddenyfrom" class="input"
+ id="adddenyfrom"
+ onchange="goto('/server/adddenyfrom/', 'adddenyfrom');"/>
+ </td>
+ </tr>
+ <xsl:apply-templates select="/status/server/acl/deny"/>
+
+ <xsl:if test="/status/server/type = 'http'">
+ <xsl:apply-templates select="/status/server/http"/>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template match="/status/backend">
+ <tr> <td colspan="4"><hr/></td></tr>
+ <tr>
+ <td class="header" colspan="3">
+ <a name="{nr}"/>
+ <b> Back end <xsl:value-of select="address"/> </b>
+ </td>
+ <td class="header">
+ <input type="button" value="Delete"
+ onclick="goto('/server/deletebackend/{nr}', '');"/>
+ </td>
+ </tr>
+ <tr>
+ <td><b>State</b></td>
+ <td>Health</td>
+ <td colspan="2">
+ <xsl:value-of select="live"/>,
+ <xsl:value-of select="available"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Connections</td>
+ <td colspan="2">
+ <xsl:value-of select="connections"/>
+ <xsl:if test="anticipated > 0">
+ (anticipating <xsl:value-of select="anticipated"/>)
+ </xsl:if>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Served</td>
+ <td colspan="2">
+ <xsl:value-of select="bytesserved"/> bytes,
+ <xsl:value-of select="clientsserved"/> clients
+ </td>
+ </tr>
+ <tr>
+ <td><b>Options</b></td>
+ <td colspan="2">Weight</td>
+ <td>
+ <input type="text" size="8" name="setbackendweight{nr}"
+ id="setbackendweight{nr}" value="{weight}"
+ onchange="goto('/backend/{nr}/weight/', 'setbackendweight{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>Max. connections</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="maxconnections = 0">
+ unlimited
+ </xsl:when>
+ <xsl:otherwise>
+ maximum value (0 for unlimited)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="setbackendmaxcon{nr}" class="input"
+ id="setbackendmaxcon{nr}" value="{maxconnections}"
+ onchange="goto('/backend/{nr}/maxconnections/', 'setbackendmaxcon{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Load average</td>
+ <td>
+ <input type="text" size="8" name="setloadaverage{nr}"
+ id="setloadaverage{nr}" value="{loadavg}"
+ onchange="goto('/backend/{nr}/loadavg/', 'setloadaverage{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Backend check (. to reset)</td>
+ <td>
+ <input type="text" size="8" name="backendcheck{nr}"
+ id="backendcheck{nr}" value="{backendcheck}"
+ onchange="goto('/backend/{nr}/backendcheck/', 'backendcheck{nr}');"/>
+ </td>
+ </tr>
+ <xsl:if test="/status/server/type = 'http'">
+ <tr>
+ <td></td>
+ <td>Host match</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="hostmatch = '.'">
+ any host request
+ </xsl:when>
+ <xsl:otherwise>
+ (. for any host)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="sethostmatch{nr}"
+ id="sethostmatch{nr}" value="{hostmatch}"
+ onchange="goto('/backend/{nr}/hostmatch/', 'sethostmatch{nr}');"/>
+ </td>
+ </tr>
+ </xsl:if>
+ <tr>
+ <td></td>
+ <td colspan="2">Up state</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="up = 'up'">
+ <select onchange="goto('/backend/{nr}/up/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/backend/{nr}/up/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Stop all connections</td>
+ <td>
+ <input type="button" value="Stop now"
+ onclick="goto('/backend/{nr}/stopconnections', '');"/>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="/status/server/http">
+ <tr>
+ <td>HTTP Goodies</td>
+ <td colspan="2">Add X-Forwarded-For</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="addxforwardedfor = 0">
+ <select onchange="goto('/server/addxforwardedfor/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/addxforwardedfor/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Sticky HTTP</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="stickyhttp = 0">
+ <select onchange="goto('/server/stickyhttp/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/stickyhttp/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td colspan="2">Replace Host: headers</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="replacehostheader = 0">
+ <select onchange="goto('/server/replacehostheader/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
+ </xsl:when>
+ <xsl:otherwise>
+ <select onchange="goto('/server/replacehostheader/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <xsl:apply-templates select="/status/server/http/serverheaders"/>
+</xsl:template>
+
+<xsl:template match="/status/server/acl/allow">
+ <xsl:for-each select="allowfrom">
+ <tr>
+ <td></td>
+ <td>Allow from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="allowfrom{nr}"
+ id="allowfrom{nr}" value="{mask}"
+ onchange="goto('/server/allowfrom/{nr}/', 'allowfrom{nr}');"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="/status/server/acl/deny">
+ <xsl:for-each select="denyfrom">
+ <tr>
+ <td></td>
+ <td>Deny from</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="denyfrom{nr}"
+ id="denyfrom{nr}" value="{mask}"
+ onchange="goto('/server/denyfrom/{nr}/', 'denyfrom{nr}');"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="/status/server/http/serverheaders">
+ <xsl:for-each select="serverheader">
+ <tr>
+ <td></td>
+ <td>Server header</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="serverheader{nr}"
+ id="serverheader{nr}" value="{header}"
+ onchange="goto('/server/changeheader/{nr}/', 'serverheader{nr}');"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ <tr>
+ <td></td>
+ <td>New server header</td>
+ <td colspan="2" align="right">
+ <input type="text" size="30" name="newserverheader"
+ id="newserverheader"
+ onchange="goto('/server/newheader/', 'newserverheader');"/>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="*"/>
+
+</xsl:stylesheet>
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/etc/status.xslt
^
|
+(symlink to status-nosavebutton.xslt)
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/etc/usage.txt
^
|
@@ -3,128 +3,179 @@
Usage: xr [flags], where the flags may be the following (long versions
may not exist on your platform):
- -a MASK, --allow-from MASK
+ --add-server-header HDR, -H HDR
+ Inserts HDR into back end bound HTTP messages. The header value is
+ appended when a pre-existing header is present.
+ --add-x-forwarded-for, -x
+ Adds X-Forwarded-For with external IP address to back end streams in
+ HTTP messages.
+ --add-xr-version, -X
+ Adds an XR version header to client and back end streams in HTTP
+ messages.
+ --allow-from MASK, -a MASH
Allow only clients that match MASK. MASK is e.g. 192.168.255.255, which
would allow the class B network 192.168.*.*
- -A MASK, --deny-from MASK
- Deny clients that match MASK.
- -b ADDRESS:PORT[:MAX[:WEIGHT]], --backend ADDRESS:PORT[:MAX[:WEIGHT]]
+ --backend ADDRESS:PORT[:MAX[:WEIGHT]], -b ADDRESS:PORT[:MAX[:WEIGHT]]
Specifies a back end, use multiple -b... to specify several back ends.
- At least one -b... must be given. Specifier MAX is optional: when given,
- defines the maximum connections for the back end. WEIGHT is optional:
- when given, specifies the weight (bigger means better server, default 1).
- -B SIZE, --buffer-size SIZE
+ At least one backend must be given. Specifier MAX is optional:
+ when given, defines the maximum connections for the back end.
+ WEIGHT is optional: when given, specifies the weight (bigger
+ means better server, default 1)
+ --backend-check METHOD, -g METHOD
+ Defines how back ends are checked. This flag must be specified
+ PRIOR to defining back ends with -b... The checker will then
+ apply to all next back ends. Alternatives are:
+ connect:IP:PORT - successful TCP connects at IP:PORT indicate
+ that the back end is alive. When IP is not stated, the back
+ end's IP is assumed.
+ get:IP:PORT/URI - A HTTP GET is sent to IP:PORT/URI. When an
+ HTTP status 200 is seen, the back end is assumed alive. When
+ /URI is not given, then "/" is assumed.
+ external:PROGRAM - The PROGRAM is called with the arguments
+ "IP:PORT", availability as "available" or "unavailable", and
+ the number of connections. The program must echo 0 to indicate
+ that the back end is alive.
+ The default behavior is a TCP connect, to the back end's IP, at
+ the back end's port. Use "--backend-check connect::" to reset
+ previous flags to the default.
+ --backend-timeout SEC, -t SEC
+ Defines network read timeouts for back ends, default 30 sec. Use 0 to
+ prevent timing out. Use "--backend-timeout RSEC:WSEC" to
+ specify separate timeouts for reads and writes, default 30:3.
+ --buffer-size SIZE, -B size
Sets the network buffer size, default is 2048 (in bytes)
- -C, --close-sockets-fast
- Sockets are closed faster to avoid TIME_WAIT states.
- -c SEC, --checkup-interval SEC
+ --checkup-interval SEC, -c SEC
Defines the back end checking period. Each SEC seconds, every back end
is checked whether it is alive. Default is 0 (off).
- -D, --debug
- Sets debugging on, more verbosity on top of -v
- -d METHOD, --dispatch-mode METHOD
+ --client-timeout SEC, -T SEC
+ Defines network read timeouts for clients, default 30 sec. Use 0 to
+ prevent timing out. Use "--client-timeout RSEC:WSEC" to specify
+ separate timeouts for reads and writes, default 30:5
+ --close-sockets-fast, -C
+ Sockets are closed faster to avoid TIME_WAIT states.
+ --debug, -D
+ Sets debugging on, more verbosity on top of --verbose
+ --defer-time USEC, -u USEC
+ If a connection is going to be deferred due to hitting the "soft" rate
+ (see --soft-maxconnrate), then this option sets how long the deferral
+ will last, in microseconds. Default is 500000 (0.5 seconds).
+ --deny-from MASK, -A mask
+ Deny clients that match MASK.
+ --dispatch-mode METHOD, -d METHOD
Defines how to dispatch over back ends, the method may be:
- f, first-available - first live back end gets all traffic
- e:EXT, external:EXT - external program EXT is queried
- h, strict-hashed-ip - client IP is hashed to determine a back
- end, client is denied when back end is down
- H, lax-hashed-ip - client IP is hashed, fallback to least-
- connections when target back end is down
- l, least-connections - back end with least connections is taken
- r, round-robin - back ends take turns
- L, weighted-load - randomly picks from back end with favor
- given to backends with lower load average.
- (NOTE: load average must be updated by the
- backend, e.g. using the web interface).
+ f, first-available - first live back end gets all traffic
+ e:EXT, external:EXT - external program EXT is queried
+ h, strict-hashed-ip - client IP is hashed to determine a back end,
+ the client is denied when back end is down.
+ H, lax-hashed-ip - client IP is hashed, fallback to least-connections
+ when target back end is down
+ l, least-connections - back end with least connections is taken
+ r, round-robin - back ends take turns
+ L, weighted-load - randomly picks from back end with favor given
+ to backends with lower load average. (NOTE: load average must
+ be updated by the backend, e.g. using the web interface).
s:SEC, strict-stored-ip:SEC - if client connected before within SEC
- seconds, then the same backend is used.
- Client is denied if that backend is down.
- Else a new is found by least-connections.
- S:SEC, lax-stored-ip:SEC - same as strict-stored-ip, but falls back
- to least-connections when a previously
- used back end is down
+ seconds, then the same backend is used. Client is denied if
+ that backend is down. Else a new is found by least-connections.
+ S:SEC, lax-stored-ip:SEC - same as strict-stored-ip, but falls back
+ to least-connections when a previously used back end is down.
Default method is l (least-connections). When external mode is selected,
program EXT is started with arguments <nbackends> <b0> <b0-availability>
<b0-connections> (b0 repeated for all back ends). Here <b0> is the back
end definition, eg. "10.1.1.1:80"; <b0-availablility> is "available" or
"unavailable", <b0-connections> is the nr. of connections. The program
must reply with a back end number (0..max) on stdout.
- -f, --foreground
+ --dns-cache-timeout SEC, -F SEC
+ DNS results for back end hostnames are cached for SEC seconds.
+ The default is 3600 (1 hour). Use 0 to suppress.
+ --foreground, -f
Suppresses forking/threading, only for debugging. Also suppresses
- wakeups (-w), checkups (-c) and the webinterface (-W).
- -h, -?, --help
+ wakeups (--wakeup-interval), checkups (--checkup-interval) and
+ the webinterface (--web-interface).
+ --hard-maxconn-excess PROGRAM, -E PROGRAM
+ When a client exceeds the hard maxconnection rate, PROGRAM is
+ invoked with the client's IP as argument. The program may e.g.
+ invoke iptables to block the offending IP.
+ --hard-maxconnrate MAXCONS, -R MAXCONS
+ Sets the "HARD" maximum average number of connections per IP allowed
+ within a given time period (see -U, --time-interval). If a
+ particular IP exceeds this number, then their connection is
+ immediately closed. Default is 0 (disabled). If both the
+ "soft" and "hard" rates are set, and the "hard" rate is lower
+ than the "soft" rate, then only the "hard" rate is obeyed.
+ --help, -?, -h
This text.
- -H HDR, --add-server-header HDR
- Inserts HDR into back end bound HTTP messages.
- -l DIR, --log-traffic-dir DIR
+ --host-match HOST, -M HOST
+ Subsequently stated backends only apply when clients request a
+ matching host. Only available when the server is in http mode.
+ --log-traffic-dir DIR, -l DIR
Log passing traffic with dumps in DIR. Only for debugging, slows
down the balancer.
- -m MAX, --max-connections MAX
+ --max-connections MAX, -m MAX
Sets the maximum number of connections to the balancer. Default is 0,
no maximum.
- -M HOST, --host-match HOST
- Subsequently stated backends only apply when clients request a
- matching host. Only available when the server is in http mode.
- -n, --tryout
- Validates all flags and stops; does not start the balancer.
- -P, --prefix-timestamp
+ --onend CMD, -Z CMD
+ Runs CMD after successful termination of a client. For the
+ arguments of CMD see -y.
+ --onfail CMD, -y CMD
+ Runs CMD when XR fails to connect to a back end. The arguments
+ to the command are: the client's IP address, and the back end address.
+ --onstart CMD, -z CMD
+ Runs CMD just before letting a back end handle a client's connection.
+ For the arguments of CMD see -y.
+ --pidfile FILE, -p FILE
+ FILE is written with the process id of XR upon startup, and
+ removed upon exit.
+ --prefix-timestamp, -P
Messages (verbose, debug, error etc.) are prefixed with a time stamp.
- -p FILE, --pidfile FILE
- FILE is written with the PID of XR upon startup
- -Q REQUESTS, --quit-after REQUESTS
+ --quit-after REQUESTS, -Q REQUESTS
Stops the balancer after REQUESTS hits. For debugging / loadtesting.
- -r, --soft-maxconnrate MAXCONS
+ --remove-reservations, -G
+ In stored-ip algorithms, outstanding reservations for expected
+ clients are removed when no more back ends are available.
+ --replace-host-header HDR, -I HDR
+ Inserts "Host: <backend>" into back end bound HTTP messages.
+ Pre-existing Host headers are overwritten. The value of <backend> is
+ the server name as in the setting of --backend (-b).
+ --server TYPE:IPADDRESS:PORT, -S TYPE:IPADDRESS:PORT
+ Specifies the server. TYPE is tcp or http or udp. IPADDRESS is the IP
+ address to listen to. PORT defines the TCP port to listen; when port
+ is 0, XR will listen to stdin (inetd-mode, not available for udp).
+ Default: tcp:0:10000 (TCP balancing, on all interfaces, via port 10000).
+ --soft-maxconn-excess PROGRAM, -e PROGRAM
+ When a client exceeds the soft maxconnection rate, PROGRAM is
+ invoked with the client's IP as argument.
+ --soft-maxconnrate MAXCONS, -r MAXCONS
Sets the "SOFT" maximum average number of connections per IP allowed
within a given time period (see -U, --time-interval). If a
particular IP exceeds this number, then their connection is
deferred (see -u, --defer-time). Default is 0 (disabled).
- -R, --hard-maxconnrate MAXCONS
- Sets the "HARD" maximum average number of connections per IP allowed
- within a given time period (see -U, --time-interval). If a
- particular IP exceeds this number, then their connection is
- immediately closed. Default is 0 (disabled). If both the
- "soft" and "hard" rates are set, and the "hard" rate is lower
- than the "soft" rate, then only the "hard" rate is obeyed.
- -S, --sticky-http
+ --sticky-http, -S
Enables sticky HTTP sessions by injecting XRTarget cookies into HTTP
- streams. Only effective with -s http:....
- -s TYPE:IPADDRESS:PORT, --server TYPE:IPADDRESS:PORT
- Specifies the server. TYPE is tcp or http. IPADDRESS is the IP address
- to listen to. PORT defines the TCP port to listen (for type tcp or
- http); when port is 0, XR will listen to stdin (inetd-mode).
- Default: tcp:0:10000 (TCP balancing, on all interfaces, via port 10000).
- -t SEC, --backend-timeout SEC
- Defines network timeouts for back ends, default 3 sec. Use 0 to
- prevent timing out.
- -T SEC, --client-timeout SEC
- Defines network timeouts for clients, default 30 sec. Use 0 to
- prevent timing out.
- -u, --defer-time USEC
- If a connection is going to be deferred due to hitting the "soft" rate
- (see --soft-maxconnrate), then this option sets how long the deferral
- will last, in microseconds. Default is 500000 (0.5 seconds).
- -U, --time-interval SEC
+ streams. Only effective with "--server http:...."
+ --time-interval SEC, -U SEC
If either --soft-maxconnrate or --hard-maxconnrate is specified, this
option allows you to specify the time period to which those numbers of
- connections apply. For example, "-r 200 -U 60" would trigger the "soft"
- limit on any IP attempting more than 200 connections in any 60 second
- period. Default is 1 (second).
- -v, --verbose
+ connections apply. For example,
+ "--soft-maxconnrate 200 --time-interval 60" would trigger the
+ "soft" limit on any IP attempting more than 200 connections in
+ any 60 second period. Default is 1 (second).
+ --tryout, -n
+ Validates all flags and stops; does not start the balancer.
+ --url-match URL, -j URL
+ Subsequently stated backends only apply when clients request a
+ matching URL. Only available when the server is in http mode.
+ --web-interface IP:PORT, -W IP:PORT
+ Starts a web interface on specified IP address and port.
+ --verbose, -v
Increases verbosity, default is silent operation.
- -V, --version
+ --version, -V
Shows the version info, and author/maintainer contacts (for reporting
bugs).
- -w SEC, --wakeup-interval SEC
+ --wakeup-interval SEC, -w SEC
Defines wakeup period (rechecking) in seconds, of unavailable back
ends. Default is 5. Use -w0 to suppress.
- -W IP:PORT, --web-interface IP:PORT
- Starts a web interface on specified IP address and port.
- -X, --add-xr-version
- Adds an XR version header to client and back end streams in HTTP
- messages.
- -x, --add-x-forwarded-for
- Adds X-Forwarded-For with external IP address to back end streams in
- HTTP messages.
+
XR's messages are sent to stderr. Invoke XR daemons using something like
-"xr -b ... [other flags] 2>&1 | logger &", or use xrctl.
+"xr --backend ... [other flags] 2>&1 | logger &", or use xrctl.
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/fdset/fdset
^
|
@@ -11,29 +11,27 @@
public:
Fdset(int t);
- int timeout() const {
- return (tsec);
- }
- void timeout (int t) {
- tsec = t;
- }
- void add (int fd) {
- set.push_back(fd);
- }
- unsigned size() const {
- return (set.size());
- }
- int fd (unsigned index) const {
- return (set[index]);
- }
-
- int readable() const;
- int writeable() const;
- int readwriteable() const;
+ int timeout() const { return tsec; }
+ void timeout (int t) { tsec = t; }
+
+ void add (int fd) { set.push_back(fd); }
+
+ unsigned size() const { return set.size(); }
+
+ 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); }
+
+ bool readable(int fd) { return FD_ISSET(fd, &readset); }
+ bool writeable(int fd) { return FD_ISSET(fd, &writeset); }
private:
int tsec;
+ fd_set readset, writeset, exceptset;
vector<int> set;
};
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/fdset/fdset1.cc
^
|
@@ -1,4 +1,7 @@
#include "fdset"
Fdset::Fdset (int t) : tsec(t), set() {
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/fdset/readable.cc
^
|
@@ -1,56 +0,0 @@
-#include "fdset"
-
-int Fdset::readable() const {
- PROFILE("Fdset::readable");
-
- fd_set readset, exceptset;
- struct timeval tv, *tvp;
-
- // No fd's? Nothing is readable.
- if (set.size() < 1)
- return (-1);
-
- // Prepare select sets.
- FD_ZERO (&readset);
- FD_ZERO (&exceptset);
- for (unsigned i = 0; i < set.size(); i++) {
- FD_SET (set[i], &readset);
- FD_SET (set[i], &exceptset);
- }
-
- // Prepare timout specifier.
- if (tsec) {
- tv.tv_sec = tsec;
- tv.tv_usec = 0;
- tvp = &tv;
- } else
- tvp = 0;
-
- // Run the select. Signal interrupts are returned as -1, so that
- // the caller can handle them gracefully.
- if (select (FD_SETSIZE, &readset, 0, &exceptset, tvp) < 0) {
- if (errno != EINTR)
- throw static_cast<Error>
- ("Select failure: failed to wait for readable state: ") +
- strerror(errno);
- else
- return (-1);
- }
-
- // Check for exceptions.
- for (unsigned i = 0; i < set.size(); i++)
- if (FD_ISSET (set[i], &exceptset))
- throw static_cast<Error>("Exception on fd/socket ") + set[i];
-
- // Check what's readable.
- for (unsigned i = 0; i < set.size(); i++)
- if (FD_ISSET (set[i], &readset)) {
- debugmsg (Mstr("Fd ") + set[i] + " has become readable\n");
- return (set[i]);
- }
-
- // Nothing..
- debugmsg ("No readable fd's at this time\n");
- return (-1);
-}
-
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/fdset/readwriteable.cc
^
|
@@ -1,54 +0,0 @@
-#include "fdset"
-
-int Fdset::readwriteable() const {
- PROFILE("Fdset::readwriteable");
-
- fd_set readset, writeset, exceptset;
- struct timeval tv, *tvp;
-
- // No fd's? Nothing is readable.
- if (set.size() < 1)
- return (-1);
-
- // Prepare select sets.
- FD_ZERO (&readset);
- FD_ZERO (&writeset);
- FD_ZERO (&exceptset);
- for (unsigned i = 0; i < set.size(); i++) {
- FD_SET (set[i], &readset);
- FD_SET (set[i], &writeset);
- FD_SET (set[i], &exceptset);
- }
-
- // Prepare timout specifier.
- if (tsec) {
- tv.tv_sec = tsec;
- tv.tv_usec = 0;
- tvp = &tv;
- } else
- tvp = 0;
-
- // Run the select.
- if (select (FD_SETSIZE, &readset, &writeset, &exceptset, tvp) < 0) {
- if (errno != EINTR)
- throw static_cast<Error>
- ("Select failure: failed to wait for read/writeablestate: ")
- + strerror(errno);
- else
- return (-1);
- }
-
- // Check for exceptions.
- for (unsigned i = 0; i < set.size(); i++)
- if (FD_ISSET (set[i], &exceptset))
- throw static_cast<Error>("Exception on fd/socket ") + set[i];
-
- // Check what's active.
- for (unsigned i = 0; i < set.size(); i++)
- if (FD_ISSET (set[i], &readset) && FD_ISSET (set[i], &writeset))
- return (set[i]);
-
- // Nothing..
- return (-1);
-}
-
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/fdset/wait.cc
^
|
@@ -0,0 +1,79 @@
+#include "fdset"
+
+void Fdset::wait(bool wait_read, bool wait_write) {
+ PROFILE("Fdset::wait");
+
+ struct timeval tv, *tvp;
+
+ // No fd's? Nothing to wait for.
+ if (set.size() < 1)
+ throw Error("Internal jam in Fdset::wait(): no fd's to wait for");
+
+ // Prepare select sets.
+ FD_ZERO (&readset);
+ FD_ZERO (&writeset);
+ FD_ZERO (&exceptset);
+ for (unsigned i = 0; i < set.size(); i++) {
+ FD_SET (set[i], &readset);
+ FD_SET (set[i], &writeset);
+ FD_SET (set[i], &exceptset);
+ debugmsg(Mstr("About to wait for fd ") + Mstr(set[i]) + "\n");
+ }
+
+ // Prepare timout specifier.
+ if (tsec) {
+ tv.tv_sec = tsec;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ debugmsg(Mstr("Waiting limitation: ") + Mstr(tsec) + "\n");
+ } else {
+ tvp = 0;
+ debugmsg(Mstr("No waiting limitation\n"));
+ }
+
+ // Run the select.
+ if (select (FD_SETSIZE,
+ wait_read ? &readset : 0,
+ wait_write ? &writeset : 0,
+ &exceptset, tvp) < 0) {
+ if (errno != EINTR)
+ throw Error(string("Select failure: failed to wait: ") +
+ strerror(errno));
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+ return;
+ }
+
+ // Check for exceptions.
+ for (unsigned i = 0; i < set.size(); i++)
+ if (FD_ISSET (set[i], &exceptset)) {
+ ostringstream o;
+ o << "Exception on fd/socket " << int(set[i]);
+ throw Error(o.str());
+ }
+
+ // More debugging: What has become readable, what has become
+ // writeable, also state if no change was seen
+ if (config.debug()) {
+ bool statechanged = false;
+ for (unsigned int i = 0; i < FD_SETSIZE; i++) {
+ if (FD_ISSET(i, &readset)) {
+ _debugmsg(Mstr("Fd ") + Mstr(i) + " is readable\n");
+ statechanged = true;
+ }
+ if (FD_ISSET(i, &writeset)) {
+ _debugmsg(Mstr("Fd ") + Mstr(i) + " is writeable\n");
+ statechanged = true;
+ }
+ }
+ if (!statechanged) {
+ ostringstream o;
+ o << "Select timeout: neither of the fd's ";
+ for (unsigned int i = 0; i < set.size(); i++)
+ o << set[i] << ' ';
+ o << "has shown activity in " << tsec << " sec\n";
+ _debugmsg(o.str());
+ }
+ }
+}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/fdset/writeable.cc
^
|
@@ -1,55 +0,0 @@
-#include "fdset"
-
-int Fdset::writeable() const {
- PROFILE("Fdset::writeable");
-
- fd_set writeset, exceptset;
- struct timeval tv, *tvp;
-
- // No fd's? Nothing is writeable.
- if (set.size() < 1)
- return (-1);
-
- // Prepare select sets.
- FD_ZERO (&writeset);
- FD_ZERO (&exceptset);
- for (unsigned i = 0; i < set.size(); i++) {
- FD_SET (set[i], &writeset);
- FD_SET (set[i], &exceptset);
- }
-
- // Prepare timout specifier.
- if (tsec) {
- tv.tv_sec = tsec;
- tv.tv_usec = 0;
- tvp = &tv;
- } else
- tvp = 0;
-
- // Run the select.
- if (select (FD_SETSIZE, 0, &writeset, &exceptset, tvp) < 0) {
- if (errno != EINTR)
- throw static_cast<Error>
- ("Select failure: failed to wait for writable state: ") +
- strerror(errno);
- else
- return (-1);
- }
-
- // Check for exceptions.
- for (unsigned i = 0; i < set.size(); i++)
- if (FD_ISSET (set[i], &exceptset))
- throw static_cast<Error>("Exception on fd/socket ") + set[i];
-
- // Check what's writeable.
- for (unsigned i = 0; i < set.size(); i++)
- if (FD_ISSET (set[i], &writeset)) {
- debugmsg (Mstr("Fd ") + set[i] + " has become writeable\n");
- return (set[i]);
- }
-
- // Nothing..
- debugmsg ("No writeable fd's at this time\n");
- return (-1);
-}
-
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/httpbuffer/cookievalue.cc
^
|
@@ -4,16 +4,19 @@
PROFILE("Httpheader::cookievalue");
string cval = headerval ("Cookie");
- size_t pos;
+ string match = c;
+ if (match[match.size() - 1] != '=')
+ match += '=';
string ret = "";
- if ( (pos = cval.find (c)) != string::npos) {
- pos++;
- pos += c.size();
+ size_t pos = cval.find(match);
+ if (pos != string::npos) {
+ pos += match.size();
for (char ch = cval[pos];
pos < cval.size() && ch != ';' && ch != ',' && ch;
ch = cval[++pos]) {
ret += ch;
}
}
+ msg(Mstr("Cookie value '") + c + Mstr("' : '") + ret + "'\n");
return (ret);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/httpbuffer/httpbuffer
^
|
@@ -19,16 +19,30 @@
bool headersreceived();
- string headerval (string var);
+ string headerval (string const &var);
string &firstline();
+ string url();
+
bool setversion(char v);
- void setheader (string var, string val);
- void addheader (string var, string val);
- void addheader (string h);
+
+ void setheader (string const &var, string const &val);
+ void setheader (string const &h);
+
+ void addheader (string const &var, string const &val);
+ void addheader (string const &h);
+
+ void replaceheader (string const &var, string const &val);
+ void replaceheader (string const &h);
+
string cookievalue (string var);
+ string paramvalue(string var);
+
RequestMethod requestmethod();
+
string requesturi();
+ void reset();
+
private:
unsigned findheader (string h);
unsigned bodystart;
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/httpbuffer/paramvalue.cc
^
|
@@ -0,0 +1,21 @@
+#include "httpbuffer"
+
+string Httpbuffer::paramvalue (string c) {
+ PROFILE("Httpheader::paramvalue");
+
+ string uri = requesturi();
+ string match = c;
+ if (match[match.size() - 1] != '=')
+ match += '=';
+ string ret = "";
+ size_t pos = uri.find(match);
+ if (pos != string::npos) {
+ pos += match.size();
+ for (char ch = uri[pos];
+ pos < uri.size() && ch != '&' && ch != '?';
+ ch = uri[++pos])
+ ret += ch;
+ }
+ msg(Mstr("Param value '") + c + Mstr("' : '") + ret + "'\n");
+ return ret;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/httpbuffer/reset.cc
^
|
@@ -0,0 +1,7 @@
+#include "httpbuffer"
+
+void Httpbuffer::reset() {
+ first_line = "";
+ bodystart = 0;
+ Netbuffer::reset();
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/httpbuffer/setversion.cc
^
|
@@ -3,15 +3,16 @@
bool Httpbuffer::setversion (char v) {
PROFILE("Httpbuffer::setversion");
- // No headers? Nothing to do.
- if (!headersreceived())
+ // No first line? Nothing to do yet.
+ unsigned croff = charfind('\n');
+ if (!croff)
return false;
- // First line must start with HTTP/1.X.
- // Poke in the version right after that.
- if (bufsz() < 8 || strncmp(bufdata(), "HTTP/1.", 7))
+ // Find the HTTP/1.x header
+ unsigned stroff = strfind("HTTP/1.");
+ if (!stroff || stroff > croff)
return false;
// Poke in the new version.
- return setchar(7, v);
+ return setchar(stroff + 7, v);
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/httpbuffer/url.cc
^
|
@@ -0,0 +1,33 @@
+#include "httpbuffer"
+
+static string methods[] = {
+ "HEAD", "GET", "POST", "PUT", "DELETE", "TRACE", "OPTIONS", "CONNECT"
+};
+
+string Httpbuffer::url() {
+ if (firstline().empty())
+ return "";
+
+ // The first line must be a method, followed by the URL, followed
+ // by optional mush, as in: GET /index.html HTTP/1.1.
+ // Match the method first.
+ unsigned url_start = 0;
+ for (unsigned i = 0; i < sizeof(methods) / sizeof(string) ; i++)
+ if (firstline().substr(0, methods[i].size()) == methods[i]) {
+ url_start = methods[i].size();
+ break;
+ }
+ if (!url_start)
+ return "";
+ while (firstline()[url_start] == ' ' && url_start < firstline().size())
+ url_start++;
+
+ string ret;
+ for (unsigned i = url_start;
+ firstline()[i] != ' ' && i < firstline().size();
+ i++)
+ ret += firstline()[i];
+
+ debugmsg("URL of request: " + ret + "\n");
+ return ret;
+}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/httpdispatcher
^
|
-(directory)
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/httpdispatcher/dispatch.cc
^
|
@@ -1,91 +0,0 @@
-#include "httpdispatcher"
-
-void HttpDispatcher::dispatch() {
- PROFILE("HttpDispatcher::dispatch");
-
- unsigned stickytarget;
- string host_header = "";
-
- // Try to dispatch. Since we're in HTTP mode, we must return an
- // error page when dispatching fails.
-
- try {
-
- // Get the client's request. May need for cookie inspection or for the
- // host header.
- while (!buf.headersreceived())
- if (!buf.netread(clientfd(), config.client_timeout()))
- throw static_cast<Error>("Didn't receive a valid "
- "client request.\n");
- if (config.verbose())
- msg ("Received client request: '" + buf.firstline() +
- "'\n");
-
- // See if hostmatching is used. This is true when a backend
- // matches against a non-dot host.
- bool hostmatchused = false;
- for (unsigned i = 0; i < balancer.nbackends(); i++)
- if (balancer.backend(i).hostmatch() != ".") {
- hostmatchused = true;
- break;
- }
- // Build new target list if host matching applies.
- if (hostmatchused) {
- host_header = buf.headerval ("Host");
- if (config.verbose())
- msg ("Will try to dispatch request host '" +
- host_header + "'\n");
-
- // We need to build tcpdispatcher's target list now!
- // Construct locally and poke into TcpDispatcher.
- msg ("Creating host-based target list for the HTTP dispatcher\n");
- BackendVector v;
- v.isdefined(true);
- for (unsigned i = 0; i < balancer.nbackends(); i++) {
- if ( (balancer.backend(i).available()) &&
- (!regexec (&(balancer.backend(i).hostregex()),
- host_header.c_str(), 0, 0, 0)) ) {
- v.add(i);
- if (config.verbose())
- msg (" Candidate target: " +
- balancer.backend(i).description() + "\n");
- }
- }
- targetlist(v);
- }
-
- // Dispatch as a normal backend if sticky HTTP is off, or if the
- // sticky target is badly specified.
- if (!config.stickyhttp() ||
- sscanf (buf.cookievalue ("XRTarget").c_str(),
- "%d", &stickytarget) < 1 ||
- stickytarget >= balancer.nbackends()) {
- issticky(false);
- TcpDispatcher::dispatch();
- } else {
- // Got a sticky target. Try to connect. If that fails, fallback
- // to non-sticky dispatching.
- targetbackend(stickytarget);
- Backend tb = balancer.backend(stickytarget);
- if (config.verbose())
- msg ("Sticky HTTP request for " + tb.description() + "\n");
- if (! tb.connect()) {
- balancer.backend(stickytarget).live(false);
- if (config.verbose())
- msg ("Failed to connect to back end " + tb.description() +
- ", trying to dispatch to other\n");
- issticky(false);
- TcpDispatcher::dispatch();
- } else {
- backendfd(tb.sock());
- issticky(true);
- }
- }
-
- } catch (Error const &e) {
- senderrorpage();
- throw e;
- }
-
-}
-
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/httpdispatcher/handle.cc
^
|
@@ -1,80 +0,0 @@
-#include "httpdispatcher"
-
-void HttpDispatcher::handle() {
- PROFILE("HttpDispatcher::handle");
-
- // The client request was already retrieved before starting the
- // dispatcher. We can continue by applying server-directed headers.
- debugmsg("Applying server-directed headers to client request\n");
- buf.setversion('0');
- buf.setheader ("Connection", "close");
- buf.setheader ("Proxy-Connection", "close");
-
- // Apply other server-side directed info
- if (config.addxrversion())
- buf.setheader ("XR", VER);
- if (config.addxforwardedfor())
- buf.addheader ("X-Forwarded-For", string(inet_ntoa(clientip())));
- for (unsigned n = 0; n < config.nserverheaders(); n++)
- buf.addheader (config.serverheader(n));
-
- // Flush client info received so far to the back end.
- debugmsg("Sending client request to back end\n");
- buf.netwrite(backendfd(), config.backend_timeout());
-
- // Let's see if we need to modify the server headers.
- bool modify_serverheaders = false;
- if (config.addxrversion() ||
- (config.stickyhttp() && !issticky()))
- modify_serverheaders = true;
-
- // Go into copy-thru mode. If required, catch the server headers on
- // their first appearance and modify them.
- while (1) {
- Fdset readset (config.client_timeout());
- readset.add(clientfd());
- readset.add(backendfd());
-
- int sock;
- if ((sock = readset.readable()) < 0)
- break;
-
- buf.reset();
-
- if (!buf.netread(sock))
- break;
-
- if (sock == backendfd() && modify_serverheaders) {
- debugmsg("First back end request seen, applying modifications\n");
- modify_serverheaders = false;
- while (! buf.headersreceived())
- if (!buf.netread (sock, config.backend_timeout()))
- throw static_cast<Error>
- ("Failed to get headers from back end");
- if (config.addxrversion())
- buf.setheader("XR", VER);
- if (config.stickyhttp() && !issticky()) {
- ostringstream o;
- o << "XRTarget=" << targetbackend();
- buf.setheader("Set-Cookie", o.str());
- }
- }
-
- // Flush info to the other connected side.
- int othersock, timeout;
- if (sock == clientfd()) {
- othersock = backendfd();
- timeout = config.backend_timeout();
- } else {
- othersock = clientfd();
- timeout = config.client_timeout();
- }
-
- debugmsg (Mstr("Had data on ") + sock +
- (Mstr(", sending to ") + othersock) + "\n");
-
- buf.netwrite(othersock, timeout);
- if (sock == backendfd())
- balancer.backend(targetbackend()).addbytes(buf.bufsz());
- }
-}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/httpdispatcher/httpdispatcher
^
|
@@ -1,24 +0,0 @@
-#ifndef _HTTPDISPATCHER_
-#define _HTTPDISPATCHER_
-
-#include "../sys/sys"
-#include "../tcpdispatcher/tcpdispatcher"
-#include "../httpbuffer/httpbuffer"
-
-class HttpDispatcher: public TcpDispatcher {
-public:
- HttpDispatcher (int fd, struct in_addr ip);
-
- void dispatch();
- void handle();
- bool issticky() const { return (is_sticky); }
- void issticky (bool s) { is_sticky = s; }
-
-private:
- void senderrorpage();
-
- Httpbuffer buf;
- bool is_sticky;
-};
-
-#endif
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/httpdispatcher/senderrorpage.cc
^
|
@@ -1,34 +0,0 @@
-#include "httpdispatcher"
-
-#define ERRORSTR \
- "HTTP/1.0 502 Internal Server Error\r\n" \
- "Content-Length: 0\r\n" \
- "\r\n"
-
-void HttpDispatcher::senderrorpage() {
- PROFILE("HttpDispatcher::senderrorpage");
-
- msg ("Sending error page to client.\n");
- try {
- string txt =
- "<html>\n"
- " <head>\n"
- " <title>Internal Server Error</title>\n"
- " </head>\n"
- " <body>\n"
- " <h1>Internal Server Error</h1>\n"
- " You request could not be completed. Please retry later.\n"
- " </body>\n"
- "</html>\n";
- ostringstream mess;
- mess <<
- "HTTP/1.0 502 Internal Server Error\r\n"
- "Content-Length: " << txt.size() << "\r\n"
- "\r\n" <<
- txt;
- Netbuffer buf(mess.str());
- buf.netwrite(clientfd(), config.client_timeout());
- } catch (Error const &e) {
- cerr << e.what() << " (while sending error page)\n";
- }
-}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/activity.cc
^
|
@@ -0,0 +1,15 @@
+#include "ipstore"
+
+void IPStore::activity(struct in_addr clientip, unsigned curbackend) {
+ if (!onoff)
+ return;
+
+ msg(Mstr("Logging activity for back end ") + Mstr(curbackend) +
+ Mstr(" from ") + inet_ntoa(clientip) + "\n");
+
+ Mutex::lock(&store);
+ store[clientip].targetbackend = (int)curbackend;
+ store[clientip].lastaccess = time(0);
+ Mutex::unlock(&store);
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/anticipated.cc
^
|
@@ -0,0 +1,26 @@
+#include "ipstore"
+
+unsigned IPStore::anticipated(unsigned b) {
+ if (!onoff || b >= balancer.nbackends())
+ return 0;
+
+ // Weed store for decisions later
+ weed();
+
+ // Get number of anticipated clients for given back end
+ unsigned ret = 0;
+
+ Mutex::lock(&store);
+ for (StoreMap::iterator iter = store.begin();
+ iter != store.end();
+ iter++)
+ if ((*iter).second.targetbackend == (int)b)
+ ret++;
+ Mutex::unlock(&store);
+
+ debugmsg(Mstr("Anticipated connections for back end ") + Mstr(b) + ": " +
+ Mstr(ret) + "\n");
+ return ret;
+}
+
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/clear.cc
^
|
@@ -0,0 +1,9 @@
+#include "ipstore"
+
+void IPStore::clear(struct in_addr clientip) {
+ debugmsg(Mstr("Erasing IP entry of ") +
+ Mstr(inet_ntoa(clientip)) + "\n");
+ Mutex::lock(&store);
+ store.erase(clientip);
+ Mutex::unlock(&store);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/clearoldest.cc
^
|
@@ -0,0 +1,36 @@
+#include "ipstore"
+
+void IPStore::clearoldest() {
+ time_t oldest_time = time(0) + 100;
+ StoreMap::iterator oldest_entry;
+ bool found = false;
+
+ Mutex::lock(&store);
+
+ dump();
+
+ // Find oldest entry.
+ for (StoreMap::iterator iter = store.begin();
+ iter != store.end();
+ iter++) {
+ if ((*iter).second.lastaccess < oldest_time) {
+ oldest_time = (*iter).second.lastaccess;
+ oldest_entry = iter;
+ found = true;
+ }
+ }
+
+ // Kill it if we got it.
+ if (found) {
+ if (config.debug()) {
+ Timestamp tm((*oldest_entry).second.lastaccess);
+ _debugmsg(Mstr("Erasing oldest IP store entry: ") +
+ Mstr(inet_ntoa(oldest_entry->first)) + " on " +
+ tm.desc() + "\n");
+ }
+ store.erase(oldest_entry);
+ }
+
+ Mutex::unlock(&store);
+ dump();
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/dump.cc
^
|
@@ -0,0 +1,18 @@
+#include "ipstore"
+
+void IPStore::dump() {
+ if (!config.debug())
+ return;
+
+ _debugmsg(Mstr("IPStore dump:\n"));
+ for (StoreMap::iterator iter = store.begin();
+ iter != store.end();
+ iter++) {
+ Timestamp tm((*iter).second.lastaccess);
+ ostringstream o;
+ o << "Client IP " << inet_ntoa(iter->first) << " on "
+ << tm.desc() << " to back end " << (*iter).second.targetbackend
+ << '\n';
+ _debugmsg(o.str());
+ }
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/ipstore
^
|
@@ -0,0 +1,45 @@
+#ifndef _IPSTORE_
+#define _IPSTORE_
+
+#include "sys/sys"
+#include "config/config"
+#include "timestamp/timestamp"
+#include "balancer/balancer"
+#include "ThreadsAndMutexes/mutex/mutex"
+
+class IPStore {
+public:
+ struct ClientData {
+ int targetbackend;
+ time_t lastaccess;
+ };
+
+ struct ClientDataCmp {
+ bool operator() (struct in_addr a, struct in_addr b) const {
+ long la, lb;
+ memcpy (&la, &a, sizeof(long));
+ memcpy (&lb, &b, sizeof(long));
+ return (la - lb) < 0;
+ }
+ };
+
+ typedef map<struct in_addr, ClientData, ClientDataCmp> StoreMap;
+
+ static int target(struct in_addr clientip);
+ static void activity(struct in_addr clientip, unsigned curbackend);
+ static unsigned anticipated(unsigned bckend);
+ static void clear(struct in_addr clientip);
+ static void clearoldest();
+
+ static void on() { onoff = true; }
+ static void off() { onoff = false; }
+
+private:
+ static void dump();
+ static void weed();
+
+ static StoreMap store;
+ static bool onoff;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/target.cc
^
|
@@ -0,0 +1,23 @@
+#include "ipstore"
+
+IPStore::StoreMap IPStore::store;
+bool IPStore::onoff = false;
+
+int IPStore::target(struct in_addr clientip) {
+ // Weed out the store, for decisions later
+ weed();
+
+ // Let's see if we know the client.
+ if (store.count(clientip) > 0) {
+ if (config.verbose()) {
+ Timestamp tm(store[clientip].lastaccess);
+ msg(Mstr("Client IP ") + Mstr(inet_ntoa(clientip)) +
+ " last visited on " + tm.desc() + " and went to " +
+ balancer.backend(store[clientip].targetbackend).description() +
+ "\n");
+ }
+ return store[clientip].targetbackend;
+ }
+ return -1;
+}
+
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/ipstore/weed.cc
^
|
@@ -0,0 +1,29 @@
+#include "ipstore"
+
+void IPStore::weed() {
+ time_t now = time(0);
+ bool done = false;
+
+ Mutex::lock(&store);
+
+ while (!done) {
+ done = true;
+ for (StoreMap::iterator iter = store.begin();
+ iter != store.end();
+ iter++) {
+ if (now - ((*iter).second.lastaccess) > config.ipstoretimeout()) {
+ if (config.debug()) {
+ Timestamp tm((*iter).second.lastaccess);
+ _debugmsg (Mstr("Stale entry: ") + Mstr(inet_ntoa(iter->first)) +
+ Mstr(" visited on ") + tm.desc() +
+ ", erasing\n");
+ }
+ done = false;
+ store.erase(iter);
+ break;
+ }
+ }
+ }
+
+ Mutex::unlock(&store);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memdata.cc
^
|
@@ -0,0 +1,4 @@
+#include "memory"
+
+Memory::MemoryLog Memory::s_memlog;
+bool Memory::s_follow;
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memdelete.cc
^
|
@@ -0,0 +1,5 @@
+#include "memory"
+
+void Memory::operator delete(void *ptr) {
+ free(ptr);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memdelete1.cc
^
|
@@ -0,0 +1,5 @@
+#include "memory"
+
+void Memory::operator delete[](void *ptr) {
+ free(ptr);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memdisplay.cc
^
|
@@ -0,0 +1,13 @@
+#include "memory"
+
+void Memory::mem_display() {
+ Mutex::lock(&s_memlog);
+ for (unsigned i = 0; i < s_memlog.size(); i++) {
+ MemoryEntry ent = s_memlog[i];
+ Mutex::lock(&cout);
+ cout << "Memory::mem_display " << ent.ptr << ' ' << ent.sz
+ << ' ' << ent.desc << '\n';
+ Mutex::unlock(&cout);
+ }
+ Mutex::unlock(&s_memlog);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memfollow.cc
^
|
@@ -0,0 +1,5 @@
+#include "memory"
+
+void Memory::mem_follow(bool b) {
+ s_follow = b;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memfollow1.cc
^
|
@@ -0,0 +1,5 @@
+#include "memory"
+
+bool Memory::mem_follow() {
+ return s_follow;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memfree.cc
^
|
@@ -0,0 +1,25 @@
+#include "memory"
+
+void Memory::free(void *ptr) {
+ if (!ptr)
+ return;
+
+ for (unsigned i = 0; i < s_memlog.size(); i++) {
+ Mutex::lock(&s_memlog);
+ MemoryEntry ent = s_memlog[i];
+ Mutex::unlock(&s_memlog);
+ if (ent.ptr == ptr) {
+ if (s_follow) {
+ Mutex::lock(&cout);
+ cout << "Memory::free(" << ptr << ")\n";
+ Mutex::unlock(&cout);
+ }
+ ::free (ent.ptr);
+ Mutex::lock(&s_memlog);
+ s_memlog.erase(s_memlog.begin() + i);
+ Mutex::unlock(&s_memlog);
+ return;
+ }
+ }
+ cerr << "FREE Request for non previously allocated memory\n";
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memmalloc.cc
^
|
@@ -0,0 +1,16 @@
+#include "memory"
+
+void *Memory::malloc(size_t sz, string const &desc) {
+ void *ptr = ::malloc(sz);
+ MemoryEntry ent = { ptr, sz, desc };
+ Mutex::lock(&s_memlog);
+ s_memlog.push_back(ent);
+ Mutex::unlock(&s_memlog);
+ if (s_follow) {
+ Mutex::lock(&cout);
+ cout << "Memory::malloc(" << sz << ") -> " << ptr << ' '
+ << desc << '\n';
+ Mutex::unlock(&cout);
+ }
+ return ptr;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memmark.cc
^
|
@@ -0,0 +1,8 @@
+#include "memory"
+
+void Memory::mem_mark(string const &desc) {
+ MemoryEntry ent = { 0, 0, "MARK " + desc };
+ Mutex::lock(&s_memlog);
+ s_memlog.push_back(ent);
+ Mutex::unlock(&s_memlog);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memnew.cc
^
|
@@ -0,0 +1,5 @@
+#include "memory"
+
+void *Memory::operator new(size_t sz) {
+ return malloc(sz);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memnew1.cc
^
|
@@ -0,0 +1,5 @@
+#include "memory"
+
+void *Memory::operator new[](size_t sz) {
+ return malloc(sz);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memory
^
|
@@ -0,0 +1,40 @@
+#ifndef _MEMORY_
+#define _MEMORY_
+
+#include "sys/sys"
+#include "ThreadsAndMutexes/mutex/mutex"
+
+class Memory {
+public:
+ // Memory accessing functions
+ static void *malloc(size_t sz, string const &desc = "");
+ static void *realloc(void *ptr, size_t newsz, string const &desc = "");
+ static void free(void *ptr);
+ static void *operator new(size_t sz);
+ static void operator delete(void *ptr);
+ static void *operator new[](size_t sz);
+ static void operator delete[](void *ptr);
+
+ // Follow actions immediately?
+ static void mem_follow(bool b);
+ static bool mem_follow();
+
+ // Dump what we have
+ static void mem_display();
+
+ // Marker in the overview
+ static void mem_mark(string const &desc = "");
+
+ // Internal storage types
+ struct MemoryEntry {
+ void *ptr;
+ size_t sz;
+ string desc;
+ };
+ typedef vector<MemoryEntry> MemoryLog;
+private:
+ static MemoryLog s_memlog;
+ static bool s_follow;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/memory/memrealloc.cc
^
|
@@ -0,0 +1,29 @@
+#include "memory"
+
+void *Memory::realloc(void *ptr, size_t newsz, string const &desc) {
+ if (!newsz) {
+ free(ptr);
+ return 0;
+ } else {
+ for (unsigned i = 0; i < s_memlog.size(); i++) {
+ MemoryEntry ent = s_memlog[i];
+ if (ent.ptr == ptr) {
+ ent.ptr = ::realloc(ptr, newsz);
+ ent.sz = newsz;
+ ent.desc = desc;
+ Mutex::lock(&s_memlog);
+ s_memlog[i] = ent;
+ Mutex::unlock(&s_memlog);
+ if (s_follow) {
+ Mutex::lock(&cout);
+ cout << "Memory::realloc(" << ptr << ", " << newsz
+ << ") -> " << ent.ptr << ' ' << desc << '\n';
+ Mutex::unlock(&cout);
+ }
+ return ent.ptr;
+ }
+ }
+ cerr << "REALLOC Request for non previously allocated memory\n";
+ }
+ return 0;
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/mstr/mstr
^
|
@@ -7,6 +7,11 @@
public:
Mstr(string s): string(s) {}
Mstr(char const *s): string(s) {}
+ Mstr(int i) {
+ ostringstream o;
+ o << i;
+ *this = o.str();
+ }
Mstr const &operator+ (int i) {
ostringstream o;
o << i;
@@ -41,6 +46,12 @@
*this += o.str();
return *this;
}
+ Mstr const &operator+(rlim_t r) {
+ ostringstream o;
+ o << r;
+ *this += o.str();
+ return *this;
+ }
};
#endif
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/checkspace.cc
^
|
@@ -5,15 +5,28 @@
if (!buf_alloced) {
buf_alloced = extra;
+ // When the first network buffer is allocated in HTTP mode, get
+ // twice as much. Most often that will be enough to fetch the whole
+ // client request, so that one realloc() will be spared.
+ if (extra == config.buffersize() &&
+ config.stype() == Servertype::t_http)
+ buf_alloced <<= 1;
+ debugmsg (Mstr("Netbuffer: reserving ") + buf_alloced +
+ " bytes for network buffer\n");
+ LOCK_MALLOC;
buf_data = (char*)malloc(buf_alloced);
+ UNLOCK_MALLOC;
if (! buf_data)
- throw static_cast<Error>("Memory fault in Netbuffer::check_space");
+ throw Error("Memory fault in Netbuffer::check_space");
} else if (buf_sz + extra > buf_alloced) {
- msg((Mstr("Reallocating net buffer from ") + buf_alloced) +
- (Mstr(" to ") + (buf_alloced + extra)) + " bytes\n");
+ debugmsg((Mstr("Netbuffer: reallocating net buffer from ") +
+ buf_alloced) +
+ (Mstr(" to ") + (buf_alloced + extra)) + " bytes\n");
buf_alloced += extra;
+ LOCK_MALLOC;
buf_data = (char*)realloc(buf_data, buf_alloced);
+ UNLOCK_MALLOC;
if (! buf_data)
- throw static_cast<Error>("Memory fault in Netbuffer::check_space");
+ throw Error("Memory fault in Netbuffer::check_space");
}
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/copy.cc
^
|
@@ -1,9 +1,15 @@
#include "netbuffer"
void Netbuffer::copy (Netbuffer const &other) {
+ debugmsg("Netbuffer: copying other\n");
+
buf_sz = other.buf_sz;
buf_alloced = other.buf_alloced;
- buf_data = new char[buf_sz];
+ LOCK_MALLOC;
+ buf_data = (char*)malloc(buf_alloced);
+ UNLOCK_MALLOC;
+ if (!buf_data)
+ throw Error("Memory fault in Netbuffer::copy");
- memcpy (buf_data, other.buf_data, buf_sz);
+ memcpy (buf_data, other.buf_data, buf_alloced);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/destroy.cc
^
|
@@ -1,7 +1,9 @@
#include "netbuffer"
void Netbuffer::destroy() {
- delete buf_data;
+ debugmsg(Mstr("Netbuffer: destroying ") + Mstr(buf_alloced) +
+ Mstr( " bytes\n"));
+ free(buf_data);
buf_data = 0;
buf_sz = 0;
buf_alloced = 0;
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/insertat.cc
^
|
@@ -2,6 +2,8 @@
bool Netbuffer::insertat(unsigned index, char const *s, unsigned len) {
PROFILE("Netbuffer::insertat");
+ debugmsg(Mstr("Netbuffer: inserting ") + Mstr(s) + Mstr(" at ") +
+ Mstr(index) + "\n");
if (!len)
len = strlen(s);
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/netbuffer
^
|
@@ -2,12 +2,25 @@
#define _NETBUFFER_
#include "sys/sys"
+#include "memory/memory"
+
#include "error/error"
#include "config/config"
#include "profiler/profiler"
#include "fdset/fdset"
+#include "servertype/servertype"
+
+/* A few defs when malloc() / realloc() are suspected to be not thread-safe.
+ * The defines are used in eg. copy() and check_space(). */
+#ifdef MISTRUST_MALLOC_THREADSAFE
+#define LOCK_MALLOC Mutex::lock((void*)malloc)
+#define UNLOCK_MALLOC Mutex::unlock((void*)malloc)
+#else
+#define LOCK_MALLOC
+#define UNLOCK_MALLOC
+#endif
-class Netbuffer {
+class Netbuffer MEM(: public Memory) {
public:
Netbuffer();
Netbuffer (Netbuffer const &other);
@@ -26,8 +39,9 @@
unsigned strfind (char const *s) const;
unsigned charfind (char ch, unsigned start = 0) const;
-
+
bool setchar(unsigned offset, char ch);
+ void setstring(string const &s);
string stringat(unsigned index, unsigned len);
@@ -35,7 +49,7 @@
bool removeat(unsigned index, unsigned len = 1);
void reset();
-
+
private:
void copy (Netbuffer const &other);
@@ -43,7 +57,8 @@
void check_space(unsigned extra);
string printable(char c) const;
-
+ string printable() const;
+
char *buf_data;
unsigned buf_sz;
unsigned buf_alloced;
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/netbuffer1.cc
^
|
@@ -1,4 +1,5 @@
#include "netbuffer"
Netbuffer::Netbuffer(): buf_data(0), buf_sz(0), buf_alloced(0) {
+ debugmsg(Mstr("Netbuffer: creating zero size buffer\n"));
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/netbuffer4.cc
^
|
@@ -2,12 +2,5 @@
Netbuffer::Netbuffer (string const &s):
buf_data(0), buf_sz(0), buf_alloced(0) {
-
- check_space(s.size() + 1);
-
- buf_sz = s.size();
-
- memcpy (buf_data, s.c_str(), buf_sz);
- buf_data[buf_sz] = 0;
- debugmsg((Mstr("Created netbuffer from string, ") + buf_sz) + " bytes\n");
+ setstring(s);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/netread.cc
^
|
@@ -6,16 +6,24 @@
if (timeout) {
Fdset set(timeout);
set.add(fd);
- if (set.readable() != fd)
- throw static_cast<Error>("Fd ") + fd +
- " failed to become readable within " + timeout + "sec";
+ set.wait_r();
+ if (! set.readable(fd)) {
+ msg(Mstr("Fd ") + Mstr(fd) +
+ Mstr(" failed to become readable within ") + Mstr(timeout) +
+ Mstr(" sec"));
+ 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());
- if (nread < 0)
- throw static_cast<Error>("Read failed on fd ") + fd;
+ if (nread < 0) {
+ msg(Mstr("Read failed on fd ") + fd + ": " + strerror(errno));
+ return 0;
+ }
buf_sz += nread;
if (config.debug() && nread) {
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/netwrite.cc
^
|
@@ -27,37 +27,55 @@
}
// Send to the socket
- unsigned totwritten = 0;
+ unsigned totwritten = 0, ntries = 0;
while (totwritten < buf_sz) {
+ // Don't go beyond 5 tries.
+ if (++ntries > 4) {
+ ostringstream o;
+ o << "Network writing to fd " << fd << " failed, "
+ << totwritten << " bytes sent of " << buf_sz;
+ throw Error(o.str());
+ }
+
// Wait for the socket to become writeable.
if (timeout) {
Fdset set (timeout);
set.add (fd);
- if (set.writeable() != fd)
- throw static_cast<Error>("Fd ") + fd +
- " failed to become writable within " + timeout + " sec";
+ set.wait_w();
+ if (! set.writeable(fd)) {
+ ostringstream o;
+ o << "Fd " << fd << " failed to become writable within "
+ << timeout << " sec";
+ throw Error(o.str());
+ }
}
// Push bytes
ssize_t nwritten;
nwritten = write (fd, buf_data + totwritten, buf_sz - totwritten);
- if (config.debug() && nwritten > 0) {
- ostringstream o;
- o << "Sent " << nwritten << " bytes to fd " << fd << ": ";
- for (unsigned i = totwritten; i < totwritten + nwritten; i++)
- o << printable(buf_data[i]);
- o << "\n";
- _debugmsg (o.str());
- }
-
- // EINVAL / EINPROGRESS errors are handled as: retry
// If any bytes were written, we're ok
- if (nwritten >= 1)
+ // EINVAL / EINPROGRESS errors are handled as: retry
+ // All other errors mean the link is broken
+ if (nwritten >= 1) {
+ ntries = 0;
+ if (config.debug()) {
+ ostringstream o;
+ o << "Sent " << nwritten << " bytes to fd " << fd << ": ";
+ for (unsigned i = totwritten; i < totwritten + nwritten; i++)
+ o << printable(buf_data[i]);
+ o << "\n";
+ _debugmsg (o.str());
+ }
totwritten += nwritten;
- else if (errno != EINVAL && errno != EINPROGRESS)
- throw static_cast<Error>("Write/send failed: errno=") +
- errno + ", " + strerror(errno) + ", result=" + nwritten;
+ } else if (errno == EINVAL || errno == EINPROGRESS) {
+ msg(Mstr("Write warning: ") + Mstr(strerror(errno)) + "\n");
+ } else {
+ ostringstream o;
+ o << "Write/send failed: errno=" << int(errno) << ", "
+ << strerror(errno) << ", result=" << int(nwritten);
+ throw Error(o.str());
+ }
}
return buf_sz;
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/opassign.cc
^
|
@@ -1,6 +1,7 @@
#include "netbuffer"
Netbuffer const &Netbuffer::operator= (Netbuffer const &other) {
+ debugmsg(Mstr("Netbuffer: copying other\n"));
if (this != &other) {
destroy();
copy (other);
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/netbuffer/printable1.cc
^
|
@@ -0,0 +1,9 @@
+#include "netbuffer"
+
+string Netbuffer::printable() const {
+ string ret;
+
+ for (unsigned i = 0; i < buf_sz; i++)
+ ret += printable(buf_data[i]);
+ return ret;
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/removeat.cc
^
|
@@ -3,11 +3,11 @@
bool Netbuffer::removeat(unsigned index, unsigned len) {
if (index >= buf_sz)
return false;
-
+
if (index + len >= buf_sz)
buf_sz = index;
else {
- memmove (buf_data + index + len, buf_data + index,
+ memmove (buf_data + index, buf_data + index + len,
buf_sz - index - len);
buf_sz -= len;
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/netbuffer/reset.cc
^
|
@@ -1,5 +1,6 @@
#include "netbuffer"
void Netbuffer::reset() {
+ debugmsg("Netbuffer: resetting\n");
buf_sz = 0;
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/netbuffer/setstring.cc
^
|
@@ -0,0 +1,11 @@
+#include "netbuffer"
+
+void Netbuffer::setstring(string const &s) {
+ debugmsg(Mstr("Netbuffer: setting to string\n"));
+
+ destroy();
+ check_space(s.size() + 1);
+ buf_sz = s.size();
+ memcpy (buf_data, s.c_str(), buf_sz);
+ buf_data[buf_sz] = 0;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/netbuffer/stringat.cc
^
|
@@ -0,0 +1,11 @@
+#include "netbuffer"
+
+string Netbuffer::stringat(unsigned index, unsigned len) {
+ string ret;
+ for (unsigned i = index; i < index + len; i++) {
+ if (i >= buf_sz)
+ break;
+ ret += buf_data[i];
+ }
+ return ret;
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/profiler/profiler
^
|
@@ -2,6 +2,7 @@
#define _PROFILER_
#include "sys/sys"
+#include "timestamp/timestamp"
#include "ThreadsAndMutexes/mutex/mutex"
class Profiler {
@@ -10,7 +11,7 @@
~Profiler();
private:
char const *fname;
- struct timeval tv_start;
+ Timestamp timestamp;
static FILE *outf;
};
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/profiler/profiler1.cc
^
|
@@ -2,8 +2,7 @@
FILE *Profiler::outf;
-Profiler::Profiler (char const *f) {
+Profiler::Profiler (char const *f): timestamp() {
fname = f;
- gettimeofday(&tv_start, 0);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/profiler/profiler2.cc
^
|
@@ -1,22 +1,9 @@
#include "profiler"
Profiler::~Profiler() {
- struct timeval tv_end;
- gettimeofday (&tv_end, 0);
- double usec =
- ( ((double)tv_end.tv_sec * 1000000 + tv_end.tv_usec) -
- ((double)tv_start.tv_sec * 1000000 + tv_start.tv_usec) );
-
- /*
- cout << fname
- << " start " << tv_start.tv_sec << " . " << tv_start.tv_usec
- << ", end " << tv_end.tv_sec << '.' << tv_end.tv_usec
- << ", usec " << usec << '\n';
- */
-
if (!outf)
outf = fopen ("/tmp/xr-prof.txt", "w");
if (outf)
fprintf (outf, "%s %s %g\n",
- timestamp().c_str(), fname, usec);
+ timestamp.desc().c_str(), fname, timestamp.elapsed());
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/servertype/servertype
^
|
@@ -1,9 +1,7 @@
#ifndef _SERVERTYPE_
#define _SERVERTYPE_
-#include "../sys/sys"
-#include "../error/error"
-
+#include <string>
using namespace std;
class Servertype {
@@ -11,19 +9,15 @@
enum Type {
t_tcp,
t_http,
+ t_udp,
};
- Servertype() : t(t_tcp) {
- }
- Servertype (string id) {
- type (id);
- }
+ Servertype(): t(t_tcp) { }
+ Servertype(string id) { type(id); }
- void type (string id);
+ void type(string id);
string typestr() const;
- Type type() const {
- return (t);
- }
+ Type type() const { return t; }
private:
Type t;
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/servertype/type1.cc
^
|
@@ -1,11 +1,15 @@
#include "servertype"
+#include "error/error"
+
void Servertype::type (string id) {
if (id == "tcp")
t = t_tcp;
else if (id == "http")
t = t_http;
+ else if (id == "udp")
+ t = t_udp;
else
- throw static_cast<Error>("Bad server type '") + id +
- "', supported are 'tcp' or 'http'";
+ throw Error("Bad server type '" + id +
+ "', supported are 'tcp' or 'http' or 'udp'");
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/servertype/typestr.cc
^
|
@@ -1,10 +1,14 @@
#include "servertype"
+#include "error/error"
+
string Servertype::typestr() const {
if (t == t_tcp)
return ("tcp");
else if (t == t_http)
return ("http");
+ else if (t == t_udp)
+ return ("udp");
else
- return ("server type unknown");
+ throw Error("Server type unknown in Servertype::typestr");
}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/sys/anymsg.cc
^
|
@@ -0,0 +1,17 @@
+#include "sys"
+#include "config/config"
+#include "ThreadsAndMutexes/mutex/mutex"
+#include "profiler/profiler"
+#include "mstr/mstr"
+#include "timestamp/timestamp"
+
+void anymsg (Mstr const &s, ostream &o, string const &label) {
+ Mutex::lock(&o);
+ if (config.prefixtimestamp()) {
+ Timestamp tm;
+ o << tm.desc() << ' ';
+ }
+ o << pthread_self() << ' ' << label << ": " << s;
+ o.flush();
+ Mutex::unlock(&o);
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/debugmsg.cc
^
|
@@ -3,14 +3,9 @@
#include "ThreadsAndMutexes/mutex/mutex"
#include "profiler/profiler"
#include "mstr/mstr"
+#include "timestamp/timestamp"
void _debugmsg (Mstr const &s) {
PROFILE("debugmsg");
-
- Mutex::lock(&cerr);
- if (config.prefixtimestamp())
- cerr << timestamp() << ' ';
- cerr << pthread_self() << " DEBUG: " << s;
- cerr.flush();
- Mutex::unlock(&cerr);
+ anymsg(s, cerr, "DEBUG");
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/ipmatch.cc
^
|
@@ -4,9 +4,9 @@
bool ipmatch (struct in_addr adr, struct in_addr mask) {
PROFILE("ipmatch");
- long laddr, lmask;
- memcpy (&laddr, &adr, sizeof(long));
- memcpy (&lmask, &mask, sizeof(long));
+ long laddr = 0, lmask = 0;
+ memcpy (&laddr, &adr, sizeof(adr));
+ memcpy (&lmask, &mask, sizeof(mask));
bool match = ( (laddr & lmask) == laddr );
debugmsg ("Matching ip " + (string)inet_ntoa(adr) + " against mask " +
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/main.cc
^
|
@@ -10,11 +10,63 @@
Config config;
Balancer balancer;
+static void showlimits() {
+ typedef struct {
+ int resource;
+ string description;
+ } Limit;
+ static Limit limit[] = {
+ { RLIMIT_CORE, "coredump size (bytes)" },
+ { RLIMIT_CPU, "cpu time (sec)" },
+ { RLIMIT_DATA, "data segment size (bytes)" },
+ { RLIMIT_FSIZE, "max file size (bytes)" },
+# ifdef RLIMIT_MEMLOCK
+ { RLIMIT_MEMLOCK, "locked mem size (bytes)" },
+# endif
+ { RLIMIT_NOFILE, "max open files" },
+# ifdef RLIMIT_NPROC
+ { RLIMIT_NPROC, "max processes" },
+# endif
+# ifdef RLIMIT_RSS
+ { RLIMIT_RSS, "max resident set size (bytes)" },
+# endif
+ { RLIMIT_STACK, "max stack size (bytes)" }
+ };
+
+ for (unsigned i = 0; i < sizeof(limit) / sizeof(Limit); i++) {
+ struct rlimit rl;
+ if (getrlimit(limit[i].resource, &rl))
+ throw Error(string("Failed to request limit: ") +
+ strerror(errno));
+ ostringstream o;
+ o << "Limits for " << limit[i].description
+ << ": hard limit " << unsigned(rl.rlim_max)
+ << ", soft limit " << unsigned(rl.rlim_cur) << '\n';
+ msg(o.str());
+ }
+}
+
+static int org_argc;
+static char **org_argv;
static void sigcatcher (int sig) {
- if (sig == SIGHUP)
+ debugmsg ("Seen signal " + sig + '\n');
+ switch (sig) {
+ case SIGHUP:
+ /* Generate a report to the log. Somewhat stale given the
+ * web interface. */
balancer.report(true);
- else if (sig != SIGPIPE)
+ break;
+ case SIGPIPE:
+ case SIGSTOP:
+ /* SIGPIPE is ignored (See below). Leaving in place for future
+ * versions. SIGSTOP is used for stopping separarte treads. */
balancer.terminate(true);
+ break;
+ case SIGSEGV:
+ /* Production-grade protection - don't ever crash! */
+ balancer.restart(true);
+ break;
+ }
}
int main (int argc, char **argv) {
@@ -22,13 +74,21 @@
PROFILE("main");
static int relevant_sig[] = {
- SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP
+ SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP,
+ // SIGSEGV
};
try {
+ // Save original commandline
+ org_argc = argc;
+ org_argv = argv;
+
// Load configuration from the commandline, promote verbosity
config.parsecmdline (argc, argv);
+ if (config.verbose())
+ showlimits();
+
msg ((Mstr("XR running as PID ") + getpid()) + "\n");
// Load the signal handler.
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/sys/maxtimeout.cc
^
|
@@ -0,0 +1,9 @@
+#include "sys"
+
+int maxtimeout(int a, int b) {
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+ return a < b ? a : b;
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/msg.cc
^
|
@@ -3,14 +3,9 @@
#include "ThreadsAndMutexes/mutex/mutex"
#include "profiler/profiler"
#include "mstr/mstr"
+#include "timestamp/timestamp"
void _msg (Mstr const &s) {
PROFILE("msg");
-
- Mutex::lock(&cerr);
- if (config.prefixtimestamp())
- cerr << timestamp() << ' ';
- cerr << pthread_self() << " INFO: " << s;
- cerr.flush();
- Mutex::unlock(&cerr);
+ anymsg(s, cerr, "INFO");
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/reportmsg.cc
^
|
@@ -2,12 +2,8 @@
#include "config/config"
#include "ThreadsAndMutexes/mutex/mutex"
#include "mstr/mstr"
+#include "timestamp/timestamp"
void reportmsg (Mstr const &s) {
- Mutex::lock(&cerr);
- if (config.prefixtimestamp())
- cerr << timestamp() << ' ';
- cerr << pthread_self() << " REPORT: " << s;
- cerr.flush();
- Mutex::unlock(&cerr);
+ anymsg(s, cerr, "REPORT");
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/serversocket.cc
^
|
@@ -3,19 +3,22 @@
#include "profiler/profiler"
#include "config/config"
-int serversocket (string addr, int port, string desc) {
+int serversocket (string addr, int port, string desc, Servertype::Type type) {
PROFILE("serversocket");
- int sock;
-
// Create the server socket, set options
- if ( (sock = socket (PF_INET, SOCK_STREAM, 0)) < 0 )
- throw static_cast<Error>("Failed to create ") + desc + " socket: " +
- strerror(errno);
+ 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 static_cast<Error>("Failed to set socket options for ") +
- desc + ": " + strerror(errno);
+ throw Error("Failed to set socket options for " +
+ desc + ": " + strerror(errno));
// Prepare binding
struct sockaddr_in saddr;
@@ -25,24 +28,25 @@
// Assign interface to listen to
if (addr[0] != '0') {
- msg ("Binding balancer to specific IP address " + addr + "\n");
+ msg ("Binding " + desc + " to specific IP address " + addr + "\n");
if ( (saddr.sin_addr.s_addr = inet_addr (addr.c_str())) == INADDR_NONE )
- throw static_cast<Error>("Cannot convert ") + desc + " IP '" +
- addr + "' to network bytes";
+ throw Error("Cannot convert " + desc + " IP '" +
+ addr + "' to network bytes");
} else {
- msg ("Binding balancer to all local IP addresses\n");
+ 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 static_cast<Error>("Failed to bind ") + desc +
- " to IP/port: " + strerror(errno);
- if (listen (sock, 5) < 0)
- throw static_cast<Error>("Failed to listen to ") + desc +
- " IP/port: " + strerror(errno);
+ 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 ("TCP server for " + desc + " listening\n");
+ msg ("Server for " + desc + " listening\n");
return (sock);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/socketclose.cc
^
|
@@ -3,14 +3,17 @@
void socketclose (int fd) {
PROFILE("socketclose");
-
- debugmsg ((Mstr("Closing socket ") + fd) + "\n");
- if (config.fastclose()) {
- struct linger l;
- l.l_onoff = 1;
- l.l_linger = 0;
- setsockopt (fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+ if (fd > 2) {
+ debugmsg ((Mstr("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);
}
- close (fd);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/str2parts.cc
^
|
@@ -5,16 +5,33 @@
PROFILE("str2parts");
string str = s;
- int pos;
+ size_t pos;
vector<string> parts;
-
- while ( (pos = str.find_first_of(sep)) >= 0) {
- if (pos > 0)
+
+ bool sep_is_first = false;
+ while ( (pos = str.find_first_of(sep)) != string::npos) {
+ if (!pos) {
+ sep_is_first = true;
+ parts.push_back("");
+ } else {
+ sep_is_first = true;
parts.push_back (str.substr(0, pos));
+ }
str = str.substr(pos + 1);
}
if (str.length() > 0)
parts.push_back (str);
+ else if (sep_is_first)
+ parts.push_back("");
+
+ /*
+ ostringstream o;
+ o << "str2parts: ";
+ for (unsigned int i = 0; i < parts.size(); i++)
+ o << "[" << parts[i] << "] ";
+ o << "\n";
+ _debugmsg(o.str());
+ */
return (parts);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/sys
^
|
@@ -21,10 +21,12 @@
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/wait.h>
#ifdef INADDR_NONE
# define HAVE_INADDR_NONE
@@ -48,10 +50,28 @@
# define PROFILE(x)
#endif
+/* Memory debugging on/off */
+#ifdef MEMDEBUG
+# define MEM(x) x
+#else
+# define MEM(x)
+#endif
+
+/* If you fear that your malloc() / realloc() may have threading problems,
+ * uncomment the following. It will cause mutex locks around the calls. */
+// #define MISTRUST_MALLOC_THREADSAFE
+
+/* If you fear racing conditions in thread_create() then uncomment this.
+ * BTW it would be really weird if thread_create() weren't thread-safe., so
+ * defining this is very likely not necessary. */
+// #define MISTRUST_THREAD_CREATE_THREADSAFE
+
using namespace std;
// This we need locally for msg(), debugmsg()
#include "mstr/mstr"
+// This is for the Servertype of serversocket()
+#include "servertype/servertype"
/* Messaging. Conditionals are defined as a macro to speed things up. */
void _msg (Mstr const &s);
@@ -60,15 +80,19 @@
#define debugmsg(x) if (config.debug()) _debugmsg(x)
void reportmsg (Mstr const &s);
void warnmsg (Mstr const &s);
+void anymsg(Mstr const &s, ostream &o, string const &label);
/* Other */
-int serversocket (string addr, int port, string description);
-string timestamp(time_t s = 0);
+int serversocket (string addr, int port, string description,
+ Servertype::Type t);
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);
+bool check_acl(string const &ipstr, struct in_addr ipaddr);
+int sysrun (string const &s);
+int maxtimeout(int a, int b);
#ifndef HAVE_INET_ATON
int inet_aton (char const *name, struct in_addr *addr);
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/sys/sysrun.cc
^
|
@@ -0,0 +1,22 @@
+#include "sys"
+#include "config/config"
+
+int sysrun(string const &s) {
+ int ret = system(s.c_str());
+ if (ret == -1) {
+ warnmsg(Mstr("Failed to start command: ") + s + "\n");
+ return -1;
+ }
+ if (WIFEXITED(ret)) {
+ int stat = WEXITSTATUS(ret);
+ if (stat)
+ warnmsg(Mstr("Command" ) + s +
+ Mstr(" exited with status ") + Mstr(stat) + "\n");
+ else
+ msg(Mstr("Command ") + s +
+ Mstr(" terminated normally.\n"));
+ return stat;
+ }
+ warnmsg(Mstr("Command ") + s + Mstr(" failed miserably!\n"));
+ return ret;
+}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/sys/timestamp.cc
^
|
@@ -1,21 +0,0 @@
-#include "sys"
-
-string timestamp(time_t s) {
- struct timeval tv;
-
- if (! s)
- gettimeofday (&tv, 0);
- else {
- tv.tv_sec = s;
- tv.tv_usec = 0;
- }
-
- struct tm *tmp = localtime(&tv.tv_sec);
-
- char buf[80];
- sprintf (buf, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d,%3.3d",
- tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
- int(tv.tv_usec / 1000));
- return (buf);
-}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/sys/warnmsg.cc
^
|
@@ -2,12 +2,8 @@
#include "config/config"
#include "ThreadsAndMutexes/mutex/mutex"
#include "mstr/mstr"
+#include "timestamp/timestamp"
void warnmsg (Mstr const &s) {
- Mutex::lock(&cerr);
- if (config.prefixtimestamp())
- cerr << timestamp() << ' ';
- cerr << pthread_self() << " WARNING: " << s;
- cerr.flush();
- Mutex::unlock(&cerr);
+ anymsg(s, cerr, "WARNING");
}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher
^
|
-(directory)
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher/dispatch.cc
^
|
@@ -1,39 +0,0 @@
-#include "tcpdispatcher"
-
-void TcpDispatcher::dispatch() {
- bool connected = false;
-
- // Build up the target list, if not yet done so. The HTTP dispatcher
- // might've created it already for host-based matching (in which case
- // we won't bother here).
- if (! target_list.isdefined()) {
- msg ("Creating target list for the TCP dispatcher\n");
- for (unsigned i = 0; i < balancer.nbackends(); i++)
- if (balancer.backend(i).available()) {
- target_list.add(i);
- if (config.verbose())
- msg (" Candidate target: " +
- balancer.backend(i).description() + "\n");
- }
- }
-
- // Call the dispatch algorithm until we can connect,
- // or until the algorithm is out of back ends (throws exception).
- while (!connected) {
- target_backend = algorithm->target(clientip(), target_list);
- Backend tb = balancer.backend(target_backend);
- if (!tb.connect()) {
- balancer.backend(target_backend).live(false);
- if (config.verbose())
- msg ("Failed to connect to back end " + tb.description() +
- ", trying to dispatch to other\n");
- } else {
- connected = true;
- backendfd(tb.sock());
- msg ((Mstr("Will dispatch client to back end ") +
- tb.description()) +
- (Mstr(" on fd ") + tb.sock()) + "\n");
- break;
- }
- }
-}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher/execute.cc
^
|
@@ -1,120 +0,0 @@
-#include "tcpdispatcher"
-
-static map <unsigned long, queue <time_t> > accesslog;
-static time_t accesslog_lastclean = 0;
-
-void TcpDispatcher::execute() {
- msg ((Mstr("Dispatch request for client fd ") + clientfd()) + "\n");
-
- // Check 'softmaxconnrate' and 'hardmaxconnrate' now!
- // Descend into this block if connrate_time() is set, AND
- // either hardmaxconnrate() is set,
- // or both softmaxconnrate() and defertime() are set.
- if (config.connrate_time() &&
- (config.hardmaxconnrate() ||
- (config.softmaxconnrate() && config.defertime()))) {
- time_t now, min_ts;
- now = time(0);
- min_ts = now - config.connrate_time();
- unsigned max_conns = max(config.hardmaxconnrate(),
- config.softmaxconnrate());
-
- Mutex::lock (&accesslog[client_ip.s_addr]);
- accesslog[client_ip.s_addr].push(now);
- Mutex::unlock (&accesslog[client_ip.s_addr]);
-
- if (accesslog_lastclean < min_ts) {
- // Clean the entire access log, it's been a while...
-
- Mutex::lock(&accesslog_lastclean);
- accesslog_lastclean = now;
- Mutex::unlock(&accesslog_lastclean);
-
- for (map<unsigned long, queue <time_t> >::iterator i =
- accesslog.begin();
- i != accesslog.end();
- i++ ) {
- if (accesslog[i->first].back() < min_ts) {
- // This IP hasn't made ANY connections in a while -- erase!
- accesslog.erase(i);
- } else {
- // Keep popping off this IP's oldest connection until we
- // have only "recent" connections left.
- Mutex::lock(&accesslog[i->first]);
- while ( accesslog[i->first].front() < min_ts
- || accesslog[i->first].size() > max_conns ) {
- accesslog[i->first].pop();
- }
- Mutex::unlock(&accesslog[i->first]);
- }
- }
- } else {
- // The "big log" doesn't need to be fully cleaned,
- // but this particular IP should be!
- Mutex::lock(&accesslog[client_ip.s_addr]);
- while ( accesslog[client_ip.s_addr].front() < min_ts
- || accesslog[client_ip.s_addr].size() > max_conns ) {
- accesslog[client_ip.s_addr].pop();
- }
- Mutex::unlock(&accesslog[client_ip.s_addr]);
- }
-
- if (config.hardmaxconnrate() &&
- accesslog[client_ip.s_addr].size() >= config.hardmaxconnrate() ) {
- // This IP has violated the "HARD" limit! Reject the connection
- ostringstream o;
- o << "Client " << inet_ntoa(client_ip)
- << " has hit the HARD maximum number of connections ("
- << config.hardmaxconnrate() << " conections in "
- << config.connrate_time() << " seconds; "
- << accesslog[client_ip.s_addr].size()
- << " connections recorded). Client is refused.\n";
- warnmsg (o.str());
- socketclose(clientfd());
- return;
- } else if (config.softmaxconnrate() &&
- (accesslog[client_ip.s_addr].size() >=
- config.softmaxconnrate())) {
- // This IP has violated the "SOFT" Limit. Go to sleep for a while.
- ostringstream o;
- o << "Client " << inet_ntoa(client_ip)
- << " has hit the SOFT maximum number of connections ("
- << config.softmaxconnrate() << " connections in "
- << config.connrate_time() << " sedonds; "
- << accesslog[client_ip.s_addr].size()
- << " connections recorded). Client is deferred for "
- << config.defertime() << " microseconds.\n";
- warnmsg (o.str());
- usleep(config.defertime());
- }
- }
-
- try {
- dispatch();
- } catch (Error const &e) {
- cerr << e.what() << "\n";
- socketclose (clientfd());
- return;
- }
-
- msg ((Mstr("Dispatchign client fd ") + clientfd()) +
- (Mstr(" to ") + balancer.backend(target_backend).description()) +
- (Mstr(", fd ") + backendfd()) + "\n");
-
- balancer.backend(target_backend).startconnection();
-
- try {
- handle();
- } catch (Error const &e) {
- cerr << e.what() << "\n";
- }
-
- balancer.backend(target_backend).endconnection();
-
- socketclose (clientfd());
- socketclose (backendfd());
-
- msg ((Mstr("Done dispatching to back end fd ") + backendfd()) +
- (Mstr(" at ") + balancer.backend(target_backend).description()) +
- "\n");
-}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher/handle.cc
^
|
@@ -1,38 +0,0 @@
-#include "tcpdispatcher"
-
-void TcpDispatcher::handle() {
-
- debugmsg (Mstr("TCP dispatcher: About to shuttle between client fd ") +
- clientfd() +
- (Mstr(" and backend fd ") + backendfd()) + "\n");
-
- while (1) {
- Fdset readset (config.client_timeout());
- readset.add (clientfd());
- readset.add (backendfd());
-
- int sock;
- if ((sock = readset.readable()) < 0)
- break;
- if (!netbuffer.netread(sock))
- break;
-
- int othersock, timeout;
- if (sock == clientfd()) {
- othersock = backendfd();
- timeout = config.backend_timeout();
- } else {
- othersock = clientfd();
- timeout = config.client_timeout();
- }
-
- debugmsg (Mstr("Had data on ") + sock +
- (Mstr(", sending to ") + othersock) + "\n");
-
- netbuffer.netwrite (othersock, timeout);
- if (sock == backendfd())
- balancer.backend(target_backend).addbytes(netbuffer.bufsz());
-
- netbuffer.reset();
- }
-}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher/tcpdispatcher
^
|
@@ -1,54 +0,0 @@
-#ifndef _TCPDISPATCHER_
-#define _TCPDISPATCHER_
-
-#include "sys/sys"
-#include "balancer/balancer"
-#include "config/config"
-#include "ThreadsAndMutexes/thread/thread"
-#include "backendvector/backendvector"
-#include "netbuffer/netbuffer"
-
-// Dispatching algorithm workers
-#include "DispatchAlgorithms/algorithm/algorithm"
-#include "DispatchAlgorithms/roundrobin/roundrobin"
-#include "DispatchAlgorithms/firstactive/firstactive"
-#include "DispatchAlgorithms/leastconn/leastconn"
-#include "DispatchAlgorithms/external/external"
-#include "DispatchAlgorithms/hashedip/hashedip"
-#include "DispatchAlgorithms/storedip/storedip"
-#include "DispatchAlgorithms/weightedload/weightedload"
-
-class TcpDispatcher: public Thread {
-public:
-
- TcpDispatcher (int fd, struct in_addr ip);
- virtual ~TcpDispatcher();
-
- virtual void execute();
-
- virtual void dispatch();
- void dispatch (string host);
- virtual void handle();
-
- int targetbackend() const { return target_backend; }
- void targetbackend(int t) { target_backend = t; }
- struct in_addr clientip() const { return client_ip; }
- 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; }
-
- BackendVector const &targetlist() const { return target_list; }
- void targetlist (BackendVector t) { target_list = t; }
-
- unsigned readchunk (int src);
-
-private:
- struct in_addr client_ip;
- int target_backend, client_fd, backend_fd;
- Algorithm *algorithm;
- BackendVector target_list;
- Netbuffer netbuffer;
-};
-
-#endif
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher/tcpdispatcher1.cc
^
|
@@ -1,34 +0,0 @@
-#include "tcpdispatcher"
-
-TcpDispatcher::TcpDispatcher(int cfd, struct in_addr cip):
- Thread(), client_ip(cip), target_backend(-1), client_fd(cfd),
- backend_fd(-1), target_list(), netbuffer() {
-
- // Instantiate dispatchmode algorithm
- switch (config.dispatchmode()) {
- case Dispatchmode::m_roundrobin:
- algorithm = new Roundrobin;
- break;
- case Dispatchmode::m_firstactive:
- algorithm = new Firstactive;
- break;
- case Dispatchmode::m_external:
- algorithm = new External;
- break;
- case Dispatchmode::m_strict_hashed_ip:
- case Dispatchmode::m_lax_hashed_ip:
- algorithm = new HashedIp;
- break;
- case Dispatchmode::m_strict_stored_ip:
- case Dispatchmode::m_lax_stored_ip:
- algorithm = new StoredIp;
- break;
- case Dispatchmode::m_weighted_load:
- algorithm = new Weightedload;
- break;
- case Dispatchmode::m_leastconn:
- default:
- algorithm = new Leastconn;
- break;
- }
-}
|
[-]
[+]
|
Deleted |
crossroads-stable.tar.gz/xr/tcpdispatcher/tcpdispatcher2.cc
^
|
@@ -1,6 +0,0 @@
-#include "tcpdispatcher"
-
-TcpDispatcher::~TcpDispatcher() {
- delete algorithm;
- debugmsg ("TCP dispatcher finished\n");
-}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/timestamp
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/timestamp/desc.cc
^
|
@@ -0,0 +1,11 @@
+#include "timestamp"
+
+string Timestamp::desc() const {
+ struct tm *tmp = localtime(&tv.tv_sec);
+ char buf[80];
+ sprintf (buf, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d,%3.3d",
+ tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ int(tv.tv_usec / 1000));
+ return buf;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/timestamp/elapsed.cc
^
|
@@ -0,0 +1,10 @@
+#include "timestamp"
+
+double Timestamp::elapsed() const {
+ struct timeval end;
+ gettimeofday(&end, 0);
+ double usec =
+ ( ((double)end.tv_sec * 1000000 + end.tv_usec) -
+ ((double)tv.tv_sec * 1000000 + tv.tv_usec) );
+ return usec / 1000000;
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/timestamp/timestamp
^
|
@@ -0,0 +1,17 @@
+#ifndef _TIMESTAMP_
+#define _TIMESTAMP_
+
+#include "sys/sys"
+
+class Timestamp {
+public:
+ Timestamp();
+ Timestamp(int sec_since_epoch);
+ double elapsed() const;
+ string desc() const;
+
+private:
+ struct timeval tv;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/timestamp/timestamp1.cc
^
|
@@ -0,0 +1,5 @@
+#include "timestamp"
+
+Timestamp::Timestamp() {
+ gettimeofday(&tv, 0);
+}
|
[-]
[+]
|
Added |
crossroads-stable.tar.gz/xr/timestamp/timestamp2.cc
^
|
@@ -0,0 +1,6 @@
+#include "timestamp"
+
+Timestamp::Timestamp(int sec_since_epoch) {
+ tv.tv_sec = sec_since_epoch;
+ tv.tv_usec = 0;
+}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/webinterface/answer.cc
^
|
@@ -1,10 +1,20 @@
#include "webinterface"
+static void stop_backend_thread(pthread_t id) {
+ Threadinfo info = Threadlist::info(id);
+ msg((Mstr("Stopping thread ") + id) +
+ (Mstr(" (backend socket ") + info.backendfd()) +
+ (Mstr(", client socket ") + info.clientfd()) + ")\n");
+ socketclose(info.backendfd());
+ socketclose(info.clientfd());
+ Threadlist::deregister(id);
+}
+
static unsigned str2uns (string const &s, string const &desc) {
unsigned ret;
if (sscanf (s.c_str(), "%u", &ret) < 1)
- throw static_cast<Error>("Bad ") + desc;
+ throw Error("Bad " + desc);
return (ret);
}
@@ -12,7 +22,22 @@
double ret;
if (sscanf (s.c_str(), "%lf", &ret) < 0)
- throw static_cast<Error>("Bad ") + desc;
+ throw Error("Bad " + desc);
+ return (ret);
+}
+
+static pthread_t str2threadid (string const &s, string const &desc) {
+ pthread_t ret;
+ long long val;
+ int convret;
+
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+ convret = sscanf(s.c_str() + 2, "%llx", &val);
+ else
+ convret = sscanf(s.c_str(), "%lld", &val);
+ if (convret < 1)
+ throw Error("Bad " + desc);
+ memcpy (&ret, &val, sizeof(ret));
return (ret);
}
@@ -21,7 +46,7 @@
ret = str2uns (s, "back end index");
if (ret >= balancer.nbackends())
- throw static_cast<Error>("Back end index out of range");
+ throw Error("Back end index out of range");
return (ret);
}
@@ -30,7 +55,7 @@
ret = str2uns (s, "header index");
if (ret >= config.nserverheaders())
- throw static_cast<Error>("Server header index out of range");
+ throw Error("Server header index out of range");
return (ret);
}
@@ -40,12 +65,12 @@
if (sscanf (s.c_str(), "%d", &i) > 0)
ret = (i != 0);
- else if (s == "on")
+ else if (s == "on" || s == "yes" || s == "true")
ret = true;
- else if (s == "off")
+ else if (s == "off" || s == "no" || s == "false")
ret = false;
else
- throw static_cast<Error>("Bad ") + desc + " switch '" + s + "'";
+ throw Error("Bad " + desc + " switch '" + s + "'");
return (ret);
}
@@ -78,7 +103,7 @@
void Webinterface::answer(Httpbuffer req) {
if (req.requestmethod() != Httpbuffer::m_get)
- throw static_cast<Error>("Only request method GET supported");
+ throw Error("Only request method GET supported");
string uri = req.requesturi();
@@ -94,6 +119,8 @@
return;
}
+ if (uri[0] == '/')
+ uri = uri.substr(1);
vector<string> parts = str2parts (uri, '/');
for (unsigned i = 0; i < parts.size(); i++)
parts[i] = decode(parts[i]);
@@ -103,7 +130,7 @@
parts[0] == "server" && parts[1] == "buffersize") {
unsigned sz = str2uns (parts[2], "buffer size");
if (sz < 1)
- throw static_cast<Error>("Buffer size may not be less than 1");
+ throw Error("Buffer size may not be less than 1");
config.buffersize(sz);
answer_status();
return;
@@ -111,10 +138,10 @@
// /server/maxconnections/
// /server/maxconnections/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "maxconnections") ) {
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "maxconnections") {
unsigned num = 0;
- if (parts.size() == 3)
+ if (parts[2] != "")
num = str2uns (parts[2], "server weight");
config.maxconn(num);
answer_status();
@@ -145,21 +172,28 @@
return;
}
+ // /server/replacehostheader/BOOLEAN
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "replacehostheader") {
+ config.replacehostheader (str2bool(parts[2], "replacehostheader"));
+ answer_status();
+ return;
+ }
+
// /server/newheader/NEWHEADER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "newheader") ) {
- if (parts.size() == 3)
- config.addserverheader(parts[2]);
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "newheader") {
+ config.addserverheader(parts[2]);
answer_status();
return;
}
// /server/changeheader/NR
// /server/changeheader/NR/VALUE
- if ( (parts.size() == 3 || parts.size() == 4) &&
- (parts[0] == "server" && parts[1] == "changeheader") ) {
+ if (parts.size() == 4 &&
+ parts[0] == "server" && parts[1] == "changeheader") {
unsigned ind = headerindex(parts[2]);
- if (parts.size() == 3)
+ if (parts[3] == "")
config.removeserverheader(ind);
else
config.changeserverheader(ind, parts[3]);
@@ -185,46 +219,79 @@
// /server/logtrafficdir
// /server/logtrafficdir/VALUE
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "logtrafficdir") ) {
- if (parts.size() == 2)
- config.dumpdir("");
- else
- config.dumpdir(parts[2]);
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "logtrafficdir") {
+ config.dumpdir(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/clientreadtimeout
+ // /server/clientreadtimeout/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "clientreadtimeout") {
+ unsigned num = 0;
+ if (parts[2] != "")
+ num = str2uns (parts[2], "client read timeout");
+ config.client_read_timeout(num);
answer_status();
return;
}
- // /server/clienttimeout
- // /server/clienttimeout/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "clienttimeout") ) {
+ // /server/clientwritetimeout
+ // /server/clientwritetimeout/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "clientwritetimeout") {
unsigned num = 0;
- if (parts.size() == 3)
- num = str2uns (parts[2], "client timeout");
- config.client_timeout(num);
+ if (parts[2] != "")
+ num = str2uns (parts[2], "client write timeout");
+ config.client_write_timeout(num);
answer_status();
return;
}
- // /server/backendtimeout
- // /server/backendtimeout/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "backendtimeout") ) {
+ // /server/backendreadtimeout
+ // /server/backendreadtimeout/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "backendreadtimeout") {
unsigned num = 0;
- if (parts.size() == 3)
- num = str2uns (parts[2], "client timeout");
- config.backend_timeout(num);
+ if (parts[2] != "")
+ num = str2uns (parts[2], "back end read timeout");
+ config.backend_read_timeout(num);
+ answer_status();
+ return;
+ }
+
+ // /server/backendwritetimeout
+ // /server/backendwritetimeout/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "backendwritetimeout") {
+ unsigned num = 0;
+ if (parts[2] != "")
+ num = str2uns (parts[2], "back end write timeout");
+ config.backend_write_timeout(num);
+ answer_status();
+ return;
+ }
+
+ // /server/dnscachetimeout
+ // /server/dnscachetimeout/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "dnscachetimeout") {
+ unsigned num = 0;
+ if (parts[2] != "")
+ num = str2uns (parts[2], "DNS cache timeout");
+ config.dnscachetimeout(num);
answer_status();
return;
}
// /server/wakeupinterval
// /server/wakeupinterval/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "wakeupinterval") ) {
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "wakeupinterval") {
unsigned num = 0;
- if (parts.size() == 3)
+ if (parts[2] != "")
num = str2uns (parts[2], "wakeup interval");
if (num)
config.checkupsec(0);
@@ -235,10 +302,10 @@
// /server/checkupinterval
// /server/checkupinterval/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "checkupinterval") ) {
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "checkupinterval") {
unsigned num = 0;
- if (parts.size() == 3)
+ if (parts[2] != "")
num = str2uns (parts[2], "checkup interval");
if (num)
config.wakeupsec(0);
@@ -247,13 +314,175 @@
return;
}
+ // /server/timeinterval/SECS
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "timeinterval") {
+ unsigned num = str2uns(parts[2], "time interval");
+ if (num < 1)
+ throw Error("Time interval may not be less than 1");
+ config.connrate_time(num);
+ answer_status();
+ return;
+ }
+
+ // /server/hardmaxconnrate/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "hardmaxconnrate") {
+ config.hardmaxconnrate(str2uns(parts[2], "hard maxconnrate"));
+ answer_status();
+ return;
+ }
+
+ // /server/softmaxconnrate/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "softmaxconnrate") {
+ config.softmaxconnrate(str2uns(parts[2], "soft maxconnrate"));
+ answer_status();
+ return;
+ }
+
+ // /server/defertime/NUMBER
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "defertime") {
+ unsigned num = str2uns(parts[2], "defer time");
+ if (num < 1)
+ throw Error("Defer time may not be less than 1");
+ config.defertime(num);
+ answer_status();
+ return;
+ }
+
+ // /server/closesocketsfast/BOOL
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "closesocketsfast") {
+ config.fastclose(str2bool(parts[2], "close sockets fast"));
+ answer_status();
+ return;
+ }
+
+ // /server/addallowfrom/ADDRESS
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "addallowfrom") {
+ config.addallow(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/allowfrom/NR
+ // /server/allowfrom/NR/ADDRESS
+ if (parts.size() == 4 &&
+ parts[0] == "server" && parts[1] == "allowfrom") {
+ unsigned ind = str2uns(parts[2], "allowfrom index");
+ if (parts[3] != "")
+ config.changeallow(parts[3], ind);
+ else
+ config.deleteallow(ind);
+ answer_status();
+ return;
+ }
+
+ // /server/adddenyfrom/ADDRESS
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "adddenyfrom") {
+ config.adddeny(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/denyfrom/NR
+ // /server/denyfrom/NR/ADDRESS
+ if (parts.size() == 4 &&
+ parts[0] == "server" && parts[1] == "denyfrom") {
+ unsigned ind = str2uns(parts[2], "denyfrom index");
+ if (parts[3] != "")
+ config.changedeny(parts[3], ind);
+ else
+ config.deletedeny(ind);
+ answer_status();
+ return;
+ }
+
+ // /server/hardmaxconnexcess/
+ // /server/hardmaxconnexcess/PROGRAM
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "hardmaxconnexcess") {
+ config.hardmaxconnexcess(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/softmaxconnexcess/
+ // /server/softmaxconnexcess/PROGRAM
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "softmaxconnexcess") {
+ config.softmaxconnexcess(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/onstart/
+ // /server/onstart/PROGRAM
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "onstart") {
+ config.onstart(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/onend/
+ // /server/onend/PROGRAM
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "onend") {
+ config.onend(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/onfail/
+ // /server/onfail/PROGRAM
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "onfail") {
+ config.onfail(parts[2]);
+ answer_status();
+ return;
+ }
+
+ // /server/addbackend/IP:PORT
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "addbackend") {
+ vector<string> address = str2parts(parts[2], ':');
+ if (address.size() != 2)
+ throw Error("When adding back ends, the address must be IP:PORT");
+ Backend b;
+ b.server(address[0]);
+ b.port(str2uns(address[1], "back end port"));
+ balancer.addbackend(b, false, false, false);
+ answer_status();
+ return;
+ }
+
+ // /server/deletebackend/NR
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "deletebackend") {
+ balancer.deletebackend(backendindex(parts[2]));
+ answer_status();
+ return;
+ }
+
+ // /server/type/http, /server/type/tcp
+ if (parts.size() == 3 && parts[0] == "server" && parts[1] == "type") {
+ config.stype(parts[2]);
+ answer_status();
+ return;
+ }
+
// /backend/NR/weight/NUMBER
if (parts.size() == 4 &&
parts[0] == "backend" && parts[2] == "weight") {
unsigned ind = backendindex(parts[1]);
unsigned num = str2uns (parts[3], "back end weight");
if (num < 1)
- throw static_cast<Error>("Weight may not be less than 1");
+ throw Error("Weight may not be less than 1");
balancer.backend(ind).weight(num);
answer_status();
return;
@@ -281,10 +510,20 @@
// /backend/NR/hostmatch/EXPRESSION
// /backend/NR/hostmatch
- if ( (parts.size() == 3 || parts.size() == 4) &&
- (parts[0] == "backend" && parts[2] == "hostmatch") ) {
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "hostmatch") {
+ unsigned ind = backendindex(parts[1]);
+ balancer.backend(ind).hostmatch(parts[3]);
+ answer_status();
+ return;
+ }
+
+ // /backend/NR/urlmatch/EXPRESSION
+ // /backend/NR/urlmatch
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "urlmatch") {
unsigned ind = backendindex(parts[1]);
- balancer.backend(ind).hostmatch(parts.size() == 3 ? "" : parts[3]);
+ balancer.backend(ind).urlmatch(parts[3]);
answer_status();
return;
}
@@ -297,5 +536,49 @@
return;
}
- throw static_cast<Error>("No action for URI '") + uri + "'";
+ // /backend/NR/backendcheck/
+ // /backend/NR/backendcheck/VALUE
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "backendcheck") {
+ unsigned ind = backendindex(parts[1]);
+ BackendCheck check;
+ if (parts[3] != "")
+ check.parse(parts[3]);
+ balancer.backend(ind).backendcheck(check);
+ answer_status();
+ return;
+ }
+
+ // /backend/NR/stopconnections
+ if (parts.size() == 3 &&
+ parts[0] == "backend" && parts[2] == "stopconnections") {
+ unsigned ind = backendindex(parts[1]);
+ bool done = false;
+ while (!done) {
+ done = true;
+ for (Threadmap::iterator it = Threadlist::map().begin();
+ it != Threadlist::map().end();
+ it++) {
+ pthread_t thread_id = (*it).first;
+ Threadinfo thread_info = (*it).second;
+ if (thread_info.backend() == (int)ind) {
+ stop_backend_thread(thread_id);
+ done = false;
+ break;
+ }
+ }
+ }
+ answer_status();
+ return;
+ }
+
+ // /thread/kill/VALUE
+ if (parts.size() == 3 && parts[0] == "thread" && parts[1] == "kill") {
+ pthread_t id = str2threadid(parts[2], "thread id");
+ stop_backend_thread(id);
+ answer_status();
+ return;
+ }
+
+ throw Error("No action for URI '/" + uri + "'");
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/webinterface/answerblob.cc
^
|
@@ -11,5 +11,5 @@
"\r\n" +
blob;
Netbuffer buf(resp);
- buf.netwrite(cfd, config.client_timeout());
+ buf.netwrite(cfd, config.client_write_timeout());
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/webinterface/answerstatus.cc
^
|
@@ -11,14 +11,26 @@
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<?xml-stylesheet type=\"text/xsl\" href=\"/xslt\"?>\n"
"<status>\n"
+ " <id>\n"
+ " <version>" << VER << "</version>\n"
+ " <distsite>" << DISTSITE << "</distsite>\n"
+ " </id>\n"
" <server>\n"
" <address>" << config.sipaddr() << ":" << config.sport() << "</address>\n"
" <type>" << config.stypestr() << "</type>\n"
+ " <clientreadtimeout>" << config.client_read_timeout() << "</clientreadtimeout>\n"
+ " <clientwritetimeout>" << config.client_write_timeout() << "</clientwritetimeout>\n"
+ " <backendreadtimeout>" << config.backend_read_timeout() << "</backendreadtimeout>\n"
+ " <backendwritetimeout>" << config.backend_write_timeout() << "</backendwritetimeout>\n"
" <dispatchmode>" << config.dispatchmodestr() << "</dispatchmode>\n"
- " <maxconnections>" << config.maxconn() << "</maxconnections>\n"
- " <clienttimeout>" << config.client_timeout() << "</clienttimeout>\n"
- " <backendtimeout>" << config.backend_timeout() << "</backendtimeout>\n"
+ " <removereservations>" << config.removereservations() << "</removereservations>\n"
+ " <webinterface>" << config.webinterfaceip() << ':' << config.webinterfaceport() << "</webinterface>\n"
+ " <dnscachetimeout>" << config.dnscachetimeout() << "</dnscachetimeout>\n"
" <buffersize>" << config.buffersize() << "</buffersize>\n"
+ " <closesocketsfast>" << config.fastclose() << "</closesocketsfast>\n"
+ " <onstart>" << config.onstart() << "</onstart>\n"
+ " <onend>" << config.onend() << "</onend>\n"
+ " <onfail>" << config.onfail() << "</onfail>\n"
" <checks>\n"
" <wakeupinterval>" << config.wakeupsec() << "</wakeupinterval>\n"
" <checkupinterval>" << config.checkupsec() << "</checkupinterval>\n"
@@ -28,10 +40,40 @@
" <debug>" << config.debug() << "</debug>\n"
" <logtrafficdir>" << config.dumpdir() << "</logtrafficdir>\n"
" </debugging>\n"
+ " <dosprotection>\n"
+ " <maxconnections>" << config.maxconn() << "</maxconnections>\n"
+ " <timeinterval>" << config.connrate_time() << "</timeinterval>\n"
+ " <hardmaxconnrate>" << config.hardmaxconnrate() << "</hardmaxconnrate>\n"
+ " <softmaxconnrate>" << config.softmaxconnrate() << "</softmaxconnrate>\n"
+ " <defertime>" << config.defertime() << "</defertime>\n"
+ " <hardmaxconnexcess>" << config.hardmaxconnexcess() << "</hardmaxconnexcess>\n"
+ " <softmaxconnexcess>" << config.softmaxconnexcess() << "</softmaxconnexcess>\n"
+ " </dosprotection>\n"
+ " <acl>\n"
+ " <allow>\n";
+ for (unsigned i = 0; i < config.nallow(); i++)
+ o <<
+ " <allowfrom>\n"
+ " <nr>" << i << "</nr>\n"
+ " <mask>" << inet_ntoa(config.allow(i)) << "</mask>\n"
+ " </allowfrom>\n";
+ o <<
+ " </allow>\n"
+ " <deny>\n";
+ for (unsigned i = 0; i < config.ndeny(); i++)
+ o <<
+ " <denyfrom>\n"
+ " <nr>" << i << "</nr>\n"
+ " <mask>" << inet_ntoa(config.deny(i)) << "</mask>\n"
+ " </denyfrom>\n";
+ o <<
+ " </deny>\n"
+ " </acl>\n"
" <http>\n"
" <addxrversion>" << config.addxrversion() << "</addxrversion>\n"
" <addxforwardedfor>" << config.addxforwardedfor() << "</addxforwardedfor>\n"
" <stickyhttp>" << config.stickyhttp() << "</stickyhttp>\n"
+ " <replacehostheader>" << config.replacehostheader() << "</replacehostheader>\n"
" <serverheaders>\n"
;
for (unsigned i = 0; i < config.nserverheaders(); i++)
@@ -44,6 +86,9 @@
o <<
" </serverheaders>\n"
" </http>\n"
+ " <backends>" << balancer.nbackends() << "</backends>\n"
+ " <terminating>" << balancer.terminate() << "</terminating>\n"
+ " <connections>" << balancer.connections() << "</connections>\n"
" </server>\n"
;
@@ -59,11 +104,57 @@
" <live>" << balancer.backend(i).livestr() << "</live>\n"
" <available>" << balancer.backend(i).availablestr() << "</available>\n"
" <connections>" << balancer.backend(i).connections() << "</connections>\n"
+ " <connecterrors>" << balancer.backend(i).connecterrors() << "</connecterrors>\n"
" <bytesserved>" << balancer.backend(i).bytesserved() << "</bytesserved>\n"
" <clientsserved>" << balancer.backend(i).clientsserved() << "</clientsserved>\n"
" <hostmatch>" << balancer.backend(i).hostmatch() << "</hostmatch>\n"
+ " <urlmatch>" << balancer.backend(i).urlmatch() << "</urlmatch>\n"
+ " <backendcheck>" << balancer.backend(i).backendcheck().setting() << "</backendcheck>\n"
" </backend>\n"
;
+
+ o <<
+ " <activity>\n"
+ " <threadlist>\n";
+ unsigned nthreads = 0, max_open_files;
+ struct rlimit rl;
+ if (getrlimit(RLIMIT_NOFILE, &rl))
+ throw Error("Failed to get limit for open files");
+ max_open_files = unsigned(rl.rlim_cur);
+ for (Threadmap::iterator it = Threadlist::map().begin();
+ it != Threadlist::map().end();
+ it++) {
+ nthreads++;
+ pthread_t thread_id = (*it).first;
+ Threadinfo thread_info = (*it).second;
+ o <<
+ " <thread>\n"
+ " <id>" << thread_id << "</id>\n"
+ " <description>" << thread_info.desc() << "</description>\n"
+ " <backend>" << thread_info.backend() << "</backend>\n"
+ " <address>";
+ if (thread_info.backend() >= 0)
+ o << balancer.backend(thread_info.backend()).description();
+ o <<
+ "</address>\n"
+ " <duration>" << thread_info.timestamp().elapsed() << "</duration>\n"
+ " <clientip>" << inet_ntoa(thread_info.clientip()) << "</clientip>\n"
+ " </thread>\n";
+ }
+ /* The estimate of the number of used fd's is:
+ * Base is 5 (stdin/stdout/stderr, listen-fd for service, listen-fd
+ * for webinterface
+ * Plus 2 x #-threads (1 to client, 1 to backend)
+ * There will be fd's in use for shared libs etc., but we don't see
+ * those..
+ */
+ o <<
+ " </threadlist>\n"
+ " <threadcount>" << nthreads << "</threadcount>\n"
+ " <openfiles>" << nthreads * 2 + 5 << "</openfiles>\n"
+ " <maxopenfiles>" << max_open_files << "</maxopenfiles>\n"
+ " </activity>\n";
+
o <<
"</status>\n\n";
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/webinterface/execute.cc
^
|
@@ -3,13 +3,15 @@
void Webinterface::execute() {
int sfd;
+ Threadlist::desc("Web interface");
+
// Create the server socket, or retry infinitely.
while (true) {
try {
msg ("Starting web interface\n");
sfd = serversocket (config.webinterfaceip(),
config.webinterfaceport(),
- "web interface");
+ "web interface", Servertype::t_tcp);
} catch (Error const &e) {
cerr << e.what() << "(webinterface, retrying in a sec)\n";
sleep (1);
@@ -23,13 +25,19 @@
try {
Fdset fdset(0);
fdset.add (sfd);
- if (fdset.readable() == 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 )
+ (socklen_t *)&size)) < 0 )
+ warnmsg(Mstr("Web interface: failed to accept "
+ "network connection: ") +
+ Mstr(strerror(errno)) + "\n");
+ else {
serve ();
- socketclose(cfd);
+ socketclose(cfd);
+ }
}
} catch (Error const &e) {
cerr << e.what() << " (webinterface)\n";
@@ -46,11 +54,10 @@
"\r\n" <<
m.str();
Netbuffer buf(o.str());
- buf.netwrite(cfd, config.client_timeout());
+ buf.netwrite(cfd, config.client_write_timeout());
socketclose(cfd);
}
}
msg ("Web interface stopping.\n");
socketclose(sfd);
- shutdown (sfd, SHUT_RDWR);
}
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/webinterface/serve.cc
^
|
@@ -4,7 +4,7 @@
msg ((Mstr("Webinterface serving request on client fd ") + cfd) + "\n");
Httpbuffer clientrequest;
- clientrequest.netread(cfd, config.client_timeout());
+ clientrequest.netread(cfd, config.client_read_timeout());
msg ("Webinterface request: " + clientrequest.firstline() + "\n");
answer(clientrequest);
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xr/webinterface/webinterface
^
|
@@ -3,6 +3,7 @@
#include "sys/sys"
#include "ThreadsAndMutexes/thread/thread"
+#include "ThreadsAndMutexes/threadlist/threadlist"
#include "fdset/fdset"
#include "httpbuffer/httpbuffer"
#include "balancer/balancer"
|
[-]
[+]
|
Changed |
crossroads-stable.tar.gz/xrctl/xrctl
^
|
@@ -1,495 +1,855 @@
#!/usr/bin/perl
use strict;
+use Getopt::Std;
+use Term::ANSIColor qw(:constants);
-# Configuration section. Enter your favorite values here.
-# -------------------------------------------------------
+# Versioning
+my $VER = "__VER__";
-# Directory where PID stamp files are stored, named xr-{service}.pid
-my $piddir = '/var/run';
+# --------------------------------------------------------------------------
+# xrctl: used to start, stop, restart etc. the XR balancer.
-# 'ps' command that prints a process ID and the invoking command. The
-# following is right for Linux, MacOSX (Darwin) and Solaris.
-my $pscmd = '/bin/ps ax -o pid,command';
-$pscmd = '/usr/bin/ps -ef "pid comm"' if (`uname` =~ /SunOS/);
-
-# Use 'logger' to send output to syslog? 0 means no.
-my $use_logger = 1;
-
-# Directory where log files are stored, named xr-{service}.log. Used
-# when logger isn't available or wanted.
-my $logdir = '/var/log';
-
-# Max log file size in bytes (used by xrctl rotate). Used when logger isn't
-# available or wanted.
-my $maxlogsize = 100000;
-
-# Nr. of historical log files to keep (used by xrctl rotate). Used when logger
-# isn't available or wanted.
-my $loghistory = 10;
-
-# Paths where executables are searched.
-my @bindirs = qw(/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin
- /opt/local/bin /opt/local/sbin);
-
-# Services to balance. All non-default flags for XR must be specified. The
-# primary key into hash %services is a self-chosen name. xrctl will supply
-# --prefix-timestamp when logging to a bare file (when logger isn't used).
-# Also xrctl will supply --pidfile for run control purposes.
-# The strings like --server are passed plaintext to the XR invocation. The
-# only exception is --host-match - in that case, there's a more complex
-# configuration that also states all back ends.
-# See the examples below to help you model your favorite dispatcher.
-my %services =
- (
-
- # Web servers balancing to 3 back ends at 10.0.0.1 thru 3. The
- # balancer will use HTTP mode and add X-Forwarded-For headers.
- 'webone' =>
- { '--server' => [ qw(http:0:80) ],
- '--backend' => [ qw(10.0.0.1:80 10.0.0.2:80 10.0.0.3:80) ],
- '--verbose' => undef,
- '--add-x-forwarded-for' => undef,
- },
-
- # Web servers balancing to 3 back ends at 10.1.1.1 thru 3. The
- # balancer will use HTTP mode, add X-Forwarded-For headers, and make the
- # HTTP sessions sticky to their back ends. NOTE - this server starts on
- # port 81 for demo purposes (or it would interfere with webone above).
- 'webtwo' =>
- { '--server' => [ qw(http:0:81) ],
- '--backend' => [ qw(10.1.1.1:80 10.1.1.2:80 10.1.1.3:80) ],
- '--verbose' => undef,
- '--add-x-forwarded-for' => undef,
- '--sticky-http' => undef,
- },
-
- # An Access Control List (ACL) example, again using web balancing.
- # Allowed clients are 127.0.0.1 (localhost) and 192.168.*.*, except
- # for 192.168.1.250. Also, flag -C / --close-sockets-fast is added to
- # avoid TIME_WAIT states under heavy load.
- 'webthree' =>
- { '--server' => [ qw(http:0:82) ],
- '--backend' => [ qw(10.1.1.1:80 10.1.1.2:80 10.1.1.3:80) ],
- '--verbose' => undef,
- '--add-x-forwarded-for' => undef,
- '--allow-from' => [ qw(127.0.0.1 192.168.255.255) ],
- '--deny-from' => [ qw(192.168.1.250) ],
- '--close-sockets-fast' => undef,
- },
-
- # Multi-hosting two websites. Site "www.onesite.org" has two back ends
- # in the 10.1.1 series, "www.othersite.org" has two back ends in the
- # 10.1.9 series. Note that the server mode must be http to use this.
- 'webfour' =>
- { '--server' => [ qw(http:0:82) ],
- '--host-match' => { 'onesite' => [ qw(10.1.1.1:80
- 10.1.1.2:80) ],
- 'othersite' => [ qw(10.1.9.1:80
- 10.1.9.2:80) ],
- },
- '--verbose' => undef,
- # Other options as --allow-from etc. can also be added here
- },
-
- # An SSH session balancer on port 2222. We set the client time out
- # to 2 hours. Requests are balanced to server1, server2 and server3,
- # all to port 22.
- 'ssh' =>
- { '--server' => [ qw(tcp:0:2222) ],
- '--backend' => [ qw(server1:22 server2:22 server3:22) ],
- '--verbose' => undef,
- '--client-timeout' => [ qw(7200) ],
- },
-
- # Windows Remote Desktop Protocol (RDP) balancing. Windows supports
- # only one concurrent client, and we don't want new connections to 'steal'
- # existing sessions - so we set the max connections of each back end to 1.
- 'rdp' =>
- { '--server' => [ qw(tcp:0:3389) ],
- '--backend' => [ qw(win1:3389:1 win2:3389:1 win33:3389:1) ],
- '--verbose' => undef,
- '--client-timeout' => [ qw(7200) ],
- },
-
- # A HTTP forwarder for travelling. Depending on the site where I plug
- # in, this reaches some proxy - or localhost:3128, which is a local squid.
- # I configure my browser to use localhost:8080 as proxy, and don't have
- # to reconfigure browsers anymore. Note the dispatch method which is
- # first-available, the first downstream proxy that works is OK for me.
- # Note also that the server type is TCP, I don't need HTTP goodies.
- # Also the server listens to 127.0.0.1 - only for localhost usage.
- 'proxy' =>
- { '--server' => [ qw(tcp:127.0.0.1:8080) ],
- '--backend' => [ qw(10.120.114.2:8080 192.168.1.250:80
- localhost:3128) ],
- '--dispatch-mode' => [ qw(first-available) ],
- '--verbose' => undef,
- },
-
- # Simple tunnel to easily access an external proxy at 10.1.1.250. The
- # proxy requires authentication, user 'user', password 'secret', which
- # is base64-encoded: dXNlcjpzZWNyZXQ=
- # Next I can "export http_proxy localhost:8090" and use wget etc. without
- # typing the proxy credentials.
- 'authproxy' =>
- { '--server' => [ qw(http:127.0.0.1:8090) ],
- '--backend' => [ qw(10.1.1.250:80) ],
- '--add-server-header' => [ 'Proxy-Authorization: '.
- 'Basic dXNlcjpzZWNyZXQ=' ],
- '--verbose' => undef,
- '--debug' => undef,
- },
- );
-
-# Main starts here, configuration ends.
-# -------------------------------------
-
-# Get the action
-my $action = shift (@ARGV);
-
-# Prepare service list unless given on the command line
-if ($#ARGV == -1) {
- push (@ARGV, sort (keys (%services)));
-} else {
- for my $s (@ARGV) {
- die ("xrctl: No such service $s\n") unless ($services{$s});
+# Default configuration file to read and default logging facility
+my $default_conf = '/etc/xrctl.xml';
+my $default_logger = 'logger';
+my $default_prefixtimestamp = undef;
+
+# Default settings, must match xr's defaults
+my $default_dispatchmode = 'least-connections';
+my $default_maxconnections = 0;
+my $default_client_timeout = 30;
+my $default_client_read_timeout = 30;
+my $default_client_write_timeout = 30;
+my $default_backend_timeout = 30;
+my $default_backend_read_timeout = 3;
+my $default_backend_write_timeout = 3;
+my $default_buffersize = 2048;
+my $default_wakeupinterval = 5;
+my $default_checkupinterval = 0;
+my $default_weight = 1;
+my $default_hostmatch = '.';
+my $default_urlmatch = '.';
+my $default_backendcheck = 'connect::';
+my $default_timeinterval = 1;
+my $default_hardmaxconnrate = 0;
+my $default_softmaxconnrate = 0;
+my $default_defertime = 500000;
+my $default_hardmaxconnexcess = 0;
+my $default_softmaxconnexcess = 0;
+my $default_dnscachetimeout = 3600;
+
+# Cmd line flags
+my %opts = (v => 0,
+ c => $default_conf,
+ );
+usage() unless (getopts('vc:', \%opts));
+usage() if ($#ARGV == -1);
+
+# Load configuration
+my $xml;
+open (my $if, $opts{c}) or die ("Cannot read configuration $opts{c}: $!\n");
+while (my $line = <$if>) {
+ $xml .= $line;
+}
+close ($if);
+my $xp = new XMLParser($xml);
+
+# Load up the system config.
+my %sysconf;
+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 '');
+ }
+ if ($sysconf{path} eq '') {
+ msg ("No path in configuration, using environment\n");
+ $sysconf{path} = $ENV{PATH};
+ }
+ if ($sysconf{logger} ne 'logger') {
+ msg ("Using non-default logger\n");
+ $default_logger = $sysconf{logger};
+ }
+ if ($sysconf{pscmd} eq '') {
+ $sysconf{pscmd} = xfind_bin('ps');
+ if (`uname` =~ /SunOS/) {
+ $sysconf{pscmd} .= ' -ef pid,comm';
+ } else {
+ $sysconf{pscmd} .= ' ax -o pid,command';
+ }
+ }
+ msg ("PS command: $sysconf{pscmd}\n");
+
+ if ($sysconf{prefixtimestamp}) {
+ $default_prefixtimestamp = 1 if istrue($sysconf{prefixtimestamp});
+ } else {
+ $default_prefixtimestamp = 1
+ if (!istrue($sysconf{uselogger}) or !find_bin('logger'));
}
+ msg ("Log lines will be prefixed with a timestamp\n")
+ if ($default_prefixtimestamp);
}
-# Verify the configuration
-verifyconf (@ARGV);
-
-# Take appropriate action
-if ($action eq 'list') {
- list(@ARGV);
-} elsif ($action eq 'status') {
- status(@ARGV);
-} elsif ($action eq 'start') {
- start(@ARGV);
-} elsif ($action eq 'stop') {
- stop(@ARGV);
-} elsif ($action eq 'force') {
- force(@ARGV);
-} elsif ($action eq 'rotate') {
- rotate(@ARGV);
-} elsif ($action eq 'restart') {
- restart(@ARGV);
+# Load up the service names.
+my @service_name;
+for (my $i = 0; ; $i++) {
+ my $serviceblock = $xp->data('service', $i) or last;
+ my $servicexp = new XMLParser($serviceblock)
+ or die ("No <service> blocks in configuration\n");
+ my $name = $servicexp->data('name')
+ or die ("<service> block lacks <name>\n");
+ push (@service_name, $name);
+ msg ("Service '$name' seen\n");
+}
+die ("No service blocks seen\n") if ($#service_name == -1);
+
+# Take action
+$|++;
+my $cmd = shift(@ARGV);
+@ARGV = @service_name if ($#ARGV == -1);
+msg ("Acting on command: $cmd\n");
+if ($cmd eq 'list') {
+ cmd_list(@ARGV);
+} elsif ($cmd eq 'start') {
+ cmd_start(@ARGV);
+} elsif ($cmd eq 'stop') {
+ cmd_stop(@ARGV);
+} elsif ($cmd eq 'kill') {
+ cmd_kill(@ARGV);
+} elsif ($cmd eq 'force') {
+ cmd_force(@ARGV);
+} elsif ($cmd eq 'stopstart') {
+ cmd_stopstart(@ARGV);
+} elsif ($cmd eq 'killstart') {
+ cmd_killstart(@ARGV);
+} elsif ($cmd eq 'status') {
+ cmd_status(@ARGV);
+} elsif ($cmd eq 'rotate') {
+ cmd_rotate(@ARGV);
+} elsif ($cmd eq 'configtest') {
+ cmd_configtest(@ARGV);
+} elsif ($cmd eq 'generateconfig') {
+ cmd_generateconfig(@ARGV);
} else {
- usage();
+ die ("Missing or unknown action $cmd\n");
}
-# Show usage and stop
-# -------------------
-sub usage() {
- die <<"ENDUSAGE";
+# --------------------------------------------------------------------------
+# Top level commands
-Usage: xrctl list [SERVICE] - show configuration of a service, or of all
- xrctl start [SERVICE] - start a service, or all configured services
- xrctl stop [SERVICE] - stop a service, or all configured services
- xrctl force [SERVICE] - start a service (or all) if not running
- xrctl restart [SERVICE] - stop and start a service, or all
- xrctl status [SERVICE] - show running status of a service, or of all
- xrctl rotate [SERVICE] - rotate logs of a service or of all
+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");
+ }
+}
-ENDUSAGE
+sub cmd_start {
+ for my $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");
+ }
}
-# List services and command lines
-# -------------------------------
-sub list {
- print ("Configured services: ",
- join (', ', sort (keys (%services))),
- "\n");
+sub cmd_stop {
+ my @pids;
for my $s (@_) {
- print ("Service $s:\n",
- " Process : ", "xr-$s\n",
- " PID file : ", pidfile($s), "\n",
- " Log file : ", logfile($s), "\n",
- " XR command:");
- my @parts = xrcommand($s);
- for (my $i = 0; $i <= $#parts; $i++) {
- next if ($i == 1);
- my $p = $parts[$i];
- $p = "'$p'" if ($p =~ /\s/);
- print (" $p");
- }
- print ("\n");
+ 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");
}
+ kill (15, @pids) if ($#pids > -1);
+ print ("Services @_: stopped\n");
}
-# Show the status of the commands
-# -------------------------------
-sub status {
+sub cmd_kill {
+ my @pids;
for my $s (@_) {
- die ("xrctl: No such service '$s'\n") unless ($services{$s});
- print ("Service $s: ", getstatus($s), "\n");
+ 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");
+ }
+ kill (9, @pids) if ($#pids > -1);
+ print ("Services @_: killed\n");
}
-# Start service(s)
-# ----------------
-sub start {
+sub cmd_force {
for my $s (@_) {
print ("Service $s: ");
- my $status = getstatus($s);
- if ($status !~ /^not/) {
- print ("already $status\n");
+ if (is_running($s)) {
+ print ("already running\n");
} else {
- rundaemon ($s, xrcommand($s));
+ start_service($s);
print ("started\n");
}
}
}
-# Stop service(s)
-# ---------------
-sub stop {
+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);
+ }
+ print ("Service(s) @_: ");
+ kill (15, @pids) if ($#pids > -1);
+ print ("stoppped\n");
for my $s (@_) {
print ("Service $s: ");
- my $status = getstatus($s);
- if ($status =~ /^not/) {
- print ($status, "\n");
- } else {
- my $pid = servicebypidfile($s);
- $pid = servicebypslist($s) unless ($pid);
- die ("Failed to get PID\n") unless ($pid);
- kill (15, $pid);
- print ("stopping\n");
- }
+ start_service($s);
+ print ("started\n");
}
}
-# Restart service(s)
-# ------------------
-sub restart {
+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);
+ }
+ print ("Service(s) @_: ");
+ kill (9, @pids) if ($#pids > -1);
+ print ("killed\n");
for my $s (@_) {
print ("Service $s: ");
- my $status = getstatus($s);
- if ($status =~ /^not/) {
- print ($status, "\n");
- } else {
- my $pid = servicebypidfile($s);
- $pid = servicebypslist($s) unless ($pid);
- die ("Failed to get PID\n") unless ($pid);
- kill (15, $pid);
- rundaemon ($s, xrcommand($s));
- print ("restarted\n");
- }
+ start_service($s);
+ print ("started\n");
}
}
-# Force service(s) up
-# -------------------
-sub force {
+sub cmd_status {
for my $s (@_) {
print ("Service $s: ");
- my $status = getstatus($s);
- if ($status =~ /^not/) {
- rundaemon ($s, xrcommand($s));
- print ("started\n");
- } else {
- print ($status, "\n");
- }
+ print (BOLD, RED, "not ", RESET) unless (is_running($s));
+ print ("running\n");
}
}
-# Rotate logs
-# -----------
-sub rotate {
- if ($use_logger and findbin('logger')) {
- print ("Rotating disabled, logging via logger/syslog\n");
+sub cmd_rotate {
+ if (istrue($sysconf{uselogger}) and find_bin($default_logger)) {
+ print ("Rotating not necessary, logging goes via logger\n");
return;
}
for my $s (@_) {
print ("Service $s: ");
- my $f = logfile($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 ("no logfile $f\n");
- } elsif ((stat($f))[7] < $maxlogsize) {
+ print ("not present\n");
+ next;
+ }
+ if ((stat($f))[7] < $sysconf{maxlogsize}) {
print ("no rotation necessary\n");
- } else {
- unlink ("$f.$loghistory", "$f.$loghistory.bz2",
- "$f.$loghistory.gz");
- for (my $i = $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 = findbin("bzip2") or
- $zipper = findbin("gzip")) {
- system ("$zipper $f.0");
- print (", zipped");
- }
- print ("\n");
- restart($s);
+ 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");
}
}
-# Verify a configuration
-# ----------------------
-sub verifyconf {
+sub cmd_configtest {
for my $s (@_) {
- my @p = xrcommand($s);
- my $cmd = "$p[0] -n";
- for my $i (2..$#p) {
- $cmd .= " '$p[$i]'";
- }
+ print ("Service $s: ");
+ my $cmd = xr_command($s) . ' --tryout';
if (system ($cmd)) {
- die ("xrctl: Configuration of service '$s' probably bad\n",
- "Testing command was:\n",
- " $cmd\n");
+ print ("FAILED, command: $cmd\n");
+ } else {
+ print ("configuration ok\n");
}
}
}
-# Get the status of one balancer service
-# --------------------------------------
-sub getstatus($) {
- my $s = shift;
- die ("xrctl: No such service '$s'\n") unless ($services{$s});
- my $fpid = servicebypidfile($s);
- my $ppid = servicebypslist($s);
+sub cmd_generateconfig {
+ print ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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 (" </system>\n");
- # print ("getstatus: fpid=$fpid, ppid=$ppid\n");
-
- if (! $fpid and ! $ppid) {
- return ("not running");
- } elsif ($fpid == $ppid) {
- return ("running");
- } elsif ($fpid and ! $ppid) {
- return ("not running (stale pidfile found)");
- } elsif (! $fpid and $ppid) {
- return ("running (but no pidfile found)");
- } else {
- return ("running (stale pidfile found)");
+ for my $s (@_) {
+ generateconfig($s);
}
+
+ print ("</configuration>\n");
}
-# Return a command to start XR
-# ----------------------------
-sub xrcommand ($) {
+
+# --------------------------------------------------------------------------
+# Small utility functions
+
+# Show usage and die.
+sub usage() {
+ die <<"ENDUSAGE";
+
+This is xrctl V$VER, the control script for XR, the Crossroads Load Balancer.
+Usage: xrctl [-FLAGS] action [SERVICE ...]
+Flags are:
+ -v increases verbosity
+ -c CONFIG specifies the configuration, default $default_conf
+Actions are:
+ configtest validates the configuration
+ list shows the xr command line
+ start starts the service(s) if they are not yet running
+ stop gracefully stops the service(s) if they are running
+ kill brutally kills the service(s), interrupting all connections
+ force forces the service(s) up: starts if not running
+ stopstart gracefully restarts the service(s) if they are running
+ killstart brutally restarts
+ status shows which services are running
+ rotate rotates logs of the service(s)
+ generateconfig queries running XR's for the current configuration and
+ shows it in the format of $default_conf
+The SERVICES following an action are the services stated in the configuration.
+When absent, all configured services are handled.
+
+ENDUSAGE
+}
+
+# Is a service running?
+sub is_running {
my $s = shift;
- my $xr = findbin('xr') or die ("xrctl: Failed to find xr along @bindirs\n");
- my %opts = %{ $services{$s} };
- my @ret = ($xr, "xr-$s", "--pidfile", pidfile($s));
- push (@ret, "--prefix-timestamp") if (! $use_logger or
- ! findbin('logger'));
- for my $o (sort (keys (%opts))) {
- if ($o eq '--host-match') {
- my %def = %{ $opts{$o} };
- for my $host (sort (keys (%def))) {
- push (@ret, '--host-match', $host);
- for my $b(@{ $def{$host} }) {
- push (@ret, '--backend', $b);
- }
- }
- } elsif (! $opts{$o}) {
- push (@ret, $o);
- } else {
- my @args = @{ $opts{$o} };
- if ($#args == -1) {
- push (@ret, $o);
- } else {
- for my $arg (@args) {
- push (@ret, $o);
- push (@ret, $arg);
- }
- }
+ open (my $if, "$sysconf{pscmd} |")
+ 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");
}
}
return (@ret);
}
-# Return the PID file of a given service
-# --------------------------------------
-sub pidfile ($) {
+# Unconditionally start a given service
+sub start_service {
my $s = shift;
- return ("$piddir/xr-$s.pid");
+ my $xr = xfind_bin('xr');
+ my @args = xr_cmdarr($s);
+ my $logstr = log_file($s);
+ my $logtype = substr($logstr, 0, 1);
+ my $logout = substr($logstr, 1);
+
+ # Try out the command line
+ my $cmdline = xr_command($s) . ' --tryout';
+ system ($cmdline)
+ and die ("Command line '$cmdline' fails to parse\n");
+
+ my $pid = fork();
+ die ("Cannot fork: $!\n") unless (defined ($pid));
+ return if ($pid > 0);
+
+ # Child branch
+ 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");
+ } else {
+ 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);
}
-# Examine the contents of a PID file
-# ----------------------------------
-sub servicebypidfile($) {
- my $s = shift;
- my $p = pidfile($s);
- if (open (my $f, $p)) {
- my $pid = <$f>;
- chomp ($pid);
- return ($pid);
+# Verbose message.
+sub msg {
+ return unless ($opts{v});
+ print (@_);
+}
+
+# Find a binary along the path
+sub find_bin {
+ my $bin = shift;
+ my @parts = split (/\s/, $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));
+ }
+ }
+ msg ("Binary '$bin' not found along $sysconf{path}\n");
+ return (undef);
+}
+sub xfind_bin {
+ my $bin = shift;
+ my $ret = find_bin ($bin)
+ or die ("Binary '$bin' cannot be found along path '$sysconf{path}'\n");
+ return ($ret);
+}
+
+# Process name according to a service name
+sub process_name {
+ my $service = shift;
+ return ("xr-$service");
+}
+
+# Log file according to a service name
+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");
+ }
} else {
- return (undef);
+ return ('>' . $sysconf{logdir} . '/' .
+ process_name($service) . '.log');
}
}
-# Get the PID of a service using the PS list
-# -------------------------------------------
-sub servicebypslist($) {
- my $s = shift;
- open (my $if, "$pscmd |") or die ("xrctl: Cannot start '$pscmd': $!\n");
- while (my $line = <$if>) {
- chomp ($line);
- my $p = sprintf ("%d", $line);
- next unless ($p);
- my $c = $line;
- $c =~ s/^[\d\s]*//;
- # print ("LF [$s], p=[$p], c=[$c]\n");
- return ($p) if ($c =~ /^[^\s]*xr-$s/);
+# XR command according to a service name as one string
+sub xr_command {
+ my $service = shift;
+ my @parts = xr_cmdarr($service);
+ msg ("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+$//;
+ $sub = "'$sub'" if ($sub =~ /\s/);
+ $ret .= ' ' . $sub;
+ }
+ msg ("Quoted command: $ret\n");
+ return ($ret);
+}
+
+# XR command according to a service name as an array, including ARGV[0]
+# pseudo-name
+sub xr_cmdarr {
+ my $service = shift;
+
+ my @cmd;
+ push (@cmd, "xr-$service");
+ push (@cmd, '--prefix-timestamp')
+ if ($default_prefixtimestamp);
+
+ # Fetch the <service> block for this service
+ my $sp = xml_serviceparser($service)
+ or die ("Failed to locate <service> block for service '$service'\n");
+
+ # Service descriptions inside the <server> block
+ my $ss = xml_serverparser($sp);
+ my $type = 'tcp';
+ $type = $ss->data('type') if ($ss->data('type'));
+ my $addr = '0:10000';
+ $addr = $ss->data('address') if ($ss->data('address'));
+ my $full = "$type:$addr";
+ push (@cmd, '--server', $full) if ($full ne 'tcp:0:10000');
+
+ # 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');
+
+ # Handle general flags and boolflags
+ push (@cmd,
+ flag($ss, '--web-interface', 'webinterface', ''),
+ 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)));
+ }
+
+ # 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);
+ }
+ $t = $ss->data('backendreadtimeout');
+ if (defined($t)) {
+ 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);
+ }
+ for (my $i = 0; ; $i++) {
+ 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')));
+ push (@cmd, '--add-x-forwarded-for')
+ if ($ss->data('addxforwardedfor') and
+ istrue($ss->data('addxforwardedfor')));
+ push (@cmd, '--sticky-http')
+ if ($ss->data('stickyhttp') and
+ istrue($ss->data('stickyhttp')));
+ push (@cmd, '--replace-host-header')
+ if ($ss->data('replacehostheader') and
+ istrue($ss->data('replacehostheader')));
+ for (my $i = 0; ; $i++) {
+ my $h = $ss->data('header', $i) or last;
+ push (@cmd, '--add-server-header', $h);
+ }
+
+ # The <backend> blocks for this service
+ my $last_hostmatch = $default_hostmatch;
+ 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);
+ }
+
+ # All done
+ my @ret;
+ # msg("Generated flags/arguments:\n");
+ for my $c (@cmd) {
+ if ($c ne '') {
+ push (@ret, $c);
+ # msg (" $c");
+ }
+ }
+ # msg ("\n");
+
+ return (@ret);
+}
+
+# Prepare a flag for the command line if it is defined and if it is
+# not equal to the default
+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));
}
return (undef);
}
-# Return the log file of a given service
-# --------------------------------------
-sub logfile($) {
- my $s = shift;
- return ('logger') if ($use_logger and findbin('logger'));
- return ("$logdir/xr-$s.log");
+# Is a boolean value true
+sub istrue {
+ my $val = shift;
+ return (1) if ($val eq 'true' or $val eq 'on' or
+ $val eq 'yes' or $val != 0);
+ return (undef);
}
-# Find a binary along the path
-# -----------------------------
-sub findbin ($) {
- my $b = shift;
- for my $d (@bindirs) {
- return ("$d/$b") if (-x "$d/$b");
+# Fetch an XMLParser for a <service> block given a service name
+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);
}
return (undef);
}
-# Run a command as a daemon
-# -------------------------
-sub rundaemon {
+# Fetch an XMLParser for a <server> block given a service parser
+sub xml_serverparser {
+ my $serviceparser = shift;
+ my $xml = $serviceparser->data('server') or return undef;
+ return new XMLParser($xml);
+}
+
+# Fetch an XMLParser for a <backend> block given a service parser and
+# an order number
+sub xml_backendparser {
+ my ($serviceparser, $order) = @_;
+ $order = 0 unless ($order);
+ my $xml = $serviceparser->data('backend', $order) or return (undef);
+ return (new XMLParser($xml));
+}
+
+# Generate a service configuration from the running XR, if it has a
+# web interface
+sub generateconfig {
my $s = shift;
- my @args = @_;
+ msg ("Generating runtime configuration for service '$s'\n");
- my $logger = findbin('logger');
- my $outfile = logfile($s);
-
- my $pid = fork();
- return if ($pid > 0);
+ my $sp = xml_serviceparser($s) or die ("No service '$s' known.\n");
+ my $webint = $sp->data('webinterface');
+ $webint =~ s/^0:/localhost:/;
+
+ if ($webint eq '') {
+ print ("\n",
+ " <!-- Configuration for service $s not generated,\n",
+ " no web interface known -->\n");
+ return;
+ }
- # Child branch
- close (STDIN);
- open (STDIN, "/dev/null")
- or die ("xrctl (daemon): Can not reopen stdin to /dev/null: $!\n");
- if ($use_logger and $logger) {
- open (STDOUT, "|$logger -t xr-$s")
- or die ("xrctl (daemon): Cannot reopen stdout to logger: $!\n");
- open (STDERR, "|$logger -t xr-$s")
- or die ("xrctl (daemon): Cannot reopen stdout to logger: $!\n");
+ print ("\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");
} else {
- open (STDOUT, ">>$outfile")
- or die ("xrctl (deamon): Cannot reopen stdout to $outfile: $!\n");
- open (STDERR, ">>$outfile")
- or die ("xrctl (deamon): Cannot reopen stderr to $outfile: $!\n");
+ 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();
+ }
+
+ # 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");
+ }
}
- my $truecmd = shift (@args);
- exec ({ $truecmd } @args);
- exit (1);
+
+ print (" </service>\n");
}
+
+# --------------------------------------------------------------------------
+# Idiotically simple XML parser. Used instead of a "real" parser so that
+# xrctl isn't dependent on modules and can run anywhere. Safe for using
+# with xr-style XML configs, but not with any XML in the free.
+
+package XMLParser;
+sub new {
+ my ($proto, $doc) = @_;
+ my $self = {};
+ die ("Missing XML document\n") unless($doc);
+
+ my $docstr = '';
+ for my $p (split (/\n/, $doc)) {
+ $docstr .= $p;
+ }
+
+ # Whitespace between tags is trash
+ $docstr =~ s{>\s+<}{><}g;
+
+ # 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;
+ }
+ }
+ }
+
+ # Activity logs is trash
+ $docstr =~ s{<activity>.*</activity>}{}g;
+
+ # print $docstr, "\n";
+
+ $self->{xml} = $docstr;
+ bless ($self, $proto);
+
+ return ($self);
+}
+
+sub data {
+ my ($self, $tag, $order) = @_;
+ 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");
+ }
+ return ($ret);
+}
+
+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);
+ }
+ return (undef);
+}
+
+sub _findlast {
+ my ($stack, $needle) = @_;
+ for (my $i = length($stack); $i >= 0; $i--) {
+ return ($i) if (substr($stack, $i, length($needle)) eq $needle);
+ }
+ return (undef);
+}
+
+1;
|