[-]
[+]
|
Changed |
crossroads.spec
|
|
[-]
[+]
|
Changed |
crossroads-devel.ChangeLog
^
|
@@ -1,3 +1,191 @@
+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)
+- SIGPIPE gets ignored, see sys/main.cc
+- Fixed re-entrancy issues for gethostbyname() that applies to some
+ unices. See Backend::connect() (xr/backend/connect.cc).
+
+2.22 [KK 2008-10-16]
+- Implemented up/down state in back ends. Fixed up the docs.
+- Rewrote msg() and debugmsg() handling: these are now macros that
+ call _msg() and _debugmsg() depending on verbose/debug flags. The
+ argument is a Mstr object, used to concatenate strings with ints,
+ doubles and so on.
+
+2.21 [KK 2008-10-14]
+- Fixed round-robin dispatching with only 1 configured back end. The
+ bug was that on the next-time around, no "other" back end would be
+ found.
+- Centralized reading/writing from fd's and buffer handling into class
+ Netbuffer. This removes superfluous buffer copying in the HTTP
+ dispatcher.
+- Rewrote HTTP dispatcher & (hopefully) optimized it.
+- Added Profiler class and xr/etc/xrprof tool.
+- Many other small changes.
+- Added docs on maxconn flags and weighted-load dispatching.
+- THIS VERSION IS A REWRITE OF THE HTTP DISPATCHER. IT IS ALPHA-QUALITY.
+
+2.20 [KK 2008-10-13]
+- Fixed -C flag recognition, --close-sockets-fast worked, but I'd
+ omitted -C from the flag set.
+- Ongoing optimization work.
+
+2.19 [KK 2008-10-13]
+- Changed the TCP Dispatcher to allow for setting a maximum #
+ connection attempts per client, with options to either defer the
+ connection or drop it.
+- Added a dispatch mdoe "weighted-load" for randomly picking a back
+ end based on the inverse of their respective load averages. (gem)
+- Verbose/debug options in the web interface are now rendered as
+ select boxes.
+- Web interface errors are rendered on the client as an HTML error
+ page.
+- Docs updated regarding web interface URI's.
+- Turned on compliler optimizing (had forgotten that before!)
+- Docs updated regarding web interface URI's.
+
+2.18 [KK 2008-10-08]
+- Improved command line parsing, so that when --checkup-interval is
+ used, suppression of "--wakeup-interval 0" is no longer needed.
+
+2.17 [KK 2008-10-07]
+- Type sizes reported in "xr -V"
+- Fixed nasty bug in sys/fdwrite.cc
+
+2.16 [KK 2008-10-05]
+- Enhanced web interface to show debug, verbose and traffic log states
+- Altering parameters for the web interface get sent in encoded form
+- Option --log-traffic was renamed to --log-traffic-dir for
+ consistency
+- Enhanced web interface to modify client and back end timeouts
+- Enhanced web interface to modify wakeup / checkup intervals.
+
+2.15
+- Sanity checks in Config::parsecmdline(): -w/-c together throws error.
+- Network sends are now using write(), unless under Solaris, which
+ uses send().
+- --log-traffic logs to file with as the base name: the request
+ number.
+- System (uname) is recorded during compilation and displayed in "xr -V"
+
+2.14 [KK 2008-09-30]
+- Removed spurious debug message.
+- Fixed usage info for buffer size flag (should be -B, not -b).
+- Implemented flag -l (--log-traffic).
+
+2.13 [KK 2008-09-17]
+- Porting issues resolved for older MacOSX (10.3, 10.4)
+- Bugfix in XML emitting of web interface. Most browsers didn't even notice..
+
+2.12 [KK 2008-09-10]
+- Small code changes for g++ v3.x backward-compatibility support.
+ (Thanks Simon M.)
+- Web interface: layout enhanced, more modification options
+- Code cleanup, duplications removed
+- Network sending ignores some errno's and retries (see
+ sys/fdwrite.cc)
+- Web interface retries binding to its socket (incase a previous
+ instance hasn't terminated yet)
+- Web interface returns an HTTP error page (status 500 only, no
+ content) during errors
+
+2.11 [KK 2008-09-04]
+- Bugfix in "first-active" dispatch mode. Previously XR would gobble
+ up fd's when no back end was available.
+- Web interface (first version) implemented.
+
+2.10 [KK 2008-09-02]
+- Bugfix in host match mode. When a back end doesn't match anything,
+ then the client isn't served. (Previously the dispatcher would fall
+ back to least-connections over all known back ends.)
+- In HTTP mode, a 502 error gets returned to the client when
+ dispatching fails or when back end processing goes haywire. There is
+ just one error page, 502 error header, 0 bytes content length.
+- Implemented back end weights for least-connections dispatching.
+- Bugfix in Backend::connect(): Socket gets closed when connecting fails.
+
+2.09 [KK 2008-09-01]
+--host-match code implemented
+
+2.08 [KK 2008-08-31]
+I'd forgotten to include the 'P' into the set of allowed flags
+(--prefix-timestamp would work, -P not). Fixed.
+
+2.07 [KK 2008-08-28]
+Stupid bug in 2.06, sorry that 2.06 got out.. Fixed.
+
+2.06 [KK 2008-08-27]
+Upped c-conf to 1.14.
+xrctl updated: 'ps' command format also suitable for SunOS.
+New class Mutex implemented. Mutex-locks are now more fine-grained
+(per one global or static).
+Solaris9 port w/gcc 3.4.6 done.
+
2.05 [KK 2008-08-15]
Thrown errors now based on std::exeception.
"xr -V" shows compilation settings.
@@ -9,7 +197,7 @@
strict and lax.
Flag -n / --tryout implemented. Implemented in xrctl.
-2.03 [KK 2008-08-10]
+2.03 [KK 2008-08-10]
Updated docs regarding the mailing list.
Fixed verbose display upon accepting a client ("current back end
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/ChangeLog
^
|
@@ -1,12 +1,123 @@
-2.18 [KK 2008-10-08]
+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)
+- SIGPIPE gets ignored, see sys/main.cc
+- Fixed re-entrancy issues for gethostbyname() that applies to some
+ unices. See Backend::connect() (xr/backend/connect.cc).
+
+2.22 [KK 2008-10-16]
+- Implemented up/down state in back ends. Fixed up the docs.
+- Rewrote msg() and debugmsg() handling: these are now macros that
+ call _msg() and _debugmsg() depending on verbose/debug flags. The
+ argument is a Mstr object, used to concatenate strings with ints,
+ doubles and so on.
+
+2.21 [KK 2008-10-14]
+- Fixed round-robin dispatching with only 1 configured back end. The
+ bug was that on the next-time around, no "other" back end would be
+ found.
+- Centralized reading/writing from fd's and buffer handling into class
+ Netbuffer. This removes superfluous buffer copying in the HTTP
+ dispatcher.
+- Rewrote HTTP dispatcher & (hopefully) optimized it.
+- Added Profiler class and xr/etc/xrprof tool.
+- Many other small changes.
+- Added docs on maxconn flags and weighted-load dispatching.
+- THIS VERSION IS A REWRITE OF THE HTTP DISPATCHER. IT IS ALPHA-QUALITY.
+
+2.20 [KK 2008-10-13]
+- Fixed -C flag recognition, --close-sockets-fast worked, but I'd
+ omitted -C from the flag set.
+- Ongoing optimization work.
+
+2.19 [KK 2008-10-13]
+- Changed the TCP Dispatcher to allow for setting a maximum #
+ connection attempts per client, with options to either defer the
+ connection or drop it.
+- Added a dispatch mdoe "weighted-load" for randomly picking a back
+ end based on the inverse of their respective load averages. (gem)
+- Verbose/debug options in the web interface are now rendered as
+ select boxes.
+- Web interface errors are rendered on the client as an HTML error
+ page.
+- Docs updated regarding web interface URI's.
+- Turned on compliler optimizing (had forgotten that before!)
+- Docs updated regarding web interface URI's.
+
+2.18 [KK 2008-10-08]
- Improved command line parsing, so that when --checkup-interval is
used, suppression of "--wakeup-interval 0" is no longer needed.
-2.17 [KK 2008-10-07]
+2.17 [KK 2008-10-07]
- Type sizes reported in "xr -V"
- Fixed nasty bug in sys/fdwrite.cc
-2.16 [KK 2008-10-05]
+2.16 [KK 2008-10-05]
- Enhanced web interface to show debug, verbose and traffic log states
- Altering parameters for the web interface get sent in encoded form
- Option --log-traffic was renamed to --log-traffic-dir for
@@ -27,11 +138,11 @@
- Fixed usage info for buffer size flag (should be -B, not -b).
- Implemented flag -l (--log-traffic).
-2.13 [KK 2008-09-17]
+2.13 [KK 2008-09-17]
- Porting issues resolved for older MacOSX (10.3, 10.4)
- Bugfix in XML emitting of web interface. Most browsers didn't even notice..
-2.12 [KK 2008-09-10]
+2.12 [KK 2008-09-10]
- Small code changes for g++ v3.x backward-compatibility support.
(Thanks Simon M.)
- Web interface: layout enhanced, more modification options
@@ -43,10 +154,10 @@
- Web interface returns an HTTP error page (status 500 only, no
content) during errors
-2.11 [KK 2008-09-04]
+2.11 [KK 2008-09-04]
- Bugfix in "first-active" dispatch mode. Previously XR would gobble
up fd's when no back end was available.
-- Web interface (first version) implemented.
+- Web interface (first version) implemented.
2.10 [KK 2008-09-02]
- Bugfix in host match mode. When a back end doesn't match anything,
@@ -68,7 +179,7 @@
2.07 [KK 2008-08-28]
Stupid bug in 2.06, sorry that 2.06 got out.. Fixed.
-2.06 [KK 2008-08-27]
+2.06 [KK 2008-08-27]
Upped c-conf to 1.14.
xrctl updated: 'ps' command format also suitable for SunOS.
New class Mutex implemented. Mutex-locks are now more fine-grained
@@ -86,7 +197,7 @@
strict and lax.
Flag -n / --tryout implemented. Implemented in xrctl.
-2.03 [KK 2008-08-10]
+2.03 [KK 2008-08-10]
Updated docs regarding the mailing list.
Fixed verbose display upon accepting a client ("current back end
states").
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/Makefile
^
|
@@ -1,21 +1,26 @@
# Top-level Makefile for XR
# -------------------------
-VER = 2.18
-BINDIR = /usr/sbin
+VER = 2.36
+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:
@echo
@echo 'Choose:'
- @echo ' make local - local program construction'
- @echo ' make install - installation to $(BINDIR)'
- @echo ' make clean - removal after local/install'
- @echo ' make tar - pack sources in an archive'
- @echo ' make commit - commit to repository (maintainer only)'
+ @echo ' make local - local program construction'
+ @echo ' make localprof - local, with profiling info'
+ @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)'
@echo
local:
@@ -23,29 +28,69 @@
xr/etc/gettools /usr/local/bin xr/etc c-conf e-ver
xr/etc/e-ver ChangeLog $(VER)
BASE=$(BASE) AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
- VER='$(VER)' $(MAKE) -C xr
+ DISTSITE='$(DISTSITE)' \
+ VER='$(VER)' PROF=$(PROF) PROFILER=$(PROFILER) $(MAKE) -C xr
-install: local
+localprof:
+ PROF=-pg PROFILER=-DPROFILER 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
+ cp 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/*
+ find . -name gmon.out -exec rm {} \;
-tar:
+tar: clean
rm -rf $(TAR) /tmp/crossroads-$(VER)
cd ..; cp -r crossroads /tmp/crossroads-$(VER)
cd /tmp; tar czf $(TAR) \
--exclude .git --exclude .svn --exclude crossroads-$(VER)/xr/build \
crossroads-$(VER)
+ rm -rf /tmp/crossroads-$(VER)
@echo
@echo 'Sources now tarred into $(TAR)'
@@ -54,4 +99,3 @@
(echo 'SVN not fully up to date: run "svn status"' && exit 1)
perl -c xrctl/xrctl
svn commit
-
\ No newline at end of file
|
[-]
[+]
|
Added |
crossroads-devel.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-devel.tar.gz/doc/xr.odt
^
|
|
Changed |
crossroads-devel.tar.gz/doc/xr.pdf
^
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/doc/xrctl.1
^
|
@@ -0,0 +1,38 @@
+.TH "XRCTL" "1" "Nov 7, 2008" "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
+force [SERVICE] - start a service (or all) if not running
+restart [SERVICE] - stop 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>.
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/doc/xrctl.xml.5
^
|
@@ -0,0 +1,213 @@
+.TH "XRCTL.XML" "5" "Nov 8, 2008" "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>
+ <!-- Where do PID files get stored? -->
+ <piddir>/var/run</piddir>
+ <!-- "ps" command that shows the PID and command. On Solaris, use
+ /usr/bin/ps -ef "pid comm" -->
+ <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>
+ <!-- If logger is not used: where do logs get written? -->
+ <logdir>/var/log</logdir>
+ <!-- If logger is not used: how big may the logs become?
+ Manipulated during "xrctl rotate". -->
+ <maxlogsize>100000</maxlogsize>
+ <!-- If logger is not used: how many history logs to keep? -->
+ <loghistory>10</loghistory>
+ <!-- Path where the "xr" binary is searched, and zippers as "gzip"
+ and "bzip2" -->
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ </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
+ 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-devel.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");
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/test/sampleconf.xml
^
|
@@ -1,53 +1,185 @@
-<!-- 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>
+ <!-- Where do PID files get stored? -->
+ <piddir>/var/run</piddir>
+ <!-- "ps" command that shows the PID and command. On Solaris, use
+ /usr/bin/ps -ef "pid comm" -->
+ <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>
+ <!-- If logger is not used: where do logs get written? -->
+ <logdir>/var/log</logdir>
+ <!-- If logger is not used: how big may the logs become?
+ Manipulated during "xrctl rotate". -->
+ <maxlogsize>100000</maxlogsize>
+ <!-- If logger is not used: how many history logs to keep? -->
+ <loghistory>10</loghistory>
+ <!-- Path where the "xr" binary is searched, and zippers as "gzip"
+ and "bzip2" -->
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ </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
+ 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>
|
[-]
[+]
|
Added |
crossroads-devel.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.
+
+
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/Checkers/checkupthread/execute.cc
^
|
@@ -2,22 +2,25 @@
void Checkupthread::execute() {
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()) {
+ 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");
+ }
}
- }
- sleep (config.checkupsec());
+ sleep (config.checkupsec());
+ } else
+ sleep(30);
}
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/Checkers/wakeupthread/execute.cc
^
|
@@ -2,18 +2,21 @@
void Wakeupthread::execute() {
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());
+ target.check();
+ if (target.live()) {
+ msg ("Wakeup call: backend " + target.description() +
+ " has awoken\n");
+ balancer.backend(i).live(true);
+ }
}
}
- }
- sleep (config.wakeupsec());
+ sleep (config.wakeupsec());
+ } else
+ sleep(30);
}
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/algorithm/algorithm
^
|
@@ -3,6 +3,7 @@
#include "ThreadsAndMutexes/thread/thread"
#include "backendvector/backendvector"
+#include "profiler/profiler"
class Algorithm: public Thread {
public:
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/external/target.cc
^
|
@@ -21,13 +21,11 @@
throw static_cast<Error>("External algorithm '") + o.str() +
"' did not reply with a number";
- ostringstream n;
- n << i;
- msg ("External algorithm says: " + n.str() + "\n");
+ msg ((Mstr("External algorithm says:) ") + i) + "\n");
if (i >= balancer.nbackends())
throw static_cast<Error>("External algorithm '") + o.str() +
- "': answer " + n.str() + " out of bounds";
+ "': answer " + i + " out of bounds";
if (pclose (f))
throw static_cast<Error>("External algorithm '") + o.str() +
"' terminated with error";
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/hashedip/target.cc
^
|
@@ -12,14 +12,10 @@
}
unsigned index = targetlist[h];
- if (config.verbose()) {
- ostringstream o;
- o << index;
- msg ("Client IP " + (string)inet_ntoa(clientip) + " hashes to " +
- o.str() + ", back end " +
- balancer.backend(index).description() +
- "\n");
- }
+ msg ((Mstr("Client IP ") + static_cast<string>(inet_ntoa(clientip))) +
+ (Mstr(" hashes to ") + index) +
+ ", back end " + balancer.backend(index).description() +
+ "\n");
// In strict mode, back end must be available, or don't proceed
// In lax mode, fall back to least-connections dispatching
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/leastconn/target.cc
^
|
@@ -2,8 +2,11 @@
unsigned Leastconn::target(struct in_addr clientip,
BackendVector const &targetlist) {
+
+ PROFILE("Leastconn::target");
+
bool found = false;
- unsigned nconn, t;
+ unsigned nconn = 0, t = 0;
for (unsigned i = 0; i < targetlist.size(); i++) {
if (! balancer.backend(targetlist[i]).available())
@@ -11,17 +14,13 @@
unsigned weighted_conn =
balancer.backend(targetlist[i]).connections() *
balancer.backend(targetlist[i]).adjustedweight();
- if (config.verbose()) {
- ostringstream o;
- o << "connections "
- << balancer.backend(targetlist[i]).connections()
- << ", adjusted weight "
- << balancer.backend(targetlist[i]).adjustedweight()
- << ", weighted connections "
- << weighted_conn;
- msg ("Back end " + balancer.backend(targetlist[i]).description() +
- ": " + o.str() + "\n");
- }
+ 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");
if (!found || weighted_conn < nconn) {
t = targetlist[i];
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/roundrobin/target.cc
^
|
@@ -2,13 +2,15 @@
unsigned Roundrobin::target(struct in_addr clientip,
BackendVector const &targetlist) {
- // No back ends? Don't even try.
+ // 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");
+ if (targetlist.size() == 1)
+ return (0);
static int prev_run_index = -1;
- unsigned first_try_val;
+ unsigned first_try_val = 0;
bool first_try_set = false;
while (true) {
// See where we will start
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/storedip/target.cc
^
|
@@ -7,31 +7,33 @@
struct ClientDataCmp {
bool operator() (struct in_addr a, struct in_addr b) const {
- long la = *((long*)&a);
- long lb = *((long*)&b);
+ 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;
+typedef map<struct in_addr, ClientData, ClientDataCmp> StoreMap;
+static StoreMap store;
unsigned StoredIp::target(struct in_addr clientip,
BackendVector const &targetlist) {
unsigned target;
time_t now = time(0);
- // 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) {
+ // Client already known, maybe timed out.
time_t diff = now - store[clientip].lastaccess;
- ostringstream o;
- o << diff;
- msg ("Client IP " + string(inet_ntoa(clientip)) +
- " last visited on " + timestamp(store[clientip].lastaccess) +
- ", " + o.str() + " sec ago, and went to " +
+
+ 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;
@@ -58,7 +60,8 @@
}
} else {
// Historical target unknown, fetch new one
- msg ("New visit from " + (string)inet_ntoa(clientip) + "\n");
+ msg ("New visit from " + static_cast<string>(inet_ntoa(clientip)) +
+ "\n");
Leastconn l;
target = l.target(clientip, targetlist);
}
@@ -67,18 +70,15 @@
ClientData entry = {target, now};
store[clientip] = entry;
- // Weed out store
- map<struct in_addr, ClientData, ClientDataCmp>::iterator
- iter = store.begin();
- while (iter != store.end()) {
- if (config.debug())
- debugmsg ("Stored-IP: " +
- (string)inet_ntoa((*iter).first) + " visited on " +
- timestamp((*iter).second.lastaccess) + "\n");
- if (now - ((*iter).second.lastaccess) > config.ipstoretimeout())
+ // Weed out store.
+ for (StoreMap::iterator iter = store.begin(); iter != store.end();
+ iter++) {
+ debugmsg (Mstr(inet_ntoa(iter->first)) + Mstr(" visited on ") +
+ timestamp((*iter).second.lastaccess) + "\n");
+ if (now - ((*iter).second.lastaccess) > config.ipstoretimeout()) {
+ debugmsg (" Erasing stale entry, stale\n");
store.erase(iter);
- else
- iter++;
+ }
}
// Return target to caller
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/weightedload
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/weightedload/target.cc
^
|
@@ -0,0 +1,44 @@
+#include "weightedload"
+
+unsigned Weightedload::target(struct in_addr clientip,
+ BackendVector const &targetlist) {
+ // First loop thru and add up the weights.
+ double total_load = 0;
+ for (unsigned i = 0; i < targetlist.size(); i++) {
+ if (balancer.backend(targetlist[i]).loadavg() == 0)
+ total_load += 1 / 0.01;
+ else
+ total_load += 1 / balancer.backend(targetlist[i]).loadavg();
+ }
+
+ // Now pick a random number from 0 to total_load
+ // 4294967295 = 2^32 - 1
+ double pick_load = total_load * mt_rand() * (1.0 / 4294967295.0);
+
+ msg ((Mstr("Weighted by Load Average; load-range is ") + total_load) +
+ (Mstr(", and the selected load-range is ") + pick_load) +
+ "\n");
+
+ // Now see which server that means!
+ total_load = 0;
+ for (unsigned i = 0; i < targetlist.size(); i++) {
+ if (balancer.backend(targetlist[i]).loadavg() == 0)
+ total_load += 1 / 0.01;
+ else
+ total_load += 1 / balancer.backend(targetlist[i]).loadavg();
+ if (total_load >= pick_load) {
+// if (config.verbose()) {
+// ostringstream o;
+// o << balancer.backend(targetlist[i]).loadavg();
+// msg ("Weighted by Load Average chose backend " +
+// (string)balancer.backend(i).description() + " which has a " +
+// "load average of " + o.str() + "\n");
+// }
+ return targetlist[i];
+ }
+ }
+
+
+ throw static_cast<Error>("Weighted-load algorithm: no available back ends ");
+ return targetlist[0]; // We need some kind of default...
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/DispatchAlgorithms/weightedload/weightedload
^
|
@@ -0,0 +1,15 @@
+#ifndef _WEIGHTEDLOAD_
+#define _WEIGHTEDLOAD_
+
+#include "sys/sys"
+#include "error/error"
+#include "balancer/balancer"
+#include "DispatchAlgorithms/algorithm/algorithm"
+
+class Weightedload: public Algorithm {
+public:
+ unsigned target(struct in_addr clientip,
+ BackendVector const &targetlist);
+};
+
+#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/Makefile
^
|
@@ -5,15 +5,18 @@
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_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 -c $(BUILDDIR)/config.cache \
+ libfunction strnstr HAVE_STRNSTR)
foo:
$(MAKE) subdirs
@@ -32,9 +35,11 @@
echo "Making: $$f"; \
BASE=$(BASE) CC=$(CONF_CC) BUILDDIR=$(BUILDDIR) VER='$(VER)' \
AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
+ DISTSITE='$(DISTSITE)' \
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_INET_ATON=$(CONF_INET_ATON) CONF_OPTFLAGS='$(CONF_OPTFLAGS)' \
+ CONF_STRNSTR=$(CONF_STRNSTR) \
$(MAKE) -C $$f -f $(BASE)/xr/etc/Makefile.class \
|| exit 1; \
done
@@ -48,7 +53,8 @@
touch webinterface/answerxslt.cc
$(BIN): $(BUILDDIR)/libxr.a
- $(CONF_CC) -g -o $(BIN) -L$(BUILDDIR) -lxr $(CONF_LIB)
+ $(CONF_CC) $(PROF) $(CONF_OPTFLAGS) -g -o $(BIN) \
+ -L$(BUILDDIR) -lxr $(CONF_LIB)
clean:
rm -f $(BIN) $(LIB) core obj/*.o etc/usage.h $(BUILDDIR)/config.cache
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/ThreadsAndMutexes/mutex/lock.cc
^
|
@@ -1,8 +1,11 @@
#include "mutex"
+#include "profiler/profiler"
std::map<void *, pthread_mutex_t> Mutex::s_lock;
void Mutex::lock (void *target) {
+ PROFILE("Mutex::lock");
+
plock(&s_lock);
plock(target);
unlock(&s_lock);
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/ThreadsAndMutexes/mutex/unlock.cc
^
|
@@ -1,7 +1,10 @@
#include "mutex"
#include "error/error"
+#include "profiler/profiler"
void Mutex::unlock(void *target) {
+ PROFILE("Mutex::unlock");
+
if (int res = pthread_mutex_unlock(&s_lock[target]))
throw static_cast<Error>("Failed to release mutex lock: ") +
strerror(res);
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/ThreadsAndMutexes/thread/start.cc
^
|
@@ -1,4 +1,5 @@
#include "thread"
+#include "profiler/profiler"
static void *_run (void *data) {
Thread *t = (Thread*) data;
@@ -6,7 +7,9 @@
try {
t->execute();
} catch (Error const &e) {
+ Mutex::lock(&cerr);
cerr << e.what() << "\n";
+ Mutex::unlock(&cerr);
}
// Cleanups
@@ -17,7 +20,8 @@
}
void Thread::start() {
-
+ PROFILE ("Thread::start");
+
if (config.foregroundmode())
_run ((void*)this);
else {
@@ -35,8 +39,9 @@
pthread_attr_destroy (&attr);
return;
} else if (res == EAGAIN) {
- msg ("Failed to start thread: " + (string)strerror(res) +
- ", retrying\n");
+ if (config.verbose())
+ msg ("Failed to start thread: " + (string)strerror(res) +
+ ", retrying\n");
sleep (1);
continue;
} else {
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/available.cc
^
|
@@ -1,14 +1,16 @@
#include "backend"
+#include "profiler/profiler"
bool Backend::available() const {
- if (config.debug()) {
- ostringstream o;
- o << (islive ? "alive" : "dead") << ", "
- << connections() << " connections of "
- << maxconn() << " max";
- debugmsg ("Backend " + description() + ": " + o.str() + "\n");
- }
+ PROFILE("Backend::available");
+
+ debugmsg((Mstr("Back end ") + description()) +
+ (Mstr(": ") + livestr()) +
+ (Mstr(", ") + upstr()) +
+ (Mstr(", ") + connections()) +
+ (Mstr(" connections of ") + maxconn()) +
+ " max\n");
if (!maxconn())
- return (islive);
- return (islive && connections() < maxconn());
+ return (islive && isup);
+ return (islive && isup && connections() < maxconn());
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/backend
^
|
@@ -6,6 +6,10 @@
#include "fdset/fdset"
#include "error/error"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "profiler/profiler"
+#include "backendcheck/backendcheck"
+#include "httpbuffer/httpbuffer"
+#include "dnsentry/dnsentry"
using namespace std;
@@ -15,43 +19,71 @@
Backend (BackendDef const &b);
virtual ~Backend();
bool connect();
+ int sock() const { return (clsocket); }
+
void check();
string description() const;
+
bool available() const;
string availablestr() const;
- string livestr() const;
- void live (bool state);
bool live() const { return (islive); };
- int sock() const { return (clsocket); }
+ 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); }
+
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()); }
void hostmatch(string const &s) { bdef.hostmatch(s); }
regex_t const &hostregex() const { return (bdef.hostregex()); }
+
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); }
+ void loadavg(double l) { loadaverage = l; }
+
void addbytes (unsigned n);
void startconnection();
void endconnection();
-
+
BackendDef const &backenddef() const {
return (bdef);
}
-private:
+ BackendCheck const &backendcheck() {
+ return bdef.backendcheck();
+ }
+ void backendcheck(BackendCheck const &b) {
+ bdef.backendcheck(b);
+ }
+
+
+private:
BackendDef bdef;
bool islive;
+ bool isup;
int clsocket;
unsigned nconn, totconn;
double bytes_served;
+ double loadaverage;
+ DNSEntry dnsentry;
};
#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/backend1.cc
^
|
@@ -1,5 +1,7 @@
#include "backend"
Backend::Backend () :
- islive(true), clsocket(-1), nconn(0), totconn(0), bytes_served(0) {
+ islive(true), isup(true), clsocket(-1),
+ nconn(0), totconn(0), bytes_served(0),
+ loadaverage(0.1), dnsentry() {
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/backend2.cc
^
|
@@ -1,6 +1,6 @@
#include "backend"
Backend::Backend (BackendDef const &b) :
- bdef(b), islive(true), clsocket(-1), nconn(0), totconn(0),
- bytes_served(0) {
+ bdef(b), islive(true), isup(true), clsocket(-1), nconn(0), totconn(0),
+ bytes_served(0), loadaverage(0.1), dnsentry() {
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/check.cc
^
|
@@ -1,6 +1,97 @@
#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_timeout());
+ httpbuffer.reset();
+ while (!httpbuffer.headersreceived())
+ httpbuffer.netread(tester.sock(), config.backend_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 static_cast<Error>("Internal fry in Backend::check()");
+ }
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/connect.cc
^
|
@@ -1,6 +1,8 @@
#include "backend"
bool Backend::connect() {
+ PROFILE("Backend::connect");
+
// Assume the backend is dead
islive = false;
@@ -12,17 +14,11 @@
strerror(errno);
// Resolve hostname, prepare binding
- struct hostent *hostaddr;
- if (! (hostaddr = gethostbyname(bdef.server().c_str())) ) {
- socketclose (clsocket);
- throw static_cast<Error>("Failed to resolve backend host '") +
- bdef.server();
- }
struct sockaddr_in backendaddr;
+
backendaddr.sin_family = AF_INET;
backendaddr.sin_port = htons(bdef.port());
- memcpy ((char *) &backendaddr.sin_addr.s_addr,
- hostaddr->h_addr_list[0], hostaddr->h_length);
+ backendaddr.sin_addr.s_addr = dnsentry.resolve(bdef.server());
// Client socket goes into nonblocking mode, so we can connect
// and enforce a timeout later.
@@ -58,15 +54,9 @@
islive = true;
}
- if (config.debug()) {
- ostringstream o;
- o << "Back end " << description() << " is ";
- if (islive)
- o << "alive on socket " << clsocket;
- else
- o << "NOT ALIVE";
- debugmsg (o.str() + "\n");
- }
+ debugmsg ((Mstr("Back end ") + description()) +
+ (Mstr(" is ") + livestr()) +
+ (Mstr(" (socket ") + clsocket) + ")\n");
return (islive);
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backend/live.cc
^
|
@@ -1,6 +1,8 @@
#include "backend"
void Backend::live (bool state) {
+ PROFILE("Backend::live");
+
Mutex::lock (&islive);
bool oldstate = islive;
islive = state;
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/backend/up.cc
^
|
@@ -0,0 +1,13 @@
+#include "backend"
+
+void Backend::up (bool state) {
+ PROFILE("Backend::up");
+
+ Mutex::lock (&isup);
+ bool oldstate = isup;
+ isup = state;
+ Mutex::unlock (&isup);
+
+ if (oldstate != state)
+ msg ("Marking back end " + description() + " as " + upstr() + "\n");
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/backend/upstr.cc
^
|
@@ -0,0 +1,5 @@
+#include "backend"
+
+string Backend::upstr() const {
+ return (up() ? "up" : "down");
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/backendcheck
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-devel.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-devel.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-devel.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 static_cast<Error>("Internal jam in BackendCheck::description");
+ }
+
+ return (o.str());
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/backendcheck/parse.cc
^
|
@@ -0,0 +1,66 @@
+#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 static_cast<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-devel.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-devel.tar.gz/xr/backenddef/backenddef
^
|
@@ -1,32 +1,38 @@
#ifndef _BACKENDDEF_
#define _BACKENDDEF_
-#include "../sys/sys"
-#include "../error/error"
+#include "sys/sys"
+#include "error/error"
+#include "profiler/profiler"
+#include "backendcheck/backendcheck"
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(""), wt(1), backend_check() {}
+ 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); }
+
+ BackendCheck const &backendcheck() { return backend_check; }
+ void backendcheck(BackendCheck const &b) { backend_check = b; }
private:
string srv;
@@ -37,6 +43,7 @@
unsigned wt;
static unsigned min_wt, max_wt;
static bool minmax_wt_set;
+ BackendCheck backend_check;
};
#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/backenddef/hostmatch.cc
^
|
@@ -1,6 +1,8 @@
#include "backenddef"
void BackendDef::hostmatch (string const &s) {
+ PROFILE("BackendDef::hostmatch");
+
host_match = s;
if (host_match == "")
host_match = ".";
|
[-]
[+]
|
Deleted |
crossroads-devel.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-devel.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-devel.tar.gz/xr/balancer/addbackend2.cc
^
|
@@ -0,0 +1,14 @@
+#include "balancer"
+
+void Balancer::addbackend (Backend const &b,
+ bool is_up, bool is_live, bool do_check) {
+ Mutex::lock(&backends);
+ backends.push_back (b);
+ Mutex::unlock(&backends);
+
+ backends[backends.size() - 1].up(is_up);
+ backends[backends.size() - 1].live(is_live);
+
+ if (do_check)
+ backends[backends.size() - 1].check();
+}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/balancer/balancer
^
|
@@ -20,7 +20,11 @@
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()); }
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/balancer/deletebackend.cc
^
|
@@ -0,0 +1,15 @@
+#include "balancer"
+
+void Balancer::deletebackend(unsigned i) {
+ if (backend(i).up())
+ throw static_cast<Error>("Only 'down' back ends can be deleted.");
+ if (backend(i).connections())
+ throw static_cast<Error>("Back end cannot be deleted, there are still ")
+ + backend(i).connections() + " connections";
+
+
+ Mutex::lock(&backends);
+ backends.erase(backends.begin() + i,
+ backends.begin() + i + 1);
+ Mutex::unlock(&backends);
+}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/balancer/init.cc
^
|
@@ -11,6 +11,8 @@
// Start the web interface if requested.
if (config.usewebinterface()) {
Webinterface *w = new Webinterface();
+ if (! w)
+ throw static_cast<Error>("Memory fault in Balancer::init");
w->start();
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/balancer/serve.cc
^
|
@@ -5,15 +5,21 @@
void Balancer::serve() {
int clsock;
- // 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 static_cast<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 static_cast<Error>("Memory fault in Balancer::serve");
ct->start();
}
@@ -28,10 +34,8 @@
}
// Wait for activity, serve it.
- ostringstream o;
- o << server_fd;
- msg ("Awaiting activity on fd " + o.str() +"\n");
- while (1) {
+ msg ((Mstr("Awaiting activity on fd ") + server_fd) + "\n");
+ while (true) {
Fdset fdset(0);
fdset.add (server_fd);
if (fdset.readable() < 0) {
@@ -40,38 +44,22 @@
msg ("Interrupt seen\n");
if (terminate()) {
msg ("Termination requested, XR will stop.\n");
- socketclose (server_fd);
- shutdown (server_fd, SHUT_RDWR);
- unsigned prev_conn = 0x19081962;
- while (1) {
- unsigned curr_conn = balancer.connections();
- if (!curr_conn)
- break;
- if (curr_conn != prev_conn) {
- ostringstream o;
- o << curr_conn;
- msg ("There are still " + o.str() + " connections\n");
- prev_conn = curr_conn;
- }
- sleep (1);
- }
- msg ("XR is idle, stopping.\n");
break;
} else if (report()) {
msg ("Report requested\n");
reportmsg ("*** XR STATUS REPORT STARTS ***\n");
for (unsigned i = 0; i < nbackends(); i++) {
- reportmsg ("Back end " + backend(i).description() + ":\n");
- reportmsg (" Status: " + backend(i).availablestr() +
- ", " + backend(i).livestr() + "\n");
- ostringstream o;
- o << backend(i).connections() << " (max "
- << backend(i).maxconn() << ")";
- reportmsg (" Connections: " + o.str() + "\n");
- ostringstream b;
- b << backend(i).bytesserved() << " bytes, "
- << backend(i).clientsserved() << " clients";
- reportmsg (" Served: " + b.str() + "\n");
+ reportmsg("Back end " + backend(i).description() + "\n");
+ reportmsg((Mstr(" Status: ") +
+ backend(i).availablestr()) +
+ (Mstr(", ") + backend(i).livestr()) + "\n");
+ reportmsg((Mstr(" Connections: ") +
+ backend(i).connections())
+ +
+ (Mstr(" (max ") + backend(i).maxconn()) + "\n");
+ reportmsg((Mstr(" Served:") + backend(i).bytesserved()) +
+ (Mstr(" bytes, ") + backend(i).clientsserved()) +
+ " clients\n");
}
report (false);
reportmsg ("*** XR STATUS REPORT ENDS ***\n");
@@ -84,7 +72,7 @@
// Got activity!
request_nr++;
-
+
if (server_fd) {
// If tcp-serving: server_fd > 0; serve and loop again
int size;
@@ -94,12 +82,13 @@
if ( (clsock = accept (server_fd, (struct sockaddr *) &clname,
(socklen_t*) &size)) < 0 )
throw static_cast<Error>("Failed to accept network connection");
-
+
string clientip = inet_ntoa(clname.sin_addr);
// If there is an allow list, the client must match it.
if (config.nallow()) {
- debugmsg ("Matching " + clientip + " against allow list\n");
+ 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))) {
@@ -115,7 +104,8 @@
}
}
// If the client is in the deny list, deny it.
- debugmsg ("Matching " + clientip + " against deny list\n");
+ 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))) {
@@ -131,32 +121,23 @@
// Show whom we've accepted
if (config.verbose()) {
- ostringstream o;
- o << clsock;
- msg ("Accepted connection from " + clientip +
- " as client fd " + o.str() +"\n");
- ostringstream n;
- n << connections();
- msg ("Balancer is serving " + n.str() + " clients\n");
+ msg ((Mstr("Accepted connection from ") + clientip) +
+ (Mstr(" as client fd ") + clsock) + "\n");
+ msg ((Mstr("Balancer is serving ") + connections()) +
+ " clients\n");
msg ("Current back end states:\n");
- for (unsigned i = 0; i < nbackends(); i++) {
- ostringstream status;
- status << backend(i).connections() << " connections";
- if (backend(i).maxconn())
- status << " (max " << backend(i).maxconn() << ")";
- status << ", status " << backend(i).availablestr();
- msg (" Back end " + backend(i).description() + ": " +
- status.str() + "\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()) {
- ostringstream o;
- o << connections() << " connections (max "
- << config.maxconn() << ")";
- msg ("Not serving client: already " + o.str() + "\n");
+ msg ((Mstr("Not serving client: already ") + connections()) +
+ (Mstr(" (max ") + config.maxconn()) + "\n");
socketclose (clsock);
continue;
}
@@ -174,24 +155,24 @@
"can't choose dispatcher");
break;
}
+ if (!d)
+ throw static_cast<Error>("Memory fault in Balancer::serve");
// Allocation boundary printout
if (config.debug()) {
void *mem = malloc(16);
free (mem);
- ostringstream o;
- o << mem;
- debugmsg ("Allocation boundary at dispatcher start: " +
- o.str() + "\n");
+ _debugmsg (Mstr("Allocation boundary at dispatcher start: ") +
+ mem + "\n");
}
-
+
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);
@@ -204,12 +185,40 @@
"can't choose dispatcher");
break;
}
+ if (!d)
+ throw static_cast<Error>("Memory fault in Balancer::serve");
d->execute();
break;
}
+
+ // If we exceed the max # of requests, stop..
+ if (config.quitafter()) {
+ msg ((Mstr("Request ") + requestnr()) +
+ (Mstr(" underway of max ") + config.quitafter()) + "\n");
+ if (requestnr() >= (long)config.quitafter()) {
+ msg ("Max requests served, will stop.\n");
+ break;
+ }
+ }
}
- // If a PID stamp was created, remove it now.
+ // We're stopping now. If a PID stamp was created, remove it.
if (config.pidfile() != "")
unlink (config.pidfile().c_str());
+
+ // 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();
+ if (!curr_conn)
+ break;
+ if (curr_conn != prev_conn) {
+ msg ((Mstr("There are still ") + curr_conn) + " connections\n");
+ prev_conn = curr_conn;
+ }
+ sleep (1);
+ }
+ msg ("XR is idle, stopping.\n");
}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/add.cc
^
|
@@ -1,12 +0,0 @@
-#include "buffer"
-
-void Buffer::add (char const *b, unsigned len) {
- if (!buf_data)
- set (b, len);
- else {
- if (! (buf_data = (char*)realloc (buf_data, buf_len + len)) )
- throw static_cast<Error>("Memory fault in Buffer::add");
- memcpy (buf_data + buf_len, b, len);
- buf_len += len;
- }
-}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/buffer/buffer
^
|
@@ -14,14 +14,23 @@
void set (char const *b, unsigned len);
void add (char const *b, unsigned len);
- unsigned size() const;
char const *data() const;
- char charat (unsigned index) const;
+ int strfind (char const *s) const;
char &operator[] (unsigned index);
string stringat (unsigned index, unsigned len) const;
void removeat (unsigned index, unsigned len = 1);
void insertat (unsigned index, char const *s, unsigned len);
void insertat (unsigned index, string s);
+
+ // This ones are called often so let's inline them.
+ unsigned size() const {
+ return (buf_len);
+ }
+ char charat (unsigned index) const {
+ if (index >= buf_len)
+ return (0);
+ return (buf_data[index]);
+ }
private:
void copy (Buffer const &other);
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/buffer1.cc
^
|
@@ -1,4 +0,0 @@
-#include "buffer"
-
-Buffer::Buffer(): buf_data(0), buf_len(0) {
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/buffer2.cc
^
|
@@ -1,5 +0,0 @@
-#include "buffer"
-
-Buffer::Buffer (Buffer const &other): buf_data(0), buf_len(0){
- copy(other);
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/buffer3.cc
^
|
@@ -1,5 +0,0 @@
-#include "buffer"
-
-Buffer::~Buffer() {
- destroy();
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/charat.cc
^
|
@@ -1,7 +0,0 @@
-#include "buffer"
-
-char Buffer::charat (unsigned index) const {
- if (index >= buf_len)
- return (0);
- return (buf_data[index]);
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/copy.cc
^
|
@@ -1,13 +0,0 @@
-#include "buffer"
-
-void Buffer::copy (Buffer const &other) {
- if (! other.buf_len) {
- buf_data = 0;
- buf_len = 0;
- } else {
- if (! (buf_data = (char*)malloc (other.buf_len)) )
- throw static_cast<Error>("Memory fault in Buffer::copy");
- memcpy (buf_data, other.buf_data, other.buf_len);
- buf_len = other.buf_len;
- }
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/data.cc
^
|
@@ -1,5 +0,0 @@
-#include "buffer"
-
-char const *Buffer::data() const {
- return (buf_data);
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/destroy.cc
^
|
@@ -1,7 +0,0 @@
-#include "buffer"
-
-void Buffer::destroy() {
- free (buf_data);
- buf_data = 0;
- buf_len = 0;
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/insertat1.cc
^
|
@@ -1,5 +0,0 @@
-#include "buffer"
-
-void Buffer::insertat (unsigned index, string s) {
- insertat (index, s.c_str(), s.size());
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/insertat2.cc
^
|
@@ -1,13 +0,0 @@
-#include "buffer"
-
-void Buffer::insertat (unsigned index, char const *s, unsigned len) {
- if (index >= buf_len)
- return;
- if (! (buf_data = (char*)realloc (buf_data, buf_len + len)) )
- throw static_cast<Error>("Memory fault in Buffer::insertat");
- memmove (buf_data + index + len,
- buf_data + index,
- buf_len - index);
- memcpy (buf_data + index, s, len);
- buf_len += len;
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/oparray.cc
^
|
@@ -1,9 +0,0 @@
-#include "buffer"
-
-char &Buffer::operator[] (unsigned index) {
- static char dummy = 0;
-
- if (index >= buf_len)
- return (dummy);
- return (buf_data[index]);
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/opequal.cc
^
|
@@ -1,9 +0,0 @@
-#include "buffer"
-
-Buffer &Buffer::operator= (Buffer const &other) {
- if (this != &other) {
- destroy();
- copy (other);
- }
- return (*this);
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/removeat.cc
^
|
@@ -1,14 +0,0 @@
-#include "buffer"
-
-void Buffer::removeat (unsigned index, unsigned len) {
- if (!buf_data || index >= buf_len)
- return;
- if (index + len >= buf_len) {
- buf_len = index;
- return;
- }
- memcpy (buf_data + index, buf_data + index + len,
- buf_len - index - len);
- buf_len -= len;
-}
-
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/set.cc
^
|
@@ -1,9 +0,0 @@
-#include "buffer"
-
-void Buffer::set (char const *b, unsigned len) {
- destroy();
- if (! (buf_data = (char*)malloc (len)) )
- throw static_cast<Error>("Memory fault in Buffer::set");
- memcpy (buf_data, b, len);
- buf_len = len;
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/size.cc
^
|
@@ -1,5 +0,0 @@
-#include "buffer"
-
-unsigned Buffer::size() const {
- return (buf_len);
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/buffer/stringat.cc
^
|
@@ -1,14 +0,0 @@
-#include "buffer"
-
-string Buffer::stringat (unsigned index, unsigned len) const {
- string ret;
-
- for (unsigned int i = index; i < index + len; i++) {
- char ch = charat(i);
- if (!ch)
- break;
- ret += ch;
- }
-
- return (ret);
-}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/config/changeallow.cc
^
|
@@ -0,0 +1,12 @@
+#include "config"
+
+void Config::changeallow (string &a, unsigned index) {
+ if (index >= allowlist.size())
+ throw static_cast<Error>("No such allow-from specifier");
+
+ struct in_addr in;
+ if (!inet_aton (a.c_str(), &in))
+ throw static_cast<Error>("Bad allow-from specfier '") + a + "'";
+ allowlist[index] = (in);
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/config/changedeny.cc
^
|
@@ -0,0 +1,12 @@
+#include "config"
+
+void Config::changedeny (string &a, unsigned index) {
+ if (index >= denylist.size())
+ throw static_cast<Error>("No such deny-from specifier");
+
+ struct in_addr in;
+ if (!inet_aton (a.c_str(), &in))
+ throw static_cast<Error>("Bad deny-from specfier '") + a + "'";
+ denylist[index] = (in);
+}
+
|
[-]
[+]
|
Changed |
crossroads-devel.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;
@@ -20,61 +21,64 @@
// Accessors
bool verbose() const { return (verbose_flag); }
void verbose (bool v) { verbose_flag = v; }
-
+
bool debug() const { return (debug_flag); }
void debug (bool d) { debug_flag = d; }
-
+
+ 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); }
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; }
-
+
unsigned backend_timeout() const { return (b_timeout); }
void backend_timeout (unsigned b) { b_timeout = b; }
-
+
unsigned wakeupsec() const { return (wakeup); }
void wakeupsec (unsigned w) { wakeup = w; }
-
+
unsigned checkupsec() const { return (checkup); }
void checkupsec (unsigned w) { checkup = w; }
-
- unsigned buffersize() const { return (bufsize); }
+
+ unsigned buffersize() const { return (bufsize); }
void buffersize (unsigned b);
-
+
bool foregroundmode() const { return (foreground_mode); }
-
+
bool addxrversion() const { return (add_xr_version); }
void addxrversion (bool b);
-
+
bool addxforwardedfor() const { return (add_x_forwarded_for); }
void addxforwardedfor (bool b);
-
+
bool stickyhttp() const { return (sticky_http); }
void stickyhttp(bool b);
-
+
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);
-
+
bool prefixtimestamp() const { return (prefix_timestamp); }
void prefixtimestamp (bool p);
-
+
bool fastclose() const { return (fast_close); }
void fastclose (bool f);
-
+
bool usewebinterface() const { return use_webinterface; }
string webinterfaceip() const { return webinterface_ip; }
int webinterfaceport() const { return webinterface_port; }
-
+
unsigned nserverheaders() const { return (serverheaders.size()); }
string serverheader (unsigned n) { return (serverheaders[n]); }
void addserverheader (string const &s);
@@ -83,18 +87,37 @@
string dumpdir() const { return (dump_dir); }
void dumpdir (string s) { dump_dir = s; }
+
+ unsigned softmaxconnrate() const { return soft_maxconnrate; }
+ void softmaxconnrate(unsigned n) { soft_maxconnrate = n; }
+ unsigned hardmaxconnrate() const { return hard_maxconnrate; }
+ void hardmaxconnrate(unsigned n) { hard_maxconnrate = n; }
+ unsigned defertime() const { return defer_time; }
+ void defertime(unsigned n) { defer_time = n; }
+ 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()); }
+ 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 {
return (denylist[n]);
- }
-
+ }
+
BackendDef const &backend (int i) const {
return (blist[i]);
}
@@ -105,15 +128,28 @@
return (dmode.modestr());
}
+ string softmaxconnexcess() const {
+ return soft_maxconn_excess_prog;
+ }
+ void softmaxconnexcess(string const &s) {
+ soft_maxconn_excess_prog = s;
+ }
+ string hardmaxconnexcess() const {
+ return hard_maxconn_excess_prog;
+ }
+ void hardmaxconnexcess(string const &s) {
+ hard_maxconn_excess_prog = s;
+ }
+
+
private:
- void setbackend (string s, string hostmatch);
+ void setbackend (string const &s, string const &hostmatch,
+ 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 bool verbose_flag;
static int lport;
static Servertype styp;
@@ -143,6 +179,14 @@
static string webinterface_ip;
static int webinterface_port;
static string dump_dir;
+ static unsigned soft_maxconnrate;
+ static unsigned hard_maxconnrate;
+ 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;
};
extern Config config;
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/config/config1.cc
^
|
@@ -29,6 +29,14 @@
string Config::webinterface_ip;
int Config::webinterface_port;
string Config::dump_dir;
+unsigned Config::soft_maxconnrate = 0;
+unsigned Config::hard_maxconnrate = 0;
+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;
Config::Config () {
}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/config/deleteallow.cc
^
|
@@ -0,0 +1,10 @@
+#include "config"
+
+void Config::deleteallow(unsigned index) {
+ if (index >= allowlist.size())
+ throw static_cast<Error>
+ ("Index out of range, cannot delete allow-from");
+ allowlist.erase(allowlist.begin() + index,
+ allowlist.begin() + index + 1);
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/config/deletedeny.cc
^
|
@@ -0,0 +1,10 @@
+#include "config"
+
+void Config::deletedeny(unsigned index) {
+ if (index >= denylist.size())
+ throw static_cast<Error>
+ ("Index out of range, cannot delete deny-from");
+ denylist.erase(denylist.begin() + index,
+ denylist.begin() + index + 1);
+}
+
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/config/parsecmdline.cc
^
|
@@ -16,8 +16,9 @@
throw static_cast<Error>("Bad command line '") +
cmdline + "'\n" + USAGE;
-# define OPTSTRING "?a:A:B:b:c:Dd:fhH:l:m:M:nPp:Ss:t:T:vVW:w:xX"
-# ifdef HAVE_GETOPT_LONG
+# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:g:hH:l:" \
+ "m:M:nPp:Q:r:R:Ss:t:T:u:U:vVW:w:xX"
+# ifdef HAVE_GETOPT_LONG
static struct option longopts[] = {
{ "allow-from", required_argument, 0, 'a' },
{ "deny-from", required_argument, 0, 'A' },
@@ -27,7 +28,11 @@
{ "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' },
+ { "backend-check", required_argument, 0, 'g' },
{ "help", no_argument, 0, 'h' },
{ "add-server-header", required_argument, 0, 'H' },
{ "log-traffic-dir", required_argument, 0, 'l' },
@@ -36,10 +41,15 @@
{ "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' },
@@ -49,17 +59,18 @@
{ 0, 0, 0, 0 }
};
# endif
-
+
int opt;
bool backend_set = false;
bool tryout = false, wakeup_used = false;
string current_hostmatch = "";
-
+ BackendCheck current_backendcheck;
+
# ifdef HAVE_GETOPT_LONG
while ( (opt = getopt_long (ac, av, OPTSTRING, longopts, 0)) > 0)
#else
while ( (opt = getopt (ac, av, OPTSTRING)) > 0 )
-#endif
+#endif
{
switch (opt) {
case 'a':
@@ -69,7 +80,7 @@
adddeny (optarg);
break;
case 'b':
- setbackend (optarg, current_hostmatch);
+ setbackend (optarg, current_hostmatch, current_backendcheck);
backend_set = true;
break;
case 'B':
@@ -88,9 +99,21 @@
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':
+ current_backendcheck.parse(optarg);
+ break;
case 'h':
case '?':
throw static_cast<Error>(USAGE);
@@ -116,6 +139,15 @@
case 'p':
pid_file = optarg;
break;
+ case 'Q':
+ quit_after = (unsigned) setinteger(optarg);
+ break;
+ case 'r':
+ soft_maxconnrate = (unsigned) setinteger (optarg);
+ break;
+ case 'R':
+ hard_maxconnrate = (unsigned) setinteger (optarg);
+ break;
case 's':
setserver (optarg);
break;
@@ -128,6 +160,12 @@
case 'T':
c_timeout = setinteger (optarg);
break;
+ case 'U':
+ defer_time = (unsigned) setinteger(optarg);
+ break;
+ case 'u':
+ connrate_timeinterval = (unsigned) setinteger (optarg);
+ break;
case 'v':
verbose_flag = true;
break;
@@ -135,7 +173,9 @@
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"
<< "Libraries : " << CONF_LIB << "\n"
<< "Type sizes : ssize_t=" << sizeof(ssize_t)
@@ -148,17 +188,27 @@
cout << "getopt.h : present\n";
# else
cout << "getopt.h : absent\n";
-# endif
-# ifdef HAVE_GETOPT_LONG
- cout << "getopt_long() : present\n";
-# else
- cout << "getopt_long() : absent (only short flags will work)\n";
# endif
# ifdef HAVE_INADDR_NONE
cout << "INADDR_NONE : present\n";
# else
cout << "INADDR_NONE : absent, defined to " << INADDR_NONE
<< "\n";
+# endif
+# ifdef HAVE_GETOPT_LONG
+ cout << "getopt_long() : present\n";
+# else
+ cout << "getopt_long() : absent (only short flags will work)\n";
+# endif
+# ifdef HAVE_INET_ATON
+ cout << "inet_aton() : present\n";
+# else
+ cout << "inet_aton() : absent\n";
+# endif
+# ifdef HAVE_STRNSTR
+ cout << "strnstr() : present\n";
+# else
+ cout << "strnstr() : absent\n";
# endif
exit (0);
case 'W':
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/config/setbackend.cc
^
|
@@ -1,20 +1,25 @@
#include "config"
-void Config::setbackend (string str, string host) {
+void Config::setbackend (string const &str, string const &host,
+ 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 static_cast<Error>
+ ("Bad back end specifier in '-b") + str +
+ "', expected: SERVER:PORT or SERVER:PORT:MAXCONNECTIONS or "
+ "SERVER:PORT:MAXCONNECTIONS:WEIGHT";
- BackendDef *bdp;
+ BackendDef *bdp = 0;
if (parts.size() == 2)
bdp = new BackendDef(parts[0], parts[1]);
else if (parts.size() == 3)
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 static_cast<Error>("Memory fault in Config::setbackend");
bdp->hostmatch(host);
+ bdp->backendcheck(backend_check);
blist.push_back (*bdp);
delete bdp;
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/config/setdispatcmode.cc
^
|
@@ -17,6 +17,12 @@
dmode.mode (Dispatchmode::m_leastconn);
else if (s == "r" || s == "round-robin")
dmode.mode (Dispatchmode::m_roundrobin);
+ else if (s == "L" || s == "weighted-load" ) {
+ timeval t1;
+ gettimeofday(&t1, NULL);
+ mt_srand(t1.tv_sec * t1.tv_usec);
+ dmode.mode (Dispatchmode::m_weighted_load);
+ }
else if (s.substr(0, 2) == "s:") {
dmode.mode (Dispatchmode::m_strict_stored_ip);
ipstoretimeout(setinteger(s.substr(2)));
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/dispatchmode/dispatchmode
^
|
@@ -7,7 +7,7 @@
class Dispatchmode {
public:
-
+
enum Mode {
m_leastconn,
m_roundrobin,
@@ -17,11 +17,12 @@
m_lax_hashed_ip,
m_strict_stored_ip,
m_lax_stored_ip,
+ m_weighted_load,
};
Dispatchmode() : mymode(m_leastconn) {
}
-
+
void mode (Mode m) { mymode = m; }
Mode mode() const { return (mymode); }
string modestr() const;
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/dnsentry
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/dnsentry/dnsentry
^
|
@@ -0,0 +1,18 @@
+#ifndef _DNSENTRY_
+#define _DNSENTRY_
+
+#include "config/config"
+#include "error/error"
+#include "ThreadsAndMutexes/mutex/mutex"
+
+class DNSEntry {
+public:
+ DNSEntry(): result(0), timestamp(0) {}
+ in_addr_t &resolve(string const &str);
+
+private:
+ in_addr_t result;
+ time_t timestamp;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-devel.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 (timestamp &&
+ time(0) <= timestamp + (time_t)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 static_cast<Error>("Failed to resolve host '") + h + "'";
+
+ return result;
+}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/etc/Makefile.class
^
|
@@ -7,9 +7,12 @@
$(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o: %.cc
@echo "Compiling: " `pwd` $<
- @$(CONF_CC) -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \
- -DMAINTAINER='"$(MAINTAINER)"' -DSYS='"$(SYS)"' -D$(SYS) \
+ @$(CONF_CC) $(PROF) $(PROFILER) $(CONF_OPTFLAGS) \
+ -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \
+ -DMAINTAINER='"$(MAINTAINER)"' -DDISTSITE='"$(DISTSITE)"' \
+ -DSYS='"$(SYS)"' -D$(SYS) \
-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 $@ $<
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/etc/c-conf
^
|
@@ -4,7 +4,8 @@
use Getopt::Std;
# Globals
-my $VER = "1.14";
+my $VER = "1.15";
+# 1.15 [KK 2008-10-12] Option "optflags" implemented.
# 1.14 [KK 2008-08-22] c-compiler and c++-compiler attempt to find by
# version, eg. '/opt/local/bin/g++-mp-4.2' is better
# than '/usr/bin/g++'
@@ -93,6 +94,7 @@
library
$base [flags] c-compiler: Returns name of C compiler
$base [flags] c++-compiler: Returns name of C++ compiler
+ $base [flags] optflags: Returns fast-code optimization flags.
Optional flags:
-c CACHE: settings will be dynamically determined unless present in
@@ -360,8 +362,8 @@
output ("-l$lib");
}
}
- warning ("Library '$lib' not found\n")
- unless ($found);
+ #warning ("Library '$lib' not found\n")
+ # unless ($found);
}
}
@@ -432,7 +434,7 @@
my $cc;
eval { $cc = find_c_compiler(); };
if ($@) {
- warn ($@);
+ warning ($@);
} else {
msg ("C compiler: '$cc'\n");
output ($cc);
@@ -459,6 +461,24 @@
warning ("No C++ compiler found\n");
}
+# Get fast code optimization flags.
+sub optflags {
+ checkhelp <<"ENDHELP";
+'optflags' tries to determine optimization flags.
+E.g.: $base optflags
+ -> -O3
+ENDHELP
+ usage() if ($#_ > -1);
+ for my $optflag ('-fast', '-O3', '-O2') {
+ if (test_compile ("int main() {}\n", $optflag)) {
+ output ($optflag);
+ return;
+ }
+ }
+ warning ("No optimization flag found.");
+}
+
+
# Get the name for an SO.
sub so_name {
checkhelp <<"ENDHELP";
@@ -551,6 +571,7 @@
}
sub test_compile {
+ my $sourcecode = shift;
my $cc = find_c_compiler();
# Create a temp .c file.
@@ -558,11 +579,16 @@
my $dst = "/tmp/$$.out";
open (my $of, ">$src")
or die ("Cannot write $src: $!\n");
- print $of (@_);
+ print $of ($sourcecode);
close ($of);
- my $cmd = "$cc $src -o $dst " .
+ my $cmd = "$cc ";
+ for my $flag (@_) {
+ $cmd .= "$flag ";
+ }
+ $cmd .= "$src -o $dst " .
cc_inc_flags() . ' ' . cc_libdir_flags() . ' ' . cc_lib_flags();
+ # print ($cmd, "\n");
my $ret = system ("$cmd >/dev/null 2>&1");
unlink ($src, $dst);
@@ -604,9 +630,9 @@
usage() if ($#_ != 1);
my ($func, $def) = @_;
- return (test_compile ("main () {\n",
- " void $func (void);\n",
- " $func();\n",
+ return (test_compile ("main () {\n" .
+ " void $func (void);\n" .
+ " $func();\n" .
"}\n"));
}
@@ -615,9 +641,9 @@
usage() if ($#_ != 1);
my ($var, $def) = @_;
- return (test_compile ("main () {\n",
- " extern int $var;\n",
- " $var = 42;\n",
+ return (test_compile ("main () {\n" .
+ " extern int $var;\n" .
+ " $var = 42;\n" .
"}\n"));
}
@@ -688,6 +714,8 @@
libfunction01 (@ARGV);
} elsif ($action eq 'libvariable01') {
libvariable01 (@ARGV);
+} elsif ($action eq 'optflags') {
+ optflags (@ARGV);
} else {
usage ();
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/etc/status.xslt
^
|
@@ -15,7 +15,7 @@
}
body {
font-family: Verdana,Helvetica;
- font-size: 10pt;
+ font-size: 8pt;
}
td {
font-family: Verdana,Helvetica;
@@ -58,24 +58,86 @@
<xsl:template match="/status">
<table>
- <xsl:apply-templates/>
+ <xsl:apply-templates select="/status/server"/>
+ <xsl:apply-templates select="/status/backend"/>
+
+ <tr> <td colspan="4"><hr/></td></tr>
+ <tr>
+ <td class="backend" colspan="2">
+ <b>Add back end ip:port</b>
+ </td>
+ <td class="backend" 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"/>
+</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="server" colspan="4">
+ <td class="server" colspan="3">
<b>Server <xsl:value-of select="address"/> </b>
</td>
+ <td class="server">
+ <input type="button" onclick="goto('/', '');" value="Refresh"/>
+ </td>
</tr>
<tr>
- <td>Type</td>
- <td colspan="3"> <xsl:value-of select="type"/> </td>
+ <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>
+ <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>
@@ -116,7 +178,7 @@
</tr>
<tr>
<td>Timeouts</td>
- <td>Client</td>
+ <td>Client read/write</td>
<td>
<xsl:choose>
<xsl:when test="clienttimeout = 0">
@@ -135,7 +197,7 @@
</tr>
<tr>
<td></td>
- <td>Backend</td>
+ <td>Backend read/write/connect</td>
<td>
<xsl:choose>
<xsl:when test="backendtimeout = 0">
@@ -152,18 +214,62 @@
onchange="goto('/server/backendtimeout/', 'backendtimeout');"/>
</td>
</tr>
- <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">
- <input type="text" size="8" name="verbose" id="verbose" value="off"
- onchange="goto('/server/verbose/', 'verbose');"/>
+ <select onchange="goto('/server/verbose/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
</xsl:when>
<xsl:otherwise>
- <input type="text" size="8" name="verbose" id="verbose" value="on"
- onchange="goto('/server/verbose/', 'verbose');"/>
+ <select onchange="goto('/server/verbose/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
</xsl:otherwise>
</xsl:choose>
</td>
@@ -174,21 +280,25 @@
<td>
<xsl:choose>
<xsl:when test="debugging/debug = 0">
- <input type="text" size="8" name="debug" id="debug" value="off"
- onchange="goto('/server/debug/', 'debug');"/>
+ <select onchange="goto('/server/debug/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
</xsl:when>
<xsl:otherwise>
- <input type="text" size="8" name="debug" id="debug" value="on"
- onchange="goto('/server/debug/', 'debug');"/>
+ <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 colspan="2">Traffic log directory</td>
- <td>
- <input type="text" size="8" name="logtrafficdir" id="logtrafficdir"
+ <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>
@@ -203,10 +313,11 @@
</td>
</tr>
<tr>
- <td> Max. connections </td>
- <td colspan="2">
+ <td>DOS Protection</td>
+ <td>Max. connections </td>
+ <td>
<xsl:choose>
- <xsl:when test="maxconnections = 0">
+ <xsl:when test="/dosprotection/maxconnections = 0">
unlimited
</xsl:when>
<xsl:otherwise>
@@ -216,10 +327,111 @@
</td>
<td>
<input type="text" size="8" name="setservermaxcon" class="input"
- id="setservermaxcon" value="{maxconnections}"
+ 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>
@@ -228,13 +440,17 @@
<xsl:template match="/status/backend">
<tr> <td colspan="4"><hr/></td></tr>
<tr>
- <td class="backend" colspan="4">
+ <td class="backend" colspan="3">
<b> Back end <xsl:value-of select="address"/> </b>
</td>
+ <td class="backend">
+ <input type="button" value="Delete"
+ onclick="goto('/server/deletebackend/{nr}', '');"/>
+ </td>
</tr>
<tr>
<td><b>State</b></td>
- <td>health</td>
+ <td>Health</td>
<td colspan="2">
<xsl:value-of select="live"/>,
<xsl:value-of select="available"/>
@@ -242,12 +458,12 @@
</tr>
<tr>
<td></td>
- <td>connections</td>
+ <td>Connections</td>
<td colspan="2"><xsl:value-of select="connections"/></td>
</tr>
<tr>
<td></td>
- <td>served</td>
+ <td>Served</td>
<td colspan="2">
<xsl:value-of select="bytesserved"/> bytes,
<xsl:value-of select="clientsserved"/> clients
@@ -255,7 +471,7 @@
</tr>
<tr>
<td><b>Options</b></td>
- <td colspan="2">weight</td>
+ <td colspan="2">Weight</td>
<td>
<input type="text" size="8" name="setbackendweight{nr}"
id="setbackendweight{nr}" value="{weight}"
@@ -264,7 +480,7 @@
</tr>
<tr>
<td></td>
- <td>max. connections</td>
+ <td>Max. connections</td>
<td>
<xsl:choose>
<xsl:when test="maxconnections = 0">
@@ -281,10 +497,28 @@
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>Host match</td>
<td>
<xsl:choose>
<xsl:when test="hostmatch = '.'">
@@ -302,12 +536,32 @@
</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>
</xsl:template>
<xsl:template match="/status/server/http">
<tr>
<td>HTTP Goodies</td>
- <td colspan="2">add X-Forwarded-For</td>
+ <td colspan="2">Add X-Forwarded-For</td>
<td>
<xsl:choose>
<xsl:when test="addxforwardedfor = 0">
@@ -327,7 +581,7 @@
</tr>
<tr>
<td></td>
- <td colspan="2">sticky HTTP</td>
+ <td colspan="2">Sticky HTTP</td>
<td>
<xsl:choose>
<xsl:when test="stickyhttp = 0">
@@ -348,13 +602,41 @@
<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 colspan="2">server header (. to delete)</td>
- <td>
- <input type="text" size="8" name="serverheader{nr}"
+ <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>
@@ -362,9 +644,9 @@
</xsl:for-each>
<tr>
<td></td>
- <td colspan="2">new server header</td>
- <td>
- <input type="text" size="8" name="newserverheader"
+ <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>
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/etc/usage.txt
^
|
@@ -24,30 +24,58 @@
Sets debugging on, more verbosity on top of -v
-d METHOD, --dispatch-mode 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
+ 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.
+ -E PROGRAM, --hard-maxconn-excess 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.
+ -e PROGRAM, --soft-maxconn-excess PROGRAM
+ When a client exceeds the soft maxconnection rate, PROGRAM is
+ invoked with the client's IP as argument.
+ -F SEC, --dns-cache-timeout SEC
+ DNS results for back end hostnames are cached for SEC seconds.
+ The default is 3600 (1 hour). Use 0 to suppress.
-f, --foreground
Suppresses forking/threading, only for debugging. Also suppresses
- wakeup (-w) and checkup (-c) threads.
+ wakeups (-w), checkups (-c) and the webinterface (-W).
+ -g, --backend-check 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 exit(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 "-g connect::" to reset previous flags
+ to the default.
-h, -?, --help
This text.
-H HDR, --add-server-header HDR
@@ -67,6 +95,20 @@
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
+ Stops the balancer after REQUESTS hits. For debugging / loadtesting.
+ -r, --soft-maxconnrate 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
Enables sticky HTTP sessions by injecting XRTarget cookies into HTTP
streams. Only effective with -s http:....
@@ -81,6 +123,16 @@
-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
+ 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
Increases verbosity, default is silent operation.
-V, --version
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/etc/xrprof
^
|
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+use strict;
+
+open (my $if, "/tmp/xr-prof.txt")
+ or die ("Cannot read /tmp/xr-prof.txt: $!\n");
+
+my %info;
+
+while (my $line = <$if>) {
+ chomp($line);
+ my (undef, undef, $name, $duration) = split (/\s+/, $line);
+ # print ("$name: $duration\n");
+ if ($info{$name}) {
+ my ($calls, $total) = @{ $info{$name} };
+ $calls++;
+ $total += $duration;
+ $info{$name} = [ $calls, $total ];
+ } else {
+ $info{$name} = [ 1, $duration ];
+ }
+}
+
+for my $k (sort bytotal keys(%info)) {
+ my ($calls, $total) = @{ $info{$k} };
+ print ("$k: $total usec\n");
+}
+
+sub bytotal {
+ my @aa = @{ $info{$a} };
+ my @bb = @{ $info{$b} };
+ return ($bb[1] <=> $aa[1]);
+}
+
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/fdset/readable.cc
^
|
@@ -1,6 +1,8 @@
#include "fdset"
int Fdset::readable() const {
+ PROFILE("Fdset::readable");
+
fd_set readset, exceptset;
struct timeval tv, *tvp;
@@ -8,10 +10,21 @@
if (set.size() < 1)
return (-1);
+ if (config.debug()) {
+ ostringstream o;
+ o << "Candidate readable fd's:";
+ for (unsigned i = 0; i < set.size(); i++)
+ o << ' ' << set[i];
+ _debugmsg(o.str() + '\n');
+ }
+
// Prepare select sets.
FD_ZERO (&readset);
FD_ZERO (&exceptset);
+ int max = 0;
for (unsigned i = 0; i < set.size(); i++) {
+ if (set[i] > max)
+ max = set[i];
FD_SET (set[i], &readset);
FD_SET (set[i], &exceptset);
}
@@ -26,7 +39,9 @@
// 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 (select (max + 1, &readset, 0, &exceptset, tvp) < 0) {
+ debugmsg (Mstr("Select interrupted with errno ") + errno +
+ " while waiting for readable fd\n");
if (errno != EINTR)
throw static_cast<Error>
("Select failure: failed to wait for readable state: ") +
@@ -43,11 +58,7 @@
// Check what's readable.
for (unsigned i = 0; i < set.size(); i++)
if (FD_ISSET (set[i], &readset)) {
- if (config.debug()) {
- ostringstream o;
- o << set[i];
- debugmsg ("Fd " + o.str() + " has become readable\n");
- }
+ debugmsg (Mstr("Fd ") + set[i] + " has become readable\n");
return (set[i]);
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/fdset/readwriteable.cc
^
|
@@ -1,6 +1,8 @@
#include "fdset"
int Fdset::readwriteable() const {
+ PROFILE("Fdset::readwriteable");
+
fd_set readset, writeset, exceptset;
struct timeval tv, *tvp;
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/fdset/writeable.cc
^
|
@@ -1,6 +1,8 @@
#include "fdset"
int Fdset::writeable() const {
+ PROFILE("Fdset::writeable");
+
fd_set writeset, exceptset;
struct timeval tv, *tvp;
@@ -26,6 +28,8 @@
// Run the select.
if (select (FD_SETSIZE, 0, &writeset, &exceptset, tvp) < 0) {
+ debugmsg (Mstr("Select interrupted with errno ") + errno +
+ " while waiting for writeable fd\n");
if (errno != EINTR)
throw static_cast<Error>
("Select failure: failed to wait for writable state: ") +
@@ -42,11 +46,7 @@
// Check what's writeable.
for (unsigned i = 0; i < set.size(); i++)
if (FD_ISSET (set[i], &writeset)) {
- if (config.debug()) {
- ostringstream o;
- o << set[i];
- debugmsg ("Fd " + o.str() + " has become writeable\n");
- }
+ debugmsg (Mstr("Fd ") + set[i] + " has become writeable\n");
return (set[i]);
}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/httpbuffer/bodyreceived.cc
^
|
@@ -1,46 +0,0 @@
-#include "httpbuffer"
-
-static bool ishex (char ch) {
- return ( (ch >= '0' && ch <= '9') ||
- (ch >= 'a' && ch <= 'f') ||
- (ch >= 'A' && ch <= 'F') );
-}
-
-Httpbuffer::Bodystatus Httpbuffer::bodyreceived() {
- // No headers? Not done.
- if (!headersdone())
- return (b_is_not_received);
-
- // If we have a content length, the body must match it.
- string str = headerval("Content-Length");
- unsigned clen;
- if (sscanf (str.c_str(), "%d", &clen) > 0) {
- if (size() - bodystart >= clen)
- return (b_is_received);
- else
- return (b_is_not_received);
- }
-
- // If we have chunks, then the last chunk must be zero.
- str = headerval ("Transfer-Encoding");
- if (!strcasecmp (str.c_str(), "chunked")) {
- for (unsigned i = bodystart; i < size(); /* increment below */ ) {
- unsigned chunk;
- if (sscanf (data() + i, "%x", &chunk) < 1)
- return (b_is_not_received);
- if (!chunk)
- return (b_is_received);
- while (ishex(charat(i)) && i < size())
- i++;
- if (charat(i) == '\r')
- i++;
- if (charat(i) == '\n')
- i++;
- i += chunk;
- }
- }
-
- // No content length, no chunks... We just dunno.
- return (b_unknown);
-}
-
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/cookievalue.cc
^
|
@@ -1,6 +1,8 @@
#include "httpbuffer"
string Httpbuffer::cookievalue (string c) {
+ PROFILE("Httpheader::cookievalue");
+
string cval = headerval ("Cookie");
size_t pos;
string ret = "";
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/firstline.cc
^
|
@@ -1,12 +1,16 @@
#include "httpbuffer"
-string Httpbuffer::firstline() const {
- string ret;
- for (unsigned i = 0; i < size(); i++) {
+string &Httpbuffer::firstline() {
+ PROFILE("Httpbuffer::firstline");
+
+ if (first_line.size())
+ return (first_line);
+
+ for (unsigned i = 0; i < bufsz(); i++) {
char ch = charat(i);
if (ch == '\n' || ch == '\r')
break;
- ret += ch;
+ first_line += ch;
}
- return (ret);
+ return (first_line);
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/httpbuffer
^
|
@@ -1,39 +1,45 @@
#ifndef _HTTPBUFFER_
#define _HTTPBUFFER_
-#include "../sys/sys"
-#include "../buffer/buffer"
-#include "../config/config"
+#include "sys/sys"
+#include "netbuffer/netbuffer"
+#include "config/config"
+#include "profiler/profiler"
-class Httpbuffer: public Buffer {
+class Httpbuffer: public Netbuffer {
public:
- enum Bodystatus {
- b_is_received,
- b_is_not_received,
- b_unknown,
- };
+
+ // Recognized request methods. Modify requestmethod.cc to add more.
enum RequestMethod {
m_get,
- // More can be added ad lib, modify requestmethod.cc if you do
m_other,
};
Httpbuffer();
- bool headersdone();
- string headerval (string var);
- Bodystatus bodyreceived();
- string firstline() const;
- bool setversion(string v);
+
+ bool headersreceived();
+
+ string headerval (string var);
+ string &firstline();
+
+ bool setversion(char v);
+
void setheader (string var, string val);
+ void setheader (string h);
+
void addheader (string var, string val);
void addheader (string h);
+
string cookievalue (string var);
- RequestMethod requestmethod() const;
- string requesturi() const;
-private:
+ RequestMethod requestmethod();
+
+ string requesturi();
+
+private:
unsigned findheader (string h);
unsigned bodystart;
+ string first_line;
};
#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/httpbuffer1.cc
^
|
@@ -1,4 +1,5 @@
#include "httpbuffer"
-Httpbuffer::Httpbuffer(): bodystart(0) {
+Httpbuffer::Httpbuffer(): bodystart(0), first_line("") {
+ PROFILE("Httpbuffer::Httpbuffer");
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/requestmethod.cc
^
|
@@ -1,12 +1,9 @@
#include "httpbuffer"
-Httpbuffer::RequestMethod Httpbuffer::requestmethod() const {
- string first = firstline();
- debugmsg ("First line of http buffer: '" + first + "'\n");
+Httpbuffer::RequestMethod Httpbuffer::requestmethod() {
+ PROFILE("Httpheader::requestmethod");
- // GCC 3.x doesn't support string.compare(start,len,otherstring)
- // so let's just get a substring and compare to the target
- if (first.substr(0, 3) == static_cast<string>("GET"))
+ if (bufsz() >= 3 && !strncmp(bufdata(), "GET", 3))
return (m_get);
return (m_other);
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/requesturi.cc
^
|
@@ -1,6 +1,8 @@
#include "httpbuffer"
-string Httpbuffer::requesturi() const {
+string Httpbuffer::requesturi() {
+ PROFILE("Httpbuffer:requesturi");
+
vector<string> parts = str2parts (firstline(), ' ');
return (parts.size() >= 2 ? parts[1] : "");
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpbuffer/setversion.cc
^
|
@@ -1,20 +1,17 @@
#include "httpbuffer"
-bool Httpbuffer::setversion (string v) {
- if (!headersdone())
- return (false);
+bool Httpbuffer::setversion (char v) {
+ PROFILE("Httpbuffer::setversion");
- // Find "HTTP/" in the first line. Add the version there.
- for (unsigned int i = 0; ; i++) {
- if (charat(i) == '\r' || charat(i) == '\n')
- return (false);
- if (stringat(i, 5) == "HTTP/") {
- i ++;
- while (charat(i) != '\n' && charat(i) != '\r')
- removeat(i);
- insertat(i, v);
- }
- return (true);
- }
- return (false);
+ // No headers? Nothing to do.
+ if (!headersreceived())
+ 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))
+ return false;
+
+ // Poke in the new version.
+ return setchar(7, v);
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpdispatcher/dispatch.cc
^
|
@@ -1,6 +1,8 @@
#include "httpdispatcher"
void HttpDispatcher::dispatch() {
+ PROFILE("HttpDispatcher::dispatch");
+
unsigned stickytarget;
string host_header = "";
@@ -11,12 +13,16 @@
// Get the client's request. May need for cookie inspection or for the
// host header.
- if (!getclientrequest())
- throw static_cast<Error>("Didn't receive a valid "
- "client request.\n");
+ while (!buf.headersreceived())
+ if (!buf.netread(clientfd(), config.client_timeout()))
+ throw static_cast<Error>("Didn't receive a valid "
+ "client request.");
+ 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.
+ // 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() != ".") {
@@ -25,8 +31,10 @@
}
// Build new target list if host matching applies.
if (hostmatchused) {
- host_header = clientrequest.headerval ("Host");
- msg ("Will try to dispatch request host '" + host_header + "'\n");
+ 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.
@@ -38,8 +46,9 @@
(!regexec (&(balancer.backend(i).hostregex()),
host_header.c_str(), 0, 0, 0)) ) {
v.add(i);
- msg (" Candidate target: " +
- balancer.backend(i).description() + "\n");
+ if (config.verbose())
+ msg (" Candidate target: " +
+ balancer.backend(i).description() + "\n");
}
}
targetlist(v);
@@ -48,7 +57,7 @@
// Dispatch as a normal backend if sticky HTTP is off, or if the
// sticky target is badly specified.
if (!config.stickyhttp() ||
- sscanf (clientrequest.cookievalue ("XRTarget").c_str(),
+ sscanf (buf.cookievalue ("XRTarget").c_str(),
"%d", &stickytarget) < 1 ||
stickytarget >= balancer.nbackends()) {
issticky(false);
@@ -58,11 +67,13 @@
// to non-sticky dispatching.
targetbackend(stickytarget);
Backend tb = balancer.backend(stickytarget);
- msg ("Sticky HTTP request for " + tb.description() + "\n");
+ if (config.verbose())
+ 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");
+ if (config.verbose())
+ msg ("Failed to connect to back end " + tb.description() +
+ ", trying to dispatch to other\n");
issticky(false);
TcpDispatcher::dispatch();
} else {
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/httpdispatcher/getclientrequest.cc
^
|
@@ -1,16 +0,0 @@
-#include "httpdispatcher"
-
-bool HttpDispatcher::getclientrequest() {
- do {
- Fdset set (config.client_timeout());
- set.add (clientfd());
- int s;
- if ( (s = set.readable()) < 0 || !readchunk(clientfd()) )
- return (false);
- clientrequest.add (databuf(), databufsize());
- } while (clientrequest.bodyreceived() == Httpbuffer::b_is_not_received);
-
- msg ("Received client request: '" + clientrequest.firstline() + "'\n");
- return (true);
-}
-
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/httpdispatcher/getserverresponse.cc
^
|
@@ -1,17 +0,0 @@
-#include "httpdispatcher"
-
-bool HttpDispatcher::getserverresponse() {
- msg ("Reading back end response.\n");
-
- do {
- Fdset set (config.backend_timeout());
- set.add (backendfd());
- int s;
- if ( (s = set.readable() < 0) || !readchunk(backendfd()) )
- return (false);
- serverresponse.add (databuf(), databufsize());
- } while (! serverresponse.headersdone());
-
- msg ("Received back end response: '" + serverresponse.firstline() + "'\n");
- return (true);
-}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpdispatcher/handle.cc
^
|
@@ -1,52 +1,80 @@
#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.
- clientrequest.setversion("1.0");
- clientrequest.setheader ("Connection", "close");
- clientrequest.setheader ("Proxy-Connection", "close");
+ 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())
- clientrequest.setheader ("XR", VER);
+ buf.setheader ("XR", VER);
if (config.addxforwardedfor())
- clientrequest.addheader ("X-Forwarded-For",
- string(inet_ntoa(clientip())));
+ buf.addheader ("X-Forwarded-For", string(inet_ntoa(clientip())));
for (unsigned n = 0; n < config.nserverheaders(); n++)
- clientrequest.addheader (config.serverheader(n));
+ buf.setheader (config.serverheader(n));
- // Send to server
- try {
- sendclientrequest();
- } catch (Error const &e) {
- msg (static_cast<string>(e.what()) + "\n");
- senderrorpage();
- return;
+ // 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());
}
-
- // Get server's response
- if (!getserverresponse()) {
- msg ("Failed to get server response\n");
- senderrorpage();
- return;
- }
-
- // Apply client-side directed info. Smuggle in stickiness if required.
- if (config.addxrversion())
- serverresponse.setheader ("XR", VER);
- if (config.stickyhttp() && !issticky()) {
- ostringstream o;
- o << targetbackend();
- serverresponse.setheader ("Set-Cookie", "XRTarget=" + o.str());
- }
-
- // Flush server buffer to the client
- fdwrite (clientfd(), config.client_timeout(),
- serverresponse.data(), serverresponse.size());
- balancer.backend(targetbackend()).addbytes(serverresponse.size());
-
- // Then switch to the TCP copy/thru protocol.
- TcpDispatcher::handle();
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpdispatcher/httpdispatcher
^
|
@@ -15,14 +15,9 @@
void issticky (bool s) { is_sticky = s; }
private:
-private:
- bool getclientrequest();
- void sendclientrequest();
- bool getserverresponse();
- void sendserverresponse();
void senderrorpage();
- Httpbuffer clientrequest, serverresponse;
+ Httpbuffer buf;
bool is_sticky;
};
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpdispatcher/httpdispatcher1.cc
^
|
@@ -1,5 +1,5 @@
#include "httpdispatcher"
HttpDispatcher::HttpDispatcher (int fd, struct in_addr ip) :
- TcpDispatcher (fd, ip), clientrequest(), serverresponse() {
+ TcpDispatcher (fd, ip) {
}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/httpdispatcher/sendclientrequest.cc
^
|
@@ -1,9 +0,0 @@
-#include "httpdispatcher"
-
-void HttpDispatcher::sendclientrequest() {
- msg ("Sending client's request '" + clientrequest.firstline() +
- "' to back end\n");
- fdwrite (backendfd(), config.backend_timeout(),
- clientrequest.data(), clientrequest.size());
- balancer.backend(targetbackend()).addbytes(clientrequest.size());
-}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/httpdispatcher/senderrorpage.cc
^
|
@@ -6,11 +6,31 @@
"\r\n"
void HttpDispatcher::senderrorpage() {
+ PROFILE("HttpDispatcher::senderrorpage");
+
msg ("Sending error page to client.\n");
try {
- fdwrite (clientfd(), config.client_timeout(),
- ERRORSTR, sizeof(ERRORSTR));
+ 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) {
+ Mutex::lock(&cerr);
cerr << e.what() << " (while sending error page)\n";
+ Mutex::unlock(&cerr);
}
}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/httpdispatcher/sendserverresponse.cc
^
|
@@ -1,9 +0,0 @@
-#include "httpdispatcher"
-
-void HttpDispatcher::sendserverresponse() {
- msg ("Sending server response '" + serverresponse.firstline() +
- "' to client.\n");
- fdwrite (clientfd(), config.client_timeout(),
- serverresponse.data(), serverresponse.size());
- balancer.backend(targetbackend()).addbytes(clientrequest.size());
-}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/mstr
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/mstr/mstr
^
|
@@ -0,0 +1,46 @@
+#ifndef _MSTR_
+#define _MSTR_
+
+#include "sys/sys"
+
+class Mstr: public string {
+public:
+ Mstr(string s): string(s) {}
+ Mstr(char const *s): string(s) {}
+ Mstr const &operator+ (int i) {
+ ostringstream o;
+ o << i;
+ *this += o.str();
+ return *this;
+ }
+ Mstr const &operator+ (unsigned i) {
+ ostringstream o;
+ o << i;
+ *this += o.str();
+ return *this;
+ }
+ Mstr const &operator+ (time_t i) {
+ ostringstream o;
+ o << i;
+ *this += o.str();
+ return *this;
+ }
+ Mstr const &operator+ (double i) {
+ ostringstream o;
+ o << i;
+ *this += o.str();
+ return *this;
+ }
+ Mstr const &operator+(string const &s) {
+ *this += s;
+ return *this;
+ }
+ Mstr const &operator+(void const *p) {
+ ostringstream o;
+ o << p;
+ *this += o.str();
+ return *this;
+ }
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/charat.cc
^
|
@@ -0,0 +1,9 @@
+#include "netbuffer"
+
+char Netbuffer::charat(unsigned index) const {
+ PROFILE("Netbuffer::charat");
+
+ if (index >= buf_sz)
+ return (0);
+ return buf_data[index];
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/charfind.cc
^
|
@@ -0,0 +1,13 @@
+#include "netbuffer"
+
+unsigned Netbuffer::charfind(char ch, unsigned start) const {
+ PROFILE("Netbuffer::charfind");
+
+ if (buf_sz <= start)
+ return 0;
+
+ char *cp = (char*)memchr(buf_data + start, ch, buf_sz);
+ if (!cp)
+ return (0);
+ return (cp - buf_data);
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/checkspace.cc
^
|
@@ -0,0 +1,19 @@
+#include "netbuffer"
+
+void Netbuffer::check_space(unsigned extra) {
+ PROFILE("Netbuffer::check_space");
+
+ if (!buf_alloced) {
+ buf_alloced = extra;
+ buf_data = (char*)malloc(buf_alloced);
+ if (! buf_data)
+ throw static_cast<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");
+ buf_alloced += extra;
+ buf_data = (char*)realloc(buf_data, buf_alloced);
+ if (! buf_data)
+ throw static_cast<Error>("Memory fault in Netbuffer::check_space");
+ }
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/copy.cc
^
|
@@ -0,0 +1,10 @@
+#include "netbuffer"
+
+void Netbuffer::copy (Netbuffer const &other) {
+ buf_sz = other.buf_sz;
+ buf_alloced = other.buf_alloced;
+ if (! (buf_data = (char*)malloc(buf_sz)) )
+ throw static_cast<Error>("Memory fault in Netbuffer::copy");
+
+ memcpy (buf_data, other.buf_data, buf_sz);
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/destroy.cc
^
|
@@ -0,0 +1,8 @@
+#include "netbuffer"
+
+void Netbuffer::destroy() {
+ delete buf_data;
+ buf_data = 0;
+ buf_sz = 0;
+ buf_alloced = 0;
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/insertat.cc
^
|
@@ -0,0 +1,18 @@
+#include "netbuffer"
+
+bool Netbuffer::insertat(unsigned index, char const *s, unsigned len) {
+ PROFILE("Netbuffer::insertat");
+
+ if (!len)
+ len = strlen(s);
+
+ if (index >= buf_sz)
+ return false;
+ check_space(len);
+ memmove (buf_data + index + len, buf_data + index, buf_sz - index);
+ memcpy (buf_data + index, s, len);
+ buf_sz += len;
+
+ return true;
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netbuffer
^
|
@@ -0,0 +1,53 @@
+#ifndef _NETBUFFER_
+#define _NETBUFFER_
+
+#include "sys/sys"
+#include "error/error"
+#include "config/config"
+#include "profiler/profiler"
+#include "fdset/fdset"
+
+class Netbuffer {
+public:
+ Netbuffer();
+ Netbuffer (Netbuffer const &other);
+ Netbuffer (string const &s);
+ virtual ~Netbuffer();
+ Netbuffer const &operator= (Netbuffer const &other);
+
+ char charat(unsigned index) const;
+ char operator[] (unsigned index) { return charat(index); }
+
+ char const *bufdata() const { return buf_data; }
+ unsigned bufsz() const { return buf_sz; }
+
+ unsigned netread (int fd, int timeout = 0);
+ unsigned netwrite (int fd, int timeout) const;
+
+ unsigned 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);
+
+ bool insertat(unsigned index, char const *s, unsigned len = 0);
+ bool removeat(unsigned index, unsigned len = 1);
+
+ void reset();
+
+
+private:
+ void copy (Netbuffer const &other);
+ void destroy();
+
+ void check_space(unsigned extra);
+ string printable(char c) const;
+
+ char *buf_data;
+ unsigned buf_sz;
+ unsigned buf_alloced;
+};
+
+#endif
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netbuffer1.cc
^
|
@@ -0,0 +1,4 @@
+#include "netbuffer"
+
+Netbuffer::Netbuffer(): buf_data(0), buf_sz(0), buf_alloced(0) {
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netbuffer2.cc
^
|
@@ -0,0 +1,5 @@
+#include "netbuffer"
+
+Netbuffer::Netbuffer (Netbuffer const &other) {
+ copy (other);
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netbuffer3.cc
^
|
@@ -0,0 +1,5 @@
+#include "netbuffer"
+
+Netbuffer::~Netbuffer() {
+ destroy();
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netbuffer4.cc
^
|
@@ -0,0 +1,6 @@
+#include "netbuffer"
+
+Netbuffer::Netbuffer (string const &s):
+ buf_data(0), buf_sz(0), buf_alloced(0) {
+ setstring(s);
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netread.cc
^
|
@@ -0,0 +1,32 @@
+#include "netbuffer"
+
+unsigned Netbuffer::netread (int fd, int timeout) {
+ PROFILE("Netbuffer::netread");
+
+ 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";
+ }
+
+ check_space(config.buffersize());
+
+ ssize_t nread = read (fd, buf_data + buf_sz, config.buffersize());
+ if (nread < 0)
+ throw static_cast<Error>("Read failed on fd ") + fd + ": " +
+ strerror(errno);
+ buf_sz += nread;
+
+ if (config.debug() && nread) {
+ ostringstream o;
+ o << "Got " << nread << " bytes from fd " << fd << ": ";
+ for (unsigned i = 0; i < (unsigned)nread; i++)
+ o << printable(buf_data[i]);
+ o << "\n";
+ _debugmsg (o.str());
+ }
+
+ return nread;
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/netwrite.cc
^
|
@@ -0,0 +1,65 @@
+#include "netbuffer"
+#include "balancer/balancer"
+
+unsigned Netbuffer::netwrite (int fd, int timeout) const {
+ PROFILE("Netbuffer::netwrite");
+
+ debugmsg((Mstr("About to write ") + buf_sz) +
+ (Mstr(" bytes to fd ") + fd) +
+ (Mstr(", timeout ") + timeout) + "\n");
+
+ if (!buf_sz)
+ return (0);
+
+ // Log to dump directory if requested
+ if (config.dumpdir().length()) {
+ ostringstream of;
+ of << config.dumpdir() << "/" << balancer.requestnr() << "." << fd;
+ FILE *f;
+ if ( (!(f = fopen (of.str().c_str(), "a"))) &&
+ (!(f = fopen (of.str().c_str(), "w"))) )
+ warnmsg ("Cannot write traffic log " + of.str() + ": " +
+ strerror(errno) + "\n");
+ else {
+ fwrite (buf_data, 1, buf_sz, f);
+ fclose (f);
+ }
+ }
+
+ // Send to the socket
+ unsigned totwritten = 0;
+ while (totwritten < buf_sz) {
+ // 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";
+ }
+
+ // 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)
+ totwritten += nwritten;
+ else if (errno != EINVAL && errno != EINPROGRESS)
+ throw static_cast<Error>("Write/send failed: errno=") +
+ errno + ", " + strerror(errno) + ", result=" + nwritten;
+ }
+
+ return buf_sz;
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/opassign.cc
^
|
@@ -0,0 +1,9 @@
+#include "netbuffer"
+
+Netbuffer const &Netbuffer::operator= (Netbuffer const &other) {
+ if (this != &other) {
+ destroy();
+ copy (other);
+ }
+ return (*this);
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/printable.cc
^
|
@@ -0,0 +1,24 @@
+#include "netbuffer"
+
+string Netbuffer::printable (char ch) const {
+ ostringstream o;
+
+ if (isprint(ch) && ch != '\\') {
+ o << ch;
+ return (o.str());
+ } else if (ch == '\n')
+ return ("\\n");
+ else if (ch == '\r')
+ return ("\\r");
+ else if (ch == '\t')
+ return ("\\t");
+ else {
+ char buf[10];
+ sprintf (buf, "%3.3o", ch & 0xff);
+ o << "\\" << buf;
+ return (o.str());
+ }
+
+ // Avoid warnings
+ return (".");
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/removeat.cc
^
|
@@ -0,0 +1,15 @@
+#include "netbuffer"
+
+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,
+ buf_sz - index - len);
+ buf_sz -= len;
+ }
+ return true;
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/reset.cc
^
|
@@ -0,0 +1,5 @@
+#include "netbuffer"
+
+void Netbuffer::reset() {
+ buf_sz = 0;
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/setchar.cc
^
|
@@ -0,0 +1,9 @@
+#include "netbuffer"
+
+bool Netbuffer::setchar(unsigned offset, char ch) {
+ PROFILE("Netbuffer::setchar");
+ if (offset >= buf_sz)
+ return false;
+ buf_data[offset] = ch;
+ return true;
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/setstring.cc
^
|
@@ -0,0 +1,10 @@
+#include "netbuffer"
+
+void Netbuffer::setstring(string const &s) {
+ destroy();
+ check_space(s.size() + 1);
+ buf_sz = s.size();
+ memcpy (buf_data, s.c_str(), buf_sz);
+ buf_data[buf_sz] = 0;
+ debugmsg((Mstr("Set netbuffer to string, ") + buf_sz) + " bytes\n");
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/netbuffer/strfind.cc
^
|
@@ -0,0 +1,15 @@
+#include "netbuffer"
+
+unsigned Netbuffer::strfind(char const *s) const {
+ PROFILE("Netbuffer::strfind");
+
+ if (!buf_sz)
+ return (0);
+
+ char *cp = strnstr(buf_data, s, buf_sz);
+ if (cp)
+ return (cp - buf_data);
+
+ return (0);
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.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;
+}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/profiler
^
|
+(directory)
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/profiler/profiler
^
|
@@ -0,0 +1,18 @@
+#ifndef _PROFILER_
+#define _PROFILER_
+
+#include "sys/sys"
+#include "ThreadsAndMutexes/mutex/mutex"
+
+class Profiler {
+public:
+ Profiler (char const *f);
+ ~Profiler();
+private:
+ char const *fname;
+ struct timeval tv_start;
+ static FILE *outf;
+};
+
+#endif
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/profiler/profiler1.cc
^
|
@@ -0,0 +1,9 @@
+#include "profiler"
+
+FILE *Profiler::outf;
+
+Profiler::Profiler (char const *f) {
+ fname = f;
+ gettimeofday(&tv_start, 0);
+}
+
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/profiler/profiler2.cc
^
|
@@ -0,0 +1,22 @@
+#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);
+}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/debugmsg.cc
^
|
@@ -1,14 +1,16 @@
#include "sys"
#include "config/config"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "profiler/profiler"
+#include "mstr/mstr"
-void debugmsg (string const &s) {
- if (config.debug()) {
- Mutex::lock(&cerr);
- if (config.prefixtimestamp())
- cerr << timestamp() << ' ';
- cerr << pthread_self() << " DEBUG: " << s;
- cerr.flush();
- Mutex::unlock(&cerr);
- }
+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);
}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/sys/fdwrite.cc
^
|
@@ -1,66 +0,0 @@
-#include "sys"
-#include "fdset/fdset"
-#include "balancer/balancer"
-
-void fdwrite (int fd, int timeout, char const *buf, unsigned buflen) {
- if (config.debug()) {
- ostringstream o;
- o << "About to write " << buflen << " bytes to fd " << fd
- << ", timeout " << timeout << "\n";
- debugmsg (o.str());
- }
-
- // Log to dump directory if requested
- if (config.dumpdir().length()) {
- ostringstream of;
- of << config.dumpdir() << "/" << balancer.requestnr() << "." << fd;
- FILE *f;
- if ( (!(f = fopen (of.str().c_str(), "a"))) &&
- (!(f = fopen (of.str().c_str(), "w"))) )
- warnmsg ("Cannot write traffic log " + of.str() + ": " +
- strerror(errno) + "\n");
- else {
- fwrite (buf, 1, buflen, f);
- fclose (f);
- }
- }
-
- // Send to the socket
- unsigned totwritten = 0;
- while (totwritten < buflen) {
- // 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";
- }
-
- // Push bytes
- ssize_t nwritten;
-# ifdef SunOS
- if (fd < 3)
- nwritten = write (fd, buf + totwritten, buflen - totwritten);
- else
- nwritten = send (fd, buf + totwritten, buflen - totwritten, 0);
-# else
- nwritten = write (fd, buf + totwritten, buflen - totwritten);
-# endif
-
- if (config.debug()) {
- ostringstream o;
- o << "Sent " << nwritten << " bytes to fd " << fd << "\n";
- debugmsg (o.str());
- }
-
- // EINVAL / EINPROGRESS errors are handled as: retry
- // If any bytes were written, we're ok
- if (nwritten >= 1)
- totwritten += nwritten;
- else if (errno != EINVAL && errno != EINPROGRESS)
- throw static_cast<Error>("Write/send failed: errno=") +
- errno + ", " + strerror(errno) + ", result=" + nwritten;
- }
-}
-
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/ipmatch.cc
^
|
@@ -2,13 +2,16 @@
#include "../config/config"
bool ipmatch (struct in_addr adr, struct in_addr mask) {
- long laddr = * ((long*)&adr);
- long lmask = * ((long*)&mask);
+ PROFILE("ipmatch");
+
+ long laddr, lmask;
+ memcpy (&laddr, &adr, sizeof(long));
+ memcpy (&lmask, &mask, sizeof(long));
bool match = ( (laddr & lmask) == laddr );
- if (config.debug())
- debugmsg ("Matching ip " + (string)inet_ntoa(adr) + " against mask " +
- (string)inet_ntoa(mask) + ": " +
- (match ? "match" : "miss") + "\n");
+ debugmsg ("Matching ip " + (string)inet_ntoa(adr) + " against mask " +
+ (string)inet_ntoa(mask) + ": " +
+ (match ? "match" : "miss") + "\n");
+
return (match);
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/main.cc
^
|
@@ -3,6 +3,7 @@
#include "config/config"
#include "balancer/balancer"
#include "error/error"
+#include "profiler/profiler"
using namespace std;
@@ -10,28 +11,34 @@
Balancer balancer;
static void sigcatcher (int sig) {
+ debugmsg ("Seen signal " + sig + '\n');
if (sig == SIGHUP)
balancer.report(true);
else if (sig != SIGPIPE)
balancer.terminate(true);
+ // Actually we wouldn't need to test for SIGPIPE, it's ignored (see below).
+ // Leaving the test in place for future versions, better an extra if
+ // than forgetting it later.
}
int main (int argc, char **argv) {
+
+ PROFILE("main");
+
static int relevant_sig[] = {
- SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP, SIGPIPE
+ SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP
};
try {
// Load configuration from the commandline, promote verbosity
config.parsecmdline (argc, argv);
- ostringstream o;
- o << getpid();
- msg ("XR running as PID " + o.str() + "\n");
+ msg ((Mstr("XR running as PID ") + getpid()) + "\n");
// Load the signal handler.
for (unsigned i = 0; i < sizeof(relevant_sig) / sizeof(int); i++)
signal (relevant_sig[i], sigcatcher);
+ signal (SIGPIPE, SIG_IGN);
// Configure the balancer and start serving.
balancer.init();
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/msg.cc
^
|
@@ -1,14 +1,16 @@
#include "sys"
#include "config/config"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "profiler/profiler"
+#include "mstr/mstr"
-void msg (string const &s) {
- if (config.verbose()) {
- Mutex::lock(&cerr);
- if (config.prefixtimestamp())
- cerr << timestamp() << ' ';
- cerr << pthread_self() << " INFO: " << s;
- cerr.flush();
- Mutex::unlock(&cerr);
- }
+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);
}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/sys/mtrand.cc
^
|
@@ -0,0 +1,198 @@
+/*
+ NOTE: All of the functions have been commented out, with two exceptions:
+ - init_genrand(seed) has been renamed to mt_srand(seed)
+ - genrand_int32(void) has been renamed to mt_rand(void)
+ The renaming was to resemble the naming of srand() and rand(). All other
+ functions were unnecessary, and as such commented out.
+*/
+
+
+
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* initializes mt[N] with a seed */
+void mt_srand(unsigned long s)
+{
+ mt[0]= s & 0xffffffffUL;
+ for (mti=1; mti<N; mti++) {
+ mt[mti] =
+ (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ mt[mti] &= 0xffffffffUL;
+ /* for >32 bit machines */
+ }
+}
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+/* slight change for C++, 2004/2/26 */
+// void init_by_array(unsigned long init_key[], int key_length)
+// {
+// int i, j, k;
+// init_genrand(19650218UL);
+// i=1; j=0;
+// k = (N>key_length ? N : key_length);
+// for (; k; k--) {
+// mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+// + init_key[j] + j; /* non linear */
+// mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+// i++; j++;
+// if (i>=N) { mt[0] = mt[N-1]; i=1; }
+// if (j>=key_length) j=0;
+// }
+// for (k=N-1; k; k--) {
+// mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
+// - i; /* non linear */
+// mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+// i++;
+// if (i>=N) { mt[0] = mt[N-1]; i=1; }
+// }
+//
+// mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+// }
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long mt_rand(void)
+{
+ unsigned long y;
+ static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if init_genrand() has not been called, */
+ mt_srand(5489UL); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
+
+/* generates a random number on [0,0x7fffffff]-interval */
+// long genrand_int31(void)
+// {
+// return (long)(genrand_int32()>>1);
+// }
+
+/* generates a random number on [0,1]-real-interval */
+// double genrand_real1(void)
+// {
+// return genrand_int32()*(1.0/4294967295.0);
+// /* divided by 2^32-1 */
+// }
+
+/* generates a random number on [0,1)-real-interval */
+// double genrand_real2(void)
+// {
+// return genrand_int32()*(1.0/4294967296.0);
+// /* divided by 2^32 */
+// }
+
+/* generates a random number on (0,1)-real-interval */
+// double genrand_real3(void)
+// {
+// return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0);
+// /* divided by 2^32 */
+// }
+
+/* generates a random number on [0,1) with 53-bit resolution*/
+// double genrand_res53(void)
+// {
+// unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+// return(a*67108864.0+b)*(1.0/9007199254740992.0);
+// }
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+
+// int main(void)
+// {
+// int i;
+// unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
+// init_by_array(init, length);
+// printf("1000 outputs of genrand_int32()\n");
+// for (i=0; i<1000; i++) {
+// printf("%10lu ", genrand_int32());
+// if (i%5==4) printf("\n");
+// }
+// printf("\n1000 outputs of genrand_real2()\n");
+// for (i=0; i<1000; i++) {
+// printf("%10.8f ", genrand_real2());
+// if (i%5==4) printf("\n");
+// }
+// return 0;
+// }
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/reportmsg.cc
^
|
@@ -1,8 +1,9 @@
#include "sys"
#include "config/config"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "mstr/mstr"
-void reportmsg (string const &s) {
+void reportmsg (Mstr const &s) {
Mutex::lock(&cerr);
if (config.prefixtimestamp())
cerr << timestamp() << ' ';
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/serversocket.cc
^
|
@@ -1,7 +1,11 @@
#include "sys"
-#include "../error/error"
+#include "error/error"
+#include "profiler/profiler"
+#include "config/config"
int serversocket (string addr, int port, string desc) {
+ PROFILE("serversocket");
+
int sock;
// Create the server socket, set options
@@ -20,28 +24,25 @@
saddr.sin_port = htons(port);
// Assign interface to listen to
- ostringstream display_ip;
if (addr[0] != '0') {
msg ("Binding balancer 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";
- display_ip << addr;
} else {
+ msg ("Binding balancer to all local IP addresses\n");
saddr.sin_addr.s_addr = htonl (INADDR_ANY);
- display_ip << "0.0.0.0";
}
- display_ip << ":" << port;
// Bind and listen
if (bind (sock, (sockaddr*) &saddr, sizeof(saddr)) < 0)
- throw static_cast<Error>("Failed to bind ") + desc + " to " +
- display_ip.str() + ": " + strerror(errno);
+ 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 " +
- display_ip.str() + ": " + strerror(errno);
+ throw static_cast<Error>("Failed to listen to ") + desc +
+ " IP/port: " + strerror(errno);
- msg ("TCP server for " + desc + " listening to " + display_ip.str() + "\n");
+ msg ("TCP server for " + desc + " listening\n");
return (sock);
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/socketclose.cc
^
|
@@ -2,12 +2,10 @@
#include "../config/config"
void socketclose (int fd) {
- if (config.debug()) {
- ostringstream o;
- o << fd;
- debugmsg ("Closing socket " + o.str() + "\n");
- }
+ PROFILE("socketclose");
+ debugmsg ((Mstr("Closing socket ") + fd) + "\n");
+
if (config.fastclose()) {
struct linger l;
l.l_onoff = 1;
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/str2parts.cc
^
|
@@ -2,17 +2,36 @@
#include "config/config"
vector<string> str2parts (string const &s, char sep) {
+ 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);
}
|
[-]
[+]
|
Added |
crossroads-devel.tar.gz/xr/sys/strnstr.cc
^
|
@@ -0,0 +1,28 @@
+#include "sys"
+
+#ifndef HAVE_STRNSTR
+// [KK 2008-10-13] Got this from
+// http://opengrok.creo.hu/dragonfly/xref/src/lib/libc/string/strnstr.c
+// and it's of course for systems that don't have this lib function
+// themselves
+char *strnstr(const char *s, const char *find, size_t slen) {
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != '\0') {
+ len = strlen(find);
+ do {
+ do {
+ if (slen < 1 || (sc = *s) == '\0')
+ return (0);
+ --slen;
+ ++s;
+ } while (sc != c);
+ if (len > slen)
+ return (0);
+ } while (strncmp(s, find, len) != 0);
+ s--;
+ }
+ return ( (char*) s);
+}
+#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/sys
^
|
@@ -25,6 +25,7 @@
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/wait.h>
#ifdef INADDR_NONE
# define HAVE_INADDR_NONE
@@ -39,24 +40,43 @@
#include <sstream>
#include <string>
#include <vector>
+#include <queue>
-/* Generic functions */
+/* Profiling support on/off */
+#ifdef PROFILER
+# define PROFILE(x) Profiler local_prof(x)
+#else
+# define PROFILE(x)
+#endif
using namespace std;
-void msg (string const &s);
-void debugmsg (string const &s);
-void reportmsg (string const &s);
+// This we need locally for msg(), debugmsg()
+#include "mstr/mstr"
+
+/* Messaging. Conditionals are defined as a macro to speed things up. */
+void _msg (Mstr const &s);
+#define msg(x) if(config.verbose()) _msg(x)
+void _debugmsg (Mstr const &s);
+#define debugmsg(x) if (config.debug()) _debugmsg(x)
+void reportmsg (Mstr const &s);
+void warnmsg (Mstr const &s);
+
+/* Other */
int serversocket (string addr, int port, string description);
string timestamp(time_t s = 0);
bool ipmatch (struct in_addr addr, struct in_addr mask);
void socketclose (int fd);
vector<string> str2parts (string const &s, char sep);
-void fdwrite (int fd, int timeout, char const *buf, unsigned buflen);
-void warnmsg (string const &s);
+void mt_srand(unsigned long s);
+unsigned long mt_rand(void);
#ifndef HAVE_INET_ATON
int inet_aton (char const *name, struct in_addr *addr);
#endif
+#ifndef HAVE_STRNSTR
+char *strnstr (char const *s, char const *find, size_t slen);
+#endif
+
#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/sys/warnmsg.cc
^
|
@@ -1,8 +1,9 @@
#include "sys"
#include "config/config"
#include "ThreadsAndMutexes/mutex/mutex"
+#include "mstr/mstr"
-void warnmsg (string const &s) {
+void warnmsg (Mstr const &s) {
Mutex::lock(&cerr);
if (config.prefixtimestamp())
cerr << timestamp() << ' ';
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/tcpdispatcher/dispatch.cc
^
|
@@ -1,6 +1,11 @@
#include "tcpdispatcher"
void TcpDispatcher::dispatch() {
+ // Check that a working algorithm is available. May be missing if
+ // constructor's "new" failed.
+ if (!algorithm)
+ throw static_cast<Error>("No algorithm in Tcpdispatcher::dispatch");
+
bool connected = false;
// Build up the target list, if not yet done so. The HTTP dispatcher
@@ -11,8 +16,9 @@
for (unsigned i = 0; i < balancer.nbackends(); i++)
if (balancer.backend(i).available()) {
target_list.add(i);
- msg (" Candidate target: " +
- balancer.backend(i).description() + "\n");
+ if (config.verbose())
+ msg (" Candidate target: " +
+ balancer.backend(i).description() + "\n");
}
}
@@ -23,17 +29,15 @@
Backend tb = balancer.backend(target_backend);
if (!tb.connect()) {
balancer.backend(target_backend).live(false);
- msg ("Failed to connect to back end " + tb.description() +
- ", trying to dispatch to other\n");
+ if (config.verbose())
+ msg ("Failed to connect to back end " + tb.description() +
+ ", trying to dispatch to other\n");
} else {
connected = true;
backendfd(tb.sock());
-
- ostringstream o;
- o << tb.sock();
- msg ("Will dispatch client to back end " + tb.description() +
- " on fd " + o.str() + "\n");
-
+ msg ((Mstr("Will dispatch client to back end ") +
+ tb.description()) +
+ (Mstr(" on fd ") + tb.sock()) + "\n");
break;
}
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/tcpdispatcher/execute.cc
^
|
@@ -1,39 +1,149 @@
#include "tcpdispatcher"
-void TcpDispatcher::execute() {
+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 << clientfd();
- msg ("Dispatch request for client fd " + o.str() + "\n");
+ o << prog << ' ' << ip;
+ msg ((Mstr("Max connection rate exceeded, invoking '") + o.str()) +
+ "'\n");
+ int ret = system(o.str().c_str());
+ if (ret == -1)
+ throw static_cast<Error>("Failed to start system call: ") +
+ strerror(errno);
+ else if (WIFEXITED(ret)) {
+ int exitstat = WEXITSTATUS(ret);
+ if (exitstat)
+ warnmsg((Mstr("Program '") + o.str()) +
+ (Mstr("' exited with exit status ") + exitstat) +
+ "\n");
+ else
+ msg ("Program terminated normally.\n");
+ } else
+ warnmsg((Mstr("Program '") + o.str()) +
+ "' terminated abnormally!\n");
+}
+
+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 (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[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());
+ run_excess(config.hardmaxconnexcess(), inet_ntoa(client_ip));
+ 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());
+ run_excess(config.softmaxconnexcess(), inet_ntoa(client_ip));
+ usleep(config.defertime());
+ }
+ }
try {
dispatch();
} catch (Error const &e) {
+ Mutex::lock(&cerr);
cerr << e.what() << "\n";
+ Mutex::unlock(&cerr);
socketclose (clientfd());
return;
}
- ostringstream co;
- co << clientfd();
- ostringstream bo;
- bo << backendfd();
- msg ("Dispatching client fd " + co.str() + " to " +
- balancer.backend(target_backend).description() +
- ", fd " + bo.str() + "\n");
-
+ msg ((Mstr("Dispatching 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) {
+ Mutex::lock(&cerr);
cerr << e.what() << "\n";
+ Mutex::unlock(&cerr);
}
balancer.backend(target_backend).endconnection();
-
+
socketclose (clientfd());
socketclose (backendfd());
- msg ("Done dispatching client fd " + co.str() + " at " +
- balancer.backend(target_backend).description() + "\n");
-
+
+ msg ((Mstr("Done dispatching to back end fd ") + backendfd()) +
+ (Mstr(" at ") + balancer.backend(target_backend).description()) +
+ "\n");
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/tcpdispatcher/handle.cc
^
|
@@ -1,12 +1,10 @@
#include "tcpdispatcher"
void TcpDispatcher::handle() {
- if (config.debug()) {
- ostringstream o;
- o << "TCP dispatcher: About to shuttle between client fd "
- << clientfd() << " and backend fd " << backendfd() << "\n";
- debugmsg (o.str());
- }
+
+ debugmsg (Mstr("TCP dispatcher: About to shuttle between client fd ") +
+ clientfd() +
+ (Mstr(" and backend fd ") + backendfd()) + "\n");
while (1) {
Fdset readset (config.client_timeout());
@@ -16,14 +14,7 @@
int sock;
if ((sock = readset.readable()) < 0)
break;
-
- if (config.debug()) {
- ostringstream o;
- o << sock;
- debugmsg ("Data waiting on fd " + o.str() + "\n");
- }
-
- if (!readchunk (sock))
+ if (!netbuffer.netread(sock))
break;
int othersock, timeout;
@@ -35,13 +26,13 @@
timeout = config.client_timeout();
}
- ostringstream o;
- o << "Had data on " << sock << ", sending to " << othersock << "\n";
- debugmsg (o.str());
-
- fdwrite (othersock, timeout, databuf(), databufsize());
+ debugmsg (Mstr("Had data on ") + sock +
+ (Mstr(", sending to ") + othersock) + "\n");
+ netbuffer.netwrite (othersock, timeout);
if (sock == backendfd())
- balancer.backend(target_backend).addbytes(databufsize());
+ balancer.backend(target_backend).addbytes(netbuffer.bufsz());
+
+ netbuffer.reset();
}
}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/tcpdispatcher/printable.cc
^
|
@@ -1,24 +0,0 @@
-#include "tcpdispatcher"
-
-string TcpDispatcher::printable (char ch) const {
- ostringstream o;
-
- if (isprint(ch) && ch != '\\') {
- o << ch;
- return (o.str());
- } else if (ch == '\n')
- return ("\\n");
- else if (ch == '\r')
- return ("\\r");
- else if (ch == '\t')
- return ("\\t");
- else {
- char buf[10];
- sprintf (buf, "%3.3o", ch & 0xff);
- o << "\\" << buf;
- return (o.str());
- }
-
- // Avoid warnings
- return (".");
-}
|
[-]
[+]
|
Deleted |
crossroads-devel.tar.gz/xr/tcpdispatcher/readchunk.cc
^
|
@@ -1,18 +0,0 @@
-#include "tcpdispatcher"
-
-unsigned TcpDispatcher::readchunk (int src) {
- ssize_t nread = read (src, data_buf, config.buffersize());
- if (nread < 0)
- throw static_cast<Error>("Read failed on fd ") + src;
-
- if (config.debug() && nread) {
- ostringstream o;
- o << "Got " << nread << " bytes from fd " << src << ": ";
- for (unsigned i = 0; i < (unsigned)nread; i++)
- o << printable(data_buf[i]);
- o << "\n";
- debugmsg (o.str());
- }
-
- return (data_bufsz = nread);
-}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/tcpdispatcher/tcpdispatcher
^
|
@@ -6,6 +6,7 @@
#include "config/config"
#include "ThreadsAndMutexes/thread/thread"
#include "backendvector/backendvector"
+#include "netbuffer/netbuffer"
// Dispatching algorithm workers
#include "DispatchAlgorithms/algorithm/algorithm"
@@ -15,13 +16,14 @@
#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();
@@ -35,21 +37,18 @@
void clientfd(int c) { client_fd = c; }
int backendfd() const { return backend_fd; }
void backendfd(int b) { backend_fd = b; }
- char const *databuf() const { return data_buf; }
- unsigned databufsize() const { return data_bufsz; }
+
BackendVector const &targetlist() const { return target_list; }
void targetlist (BackendVector t) { target_list = t; }
-
+
unsigned readchunk (int src);
-
+
private:
- string printable (char ch) const;
struct in_addr client_ip;
int target_backend, client_fd, backend_fd;
- char *data_buf;
- unsigned data_bufsz;
Algorithm *algorithm;
BackendVector target_list;
+ Netbuffer netbuffer;
};
#endif
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/tcpdispatcher/tcpdispatcher1.cc
^
|
@@ -2,10 +2,7 @@
TcpDispatcher::TcpDispatcher(int cfd, struct in_addr cip):
Thread(), client_ip(cip), target_backend(-1), client_fd(cfd),
- backend_fd(-1), data_bufsz(0), target_list() {
-
- // Set up a data buffer for network transfers
- data_buf = new char[config.buffersize()];
+ backend_fd(-1), target_list(), netbuffer() {
// Instantiate dispatchmode algorithm
switch (config.dispatchmode()) {
@@ -26,9 +23,15 @@
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;
}
+
+ // NOTE: Memory errors for algorithm pointer are not handled here,
+ // but in dispatch() (don't want to throw up in the constructor)
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/tcpdispatcher/tcpdispatcher2.cc
^
|
@@ -1,7 +1,6 @@
#include "tcpdispatcher"
TcpDispatcher::~TcpDispatcher() {
- delete data_buf;
delete algorithm;
debugmsg ("TCP dispatcher finished\n");
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/webinterface/answer.cc
^
|
@@ -8,6 +8,14 @@
return (ret);
}
+static double str2dbl (string const &s, string const &desc) {
+ double ret;
+
+ if (sscanf (s.c_str(), "%lf", &ret) < 0)
+ throw static_cast<Error>("Bad ") + desc;
+ return (ret);
+}
+
static unsigned backendindex (string const &s) {
unsigned ret;
@@ -32,13 +40,13 @@
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 + "'";
-
+
return (ret);
}
@@ -64,7 +72,7 @@
}
// debugmsg ("Decoded: '" + s + "' into '" + ret + "'\n");
-
+
return (ret);
}
@@ -86,6 +94,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,10 +113,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();
@@ -138,20 +148,19 @@
}
// /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]);
@@ -159,48 +168,37 @@
return;
}
- // /server/verbose
- // /server/verbose/VALUE
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "verbose") ) {
- if (parts.size() == 2 || parts[2] == "off" || parts[2] == "0")
- config.verbose(false);
- else
- config.verbose(true);
+ // /server/verbose/BOOLEAN
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "verbose") {
+ config.verbose(str2bool(parts[2], "verbose"));
answer_status();
return;
}
- // /server/debug
- // /server/debug/VALUE
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "debug") ) {
- if (parts.size() == 2 || parts[2] == "off" || parts[2] == "0")
- config.debug(false);
- else
- config.debug(true);
+ // /server/debug/VERBOSE
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "debug") {
+ config.debug(str2bool(parts[2], "debug"));
answer_status();
return;
}
// /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/clienttimeout
// /server/clienttimeout/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "clienttimeout") ) {
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "clienttimeout") {
unsigned num = 0;
- if (parts.size() == 3)
+ if (parts[2] != "")
num = str2uns (parts[2], "client timeout");
config.client_timeout(num);
answer_status();
@@ -209,22 +207,34 @@
// /server/backendtimeout
// /server/backendtimeout/NUMBER
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "backendtimeout") ) {
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "backendtimeout") {
unsigned num = 0;
- if (parts.size() == 3)
- num = str2uns (parts[2], "client timeout");
+ if (parts[2] != "")
+ num = str2uns (parts[2], "back end timeout");
config.backend_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 +245,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,8 +257,144 @@
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 static_cast<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 static_cast<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/addbackend/IP:PORT
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "addbackend") {
+ vector<string> address = str2parts(parts[2], ':');
+ if (address.size() != 2)
+ throw static_cast<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 &&
+ if (parts.size() == 4 &&
parts[0] == "backend" && parts[2] == "weight") {
unsigned ind = backendindex(parts[1]);
unsigned num = str2uns (parts[3], "back end weight");
@@ -269,15 +415,46 @@
return;
}
+ // /backend/NR/loadavg/FLOAT
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "loadavg") {
+ unsigned ind = backendindex(parts[1]);
+ double fnum = str2dbl (parts[3], "back end loadavg");
+ balancer.backend(ind).loadavg(fnum);
+ answer_status();
+ return;
+ }
+
// /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/up/BOOL
+ if (parts.size() == 4 && parts[0] == "backend" && parts[2] == "up") {
+ unsigned ind = backendindex(parts[1]);
+ balancer.backend(ind).up(str2bool(parts[3], "up"));
+ answer_status();
+ return;
+ }
+
+ // /backend/NR/backendcheck/
+ // /backend/NR/backendcheck/VALUE
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "backendcheck") {
unsigned ind = backendindex(parts[1]);
- balancer.backend(ind).hostmatch(parts.size() == 3 ? "" : parts[3]);
+ BackendCheck check;
+ if (parts[3] != "")
+ check.parse(parts[3]);
+ balancer.backend(ind).backendcheck(check);
answer_status();
return;
}
- throw static_cast<Error>("No action for URI '") + uri + "'";
+ throw static_cast<Error>("No action for URI '/") + uri + "'";
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/webinterface/answerblob.cc
^
|
@@ -10,5 +10,6 @@
"Content-Length: " + cl.str() + "\r\n"
"\r\n" +
blob;
- fdwrite (cfd, config.client_timeout(), resp.c_str(), resp.size() - 1);
+ Netbuffer buf(resp);
+ buf.netwrite(cfd, config.client_timeout());
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/webinterface/answerstatus.cc
^
|
@@ -11,14 +11,20 @@
"<?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"
- " <dispatchmode>" << config.dispatchmodestr() << "</dispatchmode>\n"
- " <maxconnections>" << config.maxconn() << "</maxconnections>\n"
" <clienttimeout>" << config.client_timeout() << "</clienttimeout>\n"
" <backendtimeout>" << config.backend_timeout() << "</backendtimeout>\n"
+ " <dispatchmode>" << config.dispatchmodestr() << "</dispatchmode>\n"
+ " <webinterface>" << config.webinterfaceip() << ':' << config.webinterfaceport() << "</webinterface>\n"
+ " <dnscachetimeout>" << config.dnscachetimeout() << "</dnscachetimeout>\n"
" <buffersize>" << config.buffersize() << "</buffersize>\n"
+ " <closesocketsfast>" << config.fastclose() << "</closesocketsfast>\n"
" <checks>\n"
" <wakeupinterval>" << config.wakeupsec() << "</wakeupinterval>\n"
" <checkupinterval>" << config.checkupsec() << "</checkupinterval>\n"
@@ -28,6 +34,35 @@
" <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"
@@ -41,9 +76,12 @@
" <header>" << config.serverheader(i) << "</header>\n"
" </serverheader>\n"
;
- o <<
+ o <<
" </serverheaders>\n"
" </http>\n"
+ " <backends>" << balancer.nbackends() << "</backends>\n"
+ " <terminating>" << balancer.terminate() << "</terminating>\n"
+ " <connections>" << balancer.connections() << "</connections>\n"
" </server>\n"
;
@@ -54,16 +92,19 @@
" <address>" << balancer.backend(i).description() << "</address>\n"
" <weight>" << balancer.backend(i).weight() << "</weight>\n"
" <maxconnections>" << balancer.backend(i).maxconn() << "</maxconnections>\n"
+ " <loadavg>" << balancer.backend(i).loadavg() << "</loadavg>\n"
+ " <up>" << balancer.backend(i).upstr() << "</up>\n"
" <live>" << balancer.backend(i).livestr() << "</live>\n"
" <available>" << balancer.backend(i).availablestr() << "</available>\n"
" <connections>" << balancer.backend(i).connections() << "</connections>\n"
" <bytesserved>" << balancer.backend(i).bytesserved() << "</bytesserved>\n"
" <clientsserved>" << balancer.backend(i).clientsserved() << "</clientsserved>\n"
" <hostmatch>" << balancer.backend(i).hostmatch() << "</hostmatch>\n"
+ " <backendcheck>" << balancer.backend(i).backendcheck().setting() << "</backendcheck>\n"
" </backend>\n"
;
o <<
"</status>\n\n";
-
+
answer_blob (o.str());
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/webinterface/execute.cc
^
|
@@ -17,11 +17,8 @@
}
break;
}
-
- ostringstream o;
- o << sfd;
- msg ("Web interface started on socket " + o.str() + "\n");
+ msg ((Mstr("Web interface started on socket ") + sfd) + "\n");
while (!balancer.terminate()) {
try {
Fdset fdset(0);
@@ -36,15 +33,20 @@
}
} catch (Error const &e) {
cerr << e.what() << " (webinterface)\n";
- string err = static_cast<string>
- ("HTTP/1.0 500 Server Error\r\n") +
- "X-Reason: " + e.what() + "\r\n"
- "Content-Length: 0\r\n"
- "\r\n";
- try {
- fdwrite(cfd, config.client_timeout(), err.c_str(), err.size());
- } catch (...) {
- }
+ ostringstream m;
+ m <<
+ "<h1>Web interface error</h1>\n"
+ "XR's web interface could not handle your request.<p/>\n"
+ "<i>" << e.what() << "</i>\n";
+ ostringstream o;
+ o <<
+ "HTTP/1.0 500 Server Error\r\n"
+ "X-Reason: " << e.what() << "\r\n"
+ "Content-Length: " << m.str().length() << "\r\n"
+ "\r\n" <<
+ m.str();
+ Netbuffer buf(o.str());
+ buf.netwrite(cfd, config.client_timeout());
socketclose(cfd);
}
}
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xr/webinterface/serve.cc
^
|
@@ -1,21 +1,10 @@
#include "webinterface"
void Webinterface::serve () {
- ostringstream o;
- o << cfd;
- msg ("Webinterface serving request on client fd " + o.str() + "\n");
+ msg ((Mstr("Webinterface serving request on client fd ") + cfd) + "\n");
- char databuf[config.buffersize()];
Httpbuffer clientrequest;
- do {
- Fdset set (config.client_timeout());
- set.add(cfd);
- int nread;
- if (set.readable() != cfd ||
- ( nread = read(cfd, databuf, config.buffersize()) ) < 0)
- throw static_cast<Error>("Read failure on fd ") + cfd;
- clientrequest.add(databuf, nread);
- } while (clientrequest.bodyreceived() == Httpbuffer::b_is_not_received);
+ clientrequest.netread(cfd, config.client_timeout());
msg ("Webinterface request: " + clientrequest.firstline() + "\n");
answer(clientrequest);
|
[-]
[+]
|
Changed |
crossroads-devel.tar.gz/xrctl/xrctl
^
|
@@ -1,495 +1,730 @@
#!/usr/bin/perl
use strict;
+use Getopt::Std;
-# Configuration section. Enter your favorite values here.
-# -------------------------------------------------------
+# --------------------------------------------------------------------------
+# xrctl: used to start, stop, restart etc. the XR balancer.
-# Directory where PID stamp files are stored, named xr-{service}.pid
-my $piddir = '/var/run';
-
-# '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});
- }
-}
-
-# 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);
+# Default configuration file to read and default logging facility
+my $default_conf = '/etc/xrctl.xml';
+my $default_logger = 'logger';
+
+# Default settings, must match xr's defaults
+my $default_dispatchmode = 'least-connections';
+my $default_maxconnections = 0;
+my $default_clienttimeout = 30;
+my $default_backendtimeout = 30;
+my $default_buffersize = 2048;
+my $default_wakeupinterval = 5;
+my $default_checkupinterval = 0;
+my $default_weight = 1;
+my $default_hostmatch = '.';
+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 $sysxp = new XMLParser($xp->data('system'));
+for my $tag qw(piddir pscmd uselogger logdir maxlogsize loghistory path) {
+ $sysconf{$tag} = $sysxp->data($tag);
+ msg("System config $tag: $sysconf{$tag}\n");
+}
+if ($sysconf{path} eq '') {
+ msg ("No path in configuration, using environment\n");
+ $sysconf{path} = $ENV{PATH};
+}
+
+# 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);
+ 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 'force') {
+ cmd_force(@ARGV);
+} elsif ($cmd eq 'restart') {
+ cmd_restart(@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";
-
-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
-
-ENDUSAGE
-}
+# --------------------------------------------------------------------------
+# Top level commands
-# List services and command lines
-# -------------------------------
-sub list {
- print ("Configured services: ",
- join (', ', sort (keys (%services))),
- "\n");
+sub cmd_list {
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");
+ print ("Service: $s\n");
+ print (" Process name : ", process_name($s), "\n");
+ print (" PID file : ", pid_file($s), "\n");
+ print (" Logging : ", log_file($s), "\n");
+ print (" XR command : ", xr_command($s), "\n");
}
}
-# Show the status of the commands
-# -------------------------------
-sub status {
+sub cmd_start {
+ for my $s (@_) {
+ die ("Cannot start service $s, already running\n")
+ if (is_running($s));
+ }
for my $s (@_) {
- die ("xrctl: No such service '$s'\n") unless ($services{$s});
- print ("Service $s: ", getstatus($s), "\n");
+ print ("Service $s: ");
+ start_service($s);
+ print ("started\n");
}
}
-# Start service(s)
-# ----------------
-sub start {
+sub cmd_stop {
+ my @pids;
for my $s (@_) {
- print ("Service $s: ");
- my $status = getstatus($s);
- if ($status !~ /^not/) {
- print ("already $status\n");
- } else {
- rundaemon ($s, xrcommand($s));
- print ("started\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 (15, @pids) if ($#pids > -1);
+ print ("Services @_: stopped\n");
}
-# Stop service(s)
-# ---------------
-sub stop {
+sub cmd_force {
for my $s (@_) {
print ("Service $s: ");
- my $status = getstatus($s);
- if ($status =~ /^not/) {
- print ($status, "\n");
+ if (is_running($s)) {
+ print ("already running\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_restart {
+ my @pids;
+ for my $s (@_) {
+ my @p = is_running($s)
+ or die ("Cannot restart 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);
- 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 ("not ") 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 ($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($s, 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);
-
- # 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)");
+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");
+
+ 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";
+
+Usage: xrctl [-FLAGS] action [SERVICE ...]
+Flags are:
+ -v increases verbosity
+ -c CONFIG specifies the configuration, default $default_conf
+Actions are:
+ configtest builds invocations from the configuration file and validates them
+ list shows the xr command line
+ start starts the service(s) if they are not yet running
+ stop stops the service(s) if they are running
+ force forces the service(s) up: starts if not running
+ restart restarts the service(s) if they are running
+ 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
+Services are the services stated in the configuration. When absent, all
+named 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 @args = xr_cmdarr($s);
+ my $xr = xfind_bin('xr');
+ 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;
+ for my $d (split (/:/, $sysconf{path})) {
+ if (-x "$d/$bin") {
+ msg ("Binary '$bin' found as '$d/$bin'\n");
+ return ("$d/$bin");
+ }
+ }
+ 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");
+}
+
+# PID file according to a service name
+sub pid_file {
+ my $service = shift;
+ return ($sysconf{piddir} . '/' . process_name($service) . '.pid')
+ if ($sysconf{piddir});
+ return (undef);
+}
+
+# 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)) {
+ return ("|$logger -t 'xr-$service'");
} 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 = "'$sub'" if ($sub =~ /\s/);
+ $ret .= ' ' . $sub;
+ }
+ 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, '--pidfile', pid_file($service)) if (pid_file($service));
+ push (@cmd, '--prefix-timestamp')
+ if (!istrue($sysconf{uselogger}) or !find_bin('logger'));
+
+ # 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
+ my $type = 'tcp';
+ $type = $sp->data('type') if ($sp->data('type'));
+ my $addr = '0:10000';
+ $addr = $sp->data('address') if ($sp->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');
+
+ # Handle general flags and boolflags
+ push (@cmd,
+ flag($sp, '--web-interface', 'webinterface', ''),
+ flag($sp, '--dispatch-mode', 'dispatchmode',
+ $default_dispatchmode),
+ flag($sp, '--max-connections', 'maxconnections',
+ $default_maxconnections),
+ flag($sp, '--client-timeout', 'clienttimeout',
+ $default_clienttimeout),
+ flag($sp, '--backend-timeout', 'backendtimeout',
+ $default_backendtimeout),
+ flag($sp, '--buffer-size', 'buffersize',
+ $default_buffersize),
+ flag($sp, '--wakeup-interval', 'wakeupinterval',
+ $default_wakeupinterval),
+ flag($sp, '--checkup-interval', 'checkupinterval',
+ $default_checkupinterval),
+ flag($sp, '--time-interval', 'timeinterval',
+ $default_timeinterval),
+ flag($sp, '--hard-maxconnrate', 'hardmaxconnrate',
+ $default_hardmaxconnrate),
+ flag($sp, '--soft-maxconnrate', 'softmaxconnrate',
+ $default_softmaxconnrate),
+ flag($sp, '--defer-time', 'defertime',
+ $default_defertime),
+ flag($sp, '--hard-maxconn-excess', 'hardmaxconnexcess',
+ $default_hardmaxconnexcess),
+ flag($sp, '--soft-maxconn-excess', 'softmaxconnexcess',
+ $default_softmaxconnexcess),
+ flag($sp, '--dns-cache-timeout', 'dnscachetimeout',
+ $default_dnscachetimeout),
+ flag($sp, '--log-traffic-dir', 'logtrafficdir', ''));
+ for my $k (sort (keys (%boolflags))) {
+ push (@cmd, $boolflags{$k}) if (istrue($sp->data($k)));
+ }
+
+ # ACL's
+ for (my $i = 0; ; $i++) {
+ my $mask = $sp->data('allowfrom', $i) or last;
+ push (@cmd, '--allow-from', $mask);
+ }
+ for (my $i = 0; ; $i++) {
+ my $mask = $sp->data('denyfrom', $i) or last;
+ push (@cmd, '--deny-from', $mask);
+ }
+
+ # HTTP goodies
+ push (@cmd, '--add-xr-version')
+ if ($sp->data('addxrversion') and
+ istrue($sp->data('addxrversion')));
+ push (@cmd, '--add-x-forwarded-for')
+ if ($sp->data('addxforwardedfor') and
+ istrue($sp->data('addxforwardedfor')));
+ push (@cmd, '--sticky-http')
+ if ($sp->data('stickyhttp') and
+ istrue($sp->data('stickyhttp')));
+ for (my $i = 0; ; $i++) {
+ my $h = $sp->data('header', $i) or last;
+ push (@cmd, '--add-server-header', $h);
+ }
+
+ # The <backend> blocks for this service
+ my $last_hostmatch = $default_hostmatch;
+ 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 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 $wt = $bp->data('weight') or $default_weight;
+ my $mx = $bp->data('maxconnections') or $default_maxconnections;
+ my $ad = $bp->data('address')
+ or die ("Backend in service '$service' lacks <address>\n");
+
+ if ($mx and
+ ($wt ne $default_weight or $mx ne $default_maxconnections)) {
+ $ad .= ":$mx";
+ }
+ if ($wt and ($wt ne $default_weight)) {
+ $ad .= ":$wt";
+ }
+ push (@cmd, '--backend', $ad);
+ }
+
+ # All done
+ my @ret;
+ for my $c (@cmd) {
+ push (@ret, $c) if ($c ne '');
+ }
+ 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++) {
+ $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 <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 ($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 ("Invalid or 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;
+ }
+ }
+ }
+
+ # 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;
|