crossroads

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

commit 999c56944a289bbfecfa925fa0fc096a1dc26d75
parent e8d1a84a54b585712b2b5f2a80655f4920229ff7
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:34:52 +0100

2.00

Diffstat:
MChangeLog | 697+------------------------------------------------------------------------------
MMakefile | 106++++++++++++++++++++++++++++++++++++-------------------------------------------
Ddoc/Makefile | 40----------------------------------------
Ddoc/crossroads-mgr.1 | 120-------------------------------------------------------------------------------
Ddoc/crossroads.1 | 149-------------------------------------------------------------------------------
Ddoc/crossroads.conf.7 | 1360-------------------------------------------------------------------------------
Ddoc/crossroads.html | 4562-------------------------------------------------------------------------------
Ddoc/crossroads.pdf | 0
Adoc/fig/cluster.graffle | 631+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddoc/main/benchmarking.yo | 171-------------------------------------------------------------------------------
Ddoc/main/compiling.yo | 226-------------------------------------------------------------------------------
Ddoc/main/conf/addclientheader.yo | 151------------------------------------------------------------------------------
Ddoc/main/conf/allow.yo | 84-------------------------------------------------------------------------------
Ddoc/main/conf/backendport.yo | 8--------
Ddoc/main/conf/backlog.yo | 12------------
Ddoc/main/conf/bindto.yo | 12------------
Ddoc/main/conf/checkinterval.yo | 22----------------------
Ddoc/main/conf/connectiontimeout.yo | 11-----------
Ddoc/main/conf/decay.yo | 18------------------
Ddoc/main/conf/dispatchmode.yo | 84-------------------------------------------------------------------------------
Ddoc/main/conf/headerinspection.yo | 13-------------
Ddoc/main/conf/httptiminglog.yo | 12------------
Ddoc/main/conf/maxcon-client.yo | 13-------------
Ddoc/main/conf/maxconnections.yo | 16----------------
Ddoc/main/conf/onfailure.yo | 10----------
Ddoc/main/conf/onhooks.yo | 27---------------------------
Ddoc/main/conf/port.yo | 11-----------
Ddoc/main/conf/retries.yo | 11-----------
Ddoc/main/conf/revivinginterval.yo | 26--------------------------
Ddoc/main/conf/server.yo | 14--------------
Ddoc/main/conf/shmkey.yo | 18------------------
Ddoc/main/conf/state.yo | 13-------------
Ddoc/main/conf/stickycookie.yo | 35-----------------------------------
Ddoc/main/conf/trafficlog.yo | 20--------------------
Ddoc/main/conf/type.yo | 18------------------
Ddoc/main/conf/useraccount.yo | 15---------------
Ddoc/main/conf/verbose-backend.yo | 10----------
Ddoc/main/conf/verbose.yo | 15---------------
Ddoc/main/conf/weight.yo | 15---------------
Ddoc/main/config.yo | 278-------------------------------------------------------------------------------
Ddoc/main/crossroads.yo | 44--------------------------------------------
Ddoc/main/defs.yo | 84-------------------------------------------------------------------------------
Ddoc/main/formattable.yo | 59-----------------------------------------------------------
Ddoc/main/impatient.yo | 73-------------------------------------------------------------------------
Ddoc/main/intro.yo | 294-------------------------------------------------------------------------------
Ddoc/main/keywords.yo | 63---------------------------------------------------------------
Ddoc/main/tips.yo | 54------------------------------------------------------
Ddoc/main/tips/clientip.yo | 78------------------------------------------------------------------------------
Ddoc/main/tips/debugging.yo | 107-------------------------------------------------------------------------------
Ddoc/main/tips/deeporshallow.yo | 103-------------------------------------------------------------------------------
Ddoc/main/tips/dnscaching.yo | 21---------------------
Ddoc/main/tips/examples.yo | 236-------------------------------------------------------------------------------
Ddoc/main/tips/externalhandler.yo | 448-------------------------------------------------------------------------------
Ddoc/main/tips/howselected.yo | 179-------------------------------------------------------------------------------
Ddoc/main/tips/httpstickiness.yo | 118-------------------------------------------------------------------------------
Ddoc/main/tips/ipconntrackmax.yo | 34----------------------------------
Ddoc/main/tips/ipfiltering.yo | 122-------------------------------------------------------------------------------
Ddoc/main/tips/periodic.yo | 89-------------------------------------------------------------------------------
Ddoc/main/tips/rendering.yo | 66------------------------------------------------------------------
Ddoc/main/tips/retries.yo | 41-----------------------------------------
Ddoc/main/tips/tcpstickiness.yo | 22----------------------
Ddoc/main/tips/throttling.yo | 27---------------------------
Ddoc/main/tips/virtual.yo | 77-----------------------------------------------------------------------------
Ddoc/main/tips/webinterface.yo | 118-------------------------------------------------------------------------------
Ddoc/main/using.yo | 161-------------------------------------------------------------------------------
Ddoc/man/crossroads-mgr.yo | 106-------------------------------------------------------------------------------
Ddoc/man/crossroads.conf.yo | 29-----------------------------
Ddoc/man/crossroads.yo | 120-------------------------------------------------------------------------------
Adoc/xr.odt | 0
Adoc/xr.pdf | 0
Detc/Makefile.conf | 44--------------------------------------------
Detc/Makefile.def | 78------------------------------------------------------------------------------
Detc/Makefile.help | 37-------------------------------------
Detc/dispatcher-roundrobin | 84-------------------------------------------------------------------------------
Detc/hdr.template | 5-----
Detc/healthcheck | 63---------------------------------------------------------------
Detc/rdp-helper | 140-------------------------------------------------------------------------------
Detc/svncheck | 21---------------------
Detc/svnrev | 42------------------------------------------
Detc/svnrev.txt | 1-
Detc/xml-status-short.xslt | 67-------------------------------------------------------------------
Detc/xml-status-to-html.xslt | 131-------------------------------------------------------------------------------
Dsrc/Makefile | 45---------------------------------------------
Dsrc/crossroads-daemon/Makefile | 28----------------------------
Dsrc/crossroads-daemon/main.c | 61-------------------------------------------------------------
Dsrc/crossroads-mgr/Makefile | 19-------------------
Dsrc/crossroads-mgr/crossroads-mgr.in | 460-------------------------------------------------------------------------------
Dsrc/crossroads-mgr/crossroads-mgr.usage | 25-------------------------
Dsrc/crossroads-mgr/crossroads-mgr.xslt | 212-------------------------------------------------------------------------------
Dsrc/crossroads.h | 534-------------------------------------------------------------------------------
Dsrc/crossroads/Makefile | 29-----------------------------
Dsrc/crossroads/main.c | 164-------------------------------------------------------------------------------
Dsrc/lib/Makefile | 56--------------------------------------------------------
Dsrc/lib/allocreporter.c | 69---------------------------------------------------------------------
Dsrc/lib/ansistamp.c | 27---------------------------
Dsrc/lib/backendavailable.c | 25-------------------------
Dsrc/lib/backendconnect.c | 114-------------------------------------------------------------------------------
Dsrc/lib/backendcount.c | 16----------------
Dsrc/lib/checkservice.c | 83-------------------------------------------------------------------------------
Dsrc/lib/choosebackend.c | 393-------------------------------------------------------------------------------
Dsrc/lib/cmdconfigtest.c | 12------------
Dsrc/lib/cmdrestart.c | 22----------------------
Dsrc/lib/cmdservices.c | 26--------------------------
Dsrc/lib/cmdstart.c | 36------------------------------------
Dsrc/lib/cmdstatus.c | 192-------------------------------------------------------------------------------
Dsrc/lib/cmdstop.c | 35-----------------------------------
Dsrc/lib/cmdtell.c | 73-------------------------------------------------------------------------
Dsrc/lib/configmsg.c | 144-------------------------------------------------------------------------------
Dsrc/lib/configread.c | 164-------------------------------------------------------------------------------
Dsrc/lib/configwrite.c | 153-------------------------------------------------------------------------------
Dsrc/lib/copysockets.c | 17-----------------
Dsrc/lib/data.c | 46----------------------------------------------
Dsrc/lib/deallocreporter.c | 39---------------------------------------
Dsrc/lib/decrclientcount.c | 43-------------------------------------------
Dsrc/lib/dns.c | 101-------------------------------------------------------------------------------
Dsrc/lib/error.c | 32--------------------------------
Dsrc/lib/expandprogamname.c | 30------------------------------
Dsrc/lib/gmtstamp.c | 26--------------------------
Dsrc/lib/hashpjw.c | 25-------------------------
Dsrc/lib/httpcopy.c | 122-------------------------------------------------------------------------------
Dsrc/lib/httperror.c | 18------------------
Dsrc/lib/httpheaderaddheader.c | 33---------------------------------
Dsrc/lib/httpheaderappendheader.c | 43-------------------------------------------
Dsrc/lib/httpheaderconnectiontype.c | 27---------------------------
Dsrc/lib/httpheaderfree.c | 18------------------
Dsrc/lib/httpheaderhascookie.c | 32--------------------------------
Dsrc/lib/httpheaderhttpver.c | 33---------------------------------
Dsrc/lib/httpheadernew.c | 16----------------
Dsrc/lib/httpheaderread.c | 119-------------------------------------------------------------------------------
Dsrc/lib/httpheaderremoveheader.c | 21---------------------
Dsrc/lib/httpheadersetheader.c | 36------------------------------------
Dsrc/lib/httpheaderval.c | 27---------------------------
Dsrc/lib/httpheaderwrite.c | 27---------------------------
Dsrc/lib/httpinsertheader.c | 55-------------------------------------------------------
Dsrc/lib/httplogactivity.c | 49-------------------------------------------------
Dsrc/lib/httpserve.c | 228-------------------------------------------------------------------------------
Dsrc/lib/httpserversocket.c | 72------------------------------------------------------------------------
Dsrc/lib/httpwrite.c | 18------------------
Dsrc/lib/incrclientcount.c | 41-----------------------------------------
Dsrc/lib/initsockaddr.c | 21---------------------
Dsrc/lib/interrupt.c | 54------------------------------------------------------
Dsrc/lib/ipfaddallow.c | 17-----------------
Dsrc/lib/ipfadddeny.c | 17-----------------
Dsrc/lib/ipfallowed.c | 30------------------------------
Dsrc/lib/ipfdenied.c | 30------------------------------
Dsrc/lib/ipfloadfile.c | 49-------------------------------------------------
Dsrc/lib/ipfmatch.c | 20--------------------
Dsrc/lib/ipfparse.c | 52----------------------------------------------------
Dsrc/lib/ishexdigit.c | 15---------------
Dsrc/lib/isspace.c | 15---------------
Dsrc/lib/lexer.c | 2973-------------------------------------------------------------------------------
Dsrc/lib/lexer.l | 576-------------------------------------------------------------------------------
Dsrc/lib/limitsmsg.c | 47-----------------------------------------------
Dsrc/lib/lockreporter.c | 32--------------------------------
Dsrc/lib/logactivityany.c | 19-------------------
Dsrc/lib/logactivitycontinuation.c | 10----------
Dsrc/lib/logactivityend.c | 10----------
Dsrc/lib/logactivitystart.c | 10----------
Dsrc/lib/makesocket.c | 62--------------------------------------------------------------
Dsrc/lib/markactivity.c | 147-------------------------------------------------------------------------------
Dsrc/lib/msg.c | 32--------------------------------
Dsrc/lib/msgdumpbuf.c | 48------------------------------------------------
Dsrc/lib/netallocbufs.c | 18------------------
Dsrc/lib/netbuffer.c | 31-------------------------------
Dsrc/lib/netbufread.c | 54------------------------------------------------------
Dsrc/lib/netcopy.c | 111-------------------------------------------------------------------------------
Dsrc/lib/netread.c | 101-------------------------------------------------------------------------------
Dsrc/lib/netwrite.c | 109-------------------------------------------------------------------------------
Dsrc/lib/optionsread.c | 27---------------------------
Dsrc/lib/optionswrite.c | 16----------------
Dsrc/lib/parser.c | 3538-------------------------------------------------------------------------------
Dsrc/lib/parser.h | 189-------------------------------------------------------------------------------
Dsrc/lib/parser.y | 1548-------------------------------------------------------------------------------
Dsrc/lib/parserclose.c | 29-----------------------------
Dsrc/lib/parserfilename.c | 13-------------
Dsrc/lib/parserinput.c | 40----------------------------------------
Dsrc/lib/parseropen.c | 56--------------------------------------------------------
Dsrc/lib/parserrun.c | 11-----------
Dsrc/lib/parserskipchar.c | 26--------------------------
Dsrc/lib/parserskipline.c | 21---------------------
Dsrc/lib/proxyerror.txt | 10----------
Dsrc/lib/runservice.c | 130-------------------------------------------------------------------------------
Dsrc/lib/setproctitle.c | 64----------------------------------------------------------------
Dsrc/lib/setprogramtitle.c | 20--------------------
Dsrc/lib/showconfig.c | 54------------------------------------------------------
Dsrc/lib/stagetostring.c | 21---------------------
Dsrc/lib/statetostring.c | 16----------------
Dsrc/lib/strcasestr.c | 25-------------------------
Dsrc/lib/strexpandformat.c | 191-------------------------------------------------------------------------------
Dsrc/lib/stringtostate.c | 16----------------
Dsrc/lib/strlcat.c | 30------------------------------
Dsrc/lib/strnstr.c | 35-----------------------------------
Dsrc/lib/strprintf.c | 13-------------
Dsrc/lib/strupr.c | 16----------------
Dsrc/lib/strvprintf.c | 32--------------------------------
Dsrc/lib/symtabend.c | 13-------------
Dsrc/lib/symtablookup.c | 16----------------
Dsrc/lib/symtabset.c | 11-----------
Dsrc/lib/symtabstart.c | 13-------------
Dsrc/lib/sysrun.c | 50--------------------------------------------------
Dsrc/lib/tcpserve.c | 235-------------------------------------------------------------------------------
Dsrc/lib/thruputlog.c | 58----------------------------------------------------------
Dsrc/lib/timeofday.c | 17-----------------
Dsrc/lib/trafficlog.c | 66------------------------------------------------------------------
Dsrc/lib/uidassume.c | 28----------------------------
Dsrc/lib/uidrestore.c | 13-------------
Dsrc/lib/unlockreporter.c | 26--------------------------
Dsrc/lib/usage.c | 16----------------
Dsrc/lib/usage.txt | 49-------------------------------------------------
Dsrc/lib/vsyslog.c | 16----------------
Dsrc/lib/wakeuphandler.c | 93-------------------------------------------------------------------------------
Dsrc/lib/warning.c | 32--------------------------------
Dsrc/lib/writelog.c | 15---------------
Dsrc/lib/xcalloc.c | 18------------------
Dsrc/lib/xmalloc.c | 10----------
Dsrc/lib/xrealloc.c | 27---------------------------
Dsrc/lib/xstrcat.c | 19-------------------
Dsrc/lib/xstrcatch.c | 14--------------
Dsrc/lib/xstrdup.c | 16----------------
Dtest/Makefile | 30------------------------------
Dtest/allowfrom.conf | 15---------------
Dtest/bad01.conf | 16----------------
Dtest/bad02.conf | 11-----------
Dtest/bad03.conf | 3---
Dtest/bad04.conf | 5-----
Dtest/bad05.conf | 9---------
Dtest/bad06.conf | 8--------
Dtest/bad07.conf | 11-----------
Dtest/bad08.conf | 11-----------
Dtest/bad09.conf | 5-----
Dtest/bad10.conf | 6------
Dtest/bad11.conf | 12------------
Dtest/bad11a.inc | 6------
Dtest/bad12.conf | 12------------
Dtest/bad12a.inc | 6------
Dtest/bad13.conf | 40----------------------------------------
Dtest/bad14.conf | 40----------------------------------------
Dtest/bad15.conf | 39---------------------------------------
Dtest/bad16.conf | 44--------------------------------------------
Dtest/bad17.conf | 14--------------
Dtest/bad18.conf | 13-------------
Dtest/bad19.conf | 12------------
Dtest/bad20.conf | 13-------------
Dtest/bad21.conf | 9---------
Dtest/bad22.conf | 8--------
Dtest/bad23.conf | 9---------
Dtest/breakingclient | 58----------------------------------------------------------
Dtest/chunkedserver | 110-------------------------------------------------------------------------------
Dtest/client | 55-------------------------------------------------------
Dtest/extcheck.conf | 14--------------
Dtest/extdispatcher | 14--------------
Dtest/extdispatcher.conf | 19-------------------
Dtest/hashtest.c | 45---------------------------------------------
Dtest/maxcon.conf | 27---------------------------
Dtest/runtest | 65-----------------------------------------------------------------
Dtest/server | 100-------------------------------------------------------------------------------
Dtest/t01.conf | 35-----------------------------------
Dtest/t02.conf | 36------------------------------------
Dtest/t03.conf | 13-------------
Dtest/t04.conf | 35-----------------------------------
Dtest/t05.conf | 31-------------------------------
Dtest/t06.conf | 44--------------------------------------------
Dtest/t07.conf | 13-------------
Dtest/t08.conf | 32--------------------------------
Dtest/t09.conf | 13-------------
Dtest/t09a.inc | 10----------
Dtest/t09b.inc | 11-----------
Dtest/t10.conf | 40----------------------------------------
Dtest/t11.conf | 41-----------------------------------------
Dtest/t12.conf | 41-----------------------------------------
Dtest/t13.conf | 43-------------------------------------------
Dtest/t14.conf | 42------------------------------------------
Dtest/t15.conf | 14--------------
Dtest/t16.conf | 8--------
Dtest/t20.conf | 14--------------
Dtest/virtpoolmgr | 139-------------------------------------------------------------------------------
Dtools/getkeywords | 23-----------------------
Dtools/gettools | 24------------------------
Dtools/installdoc | 34----------------------------------
Dtools/makedist | 44--------------------------------------------
Dtools/patch-header | 87-------------------------------------------------------------------------------
Dtools/patch-mgr | 30------------------------------
Dtools/untab | 32--------------------------------
Axr/Makefile | 42++++++++++++++++++++++++++++++++++++++++++
Axr/algorithm/algorithm | 12++++++++++++
Axr/algorithm/algorithm.cc | 4++++
Axr/backend/available.cc | 7+++++++
Axr/backend/availablestr.cc | 5+++++
Axr/backend/backend | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Axr/backend/backend1.cc | 5+++++
Axr/backend/backend2.cc | 5+++++
Axr/backend/backend3.cc | 4++++
Axr/backend/check.cc | 6++++++
Axr/backend/connect.cc | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/backend/description.cc | 7+++++++
Axr/backend/live.cc | 8++++++++
Axr/backend/livestr.cc | 5+++++
Axr/backenddef/backenddef | 26++++++++++++++++++++++++++
Axr/backenddef/backenddef1.cc | 13+++++++++++++
Axr/backenddef/backenddef2.cc | 4++++
Axr/balancer/addbackend.cc | 7+++++++
Axr/balancer/balancer | 44++++++++++++++++++++++++++++++++++++++++++++
Axr/balancer/balancer1.cc | 4++++
Axr/balancer/connections.cc | 9+++++++++
Axr/balancer/init.cc | 18++++++++++++++++++
Axr/balancer/serve.cc | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/buffer/add.cc | 12++++++++++++
Axr/buffer/buffer | 34++++++++++++++++++++++++++++++++++
Axr/buffer/buffer1.cc | 4++++
Axr/buffer/buffer2.cc | 5+++++
Axr/buffer/buffer3.cc | 5+++++
Axr/buffer/charat.cc | 7+++++++
Axr/buffer/copy.cc | 13+++++++++++++
Axr/buffer/data.cc | 5+++++
Axr/buffer/destroy.cc | 7+++++++
Axr/buffer/insertat1.cc | 5+++++
Axr/buffer/insertat2.cc | 13+++++++++++++
Axr/buffer/oparray.cc | 9+++++++++
Axr/buffer/opequal.cc | 9+++++++++
Axr/buffer/removeat.cc | 14++++++++++++++
Axr/buffer/set.cc | 9+++++++++
Axr/buffer/size.cc | 5+++++
Axr/buffer/stringat.cc | 14++++++++++++++
Axr/checkupthread/checkupthread | 14++++++++++++++
Axr/checkupthread/execute.cc | 23+++++++++++++++++++++++
Axr/config/Makefile.lib | 2++
Axr/config/addallow.cc | 8++++++++
Axr/config/adddeny.cc | 8++++++++
Axr/config/config | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/config/config1.cc | 28++++++++++++++++++++++++++++
Axr/config/parsecmdline.cc | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/config/setbackend.cc | 23+++++++++++++++++++++++
Axr/config/setdispatcmode.cc | 23+++++++++++++++++++++++
Axr/config/setinteger.cc | 9+++++++++
Axr/config/setserver.cc | 21+++++++++++++++++++++
Axr/dispatchmode/dispatchmode | 28++++++++++++++++++++++++++++
Axr/error/error | 21+++++++++++++++++++++
Axr/error/error1.cc | 4++++
Axr/error/error2.cc | 7+++++++
Axr/error/opplus1.cc | 6++++++
Axr/error/opplus2.cc | 6++++++
Axr/error/opplus3.cc | 10++++++++++
Axr/error/txt.cc | 10++++++++++
Axr/etc/Makefile.class | 9+++++++++
Rtools/c-conf -> xr/etc/c-conf | 0
Rtools/e-txt2c -> xr/etc/e-txt2c | 0
Rtools/e-ver -> xr/etc/e-ver | 0
Axr/etc/firstav.pl | 21+++++++++++++++++++++
Axr/etc/gettools | 26++++++++++++++++++++++++++
Axr/etc/usage.txt | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/etc/xr-test | 36++++++++++++++++++++++++++++++++++++
Axr/external/external | 14++++++++++++++
Axr/external/target.cc | 33+++++++++++++++++++++++++++++++++
Axr/fdset/fdset | 40++++++++++++++++++++++++++++++++++++++++
Axr/fdset/fdset1.cc | 4++++
Axr/fdset/readable.cc | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/fdset/readwriteable.cc | 46++++++++++++++++++++++++++++++++++++++++++++++
Axr/fdset/writeable.cc | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/firstactive/firstactive | 13+++++++++++++
Axr/firstactive/target.cc | 9+++++++++
Axr/httpbuffer/addheader.cc | 10++++++++++
Axr/httpbuffer/addheader1.cc | 14++++++++++++++
Axr/httpbuffer/bodyreceived.cc | 46++++++++++++++++++++++++++++++++++++++++++++++
Axr/httpbuffer/cookievalue.cc | 17+++++++++++++++++
Axr/httpbuffer/findheader.cc | 25+++++++++++++++++++++++++
Axr/httpbuffer/firstline.cc | 12++++++++++++
Axr/httpbuffer/headersdone.cc | 22++++++++++++++++++++++
Axr/httpbuffer/headerval.cc | 26++++++++++++++++++++++++++
Axr/httpbuffer/httpbuffer | 32++++++++++++++++++++++++++++++++
Axr/httpbuffer/httpbuffer1.cc | 4++++
Axr/httpbuffer/setheader.cc | 26++++++++++++++++++++++++++
Axr/httpbuffer/setversion.cc | 20++++++++++++++++++++
Axr/httpdispatcher/dispatch.cc | 38++++++++++++++++++++++++++++++++++++++
Axr/httpdispatcher/getclientrequest.cc | 16++++++++++++++++
Axr/httpdispatcher/getserverresponse.cc | 17+++++++++++++++++
Axr/httpdispatcher/handle.cc | 45+++++++++++++++++++++++++++++++++++++++++++++
Axr/httpdispatcher/httpdispatcher | 28++++++++++++++++++++++++++++
Axr/httpdispatcher/httpdispatcher1.cc | 5+++++
Axr/httpdispatcher/sendclientrequest.cc | 10++++++++++
Axr/httpdispatcher/sendserverresponse.cc | 12++++++++++++
Axr/leastconn/leastconn | 13+++++++++++++
Axr/leastconn/target.cc | 23+++++++++++++++++++++++
Axr/roundrobin/roundrobin | 13+++++++++++++
Axr/roundrobin/target.cc | 26++++++++++++++++++++++++++
Axr/servertype/servertype | 31+++++++++++++++++++++++++++++++
Axr/servertype/type1.cc | 11+++++++++++
Axr/stopthread/execute.cc | 30++++++++++++++++++++++++++++++
Axr/stopthread/stopthread | 13+++++++++++++
Axr/sys/debugmsg.cc | 15+++++++++++++++
Axr/sys/ipmatch.cc | 14++++++++++++++
Axr/sys/main.cc | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/sys/msg.cc | 15+++++++++++++++
Axr/sys/reportmsg.cc | 13+++++++++++++
Axr/sys/serversocket.cc | 48++++++++++++++++++++++++++++++++++++++++++++++++
Axr/sys/sys | 44++++++++++++++++++++++++++++++++++++++++++++
Axr/sys/timestamp.cc | 15+++++++++++++++
Axr/tcpdispatcher/dispatch.cc | 28++++++++++++++++++++++++++++
Axr/tcpdispatcher/execute.cc | 43+++++++++++++++++++++++++++++++++++++++++++
Axr/tcpdispatcher/handle.cc | 15+++++++++++++++
Axr/tcpdispatcher/printable.cc | 24++++++++++++++++++++++++
Axr/tcpdispatcher/readchunk.cc | 20++++++++++++++++++++
Axr/tcpdispatcher/tcpdispatcher | 47+++++++++++++++++++++++++++++++++++++++++++++++
Axr/tcpdispatcher/tcpdispatcher1.cc | 25+++++++++++++++++++++++++
Axr/tcpdispatcher/tcpdispatcher2.cc | 7+++++++
Axr/tcpdispatcher/writechunk.cc | 27+++++++++++++++++++++++++++
Axr/thread/execute.cc | 5+++++
Axr/thread/lock.cc | 7+++++++
Axr/thread/start.cc | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Axr/thread/thread | 22++++++++++++++++++++++
Axr/thread/thread1.cc | 8++++++++
Axr/thread/thread2.cc | 4++++
Axr/thread/unlock.cc | 7+++++++
Axr/wakeupthread/execute.cc | 19+++++++++++++++++++
Axr/wakeupthread/wakeupthread | 14++++++++++++++
Axrctl/xrctl | 465+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
415 files changed, 3962 insertions(+), 30282 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,696 +1 @@ - -ChangeLog for Crossroads ------------------------------------------------------------------------------- - -1.81 [KK 2008-06-26] - - Memory leaks debugging - -1.80 [KK 2008-05-19] - - "Read from <client/server> timed out" is now only an error when - it concerns the server. When it concerns the client, it's only a - verbose message. - - In http_copy(): Copy-thru mode from server to client is NOT - entered when the server sent a HTTP/1.x 3xx response. HTTP 300 - responses are not assumed to have a body. - -1.79 [KK 2008-04-01] - - Changed 'make' in '$(MAKE)' in all Makefiles to support 'gmake' - invocations. - - Added debugging to config_write() (Solaris problems) - - Changed limits_msg() to avoid RLIMIT_MEMLOCK, RLIMIT_NPROC, - RLIMIT_RSS on systems that don't have these (Solaris problems) - - Rewrote Messaging functions msg(), error(), warning() to - autosupply the service name. - - The configuration file is now searched relative to the program, - then as /etc/crossroads.conf. So if eg. the program is - /opt/crossroads/bin/crossroads, then the config may be - /opt/crossroads/etc/crossroads.conf. - -1.78 [KK 2008-03-14] - - Limits of running service are shown when verbosity is on. - - Fixed "no child processes" error when doing system() calls - under Linux. The signal handler for SIGCHLD must apparently be - off (see src/lib/sysrun.c). Also fixed for popen() calls (see - src/lib/choosebackend.c). - -1.77 [KK 2008-03-07] - - Next development version. Introduced %l for last-connect timing. - -1.76 [KK 2008-01-31] - - Parser again made stricter: a backend's maxconnections is not allowed - when the service's dispatch type is an external handler. - - Porting issues for gcc 4.0.2 (default at MacOSX Leopard) solved. - -1.75 [KK 2008-01-25] - - Changed chunked transfer copying in HTTP mode. The trailing - \r\n, following a zero-bytes chunk, also gets handled in - http_copy(). Before it was left for handling in a next loop. - - Bugfix in the parser, regarding handling of external dispatcher - definitions. - - Change in the way Crossroads handles responses from external - dispatchers: when no back ends are available, XR will sleep for - a short time. Better handling of incorrect responses. - - Irritating svn messages during build phase suppressed. - -1.74 [KK 2008-01-22] - - Bugfix in HTTP header scanning. - - strnstr() added for systems that lack it - - Warning stanza enhanced when select() fails during network writes, - back end names are displayed - -1.73 [KK 2008-01-17] - - Upon service startup the initial configuration is logged (if - verbose is on). - - Flag -P added for parser/lexer verbosity (debugging only). - <stringstate> rewritten. String-state lexer strips trailing - spaces in stringstate mode. - - Backend-level directive "httptiminglog" implemented. - - "througputlog" logging will also include the # of bytes. - -1.72 [KK 2008-01-14] - - Lexical analyzer changed: whitespace leading up to strings (eg. - external handler specifiiers) are skipped. - - Porting issues with Intel Macs solved. - - Upped c-conf to 1.11 (compatible with MacOSX's extensions - MacPorts and with Fink). - - External dispatchers are called even when NO back ends are - available. Previously asking an external dispatcher would only - occur when one or more back ends was up. Furthermore, - Crossroads will always accept the dispatcher's suggestion, even - when that back end was previously unavailable. - -1.71 [KK 2008-01-12] - - Bugfix in 1.70 related to HTTP body copy-thru mode. - -1.70 [KK 2008-01-11] - - Bugfix in 1.69 (yeah that was fast).. - -1.69 [KK 2008-01-11] - - The HTTP header processing was rewritten to work with octet - chunks. Previously TCP buffers would be processed byte by byte - to match headers. Hopefully this will speed up HTTP processing. - -1.68 [KK 2008-01-10] - - "crossroads status" now also reports the PID of each service - listener process (in both plain text and XML). Thanks Felix Ostmann - for suggesting. - -1.67 [KK 2008-01-09] - - In the case of an external dispatcher: the dispatcher will be - called even when there is only one backend available. The - dispatcher may have side-actions to perform. Thanks Nicolas - Prochazka for suggesting this. EXPERIMENTAL VERSION - use 1.65 - until this code has proven stable. - -1.66 [KK 2008-01-09] - - Forking of a servicer moved 'forward', before dispatching - phase. That way external dispatchers won't slow down the - listener. Thanks, Nicolas Prochazka, for suggesting this. - This version is EXPERIMENTAL ONLY - use 1.65 until this code - has proven stable. Incidentally, this makes the system load - lower as well... - -1.65 [KK 2008-01-08] - - Bugfix in parsing of -B option (TCP buffer size) - -1.64 [KK 2007-12-27] - - Bugfix in "dispatchmode byclientip" - - Added testing utility test/hashtest - - The parser is stricter. HTTP-related directives are now only - allowed when 'type http' is given. - -1.63 [KK 2007-12-10] - - Cosmetic change to usage text. - - Flag -p implemented. - - Commandline functions (start/stop) etc now in properly named - §sources. - [KK 2007-12-14] Promoted to Stable. - -1.62 [KK 2007-10-03] - - Extended format specifiers with %g. In time-related modifiers - like %g, %t, %T, the optional number is an offset into the - future (so %3600t is now + 1h). Thanks Gunnar S. for pointing - this out. - -1.61 [KK 2007-10-02] - - Added messaging to allowfrom/denyfrom rule handling. Bugfixes - in the parsing and handling of allow/deny rules. Thanks Chris - G. for pointing this out to me. - -1.60 [KK 2007-08-29] - - upped c-conf to 1.10 - - Support for __progname_full in systems that have it, see setproctitle() - -1.59 [KK 2007-08-22] - - 1.58 Stable - -1.58 [KK 2007-08-21] - - Bugfix in the handling of "bindto any". Thanks, Simon M. for - pointing it out. - -1.57 [KK 2007-08-20] - - Next development release. - -1.56 [KK 2007-08-20] - - This is basically only version 1.55 marked STABLE. - -1.55 [KK 2007-08-16] - - Changed error handling of the finding of duplicate ports. Same - ports in different service statements are now an error only if - one or both services don't list a bindto address (or if the two - listed bindto addresses are the same). Thanks, Rodd Johnson, - for finding this bug. - -1.54 [KK 2007-07-30] - - Changed copyright to GPL V3. - -1.53 [KK 2007-07-29] - - Misc small changes, thanks Simon Matter for all suggestions, - fixes and diffs! Changes include: ps fix for Linux systems in - crossroads-mgr, 'use POSIX' fix in Perl, C code fix for for - older gcc's. - - crossroads-mgr now supports a flag '-a address' to specify a - listening address (also suggested by Simon). - -1.52 [KK 2007-07-12] - - Added list of keywords to the docs - - Removed Log4Perl dependency in crossroads-mgr (easier for distro's) - -1.51 [KK 2007-07-10] - - Removed global MAX_BACKEND setting. The number of back ends per - service is now totally dynamic - derived from the - configuration. - - Added counts of available back ends, unavailable back ends and - so on to "crossroads -x status". The web interface - crossroads-mgr now picks this up. - - Documentation updated. Small fixes. - -1.50 [KK 2007-07-08] - - Back end states can be initially set using the "state" - directive. Command "crossroads tell" now requires keyword - "state" for setting the state. - - Introduced actual_server and actual_port in SHM block for back - end addresses. Introduced define FQDN_LENGTH. Back end addresses - can be set using "crossroads tell". - - Web front end crossroads-mgr can now redefine a back end address. - -1.49 [KK 2007-07-04] - - Next development cycle version. - -1.48 [KK 2007-07-04] - - Added strupr() for systems that lack it. - - Version tagged STABLE. - -1.47 [KK 2007-06-27] - - Added "externalhandler" to "revivinginterval" - - Added "checkinterval" - - Last connect time per back end gets stored - -1.46 [KK 2007-06-25] - - Flag -X added (for xslt reference embedding) - - "crossroads tell {service} {backend} {state}": state can be - numeric - - Added crossroads-mgr, updated all docs, etc.. - - Fixed "make distclean" - -1.45 [KK 2007-06-12] - - Configuration option 'headerinspection {deep|shallow}' - implemented. Documentation updated. Several other documentation fixes. - - New make target 'dbginstall', installs gdb-able versions into - /usr/local/bin - - Upped c-conf to 1.09: /usr/lib64 is in the library search path - (thanks Joern F. for pointing this one out). - - Added setproctitle() for non-BSD-ish systems. Previous - set_program_title() is obsolete (it woundn't save the envp). - - Flag -B <bufsize> introduced, making the TCP buffer size - variable. Previous constant TCP_BUFSZ is now DEFAULT_TCP_BUFSZ, - which is now 5k instead of 10k. - -1.44 [KK 2007-06-12] - - Small bugfix in -b flag handling (useful during debugging only). - -1.43 [KK 2007-05-22] - - Changed the way that shmkeys are computed; the old way would - cause more collisions. - - Implemented DNS caching. By default 20 gethostbyname() results - can be cached, with a ttl of 1 hour per entry; though the ttl - has a default of 0 (no caching). Flag -d can overrule the TTL. - - Fixed makefile dependency for crossroads-daemon. - - Upped c-conf to 1.08. - - Reorganized docs. Key variables (author, maintainer, ...) are - passed from the Makefile. - - Added a small test run to the making: test/t*conf should parse - OK, test/bad*conf should fail. These tests are auto-run on - 'make local'. - - Added #include to the grammar. Shell-style #-comments are no - longer allowed. Added #define to the grammar. - -1.42 [KK 2007-05-21] - - Fixed small portability issue for gcc 4.0.3 (library dependency - with libm.a) - - Added etc/svncheck to verify that SVN holds all files, before - making a distro. - -1.41 [KK 2007-05-18] - - Included c-conf 1.07 (caching provisions - way faster build) - - Fixed deliverable, version 1.40 lacked files (DISTRO WAS BROKEN) - -1.40 [KK 2007-05-16] - - Added flag -m to control shared memory access mode. - - Implemented flag -x for XML report of status. - - Added tips sections on health check scripting and on XSLT's. - - Cosmetic code changes. - - The daemon 'sec' is now in in a separate crossroads-daemon - binary. Configuration parsing, usage info etc. not included, so - that the sec daemon has a smaller footprint. - -1.39 [KK 2007-05-06] - - This is the next development release (1.38 promoted to stable). - - Fixed tcp_serve() to only warn when accept() fails (this used - to be fatal). - -1.38 [KK 2007-04-26] Many changes: - - 'crossroads status' now supports optional service/backend arguments. - - Added specific nullstring initializers to parser.y. - - Added info on ip_conntrack_max to the tips section of the docs - (Thanks, Felix). - - Rewritten the code that timeouts upon network connect (this used - to have jumps which isn't safe). - - Added docs on -t (tabular status report) flag. - - Added code to avoid logging and running on-end hooks when HTTP - mode fails to find a suitable back end. - - Added test/t8.conf which works with breaking-client, to - simulate clients that hang up. - - 'make clean' removes GDB's history files - - Removed usage of non-reentrant functions from signal handler - (interrupt.c) - - Added RETRY_WAIT, small sleep time when retrying connecting to - the same back end - - Added description of retries keyword to the Tips&Tricks section - - Fixed mark_activity() to apply decays only for 'true' - connections, not for wakeup calls - - Split up the tips section of the docs into separate sources. - - Added flag -C to show compile-time settings. - - Sped up make process by scanning c-conf output just once (:= - instead of =, probably GNU Make only, we'll see). - -1.37 [KK 2007-04-17] Intermediate release for bughunts on 64bit Linux. - -1.36 [KK 2007-04-16] Fixed small bug in total usage counting. Bugfix - in resetting temporary failure counts of back ends (while waiting - for retries to expire). Added __attribute__ compiler macro to - functions such as msg(), warning() -- and fixed format conversion issues. - -1.35 [KK 2007-04-16] Added 'retries' keyword to back end definitions. - -1.34 [KK 2007-04-03] Added SVN revision number to the overall version - ID, so that tracking historical versions becomes easier. Added - flag -t which causes tabular display in 'crossroads status' - (useful for automated parsing, e.g. for heartbeat scripts). - [KK 2007-04-06] Increased max # of back ends per service to - 20. (In the future this should be a dynamic value - but it'll - take code changes; so for now, I'm taking 20.) - -1.33 [KK 2007-04-02] Added hashpjw.c to the repository - I had - forgotten that in 1.32 (so, 1.32 is broken..). - -1.32 [KK 2007-03-30] Change net_read() and net_copy(): Read exceptions - on the client network socket don't issue error or warning - messages; clients are notorious for just dropping dead. Logging - this just clogs up the syslog. These situations are only logged - when verbosity is on. - -1.31 [KK 2007-03-21] Changed messaging from error to warning when a - TCP write fails towards the server. The fact that the server - hangs up, can be actually intended -- Crossroads can't make - assumptions about it. Marked 1.30 as stable, 1.31 is the next - development release. - -1.30 [KK 2007-03-20] Signed/unsigned fix in net_read() (return value < - 0 would get evaluated badly when taken as unsigned). Changed - state/string handling, so that "up" is a synonym for "available" - (eg. in "crossroads tell {myservice} {mybackend} up"). Fixed up - sources so that compilation with -pedantic is OK. Added flag -W - to warn against loads of things, mainly signed/unsigned comparisons. - -1.29 [KK 2007-02-25] Small code changes in error/msg/warning, no - action when no format string. - [KK 2007-03-07] fork_tcp_servicer() won't abort upon fork - failure, just warn. This avoids fatal stops when the process - table is full. - -1.28 [KK 2007-02-15] Added small dependencies to src/Makefile. Added - debugging code to http_header_add(). The signal handler (see - interrupt.c) will exit immediately upon SIGPIPE when in serving - mode. There is no need to wait for the current request to finish, - when one of the network pipes has gone. Change exiting to _exit() - instead of exit() to avoid cleanup code execution. Misc small - changes in http mode. - -1.27 [KK 2007-02-14] Verbosity gets set correctly in HTTP type - services. - -1.26 [KK 2007-02-13] Backends that are unavailable are skipped in the - determination of targets during sticky HTTP sessions. This used - to be: only backends that are available are candidate. Difference - is that backends that are now marked down, can still be selected - -- so that sticky HTTP sessions get a chance to continue. - -1.25 [KK 2007-01-26] Next development release. - -1.24 [KK 2007-01-26] This is identical to version 1.23, tagged Stable. - -1.23 [KK 2007-01-04] Removed "sampleconf" program option. There are - too many config options to have all the sample text in the - binary. Other small fixes. - -1.22 [KK 2006-12-21] Implemented username directive. - [KK 2006-12-22] Fixed counting of total sessions. - -1.21 [KK 2006-12-11] Implemented externalhandler as dispatch mode. - Added a few sample handlers, updated the docs. NOTE: The external - dispatcher handlers are ALPHA CODE at this time. - [KK 2006-12-13] Changed the 'configuration' section of the - docs. Directives are now in their own subsection, so that they - have their own TOC entry. - [KK 2006-12-14] Changed the 'onsuccess' directive to - 'onstart'. Implemented 'onend'. Commands that are run via these - hooks, plus via 'onfailure', get expanded. Added format expanders - '%n' (connections of a back end) and '%w' (weights of a back - end). Thanks Bernd Krumboeck for loads of suggestions, code, - diffs and testing! - [KK 2006-12-19] Compilation fixes, thanks again Bernd. - -1.20 [KK 2006-12-06] Updated connection handler in HTTP requests to - show the right title (which backend is being served). Fixed bug - in client connection counting. Fixed status reporter to show an - 's' after the display of seconds. - [KK 2006-12-08] Fixed status reporter to use suffix Tb (tera) or - Gb (giga) for large bytecounts. - -1.19 [KK 2006-12-04] Implemented IP filtering (configuration: - allowfrom/allowfile, denyfrom/denyfile). Updated docs. Recoded - filtering so that the order is "deny,allow" (a-la Apache). More - messaging in "dispatchmode roundrobin". - -1.18 [KK 2006-11-29] Next development version. Fixed net_copy() to - correctly decrement the client connection count (thanks Bernd - Krumboeck for the fix!). Small document fix. - [KK 2006-12-01] http_header_connectiontype() will now correctly - understand Connection-Type: TE, close (ie. when the "close" is - part of a larger setting). - -1.17 [KK 2006-11-28] Next development version. - [KK 2006-11-29] Removed debugging stuff which by mistake slipped - into 1.16 stable. Shipping this 1.17 as next-stable (instead of - the previous 1.16, which is theoretically unnecessarily slow). - -1.16 [KK 2006-11-01] New development version. Usage text updated to - point to http://crossroads.e-tunity.com. Flag -s - implemented. Small bugfix in 'restart' action. - [KK 2006-11-28] Promoted to next stable version. - -1.15 [KK 2006-10-23] Speedup in the handling of chunked HTTP message - transfer. Furthermore, chunked messages are treated as - single-shot actions (see RFC2616, liberally explained by yours - truly). - [KK 2006-11-01] Bison- and flex-generated files are no longer - removed in 'make clean'. This helps folks who want to build - Crossroads but don't have these tools. - Version 1.15 marked as 'stable'. - -1.14 [KK 2006-10-19] Bugfix in chunked transfer encoding (yeah I - know.. yet again.. hopefully the last time). This was a - particularly nasty one. - -1.13 [KK 2006-09-26] Next development release. - [KK 2006-10-03] Porting issues for Solaris 9: localtime() issues - fixed, cp/strip used instead of install -s, ctype.h macro's avoided. - -1.12 [KK 2006-09-13] Small changes in wakeup handler. Timeout handling - in HTTP service processing slightly changed. - [KK 2006-09-15] Added checks whether both client and server want - persisting HTTP connections (see http_serve()). Header - Proxy-Connection: close is inserted if the connections should get - closed. Rewrote network to- and fro- copying: net_copy() now - replaces net_read() + net_write(). - [KK 2006-09-26] Promoted to STABLE. - -1.11 [KK 2006-09-12] HTTP type services now analyze not only the first - request/response pair to apply header magic; Subsequent pairs are - also analyzed. - -1.10 [KK 2006-09-11] Small speedup in header reading. Commandline - option "restart" will now act as "start" if no Crossroads is - found running. Rewrote chunked transfer-encoding transmission in - HTTP type services. Messaging now shows the type of message with - a prefix: ERROR, WARNING or INFO. - -1.09 [KK 2006-09-05] Small changes for HP-UX port. Crossroads should - now compile on HP-UX 11.23 / ia64. (Thanks, Bernd Krumboeck): - - gcc selected if both gcc and cc are present (c-conf 1.04) - - conditional compilation of vsyslog() function - [KK 2006-09-06] Neater support of streamed HTTP transfer - (eg. video); data is sent instantly to the client. - -1.08 [KK 2006-08-28] Next development (trunk) version. - [KK 2006-08-29] Implemented net_read() / net_write() so that - socket reads and writes are centralized, with their logging and - error handling. HTTP transfer buffers are now 'unsigned - char'. Shell script test/runtest added to drive sanity checks. - [KK 2006-08-31] Rewrote HTTPP message parsing (http_msg_*()). - Added directives 6 header modification directives. Lots of code - cleanups. The HTTP 502 error text (proxy error) is now in a - separate text file. Byte and second counts of 'crossroads status' - are now correctly displayed. Documentation updated. - -1.07 [KK 2006-07-25] Next development (trunk) version. Directive - 'insertrealip' implemented. Docs updated. While creating docs, - leading tabs are now expanded (needed for PDF documentation). - [KK 2006-08-28] 1.07 promoted to next stable version. - -1.06 [KK 2006-07-25] Small documentation fix. Embedded c-conf upped to - 1.03 (protection from endless recursive directory - scans). Optimized http_serve() for short messages (no - piggybacking is necessary after the initial chat). Flag -u (user - switch) removed; user switching won't work given privileged TCP - ports, shared memory access and semaphores all mixed together (or - in other words: I'm too lazy right now to make this work - elegantly.) - Version 1.06 promoted to the next stable. - -1.05 [KK 2006-07-12] Restarting heavily sped up, plus it won't affect - creation of new listener sockets. Messaging improved, the - affected service is reported. Flag -f (foreground mode) removed; - this debugging option is no longer useful given the complexity of - it all. Added socket shutdowns to interrupt handler - (interrupt.c). Fixed up shared memory free ups. - THIS VERSION SHOULD MAKE "CROSSROADS RESTART" WORK WELL! - -1.04 [KK 2006-06-26] Next development version. - [KK 2006-07-03] Fixed http_read(); the # of historic - connections ('totuses') would be counted double in sticky HTTP - mode. - [KK 2006-07-04] Added call to sranddev() to make weighted random - dispatching more conforming to the weights (HAVE_SRANDDEV defined - in the Makefile when sranddev() is available). - -1.03 [KK 2006-06-20] Support for older gcc's (incase strlcat() is - missing; added strlcat.c). - Added -lm to linkage flags for fmod(). - This is a new development version despite the fact that 1.02 - isn't promoted to 'stable'. - [KK 2006-06-22] Changed internal-server-error HTTP - response. Fixed very small bug in weighted random dispatchmode; - the old code wouldn't correctly see similar high weights. - [KK 2006-06-26] Fixed counting of total nr. of connections in - sticky HTTP mode (as shown with 'crossroads status'). - This version is tagged as 'stable'. - -1.02 [KK 2006-05-11] Next development release. - [KK 2006-05-12] In HTTP mode, the nr of sessions per back end is - stored. Improved status reporting: the nr of sessions is shown - (when type is http), the seconds and bytes are shown in a more - readable format. - [KK 2006-05-13] Fixed weighted random balancing; 1.01 had a bug - that didn't distribute new connections according to the weights - (see choose_backend()). Introduced new make target 'commit'. - [KK 2006-05-19] New flag -a introduced, that logs incoming and - ending activity. See further log_activity_*(). Docs - updated. Cleaned up the source tree (stale sources removed). - [KK 2006-06-07] Introduced -l flag for LOG_LOCAL0 to LOG_LOCAL7 - openlog facility selection. Bugfix in log_activity*() calls. - [KK 2006-06-07] Fixed lock_reporter() / unlock_reporter() so that - running connection (in program_stage == stage_serving) don't - crash during semaphore operations. This actually happens when - there's a running connection and crossroads is stopped on the - commandline. - [KK 2006-06-07] Modified the source to use strlcat/snprintf - instead of strcat/sprintf, to avoid warnings on - e.g. OpenBSD. Enveloped include <stdint.h> with an #ifdef. - -1.01 [KK 2006-05-09] Small codechange in http_serversocket() for GCC < - 4.0. (There was accidentally post-3 code which I didn't notice:) - [KK 2006-05-10] Improved messaging in choose_backend(), in the - case of round robin balancing. Implemented weighted balancing with - "dispatchmode random". - [KK 2006-05-11] Updated the docs regarding the new primary site - http://crossroads.e-tunity.com. Promoted 1.01 to the next stable - release, versioned 1.02 as the next development release. - -1.00 [KK 2006-05-05] This version is identical to 0.33, except that - crossroads will now be hosted in a publicly accessible SVN - repository at svn://svn.e-tunity.com/crossroads. - -0.33 [KK 2006-05-05] Fixed small bug in http_findcookie() concerning - routing cookies matching. - -0.32 [KK 2006-05-02] Fixed set_program_title() to have a separate - argv[0] argument, so that 'killall' e.a. match 'crossroads'. - -0.31 [KK 2006-04-06] Slight change of functionality in - http_serversocket(). Fail over implemented for existing HTTP - sessions. Furthermore: small code optimizations. - -0.30 [KK 2006-03-17] Optimization in http_serversocket() routine. - -0.29 [KK 2006-03-14] - - Small bugfix in chunked transfer handling (added: ishexdigit()) - -0.28 [KK 2006-03-13] - - Chunked transfer handling improved. - - Flag -V implemented. - -0.27 [KK 2006-03-10] - - Small changes in HTTP handling (better support for proxies as - back ends). - - Verbosity of HTTP handling somewhat throttled. - -0.26 [KK 2006-02-27] - - Backend definitions now support HOSTNAME:PORT in a 'server' - specifier. - - Status overview compressed to fewer lines. - - Documentation updated and refined. - - Service types introduced, defaulting to 'any'. - - New service 'stickyhttp'. New directives 'stickycookie' and - 'insertcookie' to make HTTP sessions.. well.. stick. - - Old keywords 'sessiontimeout' and 'bysessions' renamed to - 'connectiontimeout' and 'byconnections'. - -0.25 [KK 2006-02-08] Program stages are displayed as strings in - debug/error messages (see stagetostring.c). - -0.24 [KK 2006-01-20] Fixed display of high-ascii chars in the dumplog. - -0.23 [KK 2006-01-03] Fixed potential race condition when creating - network sockets over and over. Now a short sleeptime is inserted - between the retries, so that e.g. new file descriptors become - available. Fixed potential fd leak. - -0.22 [KK 2005-12-12] Portability issues fixed for Solaris builds - (thanks, Lasse Osterild, for pointing this out and fixing - it). Version ID added to the docs. - -0.21 [KK 2005-12-07] Implemented the 'configtest' command line - argument. Configuration file parsing improved (no more - shift/reduce conflicts, better error checks). Docs updated. - -0.20 [KK 2005-12-06] Changes in the dispatching machine. Added a - "dispatchmode bysessions", which selects the back end with the - least active clients (adjusted for weights). All durations are - now logged with millisecond accuracy, the "dispatchmode - byduration" uses this. (All this due to Ray Pittigher's requests - to load-balance MySQL RDBMS back ends ;-) - -0.19 [KK 2005-11-29] Cosmetic fix of debug messages. Typo fix in the - documentation (thanks, Marc Uebel for pointing this one out). - Fixed the status updater: updates now occur DURING sessions, not - just at the closing. That makes balancing by duration or by size - more precise. - -0.18 [KK 2005-11-28] Fixed #ifdef/#if clausing of - SET_PROC_TITLE_BY_ARGV. (Thanks to Ray Pittigher for pointing - this out, Linux on Redhat AS4 was too critical to grok this code.) - -0.17 [KK 2005-11-23] Maintenance-related release. Messages are now logged with - priority LOG_NOTICE (which ensures debug-information on default - Mac OSX). When the creation of a listening socket fails, - crossroads will retry after X sleep-seconds (this prevents 'port - steals'). Stability of the wakeup handler improved. - -0.16 [KK 2005-11-18] Documentation updated for the throughputlog statement. - -0.15 [KK 2005-11-17] Bugfix in time calculation of thruputlog(). - -0.14 [KK 2005-11-16] Statement 'dumptraffic' renamed to 'trafficlog'. - Implemented the 'throughputlog' statement. - -0.13 [KK 2005-11-09] Implemented support for either flock(), lockf() - or none of the above; whichever is available. Added #defines for - flock()-related calls and for others, incase your Unix lacks them - (thanks to Patrick Debois & Jan Vanhaecht, a Solaris port - effort). Added linkage flags for the libs: ucb, nsl, pthread, - socket (thanks also to Patrick Debois & Jan Vanhaecht). Added a - maxclients statement at the level of back end definitions (thanks - to Martin Lonkwitz for suggesting this). - -0.12 [KK 2005-11-03] Minor documentation update. - -0.11 [KK 2005-10-28] Docs updated. Makefiles somewhat cleaned - up. Fixed a 'make install' related issue (thanks go to Johnatan - Cua or pointing this one out). - -0.10 [KK 2005-10-26] Minor bugfix. The nr. of sessions in the report - would not get decreased upon timeout of a session (thanks go to - Martin Lonkwitz for pointing this one out). - -0.09 [KK 2005-10-18] Warnings of backend connections are avoided in - the wakeup handler. This prevents too much messaging in the - system log. Implemented commandline action 'restart'. Docs updated. - -0.08 [KK 2005-10-13] Minor build changes; port to Mac OSX Darwin. - -0.07 [KK 2005-09-15] Small change of verbose messages. Verbose - messages now go to stderr (instead of stdout). Added shmctl() - calls to make sure that we're the rightful owner of the shared - memory segment. Fixed checking of the return value of shmat() - for 64bit systems [Thanks, Ray Pittigher, for helping me find - this bug]. - -0.06 [KK 2005-09-14] Improved error messaging during the parsing of - the configuration. The onfailure or onsuccess triggers now also - run during an initial connect; this used to be only during - interrupted sessions. Improved the handling of new connections - when no back ends are available: in that case, the client's - connection gets denied (instead of accepted and closed). - Increased the default sleep time (pause when no back ends are - available) to 10 secs; edit etc/Makefile.def if you want to - change this. Implemented the statement 'bindto ip-address' in - service configurations; this can be used in situations where - crossroads must only listen to a specific IP address. - -0.05 [KK 2005-09-12] Changed the wakeup handler to generate less - messages to syslog. Hearing that a back end still hasn't woken up - is not very relevant. - Moved release from alpha to beta. - -0.04 [KK 2005-07-18] Added manual page. Internal: update of project to - svn (instead of CVS). Implemented flag -u to set the effective - uid of running services. More documentation updates. - -0.03 [KK 2005-07-15] Fixed potential race condition in mark_activity() - to set back end states. Implemented set_program_title() so that - 'ps ax | grep crossroads' shows what's going on. Change in - show_status(), the output is more condensed. Implemented "over N" - in dispatchmode, and "weight N" in backend - configurations. Rewrote the backend selection algorithm. Then of - course lots of changes in the docs. - -0.02 [KK 2005-07-11] Maintenance stuff: fixed minor things in the - docs, updated timeout message in backend_connect(), fixed minor bug in - wakeup_handler(), 'tell' argument now matches services and back - ends without regard to casing. More maintenance in the 'upload' - target: a crossroads-latest.tar.gz is created to facilitate easy - downloads from public.e-tunity.com. - -0.01 [KK 2005-07-06] First release, alpha. +2.00 [KK 2008-08-07] Release of 2.00. diff --git a/Makefile b/Makefile @@ -1,66 +1,55 @@ -# Central Makefile for crossroads -# ------------------------------- +# Top-level Makefile for XR +# ------------------------- -BASE = $(shell sh -c pwd) -include etc/Makefile.def +VER = 2.00 +BINDIR = /usr/sbin +TAR = /tmp/crossroads-$(VER).tar.gz +AUTHOR = Karel Kubat <karel@kubat.nl> +MAINTAINER = Karel Kubat <karel@kubat.nl> foo: - @cat etc/Makefile.help - -compile-only: - BASE=$(BASE) $(MAKE) -C src - -local: compile-only - BASE=$(BASE) $(MAKE) -C test - -local-pg: - BASE=$(BASE) PG=-pg G=-g $(MAKE) -C src - BASE=$(BASE) $(MAKE) -C test - -local-g: - BASE=$(BASE) G=-g $(MAKE) -C src - BASE=$(BASE) $(MAKE) -C test - -local-alf: - BASE=$(BASE) G=-g ALF=-DALF $(MAKE) -C src + @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 + +local: + mkdir -p xr/build + xr/etc/gettools /usr/local/bin xr/etc c-conf e-ver + xr/etc/e-ver ChangeLog $(VER) + AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' VER='$(VER)'\ + $(MAKE) -C xr install: local - BASE=$(BASE) $(MAKE) -C src install - BASE=$(BASE) $(MAKE) -C doc install - -dbginstall: local-g - BASE=$(BASE) G=-g $(MAKE) -C src dbginstall - BASE=$(BASE) $(MAKE) -C doc install - -pginstall: local-pg - BASE=$(BASE) PG=-pg G=-g $(MAKE) -C src dbginstall - BASE=$(BASE) $(MAKE) -C doc install - -documentation: - BASE=$(BASE) $(MAKE) -C doc + mkdir -p $(BINDIR) + AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \ + 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) too.' + @echo ' The helper xrctl is not installed automatically.' + @echo ' Have fun with Crossroads $(VER)!' + @echo ' -- $(MAINTAINER)' + @echo clean: - find $(BASE) -type f -name '*~' -exec rm {} \; - find $(BASE) -type f -name .gdb_history -exec rm {} \; - find $(BASE) -type f -name gmon.out -exec rm {} \; - BASE=$(BASE) $(MAKE) -C doc clean - BASE=$(BASE) $(MAKE) -C test clean - BASE=$(BASE) $(MAKE) -C src clean - -distclean: - BASE=$(BASE) $(MAKE) -C doc distclean - BASE=$(BASE) $(MAKE) -C src distclean - -dist: documentation - $(BASE)/tools/e-ver ChangeLog $(VER) - $(BASE)/tools/makedist $(VER) - -commit: clean headers documentation local - BASE=$(BASE) $(MAKE) clean - BASE=$(BASE) etc/svncheck + rm -rf xr/build/* + +tar: + rm -rf $(TAR) /tmp/crossroads-$(VER) + cd ..; cp -r crossroads /tmp/crossroads-$(VER) + cd /tmp; tar czf $(TAR) \ + --exclude .git --exclude crossroads-$(VER)/xr/build \ + crossroads-$(VER) + @echo + @echo 'Sources now tarred into $(TAR)' + +commit: local clean + test `svn status | grep '^\?' | wc -l` -eq 0 || \ + (echo 'SVN not fully up to date' && exit 1) svn commit - svn commit -m 'Revision number update' $(BASE)/etc/svnrev.txt - -headers: - $(BASE)/tools/patch-header $(BASE)/etc/hdr.template $(VER) \ - src/*/*.c src/*.h + +\ No newline at end of file diff --git a/doc/Makefile b/doc/Makefile @@ -1,40 +0,0 @@ -include ../etc/Makefile.def - -YODLINC = -I.:/usr/local/share/yodl:/usr/share/yodl:$(BASE)/doc/main - -foo: defs main/keywords.yo - find main -type f -name '*.yo' -exec ../tools/untab {} \; - cd man && yo2man crossroads - mv man/crossroads.man crossroads.1 - cd man && yo2man crossroads-mgr - mv man/crossroads-mgr.man crossroads-mgr.1 - cd man && yo2man $(YODLINC) crossroads.conf - mv man/crossroads.conf.man crossroads.conf.7 - cd main && yo2pdf crossroads - mv main/crossroads.pdf . - rm -f main/*.aux main/*.latex main/*.log main/*.toc - cd main && yo2html crossroads - mv main/*.html . - -main/keywords.yo: $(BASE)/src/lib/parser.y - $(BASE)/tools/getkeywords < $< > $@ - -defs: - echo 'redef(VER)(1)($(VER))' > crossroads-defs.yo - echo 'redef(YEARS)(1)($(YEARS))' >> crossroads-defs.yo - echo 'redef(AUTHORNAME)(1)($(AUTHORNAME))' >> crossroads-defs.yo - echo 'redef(DNSCACHETTL)(1)($(DNS_CACHETTL))' >> crossroads-defs.yo - echo 'redef(MAINTAINERNAME)(1)($(MAINTAINERNAME))' \ - >> crossroads-defs.yo - echo 'redef(MAINTAINEREMAIL)(1)($(MAINTAINEREMAIL))' \ - >> crossroads-defs.yo - -install: - $(BASE)/tools/installdoc $(PREFIX) crossroads.1 crossroads-mgr.1 \ - crossroads.conf.7 - -clean: - rm -f crossroads-defs.yo *.man - -distclean: clean - rm -f *.html *.pdf *.1 *.7 diff --git a/doc/crossroads-mgr.1 b/doc/crossroads-mgr.1 @@ -1,120 +0,0 @@ -.SH "e-tunity" -.TH "crossroads-mgr" "1" "2005-2008" "crossroads" "" -.SH "NAME" -crossroads-mgr \- Web interface for Crossroads -.PP -.SH "SYNOPSIS" -.IP o -\fBcrossroads-mgr\fP \fI[flags]\fP \fBstart\fP \fIport\fP: starts -the web interface server -.IP o -\fBcrossroads-mgr\fP \fBstop\fP: stops the server -.IP o -\fBcrossroads-mgr\fP \fBstatus\fP: shows whether the server -is running -.PP -.SH "DESCRIPTION" - -.PP -\fBcrossroads-mgr\fP is a self-contained web server, written in -Perl, providing a web interface to control Crossroads\&. The web -interface allows one to: -.PP -.IP o -View the states of back ends, and their usage; -.IP o -Set the states of back ends (e\&.g\&., to take a back end out -of service)\&. -.PP -The web interface cannot be used to fully configure Crossroads (e\&.g\&., to -add new back ends)\&. This must be done via the configuration file -\fBcrossroads\&.conf\fP\&. Note furthermore that \fBcrossroads status\fP and -\fBcrossroads tell\fP are commandline tools that achieve the same -functionality as \fBcrossroads-mgr\fP\&. -.PP -.SH "OPTIONS" - -.PP -\fBcrossroads-mgr\fP recognizes the following flags: -.PP -.IP o -\fB-a ADDRESS\fP: Specifies the address to which the -manager binds\&. E\&.g\&., one may specify \fB-a 127\&.0\&.0\&.1\fP, after -which only requests originating from localhost will be served\&. -.IP -.IP o -\fB-b UN:PW\fP: Viewing the status is protected using -simple basic authentication, requiring username \fBUN\fP and -password \fBPW\fP\&. -.IP -For example one may specify: \fB-b user:secret\fP, after which -visitors to the web interface are prompted for a username and -password\&. -.IP -Instead of the username, a colon, and a password, the special -word \fBPROMPT\fP may be used (in caps)\&. In this case, -\fBcrossroads-mgr\fP prompts for the entry of a username and a -password\&. Using \fBPROMPT\fP will have the effect that the -username and password are not shown in a process listing\&. -.IP -.IP o -\fB-B UN:PW\fP: Setting the status of a back end is -protected using simple basic authentication, with username -\fBUN\fP and password \fBPW\fP\&. The special word \fBPROMPT\fP may be -used\&. -.IP -.IP o -\fB-f\fP: \fBcrossroads-mgr\fP stays in the foreground -instead of daemonizing\&. Useful for debugging\&. -.IP -.IP o -\fB-l FILE\fP: Verbose activity (when \fB-v\fP is given) is -written to the stated file\&. Useful for debugging\&. Also, error -messages are written to the file\&. The default is -\fB/tmp/crossroads-mgr\&.log\fP\&. Note that file logging is not -used in \&'foreground\&' mode (flag \fB-f\fP; in that case all -logging goes to terminal)\&. -.IP -.IP o -\fB-v\fP: Verbosity is increased\&. -.IP -.IP o -\fB-x CMD\fP: This specifies how \fBcrossroads-mgr\fP should -invoke \fBcrossroads\fP\&. The default is just \fBcrossroads\fP\&. An -example is: \fB/usr/local/bin/crossroads -c -/alternative/configuration\fP which overrules the default -configuration file name\&. Note that once \fB-x\fP is given, -\fBcrossroads-mgr\fP will no longer search along the PATH\&. When -using \fB-x\fP, the full access path to \fBcrossroads\fP must be -specified\&. -.IP -.IP o -\fB-X XSLT\fP: Causes \fBcrossroads-mgr\fP to send the -specified XSLT file, instead of the built-in\&. Useful for -debugging\&. -.PP -.SH "BUGS" - -.PP -.IP o -Basic authentication headers are logged when \fB-v\fP -is active\&. Make sure that the log file is not in a -world-readable place, or turn off \fB-v\fP\&. -.IP -.IP o -When using basic authentication in the form \fB-b -UN:PW\fP and \fB-B UN:PW\fP, the required credentials are -visible in the process overview list\&. Use \fBPROMPT\fP to -hide these\&. -.IP -.IP o -\fBcrossroads-mgr stop\fP and \fBcrossroads-mgr status\fP -use the process list to see which daemons are active\&. If -you start more than one daemon, then \fBcrossroads-mgr -stop\fP will stop all of them\&. -.PP -.SH "AUTHOR" -.IP o -Author: Karel Kubat -.IP o - Maintainer: Karel Kubat karel@kubat\&.nl diff --git a/doc/crossroads.1 b/doc/crossroads.1 @@ -1,149 +0,0 @@ -.SH "e-tunity" -.TH "crossroads" "1" "2005-2008" "crossroads" "" -.SH "NAME" -crossroads \- Load balancer and fail over utility -.PP -.SH "SYNOPSIS" -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBstart\fP: starts Crossroads -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBstop\fP: stops Crossroads -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBrestart\fP: restarts Crossroads -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBconfigtest\fP: verifies -configuration -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBservices\fP: shows configured -services -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBtell\fP \fIservice\fP -\fIbackend\fP \fBstate\fP \fInewstate\fP: sets a back end state -.IP o -\fBcrossroads\fP \fI[flags]\fP \fBtell\fP \fIservice\fP -\fIbackend\fP \fBserver\fP \fIhostname:port\fP: redefines the -address of a back end -.PP -.SH "DESCRIPTION" - -.PP -\fBcrossroads\fP is the main control program of Crossroads, a -balancing and fail over utility\&. The Crossroads utility consists -of two binaries: \fBcrossroads\fP and \fBcrossroads-daemon\fP\&. The -latter is controlled by the front end \fBcrossroads\fP\&. -.PP -Recognized states in \fBcrossroads tell\fP are: -.PP -.IP o -\fBavailable\fP or \fBup\fP when a backend is working; -.IP o -\fBunavailable\fP when a back end is temporarily down, but -can be woken up; -.IP o -\fBdown\fP when a back end is permanently down (e\&.g\&., for -maintenance)\&. Crossroads itself will never activate a -backend in this state\&. -.IP o -\fBwaking\fP when the live status of a back end is being checked\&. -.PP -.SH "OPTIONS" - -.PP -Crossroads recognizes the following flags: -.PP -.IP o -\fB-a\fP: Logs start and end times of activity to -\fIsyslog\fP\&. -.IP -.IP o -\fB-b\fP: Writes a binary representation of the -configuration to \fIstdout\fP\&. For debugging only\&. -.IP -.IP o -\fB-B\fP \fIsize\fP: Sets the size of network buffers\&. Larger -buffers will mean a less number of \f(CWread()\fP or -\f(CWwrite()\fP operations, but a larger memory -footprint\&. Usually the default value will do fine\&. -.IP -.IP o -\fB-C\fP: Writes the compile time configuration to -\fIstdout\fP and stops\&. Useful when submitting bug reports\&. -.IP -.IP o -\fB-c\fP \fIconfig\fP: Uses \fIconfig\fP as the configuration -file\&. The default is \fB/some/path/etc/crossroads\&.conf\fP -or \fB/etc/crossroads\&.conf\fP\&. The first alternative -applies when Crossroads is installed as -\fB/some/path/bin/crossroads\fP; in that case, Crossroads -tries to find its configuration by trying an \&'etc\&' -diretory \&'near\&' its own path\&. -.IP -.IP o -\fB-d\fP \fIsec\fP: Sets the DNS cache time-to-live to -\fIsec\fP seconds\&. Value 0 suppresses DNS entry -caching\&. The default is \fB0\fP\&. -.IP -.IP o -\fB-l\fP \fIfacility\fP: Specifies the openlog (3) facility, -default is \fBLOG_DAEMON\fP\&. Values can be 0 to 7, meaning -\fBLOG_LOCAL0\fP to \fBLOG_LOCAL9\fP\&. -.IP -.IP o -\fB-m\fP \fIpermissions\fP: Specifies shared memory -permissions, default is \fB0644\fP\&. -.IP -.IP o -\fB-s\fP: Requests \&'sloppy\&' binding to TCP ports\&. When a -port is busy, Crossroads will retry periodically to get -it\&. Default is to stop when a listening port cannot be -obtained\&. -.IP -.IP o -\fB-t\fP: Output of \fBcrossroads status\fP is shown as a -tabular view, in one or more lines of: -.IP -\fIservice\fP \fIbackend=state\fP \fI[backend=state\&.\&.\&.]\fP -.IP -.IP o -\fB-x\fP: Output of \fBcrossroads status\fP is shown as an -XML document\&. -.IP -.IP o -\fB-v\fP: Initial actions of Crossroads are more -\&'verbose\&'\&. (Once daemonized, the verbosity is controlled -using \fBverbosity\fP statements in the configuration\&.) -.IP -.IP o -\fB-V\fP: Shows the version ID and stops\&. -.IP -.IP o -\fB-?\fP: Shows an overview of the usage\&. -.PP -.SH "FILES" -\fBcrossroads\&.conf\fP, the default configuration file -.PP -.SH "SEE ALSO" -.IP o -crossroads\&.conf (7) -.IP o -The full Crossroads documentation in html or pdf format, -distributed with the sources\&. -.PP -.SH "BUGS" -Crossroads is of course bug free (haha)\&. When submitting bugs to -the maintainer\&. please always include the following information: -.PP -.IP o -The configuration file that Crossroads uses; -.IP o -The compile-time configuration, which is the output of -\fBcrossroads -C\fP; -.IP o -A good description of the bug, and of the conditions -under which it occurs\&. -.PP -.SH "AUTHOR" -.IP o -Author: Karel Kubat -.IP o - Maintainer: Karel Kubat karel@kubat\&.nl diff --git a/doc/crossroads.conf.7 b/doc/crossroads.conf.7 @@ -1,1360 +0,0 @@ -.SH "e-tunity" -.TH "crossroads\&.conf" "7" "2005-2008" "crossroads" "" -.SH "NAME" -crossroads\&.conf \- Crossroads configuration -.PP -.SH "SYNOPSIS" -This manpage describes the configuration of -Crossroads, usually the file \fBcrossroads\&.conf\fP\&. This file is often -located in an \&'etc\&' directory near the path where Crossroads is -installed\&. E\&.g\&., when Crossroads is installed as -\fB/usr/local/bin/crossroads\fP, then the configuration file can be -\f(CW/usr/local/etc/crossroads\&.conf\fP\&. Alternatively the configuration -can always be put in \fB/etc/crossroads\&.conf\fP\&. Crossroads can however -be started with a flag \fB-c\fP to specify an entirely different name\&.\&. -.PP -.SH "DESCRIPTION" - - - -The configuration that crossroads uses is normally stored in the file -\f(CWcrossroads\&.conf\fP, either in an \&'etc\&' directory near the binary, or -in \f(CW/etc/\fP\&. See section ?? for details\&. -.PP -The name of the configuration file can be overruled using the -command line flag \f(CW-c\fP\&. -.PP -This section explains the syntax of the configuration file, and what -all settings do\&. -.PP -.SH "General language elements" - -.PP -This section describes the general elements of the crossroads -configuration language\&. -.PP -\fBEmpty lines, indentation and comments\fP -.PP -Empty lines are of course allowed in the configuration\&. Also, -indentation is irrelevant as far as processing is concerned, but is -loosely inspired on a C-like style\&. So the following two examples are -identical as far as Crossroads is concerned (thought the first one -will be typically more readable): -.PP -.nf -/* Example one */ -options { - tcpbuffersize 10240; - logactivity on; -} - -/* Example two */ -options {tcpbuffersize 10240 -;logactivity on;} -.fi - -.PP -Crossroads recognizes the following comment formats: -.PP -.IP o -C-style, between \f(CW/*\fP and \f(CW*/\fP, -.IP o -C++-style, starting with \f(CW//\fP and ending with the end -of the text line\&. -.PP -Simply choose your favorite editor and use the comment that \&'looks -best\&'\&.\e (I favor C or C++ comment\&. My favorite editor \fIemacs\fP -can be put in \f(CWcmode\fP and nicely highlight what\&'s comment and what\&'s -not\&. And as a bonus it will auto-indent the configuration!) -.PP -\fBPreprocessor directives\fP -.PP -Similar to \fBC\fP or \fBC++\fP, the Crossroads grammar knows \f(CW#include\fP -and \f(CW#define\fP\&. Both directives must start at the first column of the -line (ie\&., the \f(CW#\fP sign must occur at the leftmost line -position)\&. -.PP -.IP o -\f(CW#include\fP \f(CW"\fP\fIfilename\fP\f(CW"\fP includes the stated file -name at the place of the statement\&. -.IP o -\f(CW#define\fP \fISYMBOL\fP \fIDEFINITION\fP defines \fISYMBOL\fP -as placeholder for \fIDEFINITION\fP\&. -.PP -For example, one may use the configuration: -.PP -.nf -#define SERVICEPORT 80 -service web { - port SERVICEPORT; - \&. - \&. /* More statements follow here */ - \&. -} -.fi - -.PP -The \f(CWport\fP statement is then read as \f(CWport 80\fP\&. -.PP -The statement \f(CW#define\fP can also be very nicely used when trying out -Crossroads configurations\&. Crossroads has a statement \f(CWverbosity\fP -\f(CWtrue\fP that causes debugging information to be logged\&. Once a -configuration has proven to work, you\&'ll most likely want -\f(CWverbosity\fP \f(CWfalse\fP so that overhead due to logging is -avoided\&. This can be easily implemented using \f(CW#define\fP: -.PP -.nf -/* Set DEBUG to true or false; - * true is for testing purposes, - * false is for production */ -#define DEBUG true - -service web { - verbosity DEBUG; - \&. - \&. /* More statements follow here */ - \&. -} -.fi - -.PP -\fBKeywords, numbers, identifiers, generic strings\fP -.PP -In a configuration file, statements are identified by \fIkeywords\fP, -which are reserved words\&. The list of the keywords of the Crossroads -grammar is: -\f(CWservice\fP -\f(CWport\fP -\f(CWverbosity\fP -\f(CWmaxconnections\fP -\f(CWtype\fP -\f(CWany\fP -\f(CWhttp\fP -\f(CWbackend\fP -\f(CWserver\fP -\f(CWbindto\fP -\f(CWconnectiontimeout\fP -\f(CWon\fP -\f(CWoff\fP -\f(CWdispatchmode\fP -\f(CWroundrobin\fP -\f(CWrandom\fP -\f(CWbyduration\fP -\f(CWbysize\fP -\f(CWbyconnections\fP -\f(CWbyorder\fP -\f(CWbyclientip\fP -\f(CWover\fP -\f(CWdecay\fP -\f(CWrevivinginterval\fP -\f(CWcheckinterval\fP -\f(CWretries\fP -\f(CWshmkey\fP -\f(CWweight\fP -\f(CWonstart\fP -\f(CWonfail\fP -\f(CWbacklog\fP -\f(CWthroughputlog\fP -\f(CWtrafficlog\fP -\f(CWhttptiminglog\fP -\f(CWstickycookie\fP -\f(CWaddclientheader\fP -\f(CWsetclientheader\fP -\f(CWappendclientheader\fP -\f(CWaddserverheader\fP -\f(CWsetserverheader\fP -\f(CWappendserverheader\fP -\f(CWallowfrom\fP -\f(CWdenyfrom\fP -\f(CWallowfile\fP -\f(CWdenyfile\fP -\f(CWexternalhandler\fP -\f(CWuseraccount\fP -\f(CWonend\fP -\f(CWheaderinspection\fP -\f(CWdeep\fP -\f(CWshallow\fP -\f(CWstate\fP -\f(CWavailable\fP -\f(CWunavailable\fP -\f(CWdown\fP -\f(CWoptions\fP -\f(CWlogactivity\fP -\f(CWtcpbuffersize\fP -\f(CWdnscachettl\fP -\f(CWlogfacility\fP -\f(CWshmpermissions\fP -\f(CWsloppyportbind\fP -\f(CWleaveprocesstitle\fP -.PP -Many keywords require an \fIidentifier\fP as the argument\&. E\&.g, a -service has a unique name, which must start with a letter or -underscore, followed by zero or more letters, underscores, or -digits\&. E\&.g\&., in the statement \f(CWservice myservice\fP, the keyword is -\f(CWservice\fP and the identifier is \f(CWmyservice\fP\&. -.PP -Other keywords require a numeric argument\&. Crossroads knows only -non-negative integer numbers, as in \f(CWport 8000\fP\&. Here, \f(CWport\fP is -the keyword and \f(CW8000\fP is the number\&. Octal numbers are specified by -prefixing a zero; e\&.g\&., \f(CW0644\fP is an octal number\&. -.PP -Yet other keywords require \&'generic strings\&', such as hostname -specifications or system commands\&. Such generic strings contain any -characters (including white space) up to the terminating statement -character \f(CW;\fP\&. If a string must contain a semicolon, then it must -be enclosed in single or double quotes: -.PP -.IP o -\f(CWThis is a string;\fP is a string that starts at \f(CWT\fP -and ends with \f(CWg\fP -.IP o -\f(CW"This is a string";\fP is the same, the double quotes -are not necessary -.IP o -\f(CW"This is ; a string";\fP has double quotes to protect -the inner ; -.PP -Finally, an argument can be a \&'boolean\&' value\&. Crossroads knows -\f(CWtrue\fP, \f(CWfalse\fP, \f(CWyes\fP, \f(CWno\fP, \f(CWon\fP, \f(CWoff\fP\&. The keywords -\f(CWtrue\fP, \f(CWyes\fP and \f(CWon\fP all mean the same and can be used -interchangeably; as can the keywords \f(CWfalse\fP, \f(CWno\fP and \f(CWoff\fP\&. -.PP -.SH "Daemon options" - -.PP -Crossroads supports an optional block that defines run options for the -daemon process\&. These options can be also be specified on the command line -using flags\&. -.PP -The syntax of the options block is: -.PP -.nf -options { - \&. - \&. option statements (see below) - \&. -} -.fi - -.PP -In the curly-brace block the following option statements can occur: -.PP -.IP o -\f(CWlogactivity on;\fP or \f(CWoff\fP Turns activity logging on -or off\&. This is also controlled using the flag \f(CW-a\fP\&. The -default is not to log network activity\&. -.IP -.IP o -\f(CWtcpbuffersize\fP \fInumber\fP \f(CW;\fP This defines the size -of network buffers\&. The default is 5120 bytes\&. This is also -controlled using the flag \f(CW-B\fP\&. -.IP -.IP o -\f(CWdnscachettl\fP \fInumber\fP \f(CW;\fP This controls the -time-to-live of cached DNS entries, in seconds\&. Value zero -means no caching and is the default\&. This is also controlled -using the flag \f(CW-d\fP\&. -.IP -.IP o -\f(CWlogfacility\fP \fInumber\fP \f(CW;\fP This controls the -logging facility\&. Values 0 to 7 select \f(CWLOG_LOCAL0\fP to -\f(CWLOG_LOCAL7\fP\&. Any other value selects \f(CWLOG_DAEMON\fP, which -is also the default\&. This is also controlled using the flag \f(CW-l\fP\&. -.IP -.IP o -\f(CWshmpermissions\fP \f(CWnumber\fP \f(CW;\fP This defines the -permissions (for user, group and other) of the shared memory -block\&. The number is most often specified as an octal value, -e\&.g\&. \f(CW0644\fP, which is also the default\&. This is also -controlled using the flag \f(CW-m\fP\&. -.IP -.IP o -\f(CWsloppyportbind on;\fP or \f(CWoff\fP Turns on \&'sloppy\&' port -binding of the listener\&. When \f(CWon\fP, Crossroads will not -treat port-busy conditions as fatal, but will wait and retry\&. -The default is \f(CWoff\fP\&. This is also controlled using flag -\f(CW-s\fP\&. -.IP -.IP o -\f(CWleaveprocesstitle\fP \f(CWon;\fP or \f(CWoff\fP When \f(CWon\fP, the -process title of Crossroads is not modified, so that \f(CWps\fP -will show \f(CWcrossroads-daemon\fP\&. The default is \f(CWoff\fP: -Crosroads will modify its process name into something like -\fIcrossroads - Sevice web: listening\fP or \fIcrossroads - -Service web: serving\fP\&. -.PP -.SH "Service definitions" - -.PP -Service definitions are blocks in the configuration file that -state what is for each service\&. A service definition starts with -\f(CWservice\fP, followed by a unique identifier, and by statements in -\f(CW{\fP and \f(CW}\fP\&. For example: -.PP -.nf -// Definition of service \&'www\&': -service www { - \&.\&.\&. - \&.\&.\&. // statements that define the - \&.\&.\&. // service named \&'www\&' - \&.\&.\&. -} -.fi - -.PP -The configuration file can contain many service blocks, as long as the -identifying names differ\&. The following list shows possible -statements\&. Each statement must end with a semicolon, except for the -\f(CWbackend\fP statement, which has is own block (more on this later)\&. -.PP -\fBport - Specifying the listen port\fP -.IP "Description:" -The \f(CWport\fP statement defines to which TCP port a service -\&'listens\&'\&. E\&.g\&. \f(CWport 8000\fP says that this service will accept -connections on port 8000\&. -.IP -Multiple services in one configuration cannot use the same port -number, unless they both bind to specific (and different) IP -addresses\&. See also the \f(CWbindto\fP statement\&. -.IP "Syntax:" -\f(CWport\fP \fInumber\fP -.IP "Default:" -There is no default\&. This is a required setting\&. - -.PP -\fBtype - Defining the service type\fP -.IP "Description:" -The \f(CWtype\fP statement defines how crossroads handles the stated -service\&. There are currently two types: \f(CWany\fP and -\f(CWhttp\fP\&. The type \f(CWany\fP means that crossroads doesn\&'t -interpret the contents of a TCP stream, but only distributes streams -over back ends\&. The type \f(CWhttp\fP means that crossroads has to -analyze what\&'s in the messages, does magical HTTP header tricks, and -so on -- all to ensure that multiple connections are treated as one -session, or that the back end is notified of the client\&'s IP -address\&. -.IP -Unless you really need such special features, use the type \f(CWany\fP (the -default), even for HTTP protocols\&. -.IP "Syntax:" -\f(CWtype\fP \fIspecifier\fP, where \fIspecifier\fP is \f(CWany\fP or -\f(CWhttp\fP -.IP "Default:" -\f(CWany\fP - -.PP -\fBheaderinspection - are all HTTP headers inspected\fP -.IP "Description:" -The \f(CWheaderinspection\fP directive defines whether Crossroads -must inspect all HTTP headers that are seen on one TCP connection, or -only the first ones\&. There are two possible values for this directive: -\f(CWdeep\fP and \f(CWshallow\fP\&. In \f(CWdeep\fP mode, all information that is -seen on the TCP link is monitored -and parsed, and all HTTP header blocks are analyzed and subject to -directives such as \f(CWaddclientheader\fP\&. In \f(CWshallow\fP mode, only the -first header block that the server sends, and the first header block -that forms the server\&'s answer, are analyzed\&. -.IP "Syntax:" -\f(CWheaderinspection\fP \fIspecifier\fP, where \fIspecifier\fP is -\f(CWdeep\fP or \f(CWshallow\fP -.IP "Default:" -\f(CWdeep\fP - -.PP -\fBbindto - Binding to a specific IP address\fP -.IP "Description:" -The \f(CWbindto\fP statement is used in situations where crossroads -should only listen to the stated port at a given IP address\&. E\&.g\&., -\f(CWbindto 127\&.0\&.0\&.1\fP causes crossroads to \&'bind\&' the service only to -the local IP address\&. Network connections from other hosts won\&'t be -serviced\&. By default, crossroads binds a service to all presently -active IP addresses at the invoking host\&. -.IP "Syntax:" -\f(CWbindto\fP \fIaddress\fP, where \fIaddress\fP is a numeric IP -address, such as 127\&.0\&.0\&.1, or the keyword \f(CWany\fP\&. -.IP "Default:" -\f(CWany\fP - -.PP -\fBverbosity - Controlling debug output\fP -.IP "Description:" -Verbosity statements come in two forms: \f(CWverbosity on\fP or -\f(CWverbosity off\fP\&. When \&'on\&', log messages to \f(CW/var/log/messages\fP -are generated that show what\&'s going on\&.\e (Actually, the -messages go to \f(CWsyslog(3)\fP, using facility \f(CWLOG_DAEMON\fP and -priority \f(CWLOG_INFO\fP\&. In most (Linux) cases this will mean: output to -\f(CW/var/log/messages\fP\&. On Mac OSX the messages go to -\f(CW/var/log/system\&.log\fP\&.) The keyword \f(CWverbose\fP is an alias for -\f(CWverbosity\fP\&. -.IP "Syntax:" -\f(CWverbosity\fP \fIsetting\fP or \f(CWverbose\fP \fIsetting\fP, where -\fIsetting\fP is \f(CWtrue\fP, \f(CWyes\fP or \f(CWon\fP to turn -verbosity on; or \f(CWfalse\fP, \f(CWno\fP, \f(CWoff\fP to turn it off\&. -.IP "Default:" -\f(CWoff\fP - -.PP -\fBdispatchmode - How are back ends selected\fP -.IP "Description:" - The dispatch mode controls how crossroads selects a back end from - a list of active back ends\&. The below text shows the bare - syntax\&. See section ?? for a textual explanation\&. -.IP -The settings can be: -.IP -.IP o -\f(CWdispatchmode roundrobin\fP: Simply the \&'next in line\&' is -chosen\&. E\&.g, when 3 back ends are active, then the usage -series is 1, 2, 3, 1, 2, 3, and so on\&. -.IP -Roundrobin dispatching is the default method, when no -\f(CWdispatchmode\fP statement occurs\&. -.IP -.IP o -\f(CWdispatchmode random\fP: Random selection\&. Probably only -for stress testing, though when used with weights (see below) -it is a good distributor of new connections too\&. -.IP -.IP o -\f(CWdispatchmode bysize [ over\fP \fIconnections\fP \f(CW]\fP: -The next back end is the one -that has transferred the least number of bytes\&. This -selection mechanism assumes that the more bytes, the heavier -the load\&. -.IP -The modifier \f(CWover\fP \fIconnections\fP is optional\&. (The square -brackets shown above are not part of the statement but -indicate optionality\&.) When given, -the load is computed as an average of the last stated number of -connections\&. When this modifier is absent, then the load is -computed over all connections since startup\&. -.IP -.IP o -\f(CWdispatchmode byduration [ over\fP \fIconnections\fP \f(CW]\fP: -The next back end is the one -that served connections for the shortest time\&. This mechanism -assumes that the longer the connection, the heavier the load\&. -.IP -.IP o -\f(CWdispatchmode byconnections\fP: The next back end is the one -with the least active connections\&. This mechanism assumes that -each connection to a back end represents load\&. It is usable -for e\&.g\&. database connections\&. -.IP -.IP o -\f(CWdispatchmode byorder\fP: The first back end is selected -every time, unless it\&'s unavailable\&. In that case the second -is taken, and so on\&. -.IP -.IP o -\f(CWdispatchmode byclientip\fP: The client\&'s IP address is -\&'hashed\&' into a number, which is used to pick a back end\&. The -same client IP address will therefore always be dispatched to -the same back end\&. When the back end of choice is down, -\f(CWdispatchmode byconnections\fP is used\&. -.IP -.IP o -\f(CWdispatchmode externalhandler\fP \fIprogram arguments\fP: -This is a special mode, where an external program is delegated -the responsibility to say which back end should be used -next\&. In this case, Crossroads will call the external program, -and this will of course be slower than one of the \&'built-in\&' -dispatch modes\&. However, this is the ultimate escape when -custom-made dispatch modes are needed\&. -.IP -The dispatch mode that uses an \f(CWexternalhandler\fP is - discussed separately in section ??\&. -.IP -The selection algorithm is only used when clients are serviced that -aren\&'t part of a sticky HTTP session\&. This is the case during: -.IP -.IP o -all client requests of a service type \f(CWany\fP; -.IP o -new sessions of a service type \f(CWhttp\fP\&. -.IP -When type \f(CWhttp\fP is in effect and a session is underway, then the -previously used back end is always selected -- regardless of -dispatching mode\&. -.IP -Your \&'right\&' dispatch mode will depend on the type of service\&. Given -the fact that crossroads doesn\&'t know (and doesn\&'t care) how to -estimate load from a network traffic stream, you have to choose an -appropriate dispatch mode to optimize load balancing\&. In most cases, -\f(CWroundrobin\fP or \f(CWbyconnections\fP will do the job just fine\&. -.IP "Syntax:" -\f(CWdispatchmode\fP \fImode\fP (see above for the modes), optionally -followed by \f(CWover\fP \fInumber\fP, or when the \fImode\fP is -\f(CWexternalhandler\fP, followed by \fIprogram\fP\&. -.IP "Default:" -\f(CWroundrobin\fP - -.PP -\fBrevivinginterval - Back end wakeup calls\fP -.IP "Description:" -A reviving interval definition is used when Crossroads -determines that a back end is temporarily unavailable\&. This will -happen when: -.IP -.IP o -The back end cannot be reached (network connection -fails); -.IP o -The network connection to the back end suddenly dies\&. -.IP -Once a reviving interval is set, Crossroads will periodically -check the unavailable back end(s) to see whether they have woken up\&. -.IP -An example of the definition is \f(CWrevivinginterval 10\fP\&. When this -reviving interval is given, crossroads will check each 10 seconds -whether unavailable back ends have woken up yet\&. -.IP "Syntax:" -.IP o -\f(CWrevivinginterval\fP \fInumber\fP; -.IP o - \f(CWrevivinginterval\fP \fInumber\fP \f(CWexternalhandler\fP - \fIprogram arguments\fP; - The first form connects to a back end server\&. If the connection - succeeds, then the back end is considered available\&. The second - form activates an external program (see section - ?? for a description)\&. The back end is marked -available if the program\&'s exit status is zero\&. -.IP "Default:" -0 (no wakeup calls) - -.PP -\fBcheckinterval - Periodic back end checks\fP -.IP "Description:" -When a check interval is stated, Crossroads will periodically -probe back ends to determine whether available back ends are -still there, and to see whether unavailable back ends have woken -up yet\&. -.IP -An example is \f(CWcheckinterval 10\fP\&. When this is stated, -Crossroads will probe all back ends each 10 seconds\&. -.IP "Syntax:" -.IP o -\f(CWcheckinterval\fP \fInumber\fP; -.IP o -\f(CWcheckinterval\fP \fInumber\fP \f(CWexternalhandler\fP -\fIprogram arguments\fP; -The first form checks bhy connecting to the back end server\&. -If the connection succeeds, then the back end is considered -available; otherwise the back end is considered unavailable\&. -.IP -The second form activates an external program (see section - ?? for a description)\&. The back end is considered -available if the program\&'s exit status is zero; otherwise it is -considered unavailable\&. -.IP "Default:" -0 (no periodic checks) - -.PP -\fBmaxconnections - Limiting concurrent clients at service level\fP -.IP "Description:" -The maximum number of connections is specified using -\f(CWmaxconnections\fP\&. There is one argument; the number of concurrent -established connections that may be active within one service\&. -.IP -\&'Throttling\&' the number of connections is a way of preventing Denial of -Service (DOS) attacks\&. Without a limit, numerous network connections -may spawn so many server instances, that the service ultimately breaks -down and becomes unavailable\&. -.IP -Note that \f(CWmaxconnections\fP is also allowed in a backend -description block, in which case it limits the number of TCP -connections to that particular back end\&. -.IP "Syntax:" -\f(CWmaxconnections\fP \fInumber\fP, where the number specifies the -maximum of concurrent connections to the service\&. -.IP "Default:" -0, meaning that all connections will be accepted\&. - -.PP -\fBbacklog - The TCP Back Log size\fP -.IP "Description:" -The TCP back log size is a number that controls how many -\&'waiting\&' network connections may be queued, before a client simply -cannot connect\&. The syntax is e\&.g\&. \f(CWbacklog 5\fP to cause crossroads -to have 5 waiting connections for 1 active connection\&. -The backlog queue shouldn\&'t be too -high, or clients will experience timeouts before they can actually -connect\&. The queue shouldn\&'t be too small either, because clients -would be simply rejected\&. Your mileage may vary\&. -.IP "Syntax:" -\f(CWbacklog\fP \fInumber\fP -.IP "Default:" -0, which takes the operating system\&'s default -value for socket back log size\&. - -.PP -\fBshmkey - Shared Memory Access\fP -.IP "Description:" -Different Crossroads -invocations must \&'know\&' of each others activity\&. E\&.g, \f(CWcrossroad -status\fP must be able to get to the actual state information of all -running services\&. This is internally implemented through shared -memory, which is reserved using a key\&. -.IP -Normally crossroads will supply a shared memory key, based on the -service name\&. In situations where this conflicts with existing -keys (of other programs, having their own keys), you may supply a -chosen value\&. -.IP -The actual key value doesn\&'t matter much, as long as it\&'s unique -and as long as each invocation of crossroads uses it\&. -.IP "Syntax:" -\f(CWshmkey\fP \fInumber\fP -.IP "Default:" -0, which means that crossroads will \&'guess\&' its -own key, based on the service name\&. - -.PP -\fBallow* and deny* - Allowing or denying connections\fP -.IP "Description:" -Crossroads can allow or deny -connections based on the IP address of a client\&. There are four -directives that are relevant: \f(CWallowfrom\fP, \f(CWallowfile\fP, -\f(CWdenyfrom\fP and \f(CWdenyfile\fP\&. -.IP -When using \f(CWallowfrom\fP and -\f(CWdenyfrom\fP then the IP addresses to allow or deny connections are -stated in the configuration\&. When using \f(CWallowfile\fP and -\f(CWdenyfile\fP the allow or deny connections are stated in a -separate file\&. -.IP -When \f(CWallow*\fP directives are used, then all connections are denied -unless they match the stated allowed IP\&'s\&. When \f(CWdeny*\fP directives -are used, then all connections are allowed unless they match the -stated disallowed IP\&'s\&. When denying and allowing is both used, -then the Crossroads checks the deny list first\&. -.IP -The statements \f(CWallowfrom\fP and \f(CWdenyfrom\fP are followed by a -list of filter specifications\&. The statements \f(CWallowfile\fP and -\f(CWdenyfile\fP are followed by a filename; Crossroads will read -filter specifications from those external files\&. In both cases, -Crossroads obtains filter specifications and places them in its -lists of allowed or denied IP addresses\&. The difference between -specifying filters in the configuration file or in external -files, is that Crossroads will reload the external files when it -receives signal 1 (\f(CWSIGHUP\fP), as in \f(CWkillall -1 crossroads\fP\&. -.IP -The filter specifications must obey the following syntax: they -are series of space-separated strings, consisting of up to -four numbers ranging from 0 to 255 and separated by a decimal -sign\&. Optionally a slash follows, with a bitmask which is also a -decimal number\&. For example, \f(CW127\&.0\&.0/24 10/8 192\&.168\&.2\&.1\fP is a -setting that consists of three specifiers\&. -.IP -This is probably best explained by a few examples: -.IP -.IP o -\f(CWallowfrom 10/8;\fP will allow connections from -\f(CW10\&.*\&.*\&.*\fP (a full Class A network)\&. The mask \f(CW/8\fP means -that the first 8 bits of the number (ie\&., only the \f(CW10\fP) are -significant\&. On the last 3 positions of the IP address, all -numbers are allowed\&. Given this directive, client connections -from e\&.g\&. 10\&.1\&.1\&.1 and 10\&.2\&.3\&.4 will be allowed\&. -.IP -.IP o -\f(CWallowfrom 10\&.3/16;\fP will allow all IP addresses that -start with \f(CW10\&.3\fP\&. The first 16 bits (i\&.e\&., the first 2 -numbers) are significant, the rest doesn\&'t matter\&. -.IP -.IP o -\f(CWallowfrom 10\&.3\&.1/16;\fP is the same as above\&. The third -byte of the IP address is superfluous because the netmask -specifies that only the first 16 bits (2 numbers) are taken -into account\&. -.IP -.IP o -\f(CWallowfrom 10\&.3\&.1\&.15;\fP allows traffic from only the -specified IP address\&. There is no bitmask; all four numbers -are relevant\&. -.IP -.IP o -\f(CWallowfrom 10\&.3\&.1\&.15 10\&.2/16;\fP allows traffic from one -IP address \f(CW10\&.3\&.1\&.15\fP or from a complete Class B network -\f(CW10\&.2\&.*\&.*\fP -.IP -.IP o -\f(CWallowfile /tmp/myfile\&.txt;\fP in combination with a file -\f(CW/tmp/myfile\&.txt\fP, with the contents \f(CW10\&.3\&.1\&.15 10\&.2/16\fP, -is the same as above\&. -.IP -When using ttt(allowfrom) and \f(CWdenyfrom\fP, separate specifiers -can be stated in one statement (separated by whitespace), or the -whole statement can be repeated\&. E\&.g\&., the following two -alternatives have the same effect: -.IP -.nf -/* Alternative 1: */ -allowfrom 10/8 192\&.168\&.1/24; - -/* Alternative 2: */ -allowfrom 10/8; -allowfrom 192\&.168\&.1\&.24; -.fi -.IP "Syntax:" -.IP o -\f(CWallowfrom\fP \fIfilter-specificication(s)\fP -.IP o -\f(CWdenyfrom\fP \fIfilter-specificication(s)\fP -.IP o -\f(CWallowfile\fP \fIfilename\fP -.IP o -\f(CWdenyfile\fP \fIfilename\fP -.IP "Default:" -In absence of these statements, all client IP\&'s are accepted\&. - -.PP -\fBuseraccount - Limiting the effective ID of external processes\fP -.IP "Description:" -Using the directive \f(CWuseraccount\fP, the effective user and group -ID can be restricted\&. This comes into effect when Crossroads runs -external commands, such as: -.IP o -Hooks for \f(CWonstart\fP, \f(CWonend\fP or \f(CWonfail\fP; -.IP o -External dispatchers, when \f(CWdispatchmode -externalhandler\fP is in effect\&. -Once a user name for external commands is specified, Crossroads -assumes the associated user ID and group ID before running those -commands\&. -.IP "Syntax:" -\f(CWuseraccount\fP \fIusername\fP -.IP "Default:" -None; when unspecified, external commands are run with the -ID that was in effect when Crossroads was started\&. - -.PP -.SH "Backend definitions" - -.PP -Inside the service definitions as are described in the previous -section, \fIbackend definitions\fP must also occur\&. Backend definitions -are started by the keyword \f(CWbackend\fP, followed by an identifier -(the back end name) , and statements inside \f(CW{\fP and \f(CW}\fP: -.PP -.nf -service myservice { - \&.\&.\&. - \&.\&.\&. // statements that define the - \&.\&.\&. // service named \&'myservice\&' - \&.\&.\&. - - backend mybackend { - \&.\&.\&. - \&.\&.\&. // statements that define the - \&.\&.\&. // backend named \&'mybackend\&' - \&.\&.\&. - } -} -.fi - -.PP -Each service definition must have at least one backend -definition\&. There may be more (and probably will, if you want -balancing and fail over) as long as the backend names differ\&. -The statements in the backend definition blocks are described in the -following sections\&. -.PP -Some directives (\f(CWstickycookie\fP etc\&.) only have effect when -Crossroads treats the network traffic as a stream of HTTP messages; -i\&.e\&., when the service is declared with \f(CWtype http\fP\&. Incase of -\f(CWtype any\fP, the HTTP-specific directives have no effect\&. -.PP -\fBserver - Specifying the back end address\fP -.IP "Description:" -Each back end must be identified by the network name -(server name) where it is located\&. For example: \f(CWserver -10\&.1\&.1\&.23\fP, or \f(CWserver web\&.mydomain\&.org\fP\&. A TCP port specifier -can follow the server name, as in \f(CWserver -web\&.mydomain\&.org:80\fP\&. \fBNote that\fP resolved host names can be -cached by Crossroads\&. (The DNS cache timeout can be -controlled using the invocation flag \f(CW-d\fP\&.) -.IP "Syntax:" -.IP o -\f(CWserver\fP \fIservername\fP, where \fIservername\fP is a -network name or IP address\&. In this case a separate \f(CWport\fP -statement must be used to define the TCP port; -.IP o -\f(CWserver\fP \fIservername:port\fP -.IP "Default:" -There is no default\&. This is a required setting\&. - -.PP -\fBport - Specifying a back end port\fP -.IP "Description:" -Back ends must be known by their host name and a port\&. Both can -be simultaneously specified in a \f(CWserver\fP statement\&. When the -\f(CWserver\fP statement specifies a host name only, then a \f(CWport\fP -statement must be used to specify the port\&. -.IP "Syntax:" -\f(CWport\fP \fInumber\fP -.IP "Default:" -There is no default for the port\&. It must be specified either -using \f(CWserver\fP or using \f(CWport\fP\&. - -.PP -\fBverbosity - Controlling verbosity at the back end level\fP -.IP "Description:" -Similar to \f(CWservice\fP specifications, a -\f(CWbackend\fP can have its own verbosity (\f(CWon\fP or \f(CWoff\fP)\&. When -\f(CWon\fP, traffic to and fro this back end is reported\&. -.IP "Syntax:" -.IP o -\f(CWverbosity\fP \fIsetting\fP, or -.IP o -\f(CWverbose\fP \fIsetting\fP, where \fIsetting\fP is \f(CWtrue\fP, -\f(CWyes\fP or \f(CWon\fP, or \f(CWfalse\fP, \f(CWno\fP, \f(CWoff\fP to turn it -off\&. -.IP "Default:" -\f(CWoff\fP - -.PP -\fBretries - Specifying allowed failures\fP -.IP "Description:" -Back ends that are \&'flaky\&' or on a less reliable network can be -marked as unavailable after not just one failure, but after -e\&.g\&. three\&. You can use this configuration if you suspect that -spurious errors cause otherwise \&'good\&' back ends to be marked as -unavailable, while they in fact still could be used\&. -.IP "Syntax:" -\f(CWretries\fP \fInumber\fP; where \fInumber\fP is the threshold of bad -connections\&. Once exceeded, Crossroads will mark a back end as -unavailable\&. -.IP "Default:" -1; a back end is assumed to be unavailable after the first bad -connection\&. - -.PP -\fBmaxconnections - Limiting the connections to a back end\fP -.IP "Description:" -The directive \f(CWmaxconnections\fP limits the number of allowed -connections to this client\&. -Note that this directive can also occur on the level of a service -block, in which case it limits the overall number of connections\&. -.IP -\fBFuthermore note\fP that this directive cannot be used when an external -dispatcher is in effect\&. In such cases, the external dispatcher has -full control over backend selection\&. -.IP "Syntax:" -\f(CWmaxconnections\fP \fInumber\fP \f(CW;\fP -.IP "Default:" -0; meaning no limit - -.PP -\fBweight - When a back end is more equal than others\fP -.IP "Description:" -To influence how backends are selected, a backend can specify its -\&'weight\&' in the process\&. The higher the weight, the less likely a -back end will be chosen\&. The default is 1\&. -.IP -The weighing mechanism only applies to the dispatch modes -\f(CWrandom\fP, \f(CWbyconnections\fP, \f(CWbysize\fP and \f(CWbyduration\fP\&. -The weight is in fact a penalty factor\&. E\&.g\&., if backend A has -\f(CWweight 2\fP and backend B has \f(CWweight 1\fP, then backend B will -be selected all the time, until its usage parameter is twice as -large as the parameter of A\&. Think of it as a \&'sluggishness\&' -statement\&. -.IP "Syntax:" -\f(CWweight\fP \fInumber\fP; the higher the number, the more \&'sluggish\&' -a back end is -.IP "Default:" -1; all back ends have equal weight\&. - -.PP -\fBdecay - Levelling out activity of a back end\fP -.IP "Description:" -To make sure that a \&'spike\&' of activity doesn\&'t -influence the perceived load of a back end forever, you may -specify a certain decay\&. E\&.g, the statement \f(CWdecay 10\fP makes -sure that the load that crossroads computes for this back end (be -it in seconds or in bytes) is decreased by 10% each time that -\fBan other\fP back end is hit\&. Decays are not applied to the count -of concurrent connections\&. -.IP -This means that when a given back end is hit, then its usage data -of the transferred bytes and the connection duration are updated -using the actual number of bytes and actual duration\&. However, -when a different back end is hit, then the usage data are -decreased by the specified decay\&. -.IP "Syntax:" -\f(CWdecay\fP \fInumber\fP, where \fInumber\fP is a percentage that -decreases the back end usage data when other back ends are -hit\&. -.IP "Default:" -0, meaning that no decay is applied to usage statistics\&. - -.PP -\fBstate - Setting an initial back end state\fP -.IP "Description:" -Using the \f(CWstate\fP directive a back end can be \&'primed\&' with an -initial state\&. The keyword \f(CWstate\fP can be followed by -\f(CWavailable\fP, \f(CWunavailable\fP or \f(CWdown\fP\&. Back ends marked -\f(CWunavailable\fP are excluded as candidates, but are checked when -a \f(CWrevivinginterval\fP or a \f(CWcheckinterval\fP is used\&. Back ends -marked \f(CWdown\fP are excluded and never re-checked\&. -.IP "Syntax:" -.IP o -\f(CWstate\fP \fIspecifier\fP \f(CW;\fP -.IP o -where \fIspecifier\fP is one of \f(CWavailable\fP, -\f(CWunavailable\fP or \f(CWdown\fP -.IP "Default:" -\f(CWavailable\fP, meaning that a back end is a candidate for -initial dispatching\&. - -.PP -\fBonstart, onend, onfail - Action Hooks\fP -.IP "Description:" -The three directives \f(CWonstart\fP, \f(CWonend\fP and \f(CWonfail\fP can be -specified to start system commands (external programs) when a -connection to a back end starts, fails or ends: -.IP o -\f(CWonstart\fP commands will be run when Crossroads -successfully connects to a back end, and starts servicing; -.IP o -\f(CWonend\fP commands will be run when a (previously -established) connection stops; -.IP o -\f(CWonfail\fP commands will be run when Crossroads tries to -contact a back end to serve a client, but the back end can\&'t -be reached\&. -.IP -The format is always \f(CWon\fP\fItype\fP \fIcommand\fP\&. The \fIcommand\fP -is an external program, optionally followed by arguments\&. The -command is expanded according to the following table: -.IP -.IP o -\f(CW%a\fP is the availability of the current back end, when -a current back end is established\&. \f(CW%1a\fP is the -availability of the first back end (0 when unavailable, 1 if -available); \f(CW%2a\fP is the availability of the second back -end, and so on\&. -.IP -.IP o -\f(CW%b\fP is the name of the current back end, when one is -established\&. \f(CW%1b\fP is the name of the first back end, -\f(CW%2b\fP of the second back end, and so on\&. -.IP -.IP o -\f(CW%e\fP is the count of seconds since start of epoch -(January 1st 1970 GMT)\&. \f(CW%60e\fP is the count since start of -epoch plus 60, so this is a 1 minute offset into the future\&. -.IP -.IP o -\f(CW%g\fP is a "GMT-string" representation of the current -time, in the format \fImonthname, day year hh:mm:ss\fP\&. This -format is used in e\&.g\&. cookie expiry\&. \f(CW%600g\fP is the same -representation but of a moment 600 seconds in the future (10 -minutes)\&. -.IP -.IP o -\f(CW%h\fP is the host name of the current back end\&. \f(CW%1h\fP -is the host name of the first back end, \f(CW%2h\fP of the second -back end, and so on\&. -.IP -.IP o -\f(CW%l\fP is the duration of the last successful connection -in seconds, concerning the current back end\&. \f(CW%1l\fP is the -duration of the connection to back end one, and so on\&. Note -that \f(CW%l\fP refers only to the last successful connections\&. -Unsuccessful connection attempts to back ends do not change -the value\&. -.IP -.IP o -\f(CW%p\fP is the TCP port of the current back end\&. \f(CW%1p\fP -is the TCP port of the first back end, \f(CW%2p\fP of the second -back end, and so on\&. -.IP -.IP o -\f(CW%P\fP is the process ID of the Crossroads handler that -handled this request\&. For debugging only\&. -.IP -.IP o -\f(CW%r\fP is the IP address of the connecting client\&. -.IP -.IP o -\f(CW%s\fP is the name of the current service that the client -connected to\&. -.IP -.IP o -\f(CW%t\fP is the current local time in ANSI format, in -\fIYYYY-MM-DD/hhh:mm:ss\fP\&. \f(CW%1800s\fP is an ANSI stamp of a -moment 1800 seconds in the future (half an hour)\&. -.IP -.IP o -\f(CW%T\fP is the current GMT time in ANSI format\&. \f(CW%10T\fP -offsets this 10 seconds into the future\&. -.IP -.IP o -\f(CW%v\fP is the Crossroads version\&. -.IP -.IP o -\f(CW%w\fP is the weight factor of the current back -end\&. \f(CW%1w\fP is the weight factor of the first back end, etc\&.\&. -.IP -.IP o -Any other chararacter following a \f(CW%\fP sign is taken -literally; e\&.g\&. \f(CW%z\fP is just a z\&. -.IP -.IP "Syntax:" -The syntax of the commands is as follows\&. -.IP -.IP o -\f(CWonstart\fP \fIcommandline\fP -.IP o -\f(CWonend\fP \fIcommandline\fP -.IP o -\f(CWonfail\fP \fIcommandline\fP -.IP o -\f(CWonsuccess\fP \fIcommandline\fP -.IP "Default:" -There is no default\&. Normally no external programs are run upon -connection, success or failure of a back end\&. - -.PP -\fBtrafficlog and throughputlog - Debugging and Performance Aids\fP -.IP "Description:" -Two directives are available -to log network traffic to files\&. They are \f(CWtrafficlog\fP and -\f(CWthroughputlog\fP\&. -.IP -The \f(CWtrafficlog\fP statement causes all traffic to be logged in -hexadecimal format\&. Each line is prefixed by \f(CWB\fP or \f(CWC\fP, -depending on whether the information was received from the back -end or from the client\&. -.IP -The \f(CWthroughputlog\fP statement writes shorthand transmissions to -its log, accompanied by timings and the number of bytes each -transmission was able to read or write in one chunk\&. -.IP "Syntax:" -The syntax is: -.IP -.IP o -\f(CWtrafficlog\fP \fIfilename\fP \f(CW;\fP -.IP o -\f(CWthroughputlog\fP \fIfilename\fP \f(CW;\fP -.IP "Default:" -none - -.PP -\fBhttptiminglog - Timing debugging in HTTP mode\fP -.IP "Description:" -The directive \f(CWhttptiminglog\fP turns on logging of HTTP mode -timing\&. There must be one argument, the filename where the timings -are written to\&. Turning on this option will slow down processing, -but may be helpful in finding out where delays are caused\&. -.IP "Syntax:" -The syntax is: -.IP -.IP o -\f(CWhttptiminglog\fP \fIfilename\fP \f(CW;\fP -.IP o -Where \fIfilename\fP is the output file, e\&.g\&. -\f(CW/tmp/timings\&.log\fP -.IP "Default:" -none - -.PP -\fBstickycookie - Back end selection with an HTTP cookie\fP -.IP "Description:" -The directive \f(CWstickycookie\fP \fIvalue\fP -causes Crossroads to unpack clients\&' requests, to check for -\fIvalue\fP in the cookies\&. When found, the message is routed to the -back end having the appropriate \f(CWstickycookie\fP directive\&. -.IP -E\&.g\&., consider the following configuration: -.IP -.nf -service \&.\&.\&. { - \&.\&.\&. - backend one { - \&.\&.\&. - stickycookie "BalancerID=first"; - } - backend two { - \&.\&.\&. - stickycookie "BalancerID=second"; - } -} -.fi - -.IP -When clients\&' messages contain cookies named \f(CWBalancerID\fP with -the value \f(CWfirst\fP, then such messages are routed to backend -\f(CWone\fP\&. When the value is \f(CWsecond\fP then they are routed to the -backend \f(CWtwo\fP\&. -.IP -There are basically to provide such cookies to a browser\&. First, a -back end can insert such a cookie into the HTTP response\&. E\&.g\&., -the webserver of back end \f(CWone\fP might insert a cookie named -\f(CWBalancerID\fP, having value \f(CWfirst\fP\&. -Second, Crossroads can insert such cookies using a carefully -crafted directive \f(CWaddclientheader\fP\&. -.IP "Syntax:" -\f(CWstickycookie\fP \fIcookievalue\fP -.IP "Default:" -There is no default\&. - -.PP -\fBHTTP Header Modification Directives\fP -.IP "Description:" -Crossroads understands the following -header modification directives: \f(CWaddclientheader\fP, -\f(CWappendclientheader\fP, \f(CWsetclientheader\fP, \f(CWaddserverheader\fP, -\f(CWappendserverheader\fP, \f(CWsetserverheader\fP\&. -.IP -The directive names always consist of -\fIAction\fP\fIDestination\fP\f(CWheader\fP, where: -.IP -.IP o -The action is \f(CWadd\fP, \f(CWappend\fP or \f(CWinsert\fP\&. -.IP -.IP o -Action \f(CWadd\fP adds a header, even when headers with -the same name already are present in an HTTP -message\&. Adding headers is useful for e\&.g\&. \f(CWSet-Cookie\fP -headers; a message may contain several of such headers\&. -.IP -.IP o -Action \f(CWappend\fP adds a header if it isn\&'t present -yet in an HTTP message\&. If such a header is already -present, then the value is appended to the pre-existing -header\&. This is useful for e\&.g\&. \f(CWVia\fP headers\&. Imagine -an HTTP message with a header \f(CWVia: someproxy\fP\&. Then the -directive \f(CWappendclientheader "Via: crossroads"\fP will -rewrite the header to \f(CWVia: someproxy; crossroads\fP\&. -.IP -.IP o -Action \f(CWset\fP overwrites headers with the same -name; or adds a new header if no pre-existing is found\&. -This is useful for e\&.g\&. \f(CWHost\fP headers\&. -.IP -.IP o -The destination is one of \f(CWclient\fP or \f(CWserver\fP\&. When -the destination is \f(CWserver\fP, then Crossroads will apply such -directives to HTTP messages that originate from the browser -and are being forwarded to back ends\&. When the destination is -\f(CWclient\fP, then Crossroads will apply such directives to -backend responses that are shuttled to the browser\&. -.IP -The format of the directives is e\&.g\&. \f(CWaddclientheader -"X-Processed-By: Crossroads"\fP\&. The directives expect one -argument; a string, consisting of a header name, a colon, and a -header value\&. As usual, the directive must end with a semicolon\&. -.IP -The header value may contain one of the following formatting -directives: -.IP -.IP o -\f(CW%a\fP is the availability of the current back end, when -a current back end is established\&. \f(CW%1a\fP is the -availability of the first back end (0 when unavailable, 1 if -available); \f(CW%2a\fP is the availability of the second back -end, and so on\&. -.IP -.IP o -\f(CW%b\fP is the name of the current back end, when one is -established\&. \f(CW%1b\fP is the name of the first back end, -\f(CW%2b\fP of the second back end, and so on\&. -.IP -.IP o -\f(CW%e\fP is the count of seconds since start of epoch -(January 1st 1970 GMT)\&. \f(CW%60e\fP is the count since start of -epoch plus 60, so this is a 1 minute offset into the future\&. -.IP -.IP o -\f(CW%g\fP is a "GMT-string" representation of the current -time, in the format \fImonthname, day year hh:mm:ss\fP\&. This -format is used in e\&.g\&. cookie expiry\&. \f(CW%600g\fP is the same -representation but of a moment 600 seconds in the future (10 -minutes)\&. -.IP -.IP o -\f(CW%h\fP is the host name of the current back end\&. \f(CW%1h\fP -is the host name of the first back end, \f(CW%2h\fP of the second -back end, and so on\&. -.IP -.IP o -\f(CW%l\fP is the duration of the last successful connection -in seconds, concerning the current back end\&. \f(CW%1l\fP is the -duration of the connection to back end one, and so on\&. Note -that \f(CW%l\fP refers only to the last successful connections\&. -Unsuccessful connection attempts to back ends do not change -the value\&. -.IP -.IP o -\f(CW%p\fP is the TCP port of the current back end\&. \f(CW%1p\fP -is the TCP port of the first back end, \f(CW%2p\fP of the second -back end, and so on\&. -.IP -.IP o -\f(CW%P\fP is the process ID of the Crossroads handler that -handled this request\&. For debugging only\&. -.IP -.IP o -\f(CW%r\fP is the IP address of the connecting client\&. -.IP -.IP o -\f(CW%s\fP is the name of the current service that the client -connected to\&. -.IP -.IP o -\f(CW%t\fP is the current local time in ANSI format, in -\fIYYYY-MM-DD/hhh:mm:ss\fP\&. \f(CW%1800s\fP is an ANSI stamp of a -moment 1800 seconds in the future (half an hour)\&. -.IP -.IP o -\f(CW%T\fP is the current GMT time in ANSI format\&. \f(CW%10T\fP -offsets this 10 seconds into the future\&. -.IP -.IP o -\f(CW%v\fP is the Crossroads version\&. -.IP -.IP o -\f(CW%w\fP is the weight factor of the current back -end\&. \f(CW%1w\fP is the weight factor of the first back end, etc\&.\&. -.IP -.IP o -Any other chararacter following a \f(CW%\fP sign is taken -literally; e\&.g\&. \f(CW%z\fP is just a z\&. -.IP -The following examples show common uses of header modifications\&. -.IP -.IP "Enforcing session stickiness:" -By combining -\f(CWstickycookie\fP and \f(CWaddclientheader\fP, HTTP session -stickiness is enforced\&. Consider the following configuration: -.IP -.nf -service \&.\&.\&. { - \&.\&.\&. - backend one { - \&.\&.\&. - addclientheader "Set-Cookie: BalancerID=first; path=/"; - stickycookie "BalancerID=first"; - } - backend two { - \&.\&.\&. - addclientheader "Set-Cookie: BalancerID=second; path=/"; - stickycookie "BalancerID=second"; - } -} -.fi - -.IP -The first request of an HTTP session is balanced to either -backend \f(CWone\fP or \f(CWtwo\fP\&. The server response is enriched -using \f(CWaddclientheader\fP with an appropriate cookie\&. A -subsequent request from the same browser now has that cookie -in place; and is therefore sent to the same back end where the -its predecessors went\&. -.IP -The header which is sent to the client to inject a cookie, can -furthermore be expanded to specify a timeout: -.IP -.nf -addclientheader "Set-Cookie: BalancerID=second; path=/; expires=%1800g"; -.fi - -.IP -The format specifier \f(CW%1800g\fP outputs a GMT-string date 1800 -seconds in the future (half an hour from now)\&. -.IP -.IP "Hiding the server software version:" -Many servers -(e\&.g\&. Apache) advertize their version, as in \f(CWServer: Apache -1\&.27\fP\&. This potentially provides information to attackers\&. The -following configuration hides such information: -.IP -.nf -service \&.\&.\&. { - \&.\&.\&. - backend one { - \&.\&.\&. - setclientheader "Server: WWW-Server"; - } -} -.fi - -.IP -.IP "Informing the server of the clients\&' IP address:" -Since -Crossroads sits \&'in the middle\&' between a client and a back -end, the back end perceives Crossroads as its client\&. The -following sends the true clients\&' IP address to the server, in -a header \f(CWX-Real-IP\fP: -.IP -.nf -service \&.\&.\&. { - \&.\&.\&. - backend one { - \&.\&.\&. - setserverheader "X-Real-IP: %r"; - } -} -.fi - -.IP -.IP "Keep-Alive Downgrading:" -The directives -\f(CWsetclientheader\fP and \f(CWsetserverheader\fP also play a key -role in downgrading Keep-Alive connections to -\&'single-shot\&'\&. E\&.g\&., the following configuration makes sure -that no Keep-Alive connections occur\&. -.IP -.nf -service \&.\&.\&. { - \&.\&.\&. - backend one { - \&.\&.\&. - setserverheader "Connection: close"; - setclientheader "Connection: close"; - } -} -.fi -.IP "Syntax:" -.IP o -\f(CWaddclientheader\fP \fIHeadername: headervalue\fP to add a -header in the traffic towards the client, even when another -header \fIHeadername\fP exists; -.IP o -\f(CWappendclientheader\fP \fIHeadername: headervalue\fP to -append \fIheadervalue\fP to an existing header \fIHeadername\fP -in the traffic towards the client, -or to add the whole header alltogether; -.IP o -\f(CWsetclientheader\fP \fIHeadername: headervalue\fP to -overwrite an existing header in the traffic towards the -client, or to add such a header; -.IP o -\f(CWaddserverheader\fP \fIHeadername: headervalue\fP to add a -header in the traffic towards the server, even when another -header \fIHeadername\fP exists; -.IP o -\f(CWappendserverheader\fP \fIHeadername: headervalue\fP to -append \fIheadervalue\fP to an existing header \fIHeadername\fP -in the traffic towards the server, -or to add the whole header alltogether; -.IP o -\f(CWsetserverheader\fP \fIHeadername: headervalue\fP to -overwrite an existing header in the traffic towards the -server, or to add such a header\&. -.IP "Default:" -There is no default\&. - -.PP -.SH "SEE ALSO" -.IP o -crossroads (1) -.IP o -The full Crossroads documentation, in pdf and html -format, distributed with the sources\&. -.PP -.SH "AUTHOR" - - Crossroads is written by Karel Kubat and is maintained by - Karel Kubat (karel@kubat\&.nl)\&. diff --git a/doc/crossroads.html b/doc/crossroads.html @@ -1,4562 +0,0 @@ -<a name="../crossroads-defs"></a><a name="defs"></a><html><head> -<title>Crossroads 1.81</title> -<link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css"> -<link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css"> -<link rev="made" href="mailto:info@e-tunity.com"> -</head> -<body> -<hr> -<h1>Crossroads 1.81</h1> -<h2>Karel Kubat <br> - Maintained by Karel Kubat (karel@kubat.nl)</h2> - -<h2>e-tunity</h2><h2>2005-2008, ff.</h2> - -<blockquote><em>Crossroads is a load balance and fail over utility for TCP - based services. It is a daemon program running in user - space, and features extensive configurability, polling of - back ends using 'wakeup calls', detailed status reporting, - 'hooks' for special actions when backend calls fail, and much - more. Crossroads is service-independent: it is usable for - HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP - balancing, Crossroads can modify HTTP headers, e.g. to - provide 'session stickiness' for back end processes that need - sessions, but aren't session-aware of other - back ends. Crossroads is configured via a file and accessible - via a web interface.</em></blockquote> - -<h1>Table of Contents</h1> -<dl> -<dl> -<dt><h3><a href="#l1">1: Introduction</a></h3></dt> -<dl> -<dt><a href="#l2">1.1: Obtaining Crossroads</a></dt> -<dt><a href="#l3">1.2: Reporting bugs</a></dt> -<dt><a href="#l4">1.3: Copyright and Disclaimer</a></dt> -<dt><a href="#l5">1.4: Terminology</a></dt> -<dt><a href="#l6">1.5: Porting Issues</a></dt> -<dl> -<dt><a href="#l7">1.5.1: Porting issues for pre-1.80 installations</a></dt> -<dt><a href="#l8">1.5.2: Porting issues for pre-1.63 installations</a></dt> -<dt><a href="#l9">1.5.3: Porting issues for pre-1.50 installations</a></dt> -<dt><a href="#l10">1.5.4: Porting issues for pre-1.43 installations</a></dt> -<dt><a href="#l11">1.5.5: Porting issues for pre-1.21 installations</a></dt> -<dt><a href="#l12">1.5.6: Porting issues for pre-0.26 installations</a></dt> -<dt><a href="#l13">1.5.7: Porting issues for pre-1.08 installations</a></dt> -</dl> -</dl> -<dt><h3><a href="#l14">2: Installation for the impatient</a></h3></dt> -<dt><h3><a href="#l15">3: Using Crossroads</a></h3></dt> -<dl> -<dt><a href="#l16">3.1: The Balancer: crossroads</a></dt> -<dl> -<dt><a href="#l17">3.1.1: General Commandline Syntax</a></dt> -<dt><a href="#l18">3.1.2: Status Reporting from the Command Line</a></dt> -<dt><a href="#l19">3.1.3: Logging-related options</a></dt> -<dt><a href="#l20">3.1.4: Reloading Configurations</a></dt> -</dl> -<dt><a href="#l21">3.2: The Web Frontend: crossroads-mgr</a></dt> -</dl> -<dt><h3><a href="#l22">4: The configuration</a></h3></dt> -<dl> -<dt><a href="#l23">4.1: General language elements</a></dt> -<dl> -<dt><a href="#l24">4.1.1: Empty lines, indentation and comments</a></dt> -<dt><a href="#l25">4.1.2: Preprocessor directives</a></dt> -<dt><a href="#l26">4.1.3: Keywords, numbers, identifiers, generic strings</a></dt> -</dl> -<dt><a href="#l27">4.2: Daemon options</a></dt> -<dt><a href="#l28">4.3: Service definitions</a></dt> -<dl> -<dt><a href="#l29">4.3.1: port - Specifying the listen port</a></dt> -<dt><a href="#l30">4.3.2: type - Defining the service type</a></dt> -<dt><a href="#l31">4.3.3: headerinspection - are all HTTP headers inspected</a></dt> -<dt><a href="#l32">4.3.4: bindto - Binding to a specific IP address</a></dt> -<dt><a href="#l33">4.3.5: verbosity - Controlling debug output</a></dt> -<dt><a href="#l34">4.3.6: dispatchmode - How are back ends selected</a></dt> -<dt><a href="#l35">4.3.7: revivinginterval - Back end wakeup calls</a></dt> -<dt><a href="#l36">4.3.8: checkinterval - Periodic back end checks</a></dt> -<dt><a href="#l37">4.3.9: maxconnections - Limiting concurrent clients at service level</a></dt> -<dt><a href="#l38">4.3.10: backlog - The TCP Back Log size</a></dt> -<dt><a href="#l39">4.3.11: shmkey - Shared Memory Access</a></dt> -<dt><a href="#l40">4.3.12: allow* and deny* - Allowing or denying connections</a></dt> -<dt><a href="#l41">4.3.13: useraccount - Limiting the effective ID of external processes</a></dt> -</dl> -<dt><a href="#l42">4.4: Backend definitions</a></dt> -<dl> -<dt><a href="#l43">4.4.1: server - Specifying the back end address</a></dt> -<dt><a href="#l44">4.4.2: port - Specifying a back end port</a></dt> -<dt><a href="#l45">4.4.3: verbosity - Controlling verbosity at the back end level</a></dt> -<dt><a href="#l46">4.4.4: retries - Specifying allowed failures</a></dt> -<dt><a href="#l47">4.4.5: maxconnections - Limiting the connections to a back end</a></dt> -<dt><a href="#l48">4.4.6: weight - When a back end is more equal than others</a></dt> -<dt><a href="#l49">4.4.7: decay - Levelling out activity of a back end</a></dt> -<dt><a href="#l50">4.4.8: state - Setting an initial back end state</a></dt> -<dt><a href="#l51">4.4.9: onstart, onend, onfail - Action Hooks</a></dt> -<dt><a href="#l52">4.4.10: trafficlog and throughputlog - Debugging and Performance Aids</a></dt> -<dt><a href="#l53">4.4.11: httptiminglog - Timing debugging in HTTP mode</a></dt> -<dt><a href="#l54">4.4.12: stickycookie - Back end selection with an HTTP cookie</a></dt> -<dt><a href="#l55">4.4.13: HTTP Header Modification Directives</a></dt> -</dl> -</dl> -<dt><h3><a href="#l56">5: Tips, Tricks and Random Remarks</a></h3></dt> -<dl> -<dt><a href="#l57">5.1: Configuration examples</a></dt> -<dl> -<dt><a href="#l58">5.1.1: A load balancer for three webserver back ends</a></dt> -<dt><a href="#l59">5.1.2: An HTTP forwarder when travelling</a></dt> -<dt><a href="#l60">5.1.3: SSH login with enforced idle logout</a></dt> -</dl> -<dt><a href="#l61">5.2: How back ends are selected in load balancing</a></dt> -<dl> -<dt><a href="#l62">5.2.1: Bysize, byduration or byconnections?</a></dt> -<dt><a href="#l63">5.2.2: Averaging size and duration</a></dt> -<dt><a href="#l64">5.2.3: Specifying decays</a></dt> -<dt><a href="#l65">5.2.4: Adjusting the weights</a></dt> -</dl> -<dt><a href="#l66">5.3: Periodic probes and wake up calls</a></dt> -<dl> -<dt><a href="#l67">5.3.1: Syntax</a></dt> -<dt><a href="#l68">5.3.2: Security Considerations</a></dt> -<dt><a href="#l69">5.3.3: An example</a></dt> -</dl> -<dt><a href="#l70">5.4: Throttling the number of concurrent connections</a></dt> -<dt><a href="#l71">5.5: TCP Session Stickiness</a></dt> -<dt><a href="#l72">5.6: HTTP Session Stickiness</a></dt> -<dl> -<dt><a href="#l73">5.6.1: Don't use stickiness!</a></dt> -<dt><a href="#l74">5.6.2: But if you must..</a></dt> -</dl> -<dt><a href="#l75">5.7: Passing the client's IP address</a></dt> -<dl> -<dt><a href="#l76">5.7.1: Sample Crossroads configuration</a></dt> -<dt><a href="#l77">5.7.2: Sample Apache configuration</a></dt> -</dl> -<dt><a href="#l78">5.8: Deep or shallow HTTP header inspection</a></dt> -<dt><a href="#l79">5.9: Debugging network traffic</a></dt> -<dt><a href="#l80">5.10: IP filtering: Limiting Access by Client IP Address</a></dt> -<dl> -<dt><a href="#l81">5.10.1: General Examples</a></dt> -<dt><a href="#l82">5.10.2: Using External Files</a></dt> -<dt><a href="#l83">5.10.3: Mixing Directives</a></dt> -</dl> -<dt><a href="#l84">5.11: Using an external program to dispatch</a></dt> -<dl> -<dt><a href="#l85">5.11.1: Configuring the external handler</a></dt> -<dt><a href="#l86">5.11.2: Writing the external handler</a></dt> -<dt><a href="#l87">5.11.3: Examples of external handlers</a></dt> -</dl> -<dt><a href="#l88">5.12: Linux and ip_conntrack_max</a></dt> -<dt><a href="#l89">5.13: Marking back ends as bad after more than one try</a></dt> -<dt><a href="#l90">5.14: Using the Web Interface crossroads-mgr</a></dt> -<dl> -<dt><a href="#l91">5.14.1: Starting crossroads-mgr</a></dt> -</dl> -<dt><a href="#l92">5.15: Rendering Crossroads' status in a web page</a></dt> -<dt><a href="#l93">5.16: Crossroads and DNS caching</a></dt> -<dt><a href="#l94">5.17: Managing a Pool of Virtual Back Ends</a></dt> -</dl> -<dt><h3><a href="#l95">6: Benchmarking</a></h3></dt> -<dl> -<dt><a href="#l96">6.1: Benchmark 1: Accessing a proxy via crossroads or directly</a></dt> -<dl> -<dt><a href="#l97">6.1.1: Results</a></dt> -<dt><a href="#l98">6.1.2: Discussion</a></dt> -</dl> -<dt><a href="#l99">6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</a></dt> -<dl> -<dt><a href="#l100">6.2.1: Environment</a></dt> -<dt><a href="#l101">6.2.2: Tests and results</a></dt> -</dl> -</dl> -<dt><h3><a href="#l102">7: Compiling and Installing</a></h3></dt> -<dl> -<dt><a href="#l103">7.1: Prerequisites</a></dt> -<dt><a href="#l104">7.2: Compiling and installing</a></dt> -<dt><a href="#l105">7.3: Configuring crossroads</a></dt> -<dt><a href="#l106">7.4: A boot script</a></dt> -<dl> -<dt><a href="#l107">7.4.1: SysV Style Startup</a></dt> -<dt><a href="#l108">7.4.2: BSD Style Startup</a></dt> -<dt><a href="#l109">7.4.3: Linux-related</a></dt> -</dl> - -<p><hr><p> -<p> -<a name="l1"></a> -<h2>1: Introduction</h2> -<a name="intro"></a>Crossroads is a daemon that basically accepts TCP connections at -preconfigured ports, and given a list of 'back ends' distributes each -incoming connection to one of the back ends, so that a client request -is served. Additionally, crossroads maintains an internal -administration of the back end connectivity: if a back end isn't -usable, then the client request is handled using another back -end. Crossroads will then periodically check whether a previously not -usable back end has come to life yet. Also, crossroads can select back -ends by estimating the load, so that balancing is achieved. -<p> -Using this approach, crossroads serves as load balancer and fail over -utility. Crossroads will very likely not be as reliable as hardware -based balancers, since it always will require a server to run on. This -server, in turn, may become a new Single Point of Failure (SPOS). -However, in situations where cost efficiency is an issue, crossroads -may be a good choice. Furthermore, crossroads can be deployed in -situations where a hardware based balancing already exists and -augmenting service reliability is needed. Or, crossroads may be run -off a diskless system, which again improves reliability of the -underlying hardware. -<p> -This document describes how to use crossroads, how to configure it in -order to increase the reliability of your systems, and how to compile -the program from its sources. This document is also available -in <a href="crossroads.pdf">PDF</a> format. -<p> -<a name="l2"></a> -<h3>1.1: Obtaining Crossroads</h3> -<p> -As quick reference, here are some important URL's for Crossroads: -<p> -<ul> - <li> <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a> is the site that serves - Crossroads. You can browse this at leisure - for documentation, sources, and so on. -<p> -<li> <a href="http://freshmeat.net/projects/crossr">http://freshmeat.net/projects/crossr</a> is the - Freshmeat announcement page. -<p> -<li> <a href="svn://svn.e-tunity.com/crossroads">svn://svn.e-tunity.com/crossroads</a> is the SVN - repository; anonymous reading (fetching) is allowed. In order - to commit changes, mail the author or maintainer for credentials.</ul> -<p> -<a name="l3"></a> -<h3>1.2: Reporting bugs</h3> -<p> -Crossroads was thoroughly tested and proven to work. However, on -particular Unices with particular needs and network particularities, -bugs may occur. -<p> -In such cases you can contact the maintainer to ask for -assistance. Visit the site <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a> for contact information. In questions -or bug reports, always include the following: -<p> -<ul> - <li> A description of the bug: when does it occur, what are - the circumstances, what is the expected behavior, what is the - observed behavior. -<p> -<li> The Crossroads configuration in the file <code>crossroads.conf</code> -<p> -<li> The version of Crossroads and all compile-time settings, - obtained via <code>crossroads -C</code>.</ul> -<p> -<a name="l4"></a> -<h3>1.3: Copyright and Disclaimer</h3> -<p> -Crossroads is distributed as-is, without assumptions of fitness or -usability. You are free to use crossroads to your liking. It's free, -and as with everything that's free: there's also no -warranty. Crossroads is distibuted under the GNU General Public -Licence, version 3. See <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a> for more -information. -<p> -You are allowed to make modifications to the source code of -crossroads, and you are allowed to (re)distribute crossroads, as long -as you include this text, all sources, and if applicable: all your -modifications, with each distribution. -<p> -While you are allowed to make any and all changes to the sources, I -would appreciate hearing about them. If the changes concern new -functionality or bugfixes, then I'll include them in a next release, -stating full credits. If you want to seriously contribute (to which -you are heartily encouraged), then mail me and I'll get you access to -the Crossroads SVN repository, so that you can update and commit as -you like. -<p> -<a name="l5"></a> -<h3>1.4: Terminology</h3> -<p> -Throughout this document, the following terms are used: &nbsp;(Many -more meanings of the terms will exist - yes, I am aware of that. I'm -using the terms here in a very strict sense.) -<p> -<dl> - <p><dt><strong>A client</strong><dd> is a process that initiates a network connection - to get contact with some service. - <p><dt><strong>A service</strong><dd> or <strong>server process</strong> or <strong>listener</strong> - is a central application - that accepts network connections from clients and sevices - them. - <p><dt><strong>Back ends</strong><dd> are locations where crossroads looks in - order to service its clients. Crossroads sits 'in between' - and does its tricks. Therefore, as far as the back ends - are concerned, crossroads behaves like a client. As far as - the true client is concerned, crossroads behaves like the - service. The communication is however transparent: neither - client nor back end are aware of the middle position of - crossroads. - <p><dt><strong>A connection</strong><dd> is a network conversation between client and service, - where data are transferred to and fro. As - far as crossroads is concerned, success means that a - connection can be established without errors on - the network level. Crossroads isn't aware of service - pecularities. E.g., when a webserver answers <code>HTTP/1.0 - 500 Server Error</code> then crossroads will see this as a - succesful connection, though the user behind a browser may - think otherwise. - <p><dt><strong>Back end selection algorithms</strong><dd> are methods by which - crossroads determines which back end it will talk to - next. Crossroads has a number of built-in algorithms, - which may be configured per service. - <p><dt><strong>Back end states</strong><dd> are the statusses of each back end that - is known to crossroads. A back end may be available, - (temporarily) unavailble or truly down. When a back end is - temporarily unavailable, then crossroads will periodically - check whether the back end has come to life yet (that is, - if configured so). - <p><dt><strong>A spike</strong><dd> is a sudden increase in activity, leading to - extra load on a given service. When crossroads is in - effect and when the spike occurs in one connection, - then obviously the spike will also appear at one - of the back ends. However, crossroads will see the spike - and will make sure that a subsequent request goes to an - other back end. In contrast, when several connections - arrive simultaneously and cause a spike, then crossroads - will be able to distribute the connections over several - back ends, thereby 'flattening out' the increase. - <p><dt><strong>Load balancing</strong><dd> means that incoming client requests are - distributed over more than just one back end (which wouldn't be the - case if you wouldn't be running crossroads). Enabling load - balancing is nothing more than duplicating services over - more than one back end, and having something (in this - case: crossroads) distribute the requests, so that per - back end the load doesn't get too high. - <p><dt><strong>An HTTP session</strong><dd> is a series of separate network connections - that originate from one browser. E.g., to fill the display - with text and images, the browser hits a website several times. - An HTTP session may even span several - screens. E.g., a website registration dialog may involve 3 - screens that when called from the same browser, - form a logical group of some sort. - <p><dt><strong>Headers</strong><dd> or <strong>header lines</strong> are specific parts of an HTTP - message. Crossroads has directives to add or modify - headers that are part of the request that a browser sends - to server, or those that are part of the server. - <p><dt><strong>Session stickiness</strong><dd> means that when a browser starts an - HTTP dialog, the balancer makes sure that it 'sticks' to - the same back end (i.e., subsequent requests from the - browser are forced to go to the same back end, instead of - being balanced to other ones). - <p><dt><strong>Back end usage</strong><dd> is measured by crossroads in order to be - able to determine back end selection. Crossroads stores - information about the number of active connections, the - transferred bytes and - about the connection duration. These numbers can be used to - estimate which back end is the least used -- and - therefore, presumably, the best candidate for a new - request. - <p><dt><strong>Fail over</strong><dd> is almost always used when load balancing is in - effect. The distributor of client requests (crossroads of - course) can also monitor back ends, so that incase a back - end is 'down', it is no longer accessed. - <p><dt><strong>Service downtime</strong><dd> normally occurs when a service is - switched off. Downtime is obviously avoided when fail over - is in effect: a back end can be taken out of service in a - controlled manner, without any client noticing it. -</dl> -<p> -<a name="l6"></a> -<h3>1.5: Porting Issues</h3> -<p> -This section lists some caveats when converting Crossroads -configurations to new versions. Given the changes of the syntax of the -configuration file of Crossroads, existing configuration files may -need to be made suitable for new versions. -<p> -<a name="l7"></a> -<strong>1.5.1: Porting issues for pre-1.80 installations</strong> -<p> -Please be aware that as of version 1.80, the Crossroads - configuration file may be located in an 'etc' directory along the - path where Crossroads was started. E.g., if the Crossroads binary - is <code>/usr/local/bin/crossroads</code>, then Crossroads will read the - configuration file <code>/usr/local/etc/crossroads.conf</code> before - defaulting to <code>/etc/crossroads.conf</code>. When installing - configurations to such 'etc' directories along the path, be sure - to remove stale configuration files. Crossroads will only parse - one primary configuration file. -<p> -<a name="l8"></a> -<strong>1.5.2: Porting issues for pre-1.63 installations</strong> -<p> -The parser of Crossroads 1.63 has become even - stricter. HTTP-related commands, such as <code>addclientheader</code>, may - only occur when the service type is <code>http</code>. -<p> -Previously such directives were allowed, albeit they would be - ineffective. -<p> -<a name="l9"></a> -<strong>1.5.3: Porting issues for pre-1.50 installations</strong> -<p> -As of version 1.50, the command <code>crossroads tell</code> has been - changed. To set a state, the command must be used as follows: -<p> -<center><code>crossroads</code> <code>tell</code> <em>service</em> <em>backend</em> <code>state</code> - <em>newstate</em></center> -<p> -The keyword <code>state</code> was added. This is because <code>crossroads - tell</code> can also set a new server address; in that case the keyword - <code>server</code> is required. If you have automatic health checkers or - the like that change the back end states, please modify the - commands to suit this. -<p> -<a name="l10"></a> -<strong>1.5.4: Porting issues for pre-1.43 installations</strong> -<p> -As of version 1.43, the shared memory key calculations are based - on a different algorithm. This key is e.g. necessary when starting - and stopping Crossroads; in both actions, the key must be computed - in the same way. -<p> -Therefore, when upgrading Crossroads, make sure that you stop a - running Crossroads daemon using the same binary that started - it. After this, install a new binary, and start the daemon using - the new binary. -<p> -(Incidentally, this is always a good idea, but especially so when - the 'old' binary is pre-1.43 and the 'new' one is post-1.43.) -<p> -Furthermore, shell-style comment is no longer supported as of - 1.43. The reason is that 1.43 introduces the hash mark as a - preprocessor token (in <code>#include</code>, <code>#define</code>). Therefore, if - your configuration files use shell-style comment, please convert - this to <strong>C</strong> or <strong>C++</strong> style. -<p> -<a name="l11"></a> -<strong>1.5.5: Porting issues for pre-1.21 installations</strong> -<p> -As of version 1.21, the event-hook directives <code>onsuccess</code> and - <code>onfailure</code> no longer exists. -<p> -<ul> - <li> Please replace <code>onsuccess</code> by <code>onstart</code>; - <li> Please replace <code>onfailure</code> bu <code>onfail</code>; - <li> Note that there is a new hook <code>onend</code>.</ul> -<p> -The commands that are run via <code>onstart</code>, <code>onend</code> or <code>onfail</code> - are subject to format expansion; e.g., <code>%1w</code> is expanded to the - weight of the first back end, etc.. See section <a href="crossroads.html#config">4</a> for details. -<p> -<a name="l12"></a> -<strong>1.5.6: Porting issues for pre-0.26 installations</strong> -<p> -As of version 0.26 the syntax of the configuration file has - changed. In particular: -<p> -<ul> - <li> The keyword <code>maxconnections</code> is now used instead of - <code>maxclients</code>; - <li> The keyword <code>connectiontimeout</code> is now used instead of - <code>sessiontimeout</code>.</ul> -<p> -Therefore when converting configuration files to the new syntax, - the above keywords must be changed. (The reason for these changes - is that 0.26 introduces <em>sticky HTTP sessions</em> that span - multiple TCP connections, and the term - <em>session</em> is used strictly in that sense -- and no longer for a - TCP connection.) -<p> -<a name="l13"></a> -<strong>1.5.7: Porting issues for pre-1.08 installations</strong> -<p> -As of version 1.08, the following directives no longer are - supported: -<p> -<ul> - <li> <code>insertstickycookie</code> was replaced by the more generic - directive <code>addclientheader</code>. E.g., instead of <br> - <code>insertstickycookie "XRID=100; Path=/";</code> <br> - the syntax is now <br> - <code>addclientheader "Set-Cookie: XRID=100; Path=/";</code> -<p> -<li> <code>insertrealip</code> was replaced by the more generic - directive <code>setserverheader</code>. E.g., instead of <br> - <code>insertrealip on;</code> <br> - the syntax is now <br> - <code>setserverheader "XR-Real-IP: %r";</code> <br> - This incidentally also makes it possible to change the header - name (here: <code>XR-Real-IP</code>).</ul> -<p> -<a name="l14"></a> -<h2>2: Installation for the impatient</h2> -<a name="impatient"></a> -For the impatient, here's the very-quick-but-very-superficial recipy -for getting Crossroads up and running: -<p> -<ul> -<p> -<li> If you don't have SVN or don't want to use it: -<p> -<ul> - <li> Obtain the Crossroads source archive at - <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a>. -<p> -<li> Change-dir to a 'sources' directory on your system and - unpack the archive. -<p> -<li> Change-dir into the create directory <code>crossroads/</code>.</ul> -<p> -<li> If you have SVN and want to go for the newest snapshot: -<p> -<ul> -<p> -<li> Get the latest sources and snapshots using SVN from <br> - <code>svn://svn.e-tunity.com/crossroads</code>. -<p> -<li> You'll find the newest alpha version under - <code>crossroads/trunk</code> and the stable versions under - <code>crossroads/tags</code>, - e.g. <code>crossroads/tags/release-1.00</code>. -<p> -<li> Choose which you want to use: the latest stable - release, or the bleeding edge alpha? In the former case, - change-dir to <code>crossroads/tags/release-</code><em>X.YY</em>, where - <em>X.YY</em> is a release ID. In the latter case, change-dir to - <code>crossroads/trunk</code>.</ul> -<p> -<li> Type <code>make install</code>. This installs the Crossroads - binary into <code>/usr/local/bin/</code>. If the compilation doesn't - work on your system, check <code>etc/Makefile.def</code> for hints. -<p> -<li> Create a configuration file as e.g. - <code>/etc/crossroads.conf</code>. (Other locations are permitted. See - section <a href="crossroads.html#configuring">7.3</a> for details.) In it state something - like: -<p> -<pre> -service www { - port 80; - revivinginterval 15; - backend one { - server 10.1.1.100:80; - } - backend two { - server 10.1.1.101:80; - } -} -</pre> - -<p> -That's off course assuming that you want to balance HTTP on - port 80 to two back ends at 10.1.1.100 and 10.1.1.101. -<p> -<li> Type <code>crossroads start</code>. -<p> -<li> Surf to the machine where Crossroads is running. You will - see the pages served by the back ends 10.1.1.100 or - 10.1.1.101. -<p> -<li> To monitor the status of Crossroads from the command - line, type <code>crossroads status</code>. -<p> -<li> If you want to monitor Crossroads via a web front end, - start the manager: type <code>crossroads-mgr start 1000</code>. Next - point your browser to the machine where Crossroads is - running, port 1000. -</ul> -<p> -<a name="l15"></a> -<h2>3: Using Crossroads</h2> -<a name="using"></a><a name="l16"></a> -<h3>3.1: The Balancer: crossroads</h3> -<p> -The Crossroads balancer is started from the commandline, and highly -depends on the configuration file <code>crossroads.conf</code> (the default -configuration file). It supports a number of flags (e.g., to overrule -the location of the configuration file). The actual usage information -is always obtained by typing <code>crossroads</code> without any arguments. -Crossroads then displays the allowed arguments. -<p> -The installation (see section <a href="crossroads.html#installation">7</a>) installs also a -second program called <code>crossroads-daemon</code>. This program is not meant -to be started from the command line; it is administered through the -front end <code>crossroads</code>. -<p> -<a name="l17"></a> -<strong>3.1.1: General Commandline Syntax</strong> -<p> -This section shows the most basic usage. As said above, start -<code>crossroads</code> without arguments to view the full listing of options. -<p> -<ul> - <li> <code>crossroads start</code> and <code>crossroads stop</code> are typical - actions that are run from system startup scripts. The - meaning is self-explanatory. - <li> <code>crossroads restart</code> is a combination of the former - two. Beware that a restart may cause discontinuity in - service; it is just a shorthand for typing the 'stop' and - 'start' actions after one another. The reason is that in - the split second where the listener is stopped and a new one - is not yet started, a new network connection won't be - accepted. Running connections are however not hindered by a - restart. - <li> <code>crossroad status</code> reports on each running - service. Per service, the state of each back end is - reported. - <li> <code>crossroads tell</code> <em>service backend</em> <code>state</code> - <em>newstate</em> is a - command line way of telling crossroads that a given back - end, of a given service, is in a given state. Normally - crossroads maintains state information itself, but by - using <code>crossroads tell</code>, a back end can be e.g. taken - 'off line' for servicing. - <li> <code>crossroads tel</code> <em>service backend</em> <code>server</code> - <em>hostname:port</em> redefines the back end address of a - running back end. - <li> <code>crossroads configtest</code> tells you whether the - configuration is syntactially correct. - <li> <code>crossroads services</code> reports on the configured - services. In contrast to <code>crossroads status</code>, this - option only shows what's configured -- not what's up and - running. Therefore, <code>crossroads services</code> doesn't - report on back end states. -</ul> -<p> -<a name="l18"></a> -<strong>3.1.2: Status Reporting from the Command Line</strong> -<p> -The command <code>crossroads status</code> shows a verbose human-readable -report of how Crossroads is doing. When many services are configured, -this can be a somewhat lengthy output. If you're interested in the -overview of only one service, you can use <code>crossroads status</code> -<em>servicename</em>, in which case the report will only be shown for the -stated service. -<p> -Similarly, if you're interested only in the status of a given back end -of a given service, use <code>crossroads service</code> <em>servicename</em> -<em>backendname</em>. -<p> -The flag <code>-t</code> causes the status overview to be abbreviated and -displayed in a parseable format. This flag can be used in automated -scripts that check how Crossroads is doing; e.g., in health checking -scripts. When <code>-t</code> is used, the format of the status reporter is as -follows: -<p> -<ul> - <li> Service health is reported on one line per service. - <li> The first string on the line is the service name. - <li> After this, a series follows of <em>backendname=state</em>, - where the back end name is the configured name of the back - end, and where state is e.g. <code>available</code>, <code>down</code>, - <code>unavailable</code>, <code>waking</code>. The statuses may occur in caps. - The series is repeated for all back ends of the given service.</ul> -<p> -The flag <code>-x</code> causes the overview to be presented as an XML -document. This can be handy if you want to further automate the -control over Crossroads, or if you want to show the status via the -web. (See section <a href="crossroads.html#xmlstatus">5.15</a> for more information.) -<p> -The status reporter shows the number of running connections per back -end, and the total of connections of the entire service. Due to -technical reasons the total count can be one off. (There is a -technical reason for this: the total counter gets updated at a -different rate than separate back end counts. This may be fixed in a -future release.) -<p> -<a name="l19"></a> -<strong>3.1.3: Logging-related options</strong> -<p> -Two 'flags' of Crossroads are specifically logging-related. This -section elaborates on these flags. -<p> -First, there's flag <code>-a</code>. When present, the start and end of -activity is logged using statements like -<p> -<center><em>YYYY-MM-DD HH/MM/SS starting http from 61.45.32.189 to 10.1.1.1</em></center> -<p> -Similarly, there are 'ending' statements. Using this flag and -scanning your logs for these statements may be helpful in quickly -determining your system load. -<p> -Second, there's flag <code>-l</code>. This flag selects the 'facility' of -logging and defaults to <code>LOG_DAEMON</code>. You can supply a number -between 0 and 7 to flag <code>-l</code> to select <code>LOG_LOCAL0</code> to -<code>LOG_LOCAL7</code>. This would separate the Crossroads-related logging -from other streams. Here's a very short guide; please read your Unix -manpages of <code>syslogd</code> for more information. -<p> -<ul> - <li> First edit <code>/etc/syslog.conf</code> and add a line: -<p> -<pre> -local7.* /var/log/crossroads.log -</pre> - -<p> -That instructs <code>syslogd</code> to send <code>LOG_LOCAL7</code> requests to the - logfile <code>/var/log/crossroads.log</code>. -<p> -<li> Next, restart <code>syslogd</code>. On most Unices that's done by - issuing <code>killall -1 syslogd</code>. (As a side-note, I tried this once - on an Bull/AIX system, and the box just shut down. The <code>killall</code> - command killed every process...) -<p> -<li> Now start <code>crossroads</code> with the flag <code>-l7</code>. -<p> -<li> Finally, monitor <code>/var/log/crossroads.log</code> for Crossroads' - messages.</ul> -<p> -<a name="l20"></a> -<strong>3.1.4: Reloading Configurations</strong> -<p> -Crossroads doesn't support the reloading of a configuration while -running (such as other programs, e.g. Apache do). There are various -technical reasons for this. -<p> -However, external lists of allowed or denied IP addresses can be -reloaded by sending a signal -1 (<code>SIGHUP</code>) to Crossroads. See -section <a href="crossroads.html#servicedef">4.3</a> for the details. -<p> -<a name="l21"></a> -<h3>3.2: The Web Frontend: crossroads-mgr</h3> -<p> -A web front end for Crossroads is <code>crossroads-mgr</code>. Type -<code>crossroads-mgr</code> without arguments to see the usage. -<p> -Normally the web interface is started like: -<p> -<center><code>crossroads-mgr start 1000</code></center> -<p> -where 1000 is a free TCP port. Note that <code>crossroads-mgr</code> must be -started on the system where the balancer <code>crossroads</code> itself is -running. -<p> -There are lots of other options that can be used with -<code>crossroads-mgr</code>, see section <a href="crossroads.html#tips/webinterface">5.14</a>. -<p> - - -<a name="l22"></a> -<h2>4: The configuration</h2> -<a name="config"></a>The configuration that crossroads uses is normally stored in the file -<code>crossroads.conf</code>, either in an 'etc' directory near the binary, or -in <code>/etc/</code>. See section <a href="crossroads.html#configuring">7.3</a> for details. -<p> -The name of the configuration file can be overruled using the -command line flag <code>-c</code>. -<p> -This section explains the syntax of the configuration file, and what -all settings do. -<p> -<a name="l23"></a> -<h3>4.1: General language elements</h3> -<p> -This section describes the general elements of the crossroads -configuration language. -<p> -<a name="l24"></a> -<strong>4.1.1: Empty lines, indentation and comments</strong> -<p> -Empty lines are of course allowed in the configuration. Also, -indentation is irrelevant as far as processing is concerned, but is -loosely inspired on a C-like style. So the following two examples are -identical as far as Crossroads is concerned (thought the first one -will be typically more readable): -<p> -<pre> -/* Example one */ -options { - tcpbuffersize 10240; - logactivity on; -} - -/* Example two */ -options {tcpbuffersize 10240 -;logactivity on;} -</pre> - -<p> -Crossroads recognizes the following comment formats: -<p> -<ul> - <li> C-style, between <code>/*</code> and <code>*/</code>, - <li> C++-style, starting with <code>//</code> and ending with the end - of the text line.</ul> -<p> -Simply choose your favorite editor and use the comment that 'looks -best'.&nbsp;(I favor C or C++ comment. My favorite editor <em>emacs</em> -can be put in <code>cmode</code> and nicely highlight what's comment and what's -not. And as a bonus it will auto-indent the configuration!) -<p> -<a name="l25"></a> -<strong>4.1.2: Preprocessor directives</strong> -<p> -Similar to <strong>C</strong> or <strong>C++</strong>, the Crossroads grammar knows <code>#include</code> -and <code>#define</code>. Both directives must start at the first column of the -line (ie., the <code>#</code> sign must occur at the leftmost line -position). -<p> -<ul> - <li> <code>#include</code> <code>"</code><em>filename</em><code>"</code> includes the stated file - name at the place of the statement. - <li> <code>#define</code> <em>SYMBOL</em> <em>DEFINITION</em> defines <em>SYMBOL</em> - as placeholder for <em>DEFINITION</em>.</ul> -<p> -For example, one may use the configuration: -<p> -<pre> -#define SERVICEPORT 80 -service web { - port SERVICEPORT; - . - . /* More statements follow here */ - . -} -</pre> - -<p> -The <code>port</code> statement is then read as <code>port 80</code>. -<p> -The statement <code>#define</code> can also be very nicely used when trying out -Crossroads configurations. Crossroads has a statement <code>verbosity</code> -<code>true</code> that causes debugging information to be logged. Once a -configuration has proven to work, you'll most likely want -<code>verbosity</code> <code>false</code> so that overhead due to logging is -avoided. This can be easily implemented using <code>#define</code>: -<p> -<pre> -/* Set DEBUG to true or false; - * true is for testing purposes, - * false is for production */ -#define DEBUG true - -service web { - verbosity DEBUG; - . - . /* More statements follow here */ - . -} -</pre> - -<p> -<a name="l26"></a> -<strong>4.1.3: Keywords, numbers, identifiers, generic strings</strong> -<p> -In a configuration file, statements are identified by <em>keywords</em>, -which are reserved words. The list of the keywords of the Crossroads -grammar is: -<a name="keywords.yo"></a><code>service</code> -<code>port</code> -<code>verbosity</code> -<code>maxconnections</code> -<code>type</code> -<code>any</code> -<code>http</code> -<code>backend</code> -<code>server</code> -<code>bindto</code> -<code>connectiontimeout</code> -<code>on</code> -<code>off</code> -<code>dispatchmode</code> -<code>roundrobin</code> -<code>random</code> -<code>byduration</code> -<code>bysize</code> -<code>byconnections</code> -<code>byorder</code> -<code>byclientip</code> -<code>over</code> -<code>decay</code> -<code>revivinginterval</code> -<code>checkinterval</code> -<code>retries</code> -<code>shmkey</code> -<code>weight</code> -<code>onstart</code> -<code>onfail</code> -<code>backlog</code> -<code>throughputlog</code> -<code>trafficlog</code> -<code>httptiminglog</code> -<code>stickycookie</code> -<code>addclientheader</code> -<code>setclientheader</code> -<code>appendclientheader</code> -<code>addserverheader</code> -<code>setserverheader</code> -<code>appendserverheader</code> -<code>allowfrom</code> -<code>denyfrom</code> -<code>allowfile</code> -<code>denyfile</code> -<code>externalhandler</code> -<code>useraccount</code> -<code>onend</code> -<code>headerinspection</code> -<code>deep</code> -<code>shallow</code> -<code>state</code> -<code>available</code> -<code>unavailable</code> -<code>down</code> -<code>options</code> -<code>logactivity</code> -<code>tcpbuffersize</code> -<code>dnscachettl</code> -<code>logfacility</code> -<code>shmpermissions</code> -<code>sloppyportbind</code> -<code>leaveprocesstitle</code> -<p> -Many keywords require an <em>identifier</em> as the argument. E.g, a -service has a unique name, which must start with a letter or -underscore, followed by zero or more letters, underscores, or -digits. E.g., in the statement <code>service myservice</code>, the keyword is -<code>service</code> and the identifier is <code>myservice</code>. -<p> -Other keywords require a numeric argument. Crossroads knows only -non-negative integer numbers, as in <code>port 8000</code>. Here, <code>port</code> is -the keyword and <code>8000</code> is the number. Octal numbers are specified by -prefixing a zero; e.g., <code>0644</code> is an octal number. -<p> -Yet other keywords require 'generic strings', such as hostname -specifications or system commands. Such generic strings contain any -characters (including white space) up to the terminating statement -character <code>;</code>. If a string must contain a semicolon, then it must -be enclosed in single or double quotes: -<p> -<ul> - <li> <code>This is a string;</code> is a string that starts at <code>T</code> - and ends with <code>g</code> - <li> <code>"This is a string";</code> is the same, the double quotes - are not necessary - <li> <code>"This is ; a string";</code> has double quotes to protect - the inner ;</ul> -<p> -Finally, an argument can be a 'boolean' value. Crossroads knows -<code>true</code>, <code>false</code>, <code>yes</code>, <code>no</code>, <code>on</code>, <code>off</code>. The keywords -<code>true</code>, <code>yes</code> and <code>on</code> all mean the same and can be used -interchangeably; as can the keywords <code>false</code>, <code>no</code> and <code>off</code>. -<p> -<a name="l27"></a> -<h3>4.2: Daemon options</h3><a name="daemonoptions"></a> -<p> -Crossroads supports an optional block that defines run options for the -daemon process. These options can be also be specified on the command line -using flags. -<p> -The syntax of the options block is: -<p> -<pre> -options { - . - . option statements (see below) - . -} -</pre> - -<p> -In the curly-brace block the following option statements can occur: -<p> -<ul> - <li> <code>logactivity on;</code> or <code>off</code> Turns activity logging on - or off. This is also controlled using the flag <code>-a</code>. The - default is not to log network activity. -<p> -<li> <code>tcpbuffersize</code> <em>number</em> <code>;</code> This defines the size - of network buffers. The default is 5120 bytes. This is also - controlled using the flag <code>-B</code>. -<p> -<li> <code>dnscachettl</code> <em>number</em> <code>;</code> This controls the - time-to-live of cached DNS entries, in seconds. Value zero - means no caching and is the default. This is also controlled - using the flag <code>-d</code>. -<p> -<li> <code>logfacility</code> <em>number</em> <code>;</code> This controls the - logging facility. Values 0 to 7 select <code>LOG_LOCAL0</code> to - <code>LOG_LOCAL7</code>. Any other value selects <code>LOG_DAEMON</code>, which - is also the default. This is also controlled using the flag <code>-l</code>. -<p> -<li> <code>shmpermissions</code> <code>number</code> <code>;</code> This defines the - permissions (for user, group and other) of the shared memory - block. The number is most often specified as an octal value, - e.g. <code>0644</code>, which is also the default. This is also - controlled using the flag <code>-m</code>. -<p> -<li> <code>sloppyportbind on;</code> or <code>off</code> Turns on 'sloppy' port - binding of the listener. When <code>on</code>, Crossroads will not - treat port-busy conditions as fatal, but will wait and retry. - The default is <code>off</code>. This is also controlled using flag - <code>-s</code>. -<p> -<li> <code>leaveprocesstitle</code> <code>on;</code> or <code>off</code> When <code>on</code>, the - process title of Crossroads is not modified, so that <code>ps</code> - will show <code>crossroads-daemon</code>. The default is <code>off</code>: - Crosroads will modify its process name into something like - <em>crossroads - Sevice web: listening</em> or <em>crossroads - - Service web: serving</em>.</ul> -<p> -<a name="l28"></a> -<h3>4.3: Service definitions</h3> <a name="servicedef"></a> -<p> -Service definitions are blocks in the configuration file that -state what is for each service. A service definition starts with -<code>service</code>, followed by a unique identifier, and by statements in -<code>{</code> and <code>}</code>. For example: -<p> -<pre> -// Definition of service 'www': -service www { - ... - ... // statements that define the - ... // service named 'www' - ... -} -</pre> - -<p> -The configuration file can contain many service blocks, as long as the -identifying names differ. The following list shows possible -statements. Each statement must end with a semicolon, except for the -<code>backend</code> statement, which has is own block (more on this later). -<p> -<a name="conf/port"></a><a name="l29"></a> -<strong>4.3.1: port - Specifying the listen port</strong> <a name="confport - Specifying the listen port"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The <code>port</code> statement defines to which TCP port a service - 'listens'. E.g. <code>port 8000</code> says that this service will accept - connections on port 8000. -<p> -Multiple services in one configuration cannot use the same port - number, unless they both bind to specific (and different) IP - addresses. See also the <code>bindto</code> statement. - <p><dt><strong>Syntax:</strong><dd> <code>port</code> <em>number</em> - <p><dt><strong>Default:</strong><dd> There is no default. This is a required setting. - </dl> -<p> -<a name="conf/type"></a><a name="l30"></a> -<strong>4.3.2: type - Defining the service type</strong> <a name="conftype - Defining the service type"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The <code>type</code> statement defines how crossroads handles the stated - service. There are currently two types: <code>any</code> and - <code>http</code>. The type <code>any</code> means that crossroads doesn't - interpret the contents of a TCP stream, but only distributes streams - over back ends. The type <code>http</code> means that crossroads has to - analyze what's in the messages, does magical HTTP header tricks, and - so on -- all to ensure that multiple connections are treated as one - session, or that the back end is notified of the client's IP - address. -<p> -Unless you really need such special features, use the type <code>any</code> (the - default), even for HTTP protocols. - <p><dt><strong>Syntax:</strong><dd> <code>type</code> <em>specifier</em>, where <em>specifier</em> is <code>any</code> or - <code>http</code> - <p><dt><strong>Default:</strong><dd> <code>any</code> - </dl> -<p> -<a name="conf/headerinspection"></a><a name="l31"></a> -<strong>4.3.3: headerinspection - are all HTTP headers inspected</strong> <a name="confheaderinspection - are all HTTP headers inspected"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The <code>headerinspection</code> directive defines whether Crossroads -must inspect all HTTP headers that are seen on one TCP connection, or -only the first ones. There are two possible values for this directive: -<code>deep</code> and <code>shallow</code>. In <code>deep</code> mode, all information that is -seen on the TCP link is monitored -and parsed, and all HTTP header blocks are analyzed and subject to -directives such as <code>addclientheader</code>. In <code>shallow</code> mode, only the -first header block that the server sends, and the first header block -that forms the server's answer, are analyzed. - <p><dt><strong>Syntax:</strong><dd> <code>headerinspection</code> <em>specifier</em>, where <em>specifier</em> is -<code>deep</code> or <code>shallow</code> - <p><dt><strong>Default:</strong><dd> <code>deep</code> - </dl> -<p> -<a name="conf/bindto"></a><a name="l32"></a> -<strong>4.3.4: bindto - Binding to a specific IP address</strong> <a name="confbindto - Binding to a specific IP address"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The <code>bindto</code> statement is used in situations where crossroads - should only listen to the stated port at a given IP address. E.g., - <code>bindto 127.0.0.1</code> causes crossroads to 'bind' the service only to - the local IP address. Network connections from other hosts won't be - serviced. By default, crossroads binds a service to all presently - active IP addresses at the invoking host. - <p><dt><strong>Syntax:</strong><dd> <code>bindto</code> <em>address</em>, where <em>address</em> is a numeric IP - address, such as 127.0.0.1, or the keyword <code>any</code>. - <p><dt><strong>Default:</strong><dd> <code>any</code> - </dl> -<p> -<a name="conf/verbose"></a><a name="l33"></a> -<strong>4.3.5: verbosity - Controlling debug output</strong> <a name="confverbosity - Controlling debug output"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Verbosity statements come in two forms: <code>verbosity on</code> or - <code>verbosity off</code>. When 'on', log messages to <code>/var/log/messages</code> - are generated that show what's going on.&nbsp;(Actually, the - messages go to <code>syslog(3)</code>, using facility <code>LOG_DAEMON</code> and - priority <code>LOG_INFO</code>. In most (Linux) cases this will mean: output to - <code>/var/log/messages</code>. On Mac OSX the messages go to - <code>/var/log/system.log</code>.) The keyword <code>verbose</code> is an alias for - <code>verbosity</code>. - <p><dt><strong>Syntax:</strong><dd> <code>verbosity</code> <em>setting</em> or <code>verbose</code> <em>setting</em>, where - <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn - verbosity on; or <code>false</code>, <code>no</code>, <code>off</code> to turn it off. - <p><dt><strong>Default:</strong><dd> <code>off</code> - </dl> -<p> -<a name="conf/dispatchmode"></a><a name="l34"></a> -<strong>4.3.6: dispatchmode - How are back ends selected</strong> <a name="confdispatchmode - How are back ends selected"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The dispatch mode controls how crossroads selects a back end from - a list of active back ends. The below text shows the bare - syntax. See section <a href="crossroads.html#howselected">5.2</a> for a textual explanation. -<p> -The settings can be: -<p> -<ul> - <li> <code>dispatchmode roundrobin</code>: Simply the 'next in line' is - chosen. E.g, when 3 back ends are active, then the usage - series is 1, 2, 3, 1, 2, 3, and so on. -<p> -Roundrobin dispatching is the default method, when no - <code>dispatchmode</code> statement occurs. -<p> -<li> <code>dispatchmode random</code>: Random selection. Probably only - for stress testing, though when used with weights (see below) - it is a good distributor of new connections too. -<p> -<li> <code>dispatchmode bysize [ over</code> <em>connections</em> <code>]</code>: - The next back end is the one - that has transferred the least number of bytes. This - selection mechanism assumes that the more bytes, the heavier - the load. -<p> -The modifier <code>over</code> <em>connections</em> is optional. (The square - brackets shown above are not part of the statement but - indicate optionality.) When given, - the load is computed as an average of the last stated number of - connections. When this modifier is absent, then the load is - computed over all connections since startup. -<p> -<li> <code>dispatchmode byduration [ over</code> <em>connections</em> <code>]</code>: - The next back end is the one - that served connections for the shortest time. This mechanism - assumes that the longer the connection, the heavier the load. -<p> -<li> <code>dispatchmode byconnections</code>: The next back end is the one - with the least active connections. This mechanism assumes that - each connection to a back end represents load. It is usable - for e.g. database connections. -<p> -<li> <code>dispatchmode byorder</code>: The first back end is selected - every time, unless it's unavailable. In that case the second - is taken, and so on. -<p> -<li> <code>dispatchmode byclientip</code>: The client's IP address is - 'hashed' into a number, which is used to pick a back end. The - same client IP address will therefore always be dispatched to - the same back end. When the back end of choice is down, - <code>dispatchmode byconnections</code> is used. -<p> -<li> <code>dispatchmode externalhandler</code> <em>program arguments</em>: - This is a special mode, where an external program is delegated - the responsibility to say which back end should be used - next. In this case, Crossroads will call the external program, - and this will of course be slower than one of the 'built-in' - dispatch modes. However, this is the ultimate escape when - custom-made dispatch modes are needed. -<p> -The dispatch mode that uses an <code>externalhandler</code> is - discussed separately in section <a href="crossroads.html#externalhandler">5.11</a>.</ul> -<p> -The selection algorithm is only used when clients are serviced that - aren't part of a sticky HTTP session. This is the case during: -<p> -<ul> - <li> all client requests of a service type <code>any</code>; - <li> new sessions of a service type <code>http</code>.</ul> -<p> -When type <code>http</code> is in effect and a session is underway, then the - previously used back end is always selected -- regardless of - dispatching mode. -<p> -Your 'right' dispatch mode will depend on the type of service. Given - the fact that crossroads doesn't know (and doesn't care) how to - estimate load from a network traffic stream, you have to choose an - appropriate dispatch mode to optimize load balancing. In most cases, - <code>roundrobin</code> or <code>byconnections</code> will do the job just fine. - <p><dt><strong>Syntax:</strong><dd> <code>dispatchmode</code> <em>mode</em> (see above for the modes), optionally - followed by <code>over</code> <em>number</em>, or when the <em>mode</em> is - <code>externalhandler</code>, followed by <em>program</em>. - <p><dt><strong>Default:</strong><dd> <code>roundrobin</code> - </dl> -<p> -<a name="conf/revivinginterval"></a><a name="l35"></a> -<strong>4.3.7: revivinginterval - Back end wakeup calls</strong> <a name="confrevivinginterval - Back end wakeup calls"></a> - <dl> - <p><dt><strong>Description:</strong><dd> A reviving interval definition is used when Crossroads - determines that a back end is temporarily unavailable. This will - happen when: -<p> -<ul> - <li> The back end cannot be reached (network connection - fails); - <li> The network connection to the back end suddenly dies.</ul> -<p> -Once a reviving interval is set, Crossroads will periodically - check the unavailable back end(s) to see whether they have woken up. -<p> -An example of the definition is <code>revivinginterval 10</code>. When this - reviving interval is given, crossroads will check each 10 seconds - whether unavailable back ends have woken up yet. - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>revivinginterval</code> <em>number</em>; - <li> <code>revivinginterval</code> <em>number</em> <code>externalhandler</code> - <em>program arguments</em>;</ul> - The first form connects to a back end server. If the connection - succeeds, then the back end is considered available. The second - form activates an external program (see section - <a href="crossroads.html#tips/periodic">5.3</a> for a description). The back end is marked - available if the program's exit status is zero. - <p><dt><strong>Default:</strong><dd> 0 (no wakeup calls) - </dl> -<p> -<a name="conf/checkinterval"></a><a name="l36"></a> -<strong>4.3.8: checkinterval - Periodic back end checks</strong> <a name="confcheckinterval - Periodic back end checks"></a> - <dl> - <p><dt><strong>Description:</strong><dd> When a check interval is stated, Crossroads will periodically - probe back ends to determine whether available back ends are - still there, and to see whether unavailable back ends have woken - up yet. -<p> -An example is <code>checkinterval 10</code>. When this is stated, - Crossroads will probe all back ends each 10 seconds. - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>checkinterval</code> <em>number</em>; - <li> <code>checkinterval</code> <em>number</em> <code>externalhandler</code> - <em>program arguments</em>;</ul> - The first form checks bhy connecting to the back end server. - If the connection succeeds, then the back end is considered - available; otherwise the back end is considered unavailable. -<p> -The second form activates an external program (see section - <a href="crossroads.html#tips/periodic">5.3</a> for a description). The back end is considered - available if the program's exit status is zero; otherwise it is - considered unavailable. - <p><dt><strong>Default:</strong><dd> 0 (no periodic checks) - </dl> -<p> -<a name="conf/maxconnections"></a><a name="l37"></a> -<strong>4.3.9: maxconnections - Limiting concurrent clients at service level</strong> <a name="confmaxconnections - Limiting concurrent clients at service level"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The maximum number of connections is specified using - <code>maxconnections</code>. There is one argument; the number of concurrent - established connections that may be active within one service. -<p> -'Throttling' the number of connections is a way of preventing Denial of - Service (DOS) attacks. Without a limit, numerous network connections - may spawn so many server instances, that the service ultimately breaks - down and becomes unavailable. -<p> -Note that <code>maxconnections</code> is also allowed in a backend - description block, in which case it limits the number of TCP - connections to that particular back end. - <p><dt><strong>Syntax:</strong><dd> <code>maxconnections</code> <em>number</em>, where the number specifies the - maximum of concurrent connections to the service. - <p><dt><strong>Default:</strong><dd> 0, meaning that all connections will be accepted. - </dl> -<p> -<a name="conf/backlog"></a><a name="l38"></a> -<strong>4.3.10: backlog - The TCP Back Log size</strong> <a name="confbacklog - The TCP Back Log size"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The TCP back log size is a number that controls how many - 'waiting' network connections may be queued, before a client simply - cannot connect. The syntax is e.g. <code>backlog 5</code> to cause crossroads - to have 5 waiting connections for 1 active connection. - The backlog queue shouldn't be too - high, or clients will experience timeouts before they can actually - connect. The queue shouldn't be too small either, because clients - would be simply rejected. Your mileage may vary. - <p><dt><strong>Syntax:</strong><dd> <code>backlog</code> <em>number</em> - <p><dt><strong>Default:</strong><dd> 0, which takes the operating system's default - value for socket back log size. - </dl> -<p> -<a name="conf/shmkey"></a><a name="l39"></a> -<strong>4.3.11: shmkey - Shared Memory Access</strong> <a name="confshmkey - Shared Memory Access"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Different Crossroads - invocations must 'know' of each others activity. E.g, <code>crossroad - status</code> must be able to get to the actual state information of all - running services. This is internally implemented through shared - memory, which is reserved using a key. -<p> -Normally crossroads will supply a shared memory key, based on the - service name. In situations where this conflicts with existing - keys (of other programs, having their own keys), you may supply a - chosen value. -<p> -The actual key value doesn't matter much, as long as it's unique - and as long as each invocation of crossroads uses it. - <p><dt><strong>Syntax:</strong><dd> <code>shmkey</code> <em>number</em> - <p><dt><strong>Default:</strong><dd> 0, which means that crossroads will 'guess' its - own key, based on the service name. - </dl> -<p> -<a name="conf/allow"></a><a name="l40"></a> -<strong>4.3.12: allow* and deny* - Allowing or denying connections</strong> <a name="confallow* and deny* - Allowing or denying connections"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Crossroads can allow or deny - connections based on the IP address of a client. There are four - directives that are relevant: <code>allowfrom</code>, <code>allowfile</code>, - <code>denyfrom</code> and <code>denyfile</code>. -<p> -When using <code>allowfrom</code> and - <code>denyfrom</code> then the IP addresses to allow or deny connections are - stated in the configuration. When using <code>allowfile</code> and - <code>denyfile</code> the allow or deny connections are stated in a - separate file. -<p> -When <code>allow*</code> directives are used, then all connections are denied - unless they match the stated allowed IP's. When <code>deny*</code> directives - are used, then all connections are allowed unless they match the - stated disallowed IP's. When denying and allowing is both used, - then the Crossroads checks the deny list first. -<p> -The statements <code>allowfrom</code> and <code>denyfrom</code> are followed by a - list of filter specifications. The statements <code>allowfile</code> and - <code>denyfile</code> are followed by a filename; Crossroads will read - filter specifications from those external files. In both cases, - Crossroads obtains filter specifications and places them in its - lists of allowed or denied IP addresses. The difference between - specifying filters in the configuration file or in external - files, is that Crossroads will reload the external files when it - receives signal 1 (<code>SIGHUP</code>), as in <code>killall -1 crossroads</code>. -<p> -The filter specifications must obey the following syntax: they - are series of space-separated strings, consisting of up to - four numbers ranging from 0 to 255 and separated by a decimal - sign. Optionally a slash follows, with a bitmask which is also a - decimal number. For example, <code>127.0.0/24 10/8 192.168.2.1</code> is a - setting that consists of three specifiers. -<p> -This is probably best explained by a few examples: -<p> -<ul> - <li> <code>allowfrom 10/8;</code> will allow connections from - <code>10.*.*.*</code> (a full Class A network). The mask <code>/8</code> means - that the first 8 bits of the number (ie., only the <code>10</code>) are - significant. On the last 3 positions of the IP address, all - numbers are allowed. Given this directive, client connections - from e.g. 10.1.1.1 and 10.2.3.4 will be allowed. -<p> -<li> <code>allowfrom 10.3/16;</code> will allow all IP addresses that - start with <code>10.3</code>. The first 16 bits (i.e., the first 2 - numbers) are significant, the rest doesn't matter. -<p> -<li> <code>allowfrom 10.3.1/16;</code> is the same as above. The third - byte of the IP address is superfluous because the netmask - specifies that only the first 16 bits (2 numbers) are taken - into account. -<p> -<li> <code>allowfrom 10.3.1.15;</code> allows traffic from only the - specified IP address. There is no bitmask; all four numbers - are relevant. -<p> -<li> <code>allowfrom 10.3.1.15 10.2/16;</code> allows traffic from one - IP address <code>10.3.1.15</code> or from a complete Class B network - <code>10.2.*.*</code> -<p> -<li> <code>allowfile /tmp/myfile.txt;</code> in combination with a file - <code>/tmp/myfile.txt</code>, with the contents <code>10.3.1.15 10.2/16</code>, - is the same as above.</ul> -<p> -When using ttt(allowfrom) and <code>denyfrom</code>, separate specifiers - can be stated in one statement (separated by whitespace), or the - whole statement can be repeated. E.g., the following two - alternatives have the same effect: -<p> -<pre> -/* Alternative 1: */ -allowfrom 10/8 192.168.1/24; - -/* Alternative 2: */ -allowfrom 10/8; -allowfrom 192.168.1.24; -</pre> - - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>allowfrom</code> <em>filter-specificication(s)</em> - <li> <code>denyfrom</code> <em>filter-specificication(s)</em> - <li> <code>allowfile</code> <em>filename</em> - <li> <code>denyfile</code> <em>filename</em></ul> - <p><dt><strong>Default:</strong><dd> In absence of these statements, all client IP's are accepted. - </dl> -<p> -<a name="conf/useraccount"></a><a name="l41"></a> -<strong>4.3.13: useraccount - Limiting the effective ID of external processes</strong> <a name="confuseraccount - Limiting the effective ID of external processes"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Using the directive <code>useraccount</code>, the effective user and group - ID can be restricted. This comes into effect when Crossroads runs - external commands, such as: - <ul> - <li> Hooks for <code>onstart</code>, <code>onend</code> or <code>onfail</code>; - <li> External dispatchers, when <code>dispatchmode - externalhandler</code> is in effect.</ul> - Once a user name for external commands is specified, Crossroads - assumes the associated user ID and group ID before running those - commands. - <p><dt><strong>Syntax:</strong><dd> <code>useraccount</code> <em>username</em> - <p><dt><strong>Default:</strong><dd> None; when unspecified, external commands are run with the - ID that was in effect when Crossroads was started. - </dl> -<p> -<a name="l42"></a> -<h3>4.4: Backend definitions</h3> -<p> -Inside the service definitions as are described in the previous -section, <em>backend definitions</em> must also occur. Backend definitions -are started by the keyword <code>backend</code>, followed by an identifier -(the back end name) , and statements inside <code>{</code> and <code>}</code>: -<p> -<pre> -service myservice { - ... - ... // statements that define the - ... // service named 'myservice' - ... - - backend mybackend { - ... - ... // statements that define the - ... // backend named 'mybackend' - ... - } -} -</pre> - -<p> -Each service definition must have at least one backend -definition. There may be more (and probably will, if you want -balancing and fail over) as long as the backend names differ. -The statements in the backend definition blocks are described in the -following sections. -<p> -Some directives (<code>stickycookie</code> etc.) only have effect when -Crossroads treats the network traffic as a stream of HTTP messages; -i.e., when the service is declared with <code>type http</code>. Incase of -<code>type any</code>, the HTTP-specific directives have no effect. -<p> -<a name="conf/server.yo"></a><a name="l43"></a> -<strong>4.4.1: server - Specifying the back end address</strong> <a name="confserver - Specifying the back end address"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Each back end must be identified by the network name - (server name) where it is located. For example: <code>server - 10.1.1.23</code>, or <code>server web.mydomain.org</code>. A TCP port specifier - can follow the server name, as in <code>server - web.mydomain.org:80</code>. <strong>Note that</strong> resolved host names can be - cached by Crossroads. (The DNS cache timeout can be - controlled using the invocation flag <code>-d</code>.) - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>server</code> <em>servername</em>, where <em>servername</em> is a - network name or IP address. In this case a separate <code>port</code> - statement must be used to define the TCP port; - <li> <code>server</code> <em>servername:port</em></ul> - <p><dt><strong>Default:</strong><dd> There is no default. This is a required setting. - </dl> -<p> -<a name="conf/backendport.yo"></a><a name="l44"></a> -<strong>4.4.2: port - Specifying a back end port</strong> <a name="confport - Specifying a back end port"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Back ends must be known by their host name and a port. Both can - be simultaneously specified in a <code>server</code> statement. When the - <code>server</code> statement specifies a host name only, then a <code>port</code> - statement must be used to specify the port. - <p><dt><strong>Syntax:</strong><dd> <code>port</code> <em>number</em> - <p><dt><strong>Default:</strong><dd> There is no default for the port. It must be specified either - using <code>server</code> or using <code>port</code>. - </dl> -<p> -<a name="conf/verbose-backend.yo"></a><a name="l45"></a> -<strong>4.4.3: verbosity - Controlling verbosity at the back end level</strong> <a name="confverbosity - Controlling verbosity at the back end level"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Similar to <code>service</code> specifications, a - <code>backend</code> can have its own verbosity (<code>on</code> or <code>off</code>). When - <code>on</code>, traffic to and fro this back end is reported. - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>verbosity</code> <em>setting</em>, or - <li> <code>verbose</code> <em>setting</em>, where <em>setting</em> is <code>true</code>, - <code>yes</code> or <code>on</code>, or <code>false</code>, <code>no</code>, <code>off</code> to turn it - off.</ul> - <p><dt><strong>Default:</strong><dd> <code>off</code> - </dl> -<p> -<a name="conf/retries.yo"></a><a name="l46"></a> -<strong>4.4.4: retries - Specifying allowed failures</strong> <a name="confretries - Specifying allowed failures"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Back ends that are 'flaky' or on a less reliable network can be - marked as unavailable after not just one failure, but after - e.g. three. You can use this configuration if you suspect that - spurious errors cause otherwise 'good' back ends to be marked as - unavailable, while they in fact still could be used. - <p><dt><strong>Syntax:</strong><dd> <code>retries</code> <em>number</em>; where <em>number</em> is the threshold of bad - connections. Once exceeded, Crossroads will mark a back end as - unavailable. - <p><dt><strong>Default:</strong><dd> 1; a back end is assumed to be unavailable after the first bad - connection. - </dl> -<p> -<a name="conf/maxcon-client.yo"></a><a name="l47"></a> -<strong>4.4.5: maxconnections - Limiting the connections to a back end</strong> <a name="confmaxconnections - Limiting the connections to a back end"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The directive <code>maxconnections</code> limits the number of allowed - connections to this client. - Note that this directive can also occur on the level of a service - block, in which case it limits the overall number of connections. -<p> -<strong>Futhermore note</strong> that this directive cannot be used when an external - dispatcher is in effect. In such cases, the external dispatcher has - full control over backend selection. - <p><dt><strong>Syntax:</strong><dd> <code>maxconnections</code> <em>number</em> <code>;</code> - <p><dt><strong>Default:</strong><dd> 0; meaning no limit - </dl> -<p> -<a name="conf/weight"></a><a name="l48"></a> -<strong>4.4.6: weight - When a back end is more equal than others</strong> <a name="confweight - When a back end is more equal than others"></a> - <dl> - <p><dt><strong>Description:</strong><dd> To influence how backends are selected, a backend can specify its - 'weight' in the process. The higher the weight, the less likely a - back end will be chosen. The default is 1. -<p> -The weighing mechanism only applies to the dispatch modes - <code>random</code>, <code>byconnections</code>, <code>bysize</code> and <code>byduration</code>. - The weight is in fact a penalty factor. E.g., if backend A has - <code>weight 2</code> and backend B has <code>weight 1</code>, then backend B will - be selected all the time, until its usage parameter is twice as - large as the parameter of A. Think of it as a 'sluggishness' - statement. - <p><dt><strong>Syntax:</strong><dd> <code>weight</code> <em>number</em>; the higher the number, the more 'sluggish' - a back end is - <p><dt><strong>Default:</strong><dd> 1; all back ends have equal weight. - </dl> -<p> -<a name="conf/decay"></a><a name="l49"></a> -<strong>4.4.7: decay - Levelling out activity of a back end</strong> <a name="confdecay - Levelling out activity of a back end"></a> - <dl> - <p><dt><strong>Description:</strong><dd> To make sure that a 'spike' of activity doesn't - influence the perceived load of a back end forever, you may - specify a certain decay. E.g, the statement <code>decay 10</code> makes - sure that the load that crossroads computes for this back end (be - it in seconds or in bytes) is decreased by 10% each time that - <strong>an other</strong> back end is hit. Decays are not applied to the count - of concurrent connections. -<p> -This means that when a given back end is hit, then its usage data - of the transferred bytes and the connection duration are updated - using the actual number of bytes and actual duration. However, - when a different back end is hit, then the usage data are - decreased by the specified decay. - <p><dt><strong>Syntax:</strong><dd> <code>decay</code> <em>number</em>, where <em>number</em> is a percentage that - decreases the back end usage data when other back ends are - hit. - <p><dt><strong>Default:</strong><dd> 0, meaning that no decay is applied to usage statistics. - </dl> -<p> -<a name="conf/state"></a><a name="l50"></a> -<strong>4.4.8: state - Setting an initial back end state</strong> <a name="confstate - Setting an initial back end state"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Using the <code>state</code> directive a back end can be 'primed' with an - initial state. The keyword <code>state</code> can be followed by - <code>available</code>, <code>unavailable</code> or <code>down</code>. Back ends marked - <code>unavailable</code> are excluded as candidates, but are checked when - a <code>revivinginterval</code> or a <code>checkinterval</code> is used. Back ends - marked <code>down</code> are excluded and never re-checked. - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>state</code> <em>specifier</em> <code>;</code> - <li> where <em>specifier</em> is one of <code>available</code>, - <code>unavailable</code> or <code>down</code></ul> - <p><dt><strong>Default:</strong><dd> <code>available</code>, meaning that a back end is a candidate for - initial dispatching. - </dl> -<p> -<a name="conf/onhooks"></a><a name="l51"></a> -<strong>4.4.9: onstart, onend, onfail - Action Hooks</strong> <a name="confonstart, onend, onfail - Action Hooks"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The three directives <code>onstart</code>, <code>onend</code> and <code>onfail</code> can be - specified to start system commands (external programs) when a - connection to a back end starts, fails or ends: - <ul> - <li> <code>onstart</code> commands will be run when Crossroads - successfully connects to a back end, and starts servicing; - <li> <code>onend</code> commands will be run when a (previously - established) connection stops; - <li> <code>onfail</code> commands will be run when Crossroads tries to - contact a back end to serve a client, but the back end can't - be reached.</ul> -<p> -The format is always <code>on</code><em>type</em> <em>command</em>. The <em>command</em> - is an external program, optionally followed by arguments. The - command is expanded according to the following table: -<p> -<ul> -<p> -<li> <code>%a</code> is the availability of the current back end, when - a current back end is established. <code>%1a</code> is the - availability of the first back end (0 when unavailable, 1 if - available); <code>%2a</code> is the availability of the second back - end, and so on. -<p> -<li> <code>%b</code> is the name of the current back end, when one is - established. <code>%1b</code> is the name of the first back end, - <code>%2b</code> of the second back end, and so on. -<p> -<li> <code>%e</code> is the count of seconds since start of epoch - (January 1st 1970 GMT). <code>%60e</code> is the count since start of - epoch plus 60, so this is a 1 minute offset into the future. -<p> -<li> <code>%g</code> is a "GMT-string" representation of the current - time, in the format <em>monthname, day year hh:mm:ss</em>. This - format is used in e.g. cookie expiry. <code>%600g</code> is the same - representation but of a moment 600 seconds in the future (10 - minutes). -<p> -<li> <code>%h</code> is the host name of the current back end. <code>%1h</code> - is the host name of the first back end, <code>%2h</code> of the second - back end, and so on. -<p> -<li> <code>%l</code> is the duration of the last successful connection - in seconds, concerning the current back end. <code>%1l</code> is the - duration of the connection to back end one, and so on. Note - that <code>%l</code> refers only to the last successful connections. - Unsuccessful connection attempts to back ends do not change - the value. -<p> -<li> <code>%p</code> is the TCP port of the current back end. <code>%1p</code> - is the TCP port of the first back end, <code>%2p</code> of the second - back end, and so on. -<p> -<li> <code>%P</code> is the process ID of the Crossroads handler that - handled this request. For debugging only. -<p> -<li> <code>%r</code> is the IP address of the connecting client. -<p> -<li> <code>%s</code> is the name of the current service that the client - connected to. -<p> -<li> <code>%t</code> is the current local time in ANSI format, in - <em>YYYY-MM-DD/hhh:mm:ss</em>. <code>%1800s</code> is an ANSI stamp of a - moment 1800 seconds in the future (half an hour). -<p> -<li> <code>%T</code> is the current GMT time in ANSI format. <code>%10T</code> - offsets this 10 seconds into the future. -<p> -<li> <code>%v</code> is the Crossroads version. -<p> -<li> <code>%w</code> is the weight factor of the current back - end. <code>%1w</code> is the weight factor of the first back end, etc.. -<p> -<li> Any other chararacter following a <code>%</code> sign is taken - literally; e.g. <code>%z</code> is just a z.</ul> -<p> -<p><dt><strong>Syntax:</strong><dd> The syntax of the commands is as follows. -<p> -<ul> - <li> <code>onstart</code> <em>commandline</em> - <li> <code>onend</code> <em>commandline</em> - <li> <code>onfail</code> <em>commandline</em> - <li> <code>onsuccess</code> <em>commandline</em></ul> - <p><dt><strong>Default:</strong><dd> There is no default. Normally no external programs are run upon - connection, success or failure of a back end. - </dl> -<p> -<a name="conf/trafficlog"></a><a name="l52"></a> -<strong>4.4.10: trafficlog and throughputlog - Debugging and Performance Aids</strong> <a name="conftrafficlog and throughputlog - Debugging and Performance Aids"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Two directives are available - to log network traffic to files. They are <code>trafficlog</code> and - <code>throughputlog</code>. -<p> -The <code>trafficlog</code> statement causes all traffic to be logged in - hexadecimal format. Each line is prefixed by <code>B</code> or <code>C</code>, - depending on whether the information was received from the back - end or from the client. -<p> -The <code>throughputlog</code> statement writes shorthand transmissions to - its log, accompanied by timings and the number of bytes each - transmission was able to read or write in one chunk. - <p><dt><strong>Syntax:</strong><dd> The syntax is: -<p> -<ul> - <li> <code>trafficlog</code> <em>filename</em> <code>;</code> - <li> <code>throughputlog</code> <em>filename</em> <code>;</code></ul> - <p><dt><strong>Default:</strong><dd> none - </dl> -<p> -<a name="conf/httptiminglog"></a><a name="l53"></a> -<strong>4.4.11: httptiminglog - Timing debugging in HTTP mode</strong> <a name="confhttptiminglog - Timing debugging in HTTP mode"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The directive <code>httptiminglog</code> turns on logging of HTTP mode - timing. There must be one argument, the filename where the timings - are written to. Turning on this option will slow down processing, - but may be helpful in finding out where delays are caused. - <p><dt><strong>Syntax:</strong><dd> The syntax is: -<p> -<ul> - <li> <code>httptiminglog</code> <em>filename</em> <code>;</code> - <li> Where <em>filename</em> is the output file, e.g. - <code>/tmp/timings.log</code></ul> - <p><dt><strong>Default:</strong><dd> none - </dl> -<p> -<a name="conf/stickycookie"></a><a name="l54"></a> -<strong>4.4.12: stickycookie - Back end selection with an HTTP cookie</strong> <a name="confstickycookie - Back end selection with an HTTP cookie"></a> - <dl> - <p><dt><strong>Description:</strong><dd> The directive <code>stickycookie</code> <em>value</em> - causes Crossroads to unpack clients' requests, to check for - <em>value</em> in the cookies. When found, the message is routed to the - back end having the appropriate <code>stickycookie</code> directive. -<p> -E.g., consider the following configuration: -<p> -<pre> -service ... { - ... - backend one { - ... - stickycookie "BalancerID=first"; - } - backend two { - ... - stickycookie "BalancerID=second"; - } -} -</pre> - -<p> -When clients' messages contain cookies named <code>BalancerID</code> with - the value <code>first</code>, then such messages are routed to backend - <code>one</code>. When the value is <code>second</code> then they are routed to the - backend <code>two</code>. -<p> -There are basically to provide such cookies to a browser. First, a - back end can insert such a cookie into the HTTP response. E.g., - the webserver of back end <code>one</code> might insert a cookie named - <code>BalancerID</code>, having value <code>first</code>. - Second, Crossroads can insert such cookies using a carefully - crafted directive <code>addclientheader</code>. - <p><dt><strong>Syntax:</strong><dd> <code>stickycookie</code> <em>cookievalue</em> - <p><dt><strong>Default:</strong><dd> There is no default. - </dl> -<p> -<a name="conf/addclientheader"></a><a name="l55"></a> -<strong>4.4.13: HTTP Header Modification Directives</strong> <a name="confHTTP Header Modification Directives"></a> - <dl> - <p><dt><strong>Description:</strong><dd> Crossroads understands the following - header modification directives: <code>addclientheader</code>, - <code>appendclientheader</code>, <code>setclientheader</code>, <code>addserverheader</code>, - <code>appendserverheader</code>, <code>setserverheader</code>. -<p> -The directive names always consist of - <em>Action</em><em>Destination</em><code>header</code>, where: -<p> -<ul> - <li> The action is <code>add</code>, <code>append</code> or <code>insert</code>. -<p> -<ul> - <li> Action <code>add</code> adds a header, even when headers with - the same name already are present in an HTTP - message. Adding headers is useful for e.g. <code>Set-Cookie</code> - headers; a message may contain several of such headers. -<p> -<li> Action <code>append</code> adds a header if it isn't present - yet in an HTTP message. If such a header is already - present, then the value is appended to the pre-existing - header. This is useful for e.g. <code>Via</code> headers. Imagine - an HTTP message with a header <code>Via: someproxy</code>. Then the - directive <code>appendclientheader "Via: crossroads"</code> will - rewrite the header to <code>Via: someproxy; crossroads</code>. -<p> -<li> Action <code>set</code> overwrites headers with the same - name; or adds a new header if no pre-existing is found. - This is useful for e.g. <code>Host</code> headers.</ul> -<p> -<li> The destination is one of <code>client</code> or <code>server</code>. When - the destination is <code>server</code>, then Crossroads will apply such - directives to HTTP messages that originate from the browser - and are being forwarded to back ends. When the destination is - <code>client</code>, then Crossroads will apply such directives to - backend responses that are shuttled to the browser.</ul> -<p> -The format of the directives is e.g. <code>addclientheader - "X-Processed-By: Crossroads"</code>. The directives expect one - argument; a string, consisting of a header name, a colon, and a - header value. As usual, the directive must end with a semicolon. -<p> -The header value may contain one of the following formatting - directives: -<p> -<ul> -<p> -<li> <code>%a</code> is the availability of the current back end, when - a current back end is established. <code>%1a</code> is the - availability of the first back end (0 when unavailable, 1 if - available); <code>%2a</code> is the availability of the second back - end, and so on. -<p> -<li> <code>%b</code> is the name of the current back end, when one is - established. <code>%1b</code> is the name of the first back end, - <code>%2b</code> of the second back end, and so on. -<p> -<li> <code>%e</code> is the count of seconds since start of epoch - (January 1st 1970 GMT). <code>%60e</code> is the count since start of - epoch plus 60, so this is a 1 minute offset into the future. -<p> -<li> <code>%g</code> is a "GMT-string" representation of the current - time, in the format <em>monthname, day year hh:mm:ss</em>. This - format is used in e.g. cookie expiry. <code>%600g</code> is the same - representation but of a moment 600 seconds in the future (10 - minutes). -<p> -<li> <code>%h</code> is the host name of the current back end. <code>%1h</code> - is the host name of the first back end, <code>%2h</code> of the second - back end, and so on. -<p> -<li> <code>%l</code> is the duration of the last successful connection - in seconds, concerning the current back end. <code>%1l</code> is the - duration of the connection to back end one, and so on. Note - that <code>%l</code> refers only to the last successful connections. - Unsuccessful connection attempts to back ends do not change - the value. -<p> -<li> <code>%p</code> is the TCP port of the current back end. <code>%1p</code> - is the TCP port of the first back end, <code>%2p</code> of the second - back end, and so on. -<p> -<li> <code>%P</code> is the process ID of the Crossroads handler that - handled this request. For debugging only. -<p> -<li> <code>%r</code> is the IP address of the connecting client. -<p> -<li> <code>%s</code> is the name of the current service that the client - connected to. -<p> -<li> <code>%t</code> is the current local time in ANSI format, in - <em>YYYY-MM-DD/hhh:mm:ss</em>. <code>%1800s</code> is an ANSI stamp of a - moment 1800 seconds in the future (half an hour). -<p> -<li> <code>%T</code> is the current GMT time in ANSI format. <code>%10T</code> - offsets this 10 seconds into the future. -<p> -<li> <code>%v</code> is the Crossroads version. -<p> -<li> <code>%w</code> is the weight factor of the current back - end. <code>%1w</code> is the weight factor of the first back end, etc.. -<p> -<li> Any other chararacter following a <code>%</code> sign is taken - literally; e.g. <code>%z</code> is just a z.</ul> -<p> -The following examples show common uses of header modifications. -<p> -<dl> - <p><dt><strong>Enforcing session stickiness:</strong><dd> By combining - <code>stickycookie</code> and <code>addclientheader</code>, HTTP session - stickiness is enforced. Consider the following configuration: -<p> -<pre> -service ... { - ... - backend one { - ... - addclientheader "Set-Cookie: BalancerID=first; path=/"; - stickycookie "BalancerID=first"; - } - backend two { - ... - addclientheader "Set-Cookie: BalancerID=second; path=/"; - stickycookie "BalancerID=second"; - } -} -</pre> - -<p> -The first request of an HTTP session is balanced to either - backend <code>one</code> or <code>two</code>. The server response is enriched - using <code>addclientheader</code> with an appropriate cookie. A - subsequent request from the same browser now has that cookie - in place; and is therefore sent to the same back end where the - its predecessors went. -<p> -The header which is sent to the client to inject a cookie, can - furthermore be expanded to specify a timeout: -<p> -<pre> -addclientheader "Set-Cookie: BalancerID=second; path=/; expires=%1800g"; -</pre> - -<p> -The format specifier <code>%1800g</code> outputs a GMT-string date 1800 - seconds in the future (half an hour from now). -<p> -<p><dt><strong>Hiding the server software version:</strong><dd> Many servers - (e.g. Apache) advertize their version, as in <code>Server: Apache - 1.27</code>. This potentially provides information to attackers. The - following configuration hides such information: -<p> -<pre> -service ... { - ... - backend one { - ... - setclientheader "Server: WWW-Server"; - } -} -</pre> - -<p> -<p><dt><strong>Informing the server of the clients' IP address:</strong><dd> Since - Crossroads sits 'in the middle' between a client and a back - end, the back end perceives Crossroads as its client. The - following sends the true clients' IP address to the server, in - a header <code>X-Real-IP</code>: -<p> -<pre> -service ... { - ... - backend one { - ... - setserverheader "X-Real-IP: %r"; - } -} -</pre> - -<p> -<p><dt><strong>Keep-Alive Downgrading:</strong><dd> The directives - <code>setclientheader</code> and <code>setserverheader</code> also play a key - role in downgrading Keep-Alive connections to - 'single-shot'. E.g., the following configuration makes sure - that no Keep-Alive connections occur. -<p> -<pre> -service ... { - ... - backend one { - ... - setserverheader "Connection: close"; - setclientheader "Connection: close"; - } -} -</pre> -</dl> - <p><dt><strong>Syntax:</strong><dd> <ul> - <li> <code>addclientheader</code> <em>Headername: headervalue</em> to add a - header in the traffic towards the client, even when another - header <em>Headername</em> exists; - <li> <code>appendclientheader</code> <em>Headername: headervalue</em> to - append <em>headervalue</em> to an existing header <em>Headername</em> - in the traffic towards the client, - or to add the whole header alltogether; - <li> <code>setclientheader</code> <em>Headername: headervalue</em> to - overwrite an existing header in the traffic towards the - client, or to add such a header; - <li> <code>addserverheader</code> <em>Headername: headervalue</em> to add a - header in the traffic towards the server, even when another - header <em>Headername</em> exists; - <li> <code>appendserverheader</code> <em>Headername: headervalue</em> to - append <em>headervalue</em> to an existing header <em>Headername</em> - in the traffic towards the server, - or to add the whole header alltogether; - <li> <code>setserverheader</code> <em>Headername: headervalue</em> to - overwrite an existing header in the traffic towards the - server, or to add such a header.</ul> - <p><dt><strong>Default:</strong><dd> There is no default. - </dl> -<p> -<a name="l56"></a> -<h2>5: Tips, Tricks and Random Remarks</h2> -<a name="tips"></a>The following sections elaborate on the directives as described in -section <a href="crossroads.html#config">4</a> to illustrate how crossroads works and to help you -achieve the "optimal" balancing configuration. -<p> -<a name="l57"></a> -<h3>5.1: Configuration examples</h3> -<a name="tips/examples"></a> -<a name="l58"></a> -<strong>5.1.1: A load balancer for three webserver back ends</strong> -<p> -The following configuration example binds crossroads to port 80 of the -current server, and distributes the load over three back ends. This -configuration shows most of the possible settings. -<p> -<pre> -service www { - /* We don't need session stickyness. */ - type any; - - /* Port on which we'll listen in this service: required. */ - port 8000; - - /* What IP address should this service listen? Default is 'any'. - * Alternatively you can state an explicit IP address, such as - * 127.0.0.1; that would bind the service only to 'localhost'. */ - bindto any; - - /* Verbose reporting or not. Default is off. */ - verbosity on; - - /* Dispatching mode, or: How to select a back end for an incoming - * request. Possible values: - * roundrobin: just the next back end in line - * random: like roundrobin, but at random to make things more - * confusing. Probably only good for testing. - * bysize: The backend that transferred the least nr of bytes - * is the next in line. As a modifier you can say e.g. - * bysize over 10, meaning that the 10 last connections will - * be used to compute the transfer size, instead of all - * transfers. - * byduration: The backend that was active for the shortest time - * is the next in line. As a modifier you can say e.g. - * byduration of 10 to compute over the last 10 connections. - * byconnections: The back end with the least active connections - * is the next ine line. - * byorder: The first available back end is always taken. - */ - dispatchmode byduration over 5; - - /* Interval at which we'll check whether a temporarily unavailable - * backend has woken up. - */ - revivinginterval 5; - - /* TCP backlog of connections. Default is 0 (no backlog, one - * connection may be active). - */ - backlog 5; - - /* For status reporting: a shared memory key. Default is the same - * as the port number, OR-ed by a magic number. - */ - shmkey 8000; - - /* This controls when crossroads should consider a connection as - * finished even when the TCP sockets weren't closed. This is to - * avoid hanging connections that don't do anything. NOTE THAT when - * crossroads cuts off a connection due to timeout exceed, this is - * not marked as a failure, but as a success. Default is 0: no timeout. - */ - connectiontimeout 300; - - /* The max number of allowed client connections. When present, connections - * won't be accepted if the max is about to be exceeded. When - * absent, all connections will be accepted, which might be misused - * for a DOS attack. - */ - maxconnections 300; - - /* Now let's define a couple of back ends. Number 1: */ - backend www_backend_1 { - /* The server and its port, the minimum configuration. */ - server httpserver1; - port 9010; - /* The 'decay' of usage data of this back end. Only relevant - * when the whole service has 'dispatchmode bysize' or - * 'byduration'. The number is a percentage by which the usage - * parameter is decreased upon each connection of an other back - * end. - */ - decay 10; - - /* To see what's happening in /var/log/messages: */ - verbosity on; - } - - /* The second one: */ - backend www_backend_2 { - /* Server and port */ - server httpserver2; - port 9011; - - /* Verbosity of reporting when this back end is active */ - verbosity on; - - /* Decay */ - decay 10; - - /* This back end is twice as weak as the first one */ - weight 2; - - /* Event triggers for system commands upon succesful activation - * and upon failure. - */ - onsuccess echo 'success on backend 2' | mail root; - onfailure echo 'failure on backend 2' | mail root; - } - - /* And yet another one.. this time we will dump the traffic - * to a trace file. Furthermore we don't want more than 10 concurrent - * connections here. Note that there's also a total maxconnections for the - * whole service. - */ - backend www_backend_3 { - server httpserver3; - verbosity on; - port 9000; - verbosity on; - decay 10; - trafficlog /tmp/backend.3.log; - maxconnections 10; - } -} -</pre> - -<p> -<a name="l59"></a> -<strong>5.1.2: An HTTP forwarder when travelling</strong> -<p> -As another example, here's my configuration <code>crossroads.conf</code> that I -use on my Unix laptop. The problem that I face is that I need many -HTTP proxy configurations (at home, at customers' sites and so on) but -I'm too lazy to reconfigure browsers all the time. -<p> -Here's how it used to be before crossroads: -<p> -<ul> - <li> At home, I would surf through a squid proxy on my local - machine. The browser proxy setting is then - <code>http://localhost:3128</code>. -<p> -<li> Sometimes I start up an SSH tunnel to our offices. The - tunnel has a local port 3129, and connects to a squid proxy on - our e-tunity server. Hence, the browser proxy is then - <code>http://localhost:3129</code>. -<p> -<li> At a customer's location I need the proxy - <code>http://10.120.34.113:8080</code>, because they have configured it - so. -<p> -<li> And in yet other instances, I use a HTTP diagnostic tool - <a href="http://www.xk72.com/charles">Charles</a> - that sits between browser and website and shows me - what's happening. I run charles on my own machine and it - listens to port 8888, behaving like a proxy. The browser - configuration for the proxy is then - <code>http://localhost:8888</code>.</ul> -<p> -Here's how it works with a crossroads configuration: -<p> -<ul> - <li> I have configured my browsers to use - <code>http://localhost:8080</code> as the proxy. For all situations. -<p> -<li> I use the following crossroads configuration, and let - crossroads figure out which proxy backend works, and which - doesn't. Note two particularities: -<p> -<ul> - <li> The statement <code>dispatchmode byorder</code>. This - makes sure that once crossroads determines which - backend works, it will stick to it. This usage of - crossroads doesn't need to balance over more than one - back end. -<p> -<li> The statement <code>bindto 127.0.0.1</code> makes sure - that requests from other interfaces than loopback - won't get serviced.</ul> -<p> -<pre> -service HttpProxy { - port 8080; - bindto 127.0.0.1; - verbosity on; - dispatchmode byorder; - revivinginterval 15; - - backend Charles { - server localhost:8888; - verbosity on; - } - - backend CustomerProxy { - server 10.120.34.113:8080; - verbosity on; - } - - backend SshTunnel { - server localhost:3129; - } - - backend LocalSquid { - server localhost:3128; - } -} -</pre> -</ul> -<p> -As a final note, the commandline argument <code>tell</code> can be used to -influence crossroad's own detection mechanism of back end availability -detection. E.g., if in the above example the back ends <code>SshTunnel</code> -and <code>LocalSquid</code> are both active, then <code>crossroads tell httpproxy -sshtunnel down</code> will 'take down' the back end <code>SshTunnel</code> -- and -will automatically cause crossroads to switch to <code>LocalSquid</code>. -<p> -<a name="l60"></a> -<strong>5.1.3: SSH login with enforced idle logout</strong> -<p> -The following example shows how crossroads 'throttles' SSH -logins. Connections are accepted on port -22 (the normal SSH port) and forwarded to the actual SSH daemon -which is running on port 2222. -<p> -Note the usage of the -<code>connectiontimeout</code> directive. This makes sure that users are logged -out after 10 minutes of inactivity. Note also the <code>maxconnections</code> -setting, this makes sure that no more than 10 concurrent logins occur. -<p> -<pre> -service Ssh { - port 22; - backlog 5; - maxconnections 10; - connectiontimeout 600; - backend TrueSshDaemon { - server localhost:2222; - } -} -</pre> - -<p> -<a name="l61"></a> -<h3>5.2: How back ends are selected in load balancing</h3> <a name="howselected"></a> -<a name="tips/howselected"></a>In order to tune your load balancing, you'll need to understand how -crossroads computes usage, how weighing works, and so on. In this -section we'll focus on the dispatching modes <code>bysize</code>, <code>byduration</code> -and <code>byconnections</code> only. The other dispatching types are -self-explanatory. -<p> -<a name="l62"></a> -<strong>5.2.1: Bysize, byduration or byconnections?</strong> -<p> -As stated before, crossroads doesn't know 'what a service does' and -how to judge whether a given back end is very busy or not. You -must therefore give the right hints: -<p> -<ul> - <li> In general, a service which is CPU bound, will be more - busy when it takes longer to process a request. The dispatch - mode <code>byduration</code> is appropriate here. -<p> -<li> In contrast, a service which is filesystem bound, will be - more busy when more data are transferred. The dispatch mode - <code>bysize</code> is apppropriate. -<p> -<li> The dispatch mode <code>byduration</code> can also be used when - network latency is an issue. E.g., if your balancer has back - ends that are geograpically distributed, then <code>byduration</code> - would be a good way to select best available back ends. -<p> -<li> Furthermore it is noteworthy that <code>dispatchmode - byduration</code> is not usable for interactive processes such as - SSH logins. Idle time of a - login adds to the duration, while causing (almost) no - load. Mode <code>byduration</code> should only be used for automated - processes that don't wait for user interaction (e.g., SOAP - calls and other HTTP requests). -<p> -<li> As a last remark, the dispatching mode <code>byconnections</code> can - be used if you don't have other clues for load - estimations. -<p> -E.g., consider a database connection. What's - heavier on the back end, time-consuming connections, or connections - where loads of bytes are transferred? Well, that depends. A - tough <code>select</code> query that joins multiple tables can be very - heavy on the back end, though the response set can be quite - small - and hence the number of - transferred bytes. That would suggest - dispatching by duration. However, <code>byduration</code> - balancing doesn't respresent the true world, when interactive - connections can occur where users have an idle TCP connection to - the database: - this consumes time, but no bytes (see the SSH login example - above). In this case, the dispatch mode <code>byconnections</code> may be - your best bet. -<p> -</ul> -<p> -<a name="l63"></a> -<strong>5.2.2: Averaging size and duration</strong> -<p> -The configuration statement <code>dispatchmode bysize</code> or <code>byduration</code> -allows an optional modifier <code>over</code> <em>number</em>, where the stated -number represents a connection count. When this modifier is present, then -crossroads will use a moving average over the last <em>n</em> connections to -compute duration and size figures. -<p> -In the real world you'll always want this modifier. E.g., consider two -back ends that are running for years now, and one of them is suddenly -overloaded and very busy (it experiences a 'spike' in activity). -When the <code>over</code> modifier is absent, then -the sudden load will hardly show up in the usage figures -- it will -flatten out due to the large usage figures already stored in the years -of service. -<p> -In contrast, when e.g. <code>over 3</code> is in effect, then a sudden load -does show up -- because it highly contributes to the average of three -connections. -<p> -<a name="l64"></a> -<strong>5.2.3: Specifying decays</strong> -<p> -Decays are also only relevant when crossroads computes the 'next best -back end' by size (bytes) or duration (seconds). E.g., imagine two -back ends A and B, both averaged over say 3 connections. -<p> -Now when back end A is suddenly hit by a spike, -its average would go up accordingly. But the back end would never -again be used, unless B also received a similar spike, because A's -'usage data' over its last three connections would forever be larger than -B's data. -<p> -For that reason, you should in real situations probably always -specify a decay, so that the backend selection algorithm recovers from -spikes. Note that the usage data of the back end where a decay is -specified, decay when <strong>other</strong> back ends are hit. The decay parameter -is like specifying how fast your body regenerates when someone else -does the work. -<p> -The below configuration illustrates this: -<p> -<pre> -/* Definition of the service */ -service soap { - /* Local TCP port */ - port 8080; - - /* We'll select back ends by the processing - * duration - */ - dispatchmode byduration over 3; - - /* First back end: */ - backend A { - /* Back end IP address and port */ - server 10.1.1.1:8080; - - /* When this back end is NOT hit because - * the other one was less busy, then the - * usage parameters decay 10% per connection - */ - decay 10; - } - - /* Second back end: */ - backend B { - server 10.1.1.2:8080; - decay 10; - } -} -</pre> - -<p> -<a name="l65"></a> -<strong>5.2.4: Adjusting the weights</strong> -<p> -The back end modifier <code>weight</code> is useful in situations where your -back ends differ in respect to performance. E.g,. your back ends may -be geographically distributed, and you know that a given back end is -difficult to reach and often experiences network lag. -<p> -Or you may have -one primary back end, a system with a fast CPU and enough memory, and a -small fall-back back end, with a slow CPU and short on memory. In that -case you know in advance that the second back end should be used only -rarely. Most requests should go to the big server, up to a certain load. -<p> -In such cases you will know in advance that the best performing back ends -should be selected the most often. Here's where the <code>weight</code> -statement comes in: you can simply increase the weight of the back -ends with the least performance, so that they are selected less -frequently. -<p> -E.g., consider the following configuration: -<p> -<pre> -service soap { - port 8080; - dispatchmode byduration over 3; - backend A { - server 10.1.1.1:8080; - decay 20; - } - backend B { - server 10.1.1.2:8080; - weight 2; - decay 10; - } - backend C { - server 10.1.1.3:8080; - weight 4; - decay 5; - } -} -</pre> - -<p> -This will cause crossroads to select back ends by the processing time, -averaging over the last three connections. However, backend B will kick -in only when its usage is half of the usage of A (back end B is -probably only half as fast as A). Backend C will kick in only when its -usage is a quarter of the usage of A, which is half of the usage of B -(back end C is probably very weak, and just a fall-back system incase -both A and B crash). Note also that A's usage data decay much faster -than B's and C's: we're assuming that this big server recovers quicker -than its smaller siblings. -<p> -<a name="l66"></a> -<h3>5.3: Periodic probes and wake up calls</h3> -<a name="tips/periodic"></a>Crossroads has two methods of periodic back end verifications: -<p> -<ul> -<p> -<li> The first method only checks unavailable back ends. It is - configured using the directive <code>revivinginterval</code>. - The way <code>revivinginterval</code> works is as follows: -<p> -<ol> - <li> Crossroads determines that a back end is - unavailable. This happens when a new network connection to - the back end fails, or when an existing connection - suddenly dies. -<p> -<li> The back end is the periodically checked to see - if it has woken up yet.</ol> -<p> -<li> The second method periodically checks all back ends, - available ones and unavailable ones. This is configured using - <code>checkinterval</code>. During each check, back ends can be marked - as available or as unavailable.</ul> -<p> -<a name="l67"></a> -<strong>5.3.1: Syntax</strong> -<p> -The syntax of both verifications is: -<p> -<center><em>method</em><code>interval</code> <em>seconds</em> <code>[</code> <code>externalhandler</code> -<em>program</em> <em>arguments</em> <code>]</code></center> -<p> -In this syntax, the method is <code>reviving</code> or <code>check</code>. The seconds -specifier defines the delay between consecutive checks. -<p> -When no specification <code>externalhandler</code> <em>program</em> <em>arguments</em> is -given, then Crossroads runs the verification by trying to connect to -the back end. A simple successful connection suffices to cause -Crossroads to consider the back end available. However, when the -<code>externalhandler</code> specifier is given, then Crossroads runs the -specified external program. This program must exit with status 0 to -inform Crossroads that the back end is available. All other exit -statuses will mark the back end as unavailable. -<p> -The <em>arguments</em> are expanded according to the following table: -<p> -<ul> -<p> -<li> <code>%a</code> is the availability of the current back end, when - a current back end is established. <code>%1a</code> is the - availability of the first back end (0 when unavailable, 1 if - available); <code>%2a</code> is the availability of the second back - end, and so on. -<p> -<li> <code>%b</code> is the name of the current back end, when one is - established. <code>%1b</code> is the name of the first back end, - <code>%2b</code> of the second back end, and so on. -<p> -<li> <code>%e</code> is the count of seconds since start of epoch - (January 1st 1970 GMT). <code>%60e</code> is the count since start of - epoch plus 60, so this is a 1 minute offset into the future. -<p> -<li> <code>%g</code> is a "GMT-string" representation of the current - time, in the format <em>monthname, day year hh:mm:ss</em>. This - format is used in e.g. cookie expiry. <code>%600g</code> is the same - representation but of a moment 600 seconds in the future (10 - minutes). -<p> -<li> <code>%h</code> is the host name of the current back end. <code>%1h</code> - is the host name of the first back end, <code>%2h</code> of the second - back end, and so on. -<p> -<li> <code>%l</code> is the duration of the last successful connection - in seconds, concerning the current back end. <code>%1l</code> is the - duration of the connection to back end one, and so on. Note - that <code>%l</code> refers only to the last successful connections. - Unsuccessful connection attempts to back ends do not change - the value. -<p> -<li> <code>%p</code> is the TCP port of the current back end. <code>%1p</code> - is the TCP port of the first back end, <code>%2p</code> of the second - back end, and so on. -<p> -<li> <code>%P</code> is the process ID of the Crossroads handler that - handled this request. For debugging only. -<p> -<li> <code>%r</code> is the IP address of the connecting client. -<p> -<li> <code>%s</code> is the name of the current service that the client - connected to. -<p> -<li> <code>%t</code> is the current local time in ANSI format, in - <em>YYYY-MM-DD/hhh:mm:ss</em>. <code>%1800s</code> is an ANSI stamp of a - moment 1800 seconds in the future (half an hour). -<p> -<li> <code>%T</code> is the current GMT time in ANSI format. <code>%10T</code> - offsets this 10 seconds into the future. -<p> -<li> <code>%v</code> is the Crossroads version. -<p> -<li> <code>%w</code> is the weight factor of the current back - end. <code>%1w</code> is the weight factor of the first back end, etc.. -<p> -<li> Any other chararacter following a <code>%</code> sign is taken - literally; e.g. <code>%z</code> is just a z.</ul> -<p> -<a name="l68"></a> -<strong>5.3.2: Security Considerations</strong> -<p> -When <code>externalhandler</code> is in effect, Crossroads spawns an external -process. For security reasons, you may want to run this process under -a restricted user account. -<p> -The directive <code>useraccount</code> can be used to accomplish this. -<p> -<a name="l69"></a> -<strong>5.3.3: An example</strong> -<p> -The following configuration balances SMTP requests to two back -ends. The connectivity is checked by retrieving output from each back -end. The back end is available when the standard greeting of a mail -server is seen; this greeting must contain the word SMTP. During each -check the probe is terminated by sending <code>quit</code>. -<p> -<pre> -/* Use either DEBUG on or off */ -#define DEBUG off - -/* SMTP balancer */ -service smtp { - /* Standard stuff */ - port 25; - verbosity DEBUG; - /* Check back ends every 30 seconds. User 'nobody' - * will run the external handler. */ - useraccount nobody; - checkinterval 30 - externalhandler "echo quit | netcat -w1 %h %p | grep SMTP"; - - /* Two back ends to handle mail traffic. */ - backend mailone { - server smtp1.local.network:25; - verbosity DEBUG; - } - backend second { - server smtp2.local.network:25; - verbosity DEBUG; - } -} -</pre> - -<p> -<a name="l70"></a> -<h3>5.4: Throttling the number of concurrent connections</h3> -<a name="tips/throttling"></a>If you suspect that your service may occasionally receive 'spikes' of -activity&nbsp;(which you should always assume), then it might be a -good idea to protect your service by specifying a maximum number of -concurrent connections. This protection can be specified on two levels: -<p> -<dl> - <p><dt><strong>On the service level</strong><dd> a statement like <code>maxconnections - 100;</code> states that the service as a whole will never - service more than 100 concurrent connections. This means that - all your back ends and the crossroads balancer itself - will be protected from being overloaded. - <p><dt><strong>On the back end level</strong><dd> a statement like <code>maxconnections 10;</code> - states that this particular back end will never have more - than 10 concurrent connections; regardless of the overall - setting on the service level. This means that this - particular back end will be protected from being - overloaded (regardless of what other back ends may - experience).</dl> -<p> -The <code>maxconnections</code> statement, combined with a back end selection -algorithm, allows very fine granularity. The <code>maxconnections</code> statement -on the back end level is like a hand brake: even when you specify a -back end algorithm that would protect a given back end from being used -too much, a situation may occur where that back end is about to be -hit. A <code>maxconnections</code> statement on the level of that back may then -protect it. -<p> -<a name="l71"></a> -<h3>5.5: TCP Session Stickiness</h3> -<a name="tips/tcpstickiness"></a>If you need to make sure that a client that once gets dispatched to a -given back end keeps re-visiting the back end, then Crossroads offers -the dispatch mode <code>byclientip</code>. This mode will only work when each -client is seen by Crossroads with its own specific IP address; ie., -this method won't work when clients reach Crossroads through a -masquerading firewall (in which case all clients would be seen as -having the firewall's IP address). -<p> -The <code>dispatchmode byclientip</code> works as follows: -<p> -<ul> - <li> The client's IP address is taken in its string - representation and 'hashed' into a number. -<p> -<li> The number is brought back to the number of available - back ends (using a modulo-operation). -<p> -<li> The result defines the back end of choice.</ul> -<p> -If the preferred back end is unavailable, then the action that -Crossroads takes is to dispatch as if <code>byconnections</code>: of the -available back ends, the one with the least connections is taken. -<p> -<a name="l72"></a> -<h3>5.6: HTTP Session Stickiness</h3> -<a name="tips/httpstickiness"></a>This section focuses on HTTP session stickiness. This term refers to -the ability of a balancer to route a conversation between browser and -a backend farm with webservers always to the same back end. In other -words: once a back end is selected by the balancer, it will remain the -back end of choice, even for subsequent connections. -<p> -<a name="l73"></a> -<strong>5.6.1: Don't use stickiness!</strong> -<p> -The rule of thumb as far as the balancer is concerned, is: <strong>Do not -use HTTP session stickiness unless you really have to.</strong> Enabling -session stickiness hampers failover, balancing and performance: -<p> -<ul> - <li> Failover is hampered because during the session, - the balancer has to assign new connections to the same back - end that was selected at the start of a session. If the back - end suddenly goes 'down', then the session will most likely - crash. (Actually, when a back end becomes unreachable in the - middle of a session, Crossroads will assign a new back end to - that session. This will most likely result in a malfunction - of the underlying application.) - <li> Balancing is hampered because at the start of the session, - the balancer has selected the next-best back end. But during - the session, that back end may well become overloaded. The - balancer however must continue to send the requests there. - <li> Performance is hampered because crossroads needs to 'unpack' - messages as they are passed to and fro. That's because - crossroads needs to check the HTTP headers in the messages - for persistence cookies.</ul> -<p> -There is a number of measures that you can take to avoid using session -stickiness. E.g., session data can be 'shared' between web back -ends. PHP offers functionality to store session data in a database, so -that all PHP applications have access to these data. Application -servers such as Websphere can be configured to replicate session data -between nodes. -<p> -<a name="l74"></a> -<strong>5.6.2: But if you must..</strong> -<p> -If you really need stickiness, think first whether you might use TCP -stickiness (using the client's IP address to dispatch). If you can, -then this is the preferred method, since Crossroads won't have to -unpack TCP streams. Below is a short configuration example: -<p> -<pre> -service www { - port 80; - type any; - revivinginterval 15; - dispatchmode byclientip; - - backend one { - server 10.1.1.100:80; - } - - backend two { - server 10.1.1.101:80; - } -} -</pre> - -<p> -However, if you <strong>must</strong> use HTTP-base session stickiness, then -proceed as follows: -<p> -<ul> - <li> At the level of a <code>service</code> description, set the type to - <code>http</code>. - <li> Furthermore, set the <code>headerinspection</code> to <code>shallow</code> - (unless of course you also want to modify other HTTP headers, - see section <a href="crossroads.html#tips/deeporshallow">5.8</a>). - <li> At the level of each back end description, configure the - <code>stickycookie</code> and a <code>addclientheader</code> directives.</ul> -<p> -Once crossroads sees that, it will examine each HTTP message that it -shuttles between client and back end: -<p> -<ul> - <li> If there is no persistence cookie in the HTTP headers of a - client's request, then the message must be the first one and - a new session should be established. - Crossroads selects an appropriate back - end, sends the message to that back end, catches the reply, - and inserts a <code>Set-Cookie</code> directive. - <li> If there is a persistence cookie in the HTTP headers of a - client's request, then the request is part of an already - established session. Crossroads analyzes the cookie and - forwards the request to the appropriate back end.</ul> -<p> -Below is a short example of a configuration. -<p> -<pre> -service www { - port 80; - type http; - headerinspection shallow; - revivinginterval 15; - dispatchmode byconnections; - - backend one { - server 10.1.1.100:80; - stickycookie XRID=100; - addclientheader "Set-Cookie: XRID=100; Path=/"; - /* or: XRID=100; Path=/; Expires=%600g - * This would make the session cookie expire in 600 sec (10 mins) - */ - } - - backend two { - server 10.1.1.101:80; - stickycookie XRID=101; - addclientheader "Set-Cookie: XRID=101; Path=/"; - } -} -</pre> - -<p> -Note how the cookie names and values in the directives -<code>stickycookie</code> and <code>addclientheader</code> match. That is obviously a -prerequisite for stickiness. -<p> -<a name="l75"></a> -<h3>5.7: Passing the client's IP address</h3> -<a name="tips/clientip"></a>Since Crossroads just shuttles bytes to and fro, meta-information of -network connections is lost. As far as the back ends are concerned, -their connections originate at the Crossroads junction. -For example, standard Apache access logs will show the IP address of -Crossroads. -<p> -In order to compensate for this, Crossroads can insert a special -header in HTTP connections, to inform the back end of the original -client's IP address. In order to enable this, the Crossroads -configuration must state the following: -<p> -<ul> - <li> The service type must be <code>http</code>, and not <code>any</code>; - <li> Make sure that the <code>headerinspection</code> is <code>deep</code> (or that - there is no <code>headerinspection</code> statement, since <code>deep</code> is - the default, see section <a href="crossroads.html#tips/deeporshallow">5.8</a>); - <li> In the back end definition, the following statement must - occur: <br> - <code>addserverheader "X-Real-IP: %r";</code> <br> - You are of course free to choose the header name; the here - used <code>X-Real-IP</code> is a common name for this purpose.</ul> -<p> -After this, HTTP traffic that arrives at the back ends has a new -header: <code>X-Real-IP</code>, holding the client's IP address. -<strong>Note that</strong> once the type is set to <code>http</code>, Crossroads' -performance will be hampered -- all passing messages will have to be -unpacked and analyzed. -<p> -<a name="l76"></a> -<strong>5.7.1: Sample Crossroads configuration</strong> -<p> -The below sample configuration shows two HTTP back ends that receive -the client's IP address: -<p> -<pre> - -service www { - port 80; - type http; - revivinginterval 5; - dispatchmode roundrobin; - - backend one { - server 10.1.1.100:80; - addserverheader "X-Real-IP: %r"; - } - - backend two { - server 10.1.1.200:80; - addserverheader "X-Real-IP: %r"; - } -} -</pre> - -<p> -<a name="l77"></a> -<strong>5.7.2: Sample Apache configuration</strong> -<p> -The method by which each back end analyzes the header <code>X-Real-IP</code> -will obviously be different per server implementations. However, a -common method with the Apache webserver is to log the client's IP -address into the access log. -<p> -Often this is accomplished using the log format <code>custom</code>, defined as -follows: -<p> -<pre> -LogFormat "%h %l %u %t %D \"%r\" %&gt;s %b" common -CustomLog logs/access_log common -</pre> - -<p> -The first line defines the format <code>common</code>, with the remote host -specified by <code>%h</code>. The second line sends access information to a log -file <code>logs/access_log</code>, using the previously defined format -<code>common</code>. -<p> -Furtunately, Apache's <code>LogFormat</code> allows one to log contents of -headers. By replacing the <code>%h</code> with <code>%{X-Real-IP}i</code>, the desired -information is sent to the log. Therefore, normally you can simply -redefine the <code>common</code> format to -<p> -<pre> -LogFormat "%{X-Real-IP}i %l %u %t %D \"%r\" %&gt;s %b" common -</pre> - -<p> -<a name="l78"></a> -<h3>5.8: Deep or shallow HTTP header inspection</h3> -<a name="tips/deeporshallow"></a>The service-level directive <code>headerinspection</code> defines which HTTP -headers Crossroads will analyze. Often, several HTTP requests and -responses will be served over one network -link. Browsers and servers will try to keep a TCP link open so that it -may be re-used; this is a measure to increase efficiency and to shorten load -times of pages. E.g., a typical HTML page -will require a style sheet and a few images - and these can be -retrieved over the same link that originally served the HTML page. -<p> -Re-using the TCP link occurs more often than not. It is the default in -HTTP/1.1 (unless <code>Connection: close</code> is specified as one of the HTTP -headers). The older HTTP protocol, HTTP/1.0, by default passes just -one request and response over a TCP link, after which the link is -closed (unless <code>Connection: keep-alive</code> is specified as one of the -HTTP headers). -<p> -If you define your service as <code>type http</code>, then by default -Crossroads will inspect all HTTP headers that it sees: not only the -first browser request and server answer, but also subsequent requests -and answers that travel over the same link. This is called 'deep -inspection mode', in which Crossroads applies -directives such as <code>addclientheader</code> to all header -blocks. Inspecting the full TCP stream to catch header blocks can be -resource consuming. You can optionally make Crossroads's resource -consumption 'lighter' by instructing it to inspect only the first -HTTP header block, and to simply pass-through over all subsequent information -(which might well include next header blocks of a re-used TCP -connection) This is done using the directive <code>headerinspection</code>: -<p> -<pre> -service web { - type http; - headerinspection shallow; - backend a { .... } - backend b { .... } -} -</pre> - -<p> -The situations where <code>shallow</code> mode can be used, depends on what you -need to do: -<p> -<ul> - <li> <code>shallow</code> mode can be used when the inspection and - modification of the first HTTP header block suffices. For example, - HTTP session stickiness is a good example: -<p> -<pre> -service web { - type http; - headerinspection shallow; - backend a { - server 10.1.1.1:80; - stickycookie "BalancerID=1"; - addclientheader "Set-Cookie: BalancerID=1"; - } - . - . other back ends are defined here - . -} -</pre> - -<p> -In this case, Crossroads will inspect only the first header block that - the client sends for the presence of a cookie <code>BalancerID</code>. If the - cookie has value 1, then the request will be routed to back end - <code>a</code>. Similarly, Crossroads will inspect only the first header block - that the server sends, and will inject a <code>Set-Cookie</code> - instruction. -<p> -In this example, deep mode is not necessary because Crossroads - will use the first header that the client sends to route the - information to a given back end. - If more than one HTTP transactions follow over - the same TCP link, then by definition the link will go to the - same back end - even without inspecting all HTTP headers that - follow the first ones. -<p> -<li> <code>deep</code> mode is necessary in situations where all header - blocks must be inspected and modified. E.g., if you want to hide your - HTTP server identification, you might use: -<p> -<pre> -service web { - type http; - headerinspection deep; - backend a { - server 10.1.1.1:80; - setclientheader "Server: MyWebServer"; - } - . - . other back ends are defined here - . -} -</pre> - -<p> -Here, all HTTP header blocks that come from the server will be parsed, - and <code>Server</code> headers will be overwritten. - In a similar vein, if you want to pass the client's IP address, you - will also need deep mode. -<p> -In these examples, <code>shallow</code> mode is not usable, because the - outbound header modifications should apply to all headers of a - given series. Imagine that one would use <code>shallow</code> mode - here: then, in a series of 5 HTTP transactions that pass over - the same TCP link, only the first transaction would hide the - HTTP server signature. All subsequent transactions would still - show the HTTP server signature to the world.</ul> -<p> -<a name="l79"></a> -<h3>5.9: Debugging network traffic</h3> -<a name="tips/debugging"></a> Incase the traffic between client and backend must be debugged, - the statement <code>trafficlog</code> <em>filename</em> can be issued. This - causes the traffic to be dumped in hexadecimal format to the - stated filename. -<p> -Traffic sent by the client is prefixed by a <strong>C</strong>, traffic sent by - the back end is prefixed by a <strong>B</strong>. Below is a sample traffic - dump of a browser trying to get a HTML page. The server replies - that the page was not modified. -<p> -<pre> -C 0000 47 45 54 20 68 74 74 70 3a 2f 2f 77 77 77 2e 63 GET http://www.c -C 0010 73 2e 68 65 6c 73 69 6e 6b 69 2e 66 69 2f 6c 69 s.helsinki.fi/li -C 0020 6e 75 78 2f 6c 69 6e 75 78 2d 6b 65 72 6e 65 6c nux/linux-kernel -C 0030 2f 32 30 30 31 2d 34 37 2f 30 34 31 37 2e 68 74 /2001-47/0417.ht -C 0040 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a 43 6f 6e ml HTTP/1.1..Con -C 0050 6e 65 63 74 69 6f 6e 3a 20 63 6c 6f 73 65 0d 0a nection: close.. -. -. etcetera -. -B 0000 48 54 54 50 2f 31 2e 30 20 33 30 34 20 4e 6f 74 HTTP/1.0 304 Not -B 0010 20 4d 6f 64 69 66 69 65 64 0d 0a 44 61 74 65 3a Modified..Date: -B 0020 20 54 75 65 2c 20 31 32 20 4a 75 6c 20 32 30 30 Tue, 12 Jul 200 -B 0030 35 20 30 39 3a 34 39 3a 34 37 20 47 4d 54 0d 0a 5 09:49:47 GMT.. -B 0040 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 Content-Type: te -B 0050 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset -. -. etcetera -. -</pre> - -<p> -Turning on traffic dumps will <em>significantly</em> slow down - crossroads. -<p> -Besides <code>trafficlog</code>, there is also a directive - <code>throughputlog</code>. This directive also takes one argument, a - filename. The file is appended, and the following information is - logged: -<p> -<ul> - <li> The process ID of the crossroads image that serves the - TCP connection; - <li> The time of the request, in seconds and microseconds - since start of the run; - <li> A <strong>C</strong> when the request originated at the client, or - <strong>B</strong> when the request originated at the back end; - <li> The first 100 bytes of the request.</ul> -<p> -As an example, consider the following (the lines are shortened for - brevity and prefixed by line numbers for clarity): -<p> -<pre> - -1 0027566 0.000000 C 462 GET http://public.e-tunity.com/ ... -2 0027566 0.052974 B 394 HTTP/1.1 200 OK..Via: 1.1 tinyp ... -3 0027566 0.053413 B 1073 &lt;html&gt;.. &lt;head&gt;.. &lt;titl ... -4 0027566 0.053589 B 1448 1&gt; &lt;/td&gt;.. &lt;/tr&gt;.. &lt;/table ... -5 0027566 0.065679 B 725 for more. info.... &lt;li&gt; To ... -</pre> - -<p> -This tells us that: -<p> -<ul> - <li> Line 1: PID 27566 served a request that originated at the - client (C). The corresponding timing is 0 seconds, which means - that this is the start of the run. The request was 462 bytes - long and started with <code>GET http://public...</code> -<p> -<li> Line 2: 0.052974 seconds later, the backend (B) replied - with <code>HTTP/1.1 200 OK</code>. -<p> -<li> Lines 3, 4 and 5: The backend (B) proceeded by sending - chunks.</ul> -<p> -The buffer sizes (462, 394 and so on) also tell us that the - network sends relatively small chunks. In this case we might save - some processing memory by setting the TCP buffer size to say 2K, - instead of the default 5K. In this situation, most of the default - 5K buffers will be unused. Setting the TCP buffer size to 2K can be done - using the flag <code>-B 2048</code> or by using the statement - <code>tcpbuffersize</code> <code>2048;</code> in an <code>options</code> block of the configuration. -<p> -It is also worth while remembering that the start time of a <strong>C</strong> - request is the time that crossroads sees the activity. Any latency - between the true client and crossroads is obviously not - included. This is illustrated by the below simple ASCII art: -<p> -<pre> - -client ----&gt;----&gt;----&gt;---&gt;*crossroads ====&gt;====&gt;====&gt; - \ - back end - / -client ----&lt;----&lt;----&lt;---&lt; crossroads ====&lt;====&lt;====&lt; - -</pre> - -<p> -This simple picture shows a typical request that originates - at a client, travels to crossroads, and is relayed via the back - end. The <strong>C</strong> entry in a throughput log is the time when - crossroads sees the request, indicated by an asterisk. The <strong>B</strong> - entries are the times that it takes the back end to answer, - indicated by <code>===</code> style lines. Therefore, the true roundtrip - time will be longer than the number of seconds that are logged in - the throughput log: the latency between client and crossroads - isn't included in that measurement. -<p> -Summarizing, the throughput times and buffer sizes of a - client-back end connection can be analyzed using the directive - <code>throughputlog</code>. In a real-world analysis, you'd probably want - to write up a script to analyze the output and to compute round - trip times. Such scripts are not (yet) included in Crossroads. -<p> -<a name="l80"></a> -<h3>5.10: IP filtering: Limiting Access by Client IP Address</h3> -<a name="tips/ipfiltering"></a><a name="l81"></a> -<strong>5.10.1: General Examples</strong> -<p> -The directives <code>allowfrom</code>, <code>denyfrom</code>, <code>allowfile</code> and -<code>denyfile</code> can be used to instruct Crossroads to specifically allow -access by using a "whitelist" of IP addresses, or to specifically deny -access by using a "blacklist". E.g., the following configuration -allows access to service <code>webproxy</code> only to <em>localhost</em>: -<p> -<pre> -service webproxy { - port 8000; - allowfrom 127.0.0.1; - backend one { - . - . Back end definitions occur here - . - } - . - . Other back ends or other service directives - . may occur here - . -} -</pre> - -<p> -In this example there is a "whitelist" having only one entry: IP -address 127.0.0.1, or <em>localhost</em>. (Incidentally, the same behaviour -could be accomplished by stating <em>bindto 127.0.0.1</em>, in which case -Crossroads would only listen to the local network device.) -<p> -In the same vein, the directive <code>allowfrom 127.0.0.1 192.168.1/24</code> -would allow access to <em>localhost</em> and to all IP addresses that start -with 192.168.1. The specifier <code>192.168.1/24</code> states that there are -three network bytes (192, 168 and 1), and 24 bits (or 3 bytes) are -relevant; so that the fourth network byte doesn't matter. -<p> -<a name="l82"></a> -<strong>5.10.2: Using External Files</strong> -<p> -The directives <code>allowfile</code> and <code>denyfile</code> allow you to specify IP -addresses in external files. The Crossroads configuration states -e.g. <code>allowfile /tmp/allow.txt</code>, and the IP addresses are then in -<code>/tmp/allow.txt</code>. The format of <code>/tmp/allow.txt</code> is as follows: -<p> -<ul> - <li> The specifications follow again <em>p.q.r.s/mask</em>, where - p, q, r and s are network bytes which can be left out on the - right hand side when the mask allows it; -<p> -<li> The specifications must be separated by white space - (spaces, tabs or newlines).</ul> -<p> -E.g., the following is a valid example of an external specification -file: -<p> -<pre> -127.0.0.1 -192.168.1/24 -10/8 -</pre> - -<p> -When external files are in effect, then the signal <code>SIGHUP</code> (1) -causes Crossroads to reload the external file. E.g., while Crossroads -is running, you may edit <code>/tmp/allow.txt</code>, and then issue <code>killall --1 crossroads</code>. The new contents of <code>/tmp/allow.txt</code> will be -reloaded. -<p> -<a name="l83"></a> -<strong>5.10.3: Mixing Directives</strong> -<p> -Crossroads allows to mix all directives in one service -description. However, some mixes are less meaningful than others. It's -up to you to take this into account. -<p> -The following rules apply: -<p> -<ul> - <li> Blacklisting and whitelisting can be used together. When - combined, the blacklist will always be interpreted - first. E.g., consider the following directives: -<p> -<pre> -allowfrom 192.168.1/24 -denyfrom 192.168.1.100 -</pre> - -<p> -Given the fact that the deny list is checked first, client - 192.168.1.100 won't be able to access Crossroads. Then the - allow list will be checked, stating that all clients whose IP - address starts with 192.168.1 may connect. The effect will be - that e.g., client 192.168.1.1 may connect, 192.168.1.2 may - connect too, 192.168.1.100 will be blocked, and 10.1.1.1 will - be blocked as well. -<p> -Now consider the following directives: -<p> -<pre> -allowfrom 192.168.1.100 127.0.0.1 -denyfrom 192.168.1/24 -</pre> - -<p> -This will first of all deny access to all IP addresses that - start with 192.168.1. So the rule that allows 192.168.1.100 - won't ever be effective. The net result will be that access - will be granted to 127.0.0.1. -<p> -<li> Blacklisting or whitelisting can be left out. A list is - considered empty when no appropriate directives occur in the - configuration file <code>crossroads.conf</code>, or when the directive - points to an empty or non-existent external file. -<p> -<li> Using <code>*from</code> and <code>*file</code> statements is allowed, but - doesn't make sense. E.g., the following configuration sample - is such a case: -<p> -<pre> -allowfrom 127.0.0.1 192.168.1/24 -allowfile /tmp/allow.txt -</pre> - -<p> -There is a technical reason for this. Once Crossroads - processes the <code>allowfile</code> directive, then the whole - whitelist is cleared (thereby removing the entries 127.0.0.1 - and 192.168.1/24), and new entries are reloaded from the - file. The net result is that the <code>allowfrom</code> specification - is overruled.</ul> -<p> -Crossroads only performs syntactic checking of the configuration. Some -of the above samples are syntactically correct, but make no semantic -sense: Crossroads doesn't warn for such situations. -<p> -<a name="l84"></a> -<h3>5.11: Using an external program to dispatch</h3> <a name="externalhandler"></a> -<a name="tips/externalhandler"></a>As mentioned before, Crossroads supports several built-in dispatch -modes. However, you are always free to hook-in your own dispatch mode -that determines the next back end using your own specific -algorithm. This section explains how to do it. -<p> -<a name="l85"></a> -<strong>5.11.1: Configuring the external handler</strong> -<p> -First, the <code>dispatchmode</code> statement needs to inform Crossroads that -an external program will do the job. The syntax is: <code>dispatchmode -externalhandler</code> <em>program arguments</em>. The <em>program</em> must point to -an executable program that will be started by Crossroads. The -specifier <em>arguments</em> can be anything you want; those will be the -arguments to your dispatch helper. You use the following special -format specifiers in the argument list: -<p> -<ul> -<p> -<li> <code>%a</code> is the availability of the current back end, when - a current back end is established. <code>%1a</code> is the - availability of the first back end (0 when unavailable, 1 if - available); <code>%2a</code> is the availability of the second back - end, and so on. -<p> -<li> <code>%b</code> is the name of the current back end, when one is - established. <code>%1b</code> is the name of the first back end, - <code>%2b</code> of the second back end, and so on. -<p> -<li> <code>%e</code> is the count of seconds since start of epoch - (January 1st 1970 GMT). <code>%60e</code> is the count since start of - epoch plus 60, so this is a 1 minute offset into the future. -<p> -<li> <code>%g</code> is a "GMT-string" representation of the current - time, in the format <em>monthname, day year hh:mm:ss</em>. This - format is used in e.g. cookie expiry. <code>%600g</code> is the same - representation but of a moment 600 seconds in the future (10 - minutes). -<p> -<li> <code>%h</code> is the host name of the current back end. <code>%1h</code> - is the host name of the first back end, <code>%2h</code> of the second - back end, and so on. -<p> -<li> <code>%l</code> is the duration of the last successful connection - in seconds, concerning the current back end. <code>%1l</code> is the - duration of the connection to back end one, and so on. Note - that <code>%l</code> refers only to the last successful connections. - Unsuccessful connection attempts to back ends do not change - the value. -<p> -<li> <code>%p</code> is the TCP port of the current back end. <code>%1p</code> - is the TCP port of the first back end, <code>%2p</code> of the second - back end, and so on. -<p> -<li> <code>%P</code> is the process ID of the Crossroads handler that - handled this request. For debugging only. -<p> -<li> <code>%r</code> is the IP address of the connecting client. -<p> -<li> <code>%s</code> is the name of the current service that the client - connected to. -<p> -<li> <code>%t</code> is the current local time in ANSI format, in - <em>YYYY-MM-DD/hhh:mm:ss</em>. <code>%1800s</code> is an ANSI stamp of a - moment 1800 seconds in the future (half an hour). -<p> -<li> <code>%T</code> is the current GMT time in ANSI format. <code>%10T</code> - offsets this 10 seconds into the future. -<p> -<li> <code>%v</code> is the Crossroads version. -<p> -<li> <code>%w</code> is the weight factor of the current back - end. <code>%1w</code> is the weight factor of the first back end, etc.. -<p> -<li> Any other chararacter following a <code>%</code> sign is taken - literally; e.g. <code>%z</code> is just a z.</ul> -<p> -Note that the format specifiers such as <code>%b</code> don't make sense in the -phase in which an external handler is called, since there is no -current back end yet (the job of the handler is to supply one, so at -the time of calling, <code>%b</code> is undefined). -<p> -<a name="l86"></a> -<strong>5.11.2: Writing the external handler</strong> -<p> -The external handler is activated using the arguments that are -specified in the configuration file. The external handler can do -whatever it wants, but ultimately, it must write a back end name on -its <em>stdout</em>. Crossroads reads this, and if the back end is -available, uses that back end for the connection. -<p> -<a name="l87"></a> -<strong>5.11.3: Examples of external handlers</strong> -<p> -This section shows some examples of Crossroads configurations -vs. external handlers. The sample handlers that are shown here, are -also included in the Crossroads distribution, under the directory -<code>etc/</code>. Also note that the examples shown here are just -quick-and-dirty Perl scripts, meant to illustrate only. Your -applications may need other external handlers, but you can use the -shown scripts as a starting point. -<p> -<p><strong>Round-robin dispatching</strong><br> -<p> -This example is trivial in the sense that round-robin dispatching is -already built into Crossroads, so -that using an external handler for this purpose only slows down -Crossroads. However, it's a good starting example. -<p> -The Crossroads configuration is shown below: -<p> -<pre> -service test { - port 8001; - verbosity on; - revivinginterval 5; - - dispatchmode externalhandler - /usr/local/src/crossroads/etc/dispatcher-roundrobin - %1b %1a %2b %2a; - - backend testone { - server localhost:3128; - verbosity on; - } - backend testtwo { - server locallhost:3128; - verbosity on; - } -} -</pre> - -<p> -The relevant <code>dispatchmode</code> statement invokes the external program -<code>dispatcher-roundrobin</code> with four arguments: the name of the first -back end (<code>testone</code>), its availability (0 or 1), the name of the -second back end (<code>testtwo</code>) and its availability (0 or 1). -<p> -The external handler, which is also included in the Crossroads -distribution, is shown below. It is a Perl script. -<p> -<pre> -#!/usr/bin/perl - -use strict; - -# Example of a round-robin external dispatcher. This is totally -# superfluous, Crossroads has this on-board; if you use the external -# program for determining round-robin dispatching, then you'll only -# slow things down. This script is just meant as an example. - -# Globals / configuration -# ----------------------- -my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress -my $statefile = '/tmp/rr.last'; # Where we keep the last used - -# Logging -# ------- -sub msg { - return if ($log eq '/dev/null' or $log eq ''); - open (my $of, "&gt;&gt;$log") or return; - print $of (scalar(localtime()), ' ', @_); -} - -# Read the last used back end -# --------------------------- -sub readlast() { - my $ret; - - if (open (my $if, $statefile)) { - $ret = &lt;$if&gt;; - chomp ($ret); - close ($if); - msg ("Last used back end: $ret\n"); - return ($ret); - } - msg ("No last-used back end (yet)\n"); - return (undef); -} - -# Write back the last used back end, reply to Crossroads and stop -# --------------------------------------------------------------- -sub reply ($) { - my $last = shift; - - if (open (my $of, "&gt;$statefile")) { - print $of ("$last\n"); - } - print ("$last\n"); - exit (0); -} - -# Main starts here -# ---------------- - -# Collect the cmdline arguments. We expect pairs of backend-name / -# backend-availablility, and we'll store only the available ones. -msg ("Dispatch request received\n"); -my @backend; -for (my $i = 0; $i &lt;= $#ARGV; $i += 2) { - push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]); -} -msg ("Available back ends: @backend\n"); - -# Let's see what the last one is. If none found, then we return the -# first available back end. Otherwise we need to go thru the list of -# back ends, and return the next one in line. -my $last = readlast(); -if ($last eq '') { - msg ("Returning first available back end $backend[0]\n"); - reply ($backend[0]); -} - -# There **was** a last back end. Try to match it in the list, -# then return the next-in-line. -for (my $i = 0; $i &lt; $#backend; $i++) { - if ($last eq $backend[$i]) { - msg ("Returning next back end ", $backend[$i + 1], "\n"); - reply ($backend[$i + 1]); - } -} - -# No luck.. run back to the first one. -msg ("Returning first back end $backend[0]\n"); -reply ($backend[0]); -</pre> - -<p> -The working of the script is basically as follows: -<p> -<ul> - <li> The argument list is scanned. Back ends that are - available are collected in an array <code>@backend</code>. -<p> -<li> The script queries a state file <code>/tmp/rr.last</code>. If a - back end name occurs there, then the next back end is looked - up in <code>@backend</code> and returned to Crossroads. If no last back - is unknown or can't be matched, then the first available back - end (first element of <code>@backend</code>) is returned to Crossroads. -<p> -<li> Informing Crossroads is done via the subroutine - <code>reply()</code>. This code writes the selected back end to file - <code>/tmp/rr.last</code> (for future usage) and prints the back end - name to <em>stdout</em>. -<p> -<li> The script logs its actions to a file - <code>/tmp/exthandler.log</code>. This log file can be inspected for - the script's actions.</ul> -<p> -<p><strong>Dispatching by the client IP address</strong><br> -<p> -The following example shows a useful real-life situation that -illustrates how dispatching by client IP address works. <strong>Note that</strong> -as of Crossroads 1.31, <code>dispatchmode byclientip</code> is implemented -- -so that the below description is somewhat superfluous. The code -snippets however can help you in modelling your own specific dispatch -modes, aided by external helpers. (Incidentally, the <code>dispatchmode</code> -<code>byclientip</code> was modeled after the shown script. The functionality -proved so useful that it was embedded into Crossroads.) -<p> -Our hypothetical dispatching situation is as follows: -<p> -<ul> - <li> Crossroads is used as a single-address point to forward - Remote Desktop requests to a farm of Windows systems, where - users can work via remote access; -<p> -<li> However, users may stop their session, and when they - re-connect, they expect to be sent to the Windows system that - they had worked on previously; -<p> -<li> Client PC's have their distinct IP addresses, which - distinguish them. -<p> -<li> Of four windows systems, two are large servers, and two - are small ones. We'll want to assign large servers to clients - when we have a choice.</ul> -<p> -The requirements resemble session stickiness in HTTP, except that the remote -desktop protocol doesn't support stickiness. This situation is a -perfect example of how an external handler can help: -the potential delay due to the calling of an external -handler won't even be noticed. Remote Desktop is a network service where -the connection time isn't critical; users won't notice a slightly -larger connection time due to the fact that Crossroads invokes an -external program. We expect only a few (albeit lengthy) TCP connections. -<p> -The approach to the solution of this problem uses several external -program hooks: -<p> -<ul> - <li> An external dispatcher handler will be responsible for - suggesting a back end, given a client IP and given the current - timestamp. This handler will consult an internal - administration to see whether the stated IP address should - re-use a back end, or to determine which back end is free for usage. - <li> An external hook <code>onstart</code> will be responsible for - updating the internal administration; i.e., to flag a back end - as 'occupied'. - <li> The external hooks <code>onfailure</code> and <code>onend</code> will be - responsible for flagging a back end as 'free' again; i.e., for - erasing any previous information that states that the back end - was occupied.</ul> -<p> -The Crossroads configuration is shown below. Only four Windows back -ends are shown. Each back end is configured on a -given IP address, port 3389, and is limited to one concurrent connection -(otherwise a new user might 'steal' a running desktop session). -<p> -<pre> -service rdp { - port 3389; - revivinginterval 5; - - /* rdp-helper dispatch IP STAMP ... will suggest a back end to use, - * arguments are for all back ends: name, availability, weight */ - dispatchmode externalhandler - /usr/local/src/crossroads/etc/rdp-helper dispatch %r %e - %1b %1a %1w - %2b %2a %2w - %3b %3a %3w - %4b %4a %4w; - - backend win1 { - server 10.1.1.1:3389; - maxconnections 1; - /* rdp-helper start IP STAMP BACKEND will log the actual start - * of a connection; - * rdp-helper end IP will log the ending of a connection */ - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } - backend win2 { - server 10.1.1.2:3389; - maxconnections 1; - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } - backend win3 { - server 10.1.1.3:3389; - maxconnections 1; - weight 2; - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } - backend win4 { - server 10.1.1.4:3389; - maxconnections 1; - weight 3; - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } -} -</pre> - -<p> -Depending on the dispatcher stage, the exernal handler <code>rdp-helper</code> -is invoked in different ways: -<p> -<dl> - <p><dt><strong>During dispatching</strong><dd> the helper is called to suggest a back - end. The arguments are an action indicator <code>dispatch</code>, the - client's IP address, the timestamp, and four triplets that - represent back ends: per back end its name, its availability, - and its weight. The purpose of the helper is to tell - Crossroads which back end to use. -<p> -<p><dt><strong>During connection start</strong><dd> the helper will be invoked to - inform it of the start of a connection, given a client IP - address. -<p> -<p><dt><strong>When a connection terminates</strong><dd> the helper will be invoked - to inform it that the connection has ended.</dl> -<p> -Here's the external handler as Perl script. It uses the module -<code>GDBM_File</code> which most likely will not be part of standard Perl -distributions, but can be added using CPAN. (Alternatively, any other -database module can be used.) -<p> -<pre> -#!/usr/bin/perl - -use strict; -use GDBM_File; - -# Global variables and configuration -# ---------------------------------- -my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress -my $cdb = '/tmp/client.db'; # GDBM database of clients -my %db; # .. and memory representation of it -my $timeout = 24*60*60; # Timeout of a connection in secs - -# Logging -# ------- -sub msg { - return if ($log eq '/dev/null' or $log eq ''); - open (my $of, "&gt;&gt;$log") or return; - print $of (scalar(localtime()), ' ', @_); - close ($of); -} - -# Reply a back end to the caller and stop processing. -# --------------------------------------------------- -sub reply ($) { - my $b = shift; - msg ("Suggesting $b to Crossroads.\n"); - print ("$b\n"); - exit (0); -} - -# Is a value in an array -# ---------------------- -sub inarray { - my $val = shift; - for my $other (@_) { - return (1) if ($other eq $val); - } - return (0); -} - -# A connection is starting -# ------------------------ -sub start { - my ($ip, $stamp, $backend) = @_; - msg ("Logging START of connection for IP $ip on stamp $stamp, ", - "back end $backend\n"); - $db{$ip} = "$backend:$stamp"; -} - -# A connection has ended -# ---------------------- -sub end { - my $ip = shift; - msg ("Logging END of connection for IP $ip\n"); - $db{$ip} = undef; -} - -# Request to determine a back end -# ------------------------------- -sub dispatch { - my $ip = shift; - my $stamp = shift; - - msg ("Request to dispatch IP $ip on stamp $stamp\n"); - - # Read the next arguments. They are triplets of - # backend-name / availability / weight. Store if the back end is - # available. - my (@backends, @weights); - for (my $i = 0; $i &lt; $#_; $i += 3) { - if ($_[$i + 1] != 0) { - push (@backends, $_[$i]); - push (@weights, $_[$i + 2]); - msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n"); - } - } - - # See if this is a reconnect by a previously seen client IP. We'll - # treat this as a reconnect if the timeout wasn't yet exceeded. - if ($db{$ip} ne '') { - my ($last_backend, $last_stamp) = split (/:/, $db{$ip}); - msg ("IP $ip had last connected on $last_stamp to $last_backend\n"); - if ($stamp &lt; $last_stamp + $timeout) { - msg ("Timeout not yet exceeded, this may be a reconnect\n"); - # We'll allow a reconnect only if the stated last_backend is - # free (sanity check). - if (inarray ($last_backend, @backends)) { - msg ("Last back end $last_backend is available, ", - "letting through\n"); - reply ($last_backend); - } else { - msg ("Last used back end isn't free, suggesting a new one\n"); - } - } else { - msg ("Timeout exceeded, suggesting a new back end\n"); - } - } else { - msg ("Np preveious connection data, suggesting a new back end\n"); - } - - my $bestweight = -1; - my $bestbackend; - for (my $i = 0; $i &lt;= $#weights; $i++) { - if ($bestweight == -1 or $bestweight &gt; $weights[$i]) { - $bestweight = $weights[$i]; - $bestbackend = $backends[$i]; - } - } - - msg ("Best back end: $bestbackend (given weight $bestweight)\n"); - reply ($bestbackend); -} - -# Main starts here -# ---------------- -msg ("Start of run, attaching GDBM database '$cdb'\n"); -tie (%db, 'GDBM_File', $cdb, &amp;GDBM_WRCREAT, 0600); - -# The first argument must be an action 'dispatch', 'start' or 'end'. -# Depending on the action, we do stuff. -my $action = shift (@ARGV); -if ($action eq 'dispatch') { - dispatch (@ARGV); -} elsif ($action eq 'start') { - start (@ARGV); -} elsif ($action eq 'end') { - end (@ARGV); -} else { - print STDERR ("Usage: rdp-helper {dispatch|start|end} args\n"); - exit (1); -} -</pre> - -<p> -<a name="l88"></a> -<h3>5.12: Linux and ip_conntrack_max</h3> -<a name="tips/ipconntrackmax"></a>The kernel value of <code>ip_conntrack_max</code> is important for routers and -balancers under Linux. Basically it's the maximum number of tracked -connections. Felix A.W.O. describes the following situation: -<p> -<ul> - <li> Crossroads seems to mark back ends as unavailable, while - in fact nothing is afoot. - <li> This happens under heavy load. - <li> In <code>/var/log/messages</code> one may see the message: - <code>kernel: ip_conntrack: table full, dropping packet</code>.</ul> -<p> -The reason for Crossroad's behavior is that the kernel refuses to -build up a requested network connection. For Crossroads, this looks -just as a non-responding back end. Crossroads therefore marks the back -end as unavailable. -<p> -The solution is as follows: -<p> -<ul> - <li> Try <code>cat /proc/sys/net/ipv4/ip_conntrack_max</code> to see - the current value. - <li> Add something to the shown value (e.g., multiply by two), - and inform - the kernel of the new value, using <code>echo</code> <em>new-value</em> <code>&gt; - /proc/sys/net/ipv4/ip_conntrack_max</code> - <li> Make sure that the same step occurs somewhere in your - boot sequence as well, or that the new value is stated in a - configuration file such as <code>/etc/sysctl.conf</code>.</ul> -<p> -The value for <em>new-value</em> is something that you'll have to figure -out yourself. Note however that each count will cause the kernel to -reserve 350 bytes. So if you set <code>ip_conntrack_max</code> to 100.000, then -you're already taking 33.3Mb off the total available memory. -<p> -<a name="l89"></a> -<h3>5.13: Marking back ends as bad after more than one try</h3> -<a name="tips/retries"></a>Crossroads allows you to specify on a per-back end basis how many -retries are needed before a back end is considered unavailable. The -default is just one, meaning that after one failed connection, -Crossroads will mark a back end as unavailable (the back end may be -'revived', if you use <code>revivinginterval</code> or <code>checkinterval</code>). -<p> -Increasing the number is specified using the keyword <code>retries</code>. The -following configuration defines two back ends; the one on the IP -address 5.6.7.8 is somehow 'flaky', and Crossroads should try -connecting 3 times before crossing it off: -<p> -<pre> -service www { - port 80; - backend plugh { - server 1.2.3.4:80; - } - backend xyzzy { - server 5.6.7.8:80; - retries 3; - } -} -</pre> - -<p> -There may be several reasons for increasing the retries number: -<p> -<ul> - <li> The network connections to the server may spuriously - hamper, but such rare errors don't mean that the back end server is - down. -<p> -<li> The back end server is a 'slow starter' and can't handle spikes - very well. E.g., it may be a webserver which starts with only - one daemon; extra capacity is added as network connections - arrive, but adding capacity take a little time.</ul> -<p> -Whatever the reason, the keyword <code>retries</code> might be of help -here. This keyword should however be used carefully: Crossroads will -retry connecting with a small one-second delay in between. A high -<code>retries</code> number means also lots of one-second delays, in which time -a client is kept waiting. -<p> -<a name="l90"></a> -<h3>5.14: Using the Web Interface crossroads-mgr</h3> -<a name="tips/webinterface"></a>The mini-webserver <code>crossroads-mgr</code> provides an intuitive web -interface to the state of Crossroads. Once started, an administrator -may view the state of the balancer, and may influence the state of all -back ends. -<p> -As an example, a procedure is described here where a given back end is -gracefully taken out of service. Below is a sample Crossroads -configuration, which distributes requests over three back ends: -<p> -<pre> -service www { - port 80; - type http; - backend one { - server 10.1.1.1:80; - stickycookie "BalancerID=one"; - addclientheader "Set-Cookie: BalancerID=one"; - } - backend two { - server 10.1.1.1:80; - stickycookie "BalancerID=two"; - addclientheader "Set-Cookie: BalancerID=two"; - } - backend three { - server 10.1.1.1:80; - stickycookie "BalancerID=three"; - addclientheader "Set-Cookie: BalancerID=three"; - } -} -</pre> - -<p> -In order to take a given back end gracefully offline (say back end -<code>two</code>), either of the following procedures can be followed: -<p> -<ol> - <li> Back end <code>two</code> is taken down via the commandline, using - <code>crossroads tell www two down</code>. Next we wait for say one hour to - let existing sessions die out. After that, the back end can be turned - off. -<p> -<li> Back end <code>two</code> is taken down via the web interface, by - switching the status from <code>available</code> to <code>down</code>. Next we wait - for say one our, and turn off the back end.</ol> -<p> -The web interface doesn't offer extra functionality over the command -line tools; but all information is available at one glance, and -accessible without a shell access to the balancer. -<p> -<a name="l91"></a> -<strong>5.14.1: Starting crossroads-mgr</strong> -<p> -The basic command to start <code>crossroads-mgr</code> is -<p> -<center><code>crossroads-mgr start</code> <em>portnumber</em></center> -<p> -where the port number specifies to which TCP port the manager will -listen. There is however one important security aspect that needs -attention. Unless specified otherwise, anyone who points their browser -to the balancer and the given port, can view back end states and even -change them. This may be a too lax policy. -<p> -The web interface daemon has two methods to limit access: a listening -address can be specified via the command line, and basic -authentication (username / password protection) can be turned on. -<p> -Specifying a listening address is done using the flag <code>-a</code>. E.g., -after -<p> -<center><code>crossroads-mgr -a 127.0.0.1 start 10000</code></center> -<p> -the manager is started to listen to port 10000, but only on the -address 127.0.0.1 which is localhost. Requests from other addresses -will not be served. -<p> -Enforcing basic authentication is turned on using two command line -flags: -<p> -<ul> - <li> Flag <code>-b</code> turns on basic authentication to view the status. - <li> Flag <code>-B</code> turns on basic authentication to change back end - states.</ul> -<p> -Both flags must be followed by the required credentials. The most -simple way to state the credentials, is to postfix the flag with the -required user name, a colon, and the required password. E.g., -<code>-Buser:secret</code> says that anyone who tries to change back end -states, must supply the user name <code>user</code> and the password -<code>secret</code>. -<p> -In this example the full invocation would be e.g.: -<p> -<center><code>crossroads-mgr -Buser:secret start 1000</code></center> -<p> -The disadvantage is here that the credentials are visible for anyone -who has shell access to the balancer. A process overview, generated -with say <code>ps ax | grep crossroads-mgr</code>, would show the required -username and password. In order to avoid such a leak, -<code>crossroads-mgr</code> can be started as follows: -<p> -<center><code>crossroads-mgr -BPROMPT start 1000</code></center> -<p> -The 'magic' word <code>PROMPT</code> instructs <code>crossroads-mgr</code> to read the -username and password from <em>stdin</em>. The invocation can be further -scripted, using something like: -<p> -<center><code>echo user:secret | crossroads-mgr -BPROMPT start 1000</code></center> -<p> -The same trick can be used with the flag <code>-b</code>. When both flags are -present, and both 'magic' words <code>PROMPT</code> occur, then -<code>crossroads-mgr</code> will first ask for the credentials of the 'viewer', -and next for the credentials of the 'modifier' (even when flag <code>-B</code> -is stated first). So the following example starts <code>crossroads-mgr</code> -and requires the user name <code>viewer</code>, with password <code>showme</code> to view the -status, and it requires the user name <code>modifier</code>, with password -<code>changeit</code> to change states: -<p> -<pre> -echo -e 'viewer:showme\nmodifier:changeit' | - crossroads-mgr -bPROMPT -BPROMPT start 1000 -</pre> - -<p> -<a name="l92"></a> -<h3>5.15: Rendering Crossroads' status in a web page</h3> <a name="xmlstatus"></a> -<a name="tips/rendering"></a>The Crossroads flag <code>-x</code> causes the status output to be generated as -an XML document. This format can be nicely used to render the output -as an HTML page. -<p> -E.g., the following sample shows how Crossroads reports its status in -XML format (the actual XML structure that Crossroads outputs may be -different from this example, depending on the version): -<p> -<pre> -&lt;?xml version="1.0" encoding="UTF-8"?&gt; -&lt;status&gt; - &lt;service id="1" name="smtp"&gt; - &lt;connections&gt;1&lt;/connections&gt; - &lt;lastbackend&gt;0&lt;/lastbackend&gt; - &lt;backend id="0" name="first"&gt; - &lt;availability id="0"&gt;available&lt;/availability&gt; - &lt;clients&gt;0&lt;/clients&gt; - &lt;failures&gt;0&lt;/failures&gt; - &lt;connections&gt;2&lt;/connections&gt; - &lt;duration sec="0.559882"&gt;0.56s&lt;/duration&gt; - &lt;throughput bytes="3564"&gt;3.48Kb&lt;/throughput&gt; - &lt;/backend&gt; - &lt;backend id="1" name="second"&gt; - &lt;availability id="0"&gt;available&lt;/availability&gt; - &lt;clients&gt;0&lt;/clients&gt; - &lt;failures&gt;0&lt;/failures&gt; - &lt;connections&gt;2&lt;/connections&gt; - &lt;duration sec="23.7636"&gt;23.76s&lt;/duration&gt; - &lt;throughput bytes="9055"&gt;8.84Kb&lt;/throughput&gt; - &lt;/backend&gt; - &lt;/service&gt; -&lt;/status&gt; -</pre> - -<p> -A custom-made XSLT transformation stylesheet can be used to convert -this to any output format - and also to HTML. Such a style sheet is -included in the Crossroads distribution as -<code>etc/xml-status-to-html.xslt</code>. The sheet is lengthy, and is -therefore not included in this document. (You're welcome to modify it -to suit your specific needs. If you have cool tips, send them along to -me and I'll include them in the next distribution!) -<p> -If you want to show this output in a webpage which is generated on -demand by a webserver, then you might run into the following -problem. The status reporter (<code>crossroads -x status</code>) - must be able to access the shared memory segment of the running -Crossroads instance. By default, the shared memory is protected for -the user that started Crossroads, which will often not be the user who -runs the webserver. Under the auspices of the webserver user, -<code>crossroads status</code> might abort with a message: "ERROR: Cannot get -shared memory for service <em>name</em>, key <em>number</em>: Permission denied." -<p> -The solution for this problem is to make the shared memory access -somewhat more liberal. There are basically two options: -<p> -<ul> - <li> Start Crossroads with the flag <code>-m 0666</code>, which makes - the shared memory segment available to all. The octal number - 0666 works just like a file permission setting under - Unix. Now, any user can run <code>crossroads status</code>. -<p> -<li> If you want to make the access to Crossroads' shared - memory somewhat stricter, then make the user who starts - Crossroads and the user who runs the webserver member of a new - Unix group. Start Crossroads with the flag <code>-m 0664</code>. Now, - users belonging to the same group as the Crossroads starter - can run <code>crossroads status</code>.</ul> -<p> -<a name="l93"></a> -<h3>5.16: Crossroads and DNS caching</h3> -<a name="tips/dnscaching"></a> -The option <code>-d</code> allows you to control Crossroads' built in DNS -caching mechanism. Most often you will not need to use this: -DNS lookups in Crossroads occur only to find back ends; and back ends -are usually 'near' to the balancer. -<p> -You might want or need to use DNS caching if: -<p> -<ul> - <li> Back end servers are specified as hostnames, and not as - IP addresses; - <li> DNS resolving of those host names is perceptibly slow.</ul> -<p> -You can test DNS resolving from the command line using e.g. the -commands <code>nslookup</code> and <code>host</code>. -<p> -If DNS resolving is an issue, then you can specify the flag <code>-d</code> -<em>nsec</em> on the command line while invoking Crossroads. This instructs -Crossroads to use its DNS cache to store results. Each result is -stored for up to <em>nsec</em> seconds - after that, a new request for the -back end will lead to a new DNS lookup. -<p> -<a name="l94"></a> -<h3>5.17: Managing a Pool of Virtual Back Ends</h3> -<a name="tips/virtual"></a>Crossroads can be used as a central dispatcher for computing on -demand. E.g., imagine a situation where a web service exists on one or -two servers, with the option of adding new servers in case of a high -load. The "other" servers, besides the first two ones, are a virtual -pool - the pool size and the IP addresses may not be known in advance. -<p> -The following Crossroads' features are used in this example: -<p> -<ul> -<p> -<li> The back ends of Crossroads can have an initial state. For - the two real servers which are already present this would be - <code>up</code> (the default). For the non-present ones this would be - <code>down</code>: Crossroads excludes back ends that are down from the set - of candidate workers, and does not check their presence using - <code>revivalinterval</code> or <code>checkinterval</code>. -<p> -<li> The back ends must be configured to have a server address and - port, but this can be later reconfigured runtime. E.g., the - virtual pool servers would be initially configured on - <code>localhost:80</code>, and later the addresses would be filled in.</ul> -<p> -A sample Crossroads configuration is shown below. It defines 20 back -ends: two are always present, and 18 are in the virtual pool. -<p> -<pre> -service vpool { - port 80; - revivinginterval 3; - dispatchmode byconnections; - - /* The two back ends that are always present: */ - backend real_01 { server 10.1.1.1:80; } - backend real_02 { server 10.1.1.2:80; } - - /* The virtual pool: */ - backend virt_01 { server localhost:80; state down; } - backend virt_02 { server localhost:80; state down; } - backend virt_03 { server localhost:80; state down; } - backend virt_04 { server localhost:80; state down; } - backend virt_05 { server localhost:80; state down; } - backend virt_06 { server localhost:80; state down; } - backend virt_07 { server localhost:80; state down; } - backend virt_08 { server localhost:80; state down; } - backend virt_09 { server localhost:80; state down; } - backend virt_10 { server localhost:80; state down; } - backend virt_11 { server localhost:80; state down; } - backend virt_12 { server localhost:80; state down; } - backend virt_13 { server localhost:80; state down; } - backend virt_14 { server localhost:80; state down; } - backend virt_15 { server localhost:80; state down; } - backend virt_16 { server localhost:80; state down; } - backend virt_17 { server localhost:80; state down; } - backend virt_18 { server localhost:80; state down; } - -} -</pre> - -<p> -New back ends can be enabled using <code>crossroads tell</code>. E.g., to -enable back end <code>virt_01</code> on IP address 10.100.1.1:80, the commands -are: -<p> -<pre> -crossroads tell vpool virt_01 server 10.100.1.1:80 - crossroads tell vpool virt_01 up -</pre> - -<p> -To disable a back end, its state is reset to <code>down</code>. The IP address -doesn't even have to be erased: -<p> -<pre> -crossroads tell vpool virt_01 down -</pre> - -<p> -A script to monitor the total number of connections, and to add a back -end or to remove one, is left to the reader. The output of -<code>crossroads -x status</code> can be very helpful here: it reports on the -states of all back ends, their connections, the total number of -available or down back ends, etc.. -<p> -<a name="l95"></a> -<h2>6: Benchmarking</h2> -<a name="benchmarking"></a>This section shows how crossroads affects the -transmitting of HTML data when used as an intermediate 'station' -through which all data travels. -<p> -<a name="l96"></a> -<h3>6.1: Benchmark 1: Accessing a proxy via crossroads or directly</h3> -<p> -The benchmark was run on a system where the following was varied: -<p> -<ol> - <li> A website was recursively spidered through a local squid - proxy. The spidering was repeated 10 times, the total was recorded. -<p> -<li> Crossroads was placed in front of the squid proxy, and - the website was again recursively spidered. Again, the - spidering was repeated 10 times and the total was recorded.</ol> -<p> -The crossroads configuration of the second alternative is shown below: -<p> -<pre> -service HttpProxy { - port 8080; - verbosity on; - backend LocalSquid { - server 127.0.0.1; - port 3128; - verbosity on; - } -} -</pre> - -<p> -<a name="l97"></a> -<strong>6.1.1: Results</strong> -<p> -The results of this test are that crossroads causes a negligible -delay, if it is statistically relevant at all. Without crossroads, the -timing results are: -<p> -<pre> -real 0m8.146s -user 0m0.130s -sys 0m0.253s -</pre> - -<p> -When using crossroads as a middle station, the results are: -<p> -<pre> -real 0m9.481s -user 0m0.141s -sys 0m0.230s -</pre> - -<p> -<a name="l98"></a> -<strong>6.1.2: Discussion</strong> -<p> -The above shown results are quite favorable to crossroads. However, -one should know that situations will exist where crossroads leans -towards the 'worst case' scenario, causing up to 50% -delay. -<p> -E.g., imagine a test where a <code>wget</code> command retrieves a -HTML document from an Apache server on <code>localhost</code>. Now we have -(almost) no overhead due to network throttling, hostname lookups and -so on. When this test would be run either with or without crossroads -in between, then theoretically, crossroads would cause a much larger -delay, because it has to read from the server, and then write the same -information to <code>wget</code>. Each read/write occurs twice when crossroads -sits in between. -<p> -This worst case scenario will however (fortunately) occur only very -seldom in the real world: -<p> -<ul> - <li> Normally network issues, such as the above mentioned host - name lookups or throughput restrictions, will add - significantly to the duration of a request. The 'twice as - many' read/writes caused by crossroads are then relatively - irrelevant. -<p> -<li> Normally a significant amount of time will be spent in a - back end, due to processing (e.g., when calling a servlet on a - back end). Again, this processing time will weigh much heavier - than the multiple read/writes.</ul> -<p> -<a name="l99"></a> -<h3>6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</h3> -<p> -LVS is a kernel-based balancer that acts like a masquerading -firewall: TCP packets that arrive at the balancer are sent to one of -the configured back ends. LVS has the advantage over crossroads that -there is no stop-and-go in the transmission; in contrast, crossroads -needs to send data via an internal buffer. Crossroads has the -advantage that it offers instantaneous failover because it tries to -contact the back end for upon each new TCP connection; in contrast, -LVS isn't aware of downtime of back ends (unless one implements an -external heartbeat). Also, crossroads offers more complex balancing -than LVS. -<p> -<a name="l100"></a> -<strong>6.2.1: Environment</strong> -<p> -On the balancer, LVS was run on port 80, its forwarding set up for two -equally weighted back ends, using <code>ipvsadm</code>: -<p> -<pre> -ipvsadm -a -t 192.168.1.250:http -r 10.1.1.100:http -m -w 1 -ipvsadm -a -t 192.168.1.250:http -r 10.1.1.101:http -m -w 1 -</pre> - -<p> -Crossroads was run on port 81. The configuration file is shown below: -<p> -<pre> -service http { - port 81; - dispatchmode roundrobin; - revivinginterval 5; - backend one { - server 10.1.1.100; - port 80; - } - backend two { - server 10.1.1.101; - port 80; - } -} -</pre> - -<p> -<a name="l101"></a> -<strong>6.2.2: Tests and results</strong> -<p> -In the first test, ports 80 and 81 on the balancer were 'bombed' with -50 concurrent clients, each requesting a small page 50 times. The -following timings where measured: -<p> -<ul> - <li> How long it takes to establish a connection; - <li> How long it takes to retrieve the page.</ul> -<p> -The results of this test were: -<p> -<ul> - <li> On average, each client took 0.12 seconds to connect - to LVS, and each page was retrieved in 0.14 seconds; - <li> On average, each client took 0.11 seconds to connect to - crossroads, and each page was retrieved in 0.13 seconds.</ul> -<p> -In this setup there seems to be no difference between the performance -of LVS and crossroads! -<p> -In a second test, the size of the retrieved page was varied from 2.000 -to 2.000.000 bytes. This test was taken to see whether crossroads would -show performance degradation when transferring larger amounts of data. -<p> -For each page size, 30 concurrent clients were started, that retrieved -the page 50 times. Again, the connect times and processing times where -recorded. -<p> -The results of the total time (connect time + retrieval time) -are shown in the below table: -<p> -<table> - - <td colspan=3><hr></td> - - -<tr> - - <td> <strong>Bytes</strong></td> <td> <strong>LVS timing</strong></td> <td> <strong>Crossroads timing</strong></td> - -</tr> - - -<tr> - - <td> 2000</td> <td> 0.130741688</td> <td> 0.12739582</td> - -</tr> - - -<tr> - - <td> 20000</td> <td> 0.490916224</td> <td> 0.50376901</td> - -</tr> - - -<tr> - - <td> 200000</td> <td> 3.799440328</td> <td> 4.33125273</td> - -</tr> - - -<tr> - - <td> 2000000</td> <td> 45.25090855</td> <td> 45.9600728</td> - -</tr> - - <td colspan=3><hr></td> - -</table> -<p> -Again, the results show that crossroads performs just as effectively -as LVS, even with large data chunks! -<p> -<a name="l102"></a> -<h2>7: Compiling and Installing</h2> <a name="installation"></a> -<a name="compiling"></a><a name="l103"></a> -<h3>7.1: Prerequisites</h3> -<p> -The creation of crossroads requires: -<p> -<ul> - <li> Standard Unix tools, such as <code>sed</code>, <code>awk</code>; -<p> -<li> Perl (5.00 or better). The web interface - <code>crossroads-mgr</code> requires specific modules; -<p> -<li> A POSIX-compliant C compiler; -<p> -<li> Support for SYSV IPC, networking and so on. -</ul> -<p> -Basically a Linux or Apple MacOSX box will do nicely. To compile and install -crossroads, follow these steps. -<p> -<a name="l104"></a> -<h3>7.2: Compiling and installing</h3> -<p> -<ul> - <li> Obtain the source distribution. It can be found on - <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a>. The distribution comes as an - archive <code>crossroads-</code><em>type</em><code>.tar.gz</code>, where <em>type</em> is - <code>stable</code> or <code>devel</code>. -<p> -<li> Unpack the archive in a sources directory using <code>tar - xzf crossroads-</code><em>X.YY</em><code>.tar.gz</code>. The contents spill into a - subdirectory <code>crossroads-</code><em>X.YY/</em>. -<p> -<li> Change-dir into the directory. -<p> -<li> Next, edit <code>etc/Makefile.def</code> and verify that all - compilation settings are to your likings. The settings are - explained in the file. <strong>Note that</strong> the default distribution - of <code>Makefile.def</code> is suited for Linux or Apple MacOSX - systems. On other Unices, or on non-Unix systems, you must - particularly pay attention to <code>SET_PROC_TITLE_BY...</code>. When - in doubt, set the <code>SET_PROC_TITLE...</code> - settings to 0. Crossroads will work nevertheless, but it won't show - nice titles in <code>ps</code> listings. Also there's a macro - <code>EXTRA_LIBS</code> to add linkage flags (an example for a Solaris - build is included). -<p> -<li> Now crossroads is ready for compilation. Do a <code>make - local</code> followed by <code>make install</code>. The latter step may have - to be done by the user <code>root</code> if the <code>BINDIR</code> setting of - <code>etc/Makefile.def</code> points to a root-owned directory. -<p> -<li> The manual pages on the basic usage of Crossroads and on - the layout of the configuration file are installed as - well, if a suitable <code>man/</code> directory is present under the - installation prefix directory. If these pages are not - installed, then you can always copy <code>doc/crossroads.1</code> to a - suitable <code>man/man1/</code> directory, and - <code>doc/crossroads.conf.7</code> to a suitable <code>man/man7/</code> - directory. After this, <code>man crossroads</code> and <code>man - crossroads.conf</code> would show the appropriate manual pages. -<p> -<li> The full documentation doesn't install in this process. If you - want to install the documentation, then proceed as follows: -<p> -<ul> - <li> Optionally, <code>cp doc/crossroads.html</code> - <em>htmldirectory/</em>; where <em>htmldirectory</em> is the destination - directory for your HTML manuals; -<p> -<li> Optionally, <code>cp doc/crossroads.pdf</code> - <em>pdfdirectory/</em>; where <em>pdfdirectory</em> is the - destination directory for your PDF manuals.</ul> -<p> -<li> In order to use the web interface <code>crossroads-mgr</code>, - make sure that the following Perl modules are available: - <code>HTTP::Daemon</code>, <code>Getopt::Std</code>, - <code>MIME::Base64</code>. When in doubt, start the commandline tool - <code>cpan</code> and enter <code>install HTTP::Daemon</code>, which will - install the named module if necessary. You can repeat this for - all listed modules. -<p> -</ul> -<p> -<a name="l105"></a> -<h3>7.3: Configuring crossroads</h3><a name="configuring"></a> -<p> -Now that the binary is available on your system, you need to create a -configuration <code>crossroads.conf</code>. Use this manual to get started. You -can locate the configuration file in any of the following locations on -your file system: -<p> -<ul> - <li> Along an 'etc' directory that matches the path where - Crossroads is installed. E.g, when the Crossroads binary is - <code>/usr/local/bin/crossroads</code>, then the configuration can be - put in <code>/usr/local/etc/crossroads.conf</code>. Alternatively, if - you install Crossroads in <code>/opt/crossroads/bin/</code>, then you - can install the configuration into <code>/opt/crossroads/etc/</code>. -<p> -<li> As the file <code>/etc/crossroads.conf</code>. When Crossroads - fails to locate a configuration file near its path, it tries - this name.</ul> -<p> -Once you have the configuration ready, start crossroads with -<code>crossroads start</code>. Test the availability of your services and back -ends. Monitor how crossroads is doing with: -<p> -<ul> - <li> In one terminal, run the script: - <pre> -while [ 1 ] ; do - tput clear - crossroads status - sleep 3 -done -</pre> - -<p> -<strong>Note</strong> that depending on your system you might need - <code>sleep 3s</code>, i.e., with an <code>s</code> appended. -<p> -<li> In another terminal, run: - <pre> -while [ 1 ] ; do - tput clear - ps ax | grep crossroads | grep -v grep - sleep 3 -done -</pre> - -<p> -<strong>Note</strong> that depending on your system you might need - <code>ps -ef</code> instead of <code>ps ax</code>. -<p> -<li> In yet another terminal, run <code>tail -f - /var/log/messages</code> (supply the appropriate system log file if - <code>/var/log/messages</code> doesn't work for you).</ul> -<p> -Now thoroughly test the availability of your back ends through -crossroads. The status display will show an updated view of which back -ends are selected and how busy they are. The process list will show -which crossroads daemons are running. Finally, the tailing of -<code>/var/log/messages</code> shows what's going on -- especially if you have -<code>verbosity true</code> statements in the configuration. -<p> -<a name="l106"></a> -<h3>7.4: A boot script</h3> -<p> -Finally, you may want to create a boot-time startup script. The exact -procedure depends on the used Unix flavor. -<p> -<a name="l107"></a> -<strong>7.4.1: SysV Style Startup</strong> -<p> -On SysV style systems, there's a startup script directory -<code>/etc/init.d</code> where bootscripts for all utilities are located. -You may have the <code>chkconfig</code> utility to automate the task of -inserting scripts into the boot sequence, but -otherwise the steps will resemble the following. -<p> -<ul> - <li> Create a script <code>crossroads</code> in <code>/etc/init.d</code> similar to the - following: -<p> -<pre> -#!/bin/sh -/usr/local/bin/crossroads -v $1 -/usr/local/bin/crossroads-mgr $1 1000 -</pre> - -<p> -<code>init</code> will supply the right value for <code>$1</code>: "start" - during startup, and "stop" during shutdown. Note that - the stated directory <code>/usr/local/bin</code> must correspond with - the installation path. -<p> -In the first line, The flag <code>-v</code> causes the startup to be - more 'verbose'. However, once daemonized, the verbosity of - Crossroads is controlled by the appropriate statements in the - configuration. -<p> -The second line starts the web interface <code>crossorads-mgr</code> on - TCP port 1000. Access to the web interface is free for all; - add your own flags <code>-b</code> or <code>-B</code> to enforce basic - authentication (see the manual page of <code>crossroads-mgr</code> and - section <a href="crossroads.html#tips/webinterface">5.14</a>). -<p> -<li> Determine your 'runlevel': usually 3 when your system is - running in text-mode only, or 5 when you are using a graphical - interface. If your runlevel is 3, then: -<p> -<pre> -root&gt; cd /etc/rc.d/rc3.d -root&gt; ln -s /etc/init.d/crossroads S99crossroads -root&gt; ln -s /etc/init.d/crossroads K99crossroads -</pre> - -<p> -This creates startup (<code>S*</code>) and stop (<code>K*</code>) links that - will be run when the system enters or leaves a given runlevel. -<p> -If your runlevel is 5, then the right <code>cd</code> command is to - <code>/etc/rc.d/rc5.d</code>. Alternatively, you can create the - symlinks in both runlevel directories.</ul> -<p> -<a name="l108"></a> -<strong>7.4.2: BSD Style Startup</strong> -<p> -On BSD style systems, daemons are booted directly from <code>/etc/rc</code> and -related scripts. Incase you have a file <code>/etc/rc.local</code>, edit it, -and add the statement: -<p> -<pre> -/usr/local/bin/crossroads start -/usr/local/bin/crossroads-mgr start 1000 -</pre> - -<p> -If your BSD system lacks <code>/etc/rc.local</code>, then you may need to start -Crossroads from <code>/etc/rc</code>. Your mileage may vary. -<p> -<a name="l109"></a> -<strong>7.4.3: Linux-related</strong> -<p> -When using Crossroads on Linux, the following may be relevant: -<p> -<ul> -<p> -<li> Crossroads supports flag <code>-p</code>, which causes Crossroads not - to modify the process name. In the absence of the flag, Crossroads - alters its process name to something like <code>crossroads</code> <code>-</code> - <code>Service</code> <em>myservice:</em> <code>listening</code>. -<p> -Flag <code>-p</code> may be handy if you want to suppress this - behavior. The process name is then <code>crossroads-daemon</code>. Some - boot scripts use the <code>/proc</code> filesystem to find PID's by - filename; in those cases, flag <code>-p</code> can be used. -<p> -</ul> -<p> -</body> -</html> diff --git a/doc/crossroads.pdf b/doc/crossroads.pdf Binary files differ. diff --git a/doc/fig/cluster.graffle b/doc/fig/cluster.graffle @@ -0,0 +1,631 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>ActiveLayerIndex</key> + <integer>0</integer> + <key>AutoAdjust</key> + <true/> + <key>CanvasColor</key> + <dict> + <key>w</key> + <string>1</string> + </dict> + <key>CanvasOrigin</key> + <string>{0, 0}</string> + <key>CanvasScale</key> + <real>1</real> + <key>ColumnAlign</key> + <integer>1</integer> + <key>ColumnSpacing</key> + <real>36</real> + <key>CreationDate</key> + <string>2008-07-15 11:11:27 +0200</string> + <key>Creator</key> + <string>Karel Kubat</string> + <key>DisplayScale</key> + <string>1 cm = 1 cm</string> + <key>GraphDocumentVersion</key> + <integer>5</integer> + <key>GraphicsList</key> + <array> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>30</integer> + <key>Points</key> + <array> + <string>{288.594, 104.284}</string> + <string>{367.5, 102.219}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>HeadArrow</key> + <string>FilledArrow</string> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>27</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>27</integer> + </dict> + <key>ID</key> + <integer>29</integer> + <key>Points</key> + <array> + <string>{116.479, 138.195}</string> + <string>{154.614, 126.669}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>HeadArrow</key> + <string>FilledArrow</string> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>9</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>27</integer> + </dict> + <key>ID</key> + <integer>28</integer> + <key>Points</key> + <array> + <string>{116.486, 80.1664}</string> + <string>{153.816, 89.2202}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>HeadArrow</key> + <string>FilledArrow</string> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>8</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{147, 61}, {152, 90}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>27</integer> + <key>Shape</key> + <string>Cloud</string> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>7</integer> + </dict> + <key>ID</key> + <integer>26</integer> + <key>Points</key> + <array> + <string>{421.842, 114.609}</string> + <string>{454.158, 130.391}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>HeadArrow</key> + <string>FilledArrow</string> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>4</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>6</integer> + </dict> + <key>ID</key> + <integer>25</integer> + <key>Points</key> + <array> + <string>{422.5, 101.5}</string> + <string>{453.5, 101.5}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>HeadArrow</key> + <string>FilledArrow</string> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>4</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>5</integer> + </dict> + <key>ID</key> + <integer>24</integer> + <key>Points</key> + <array> + <string>{421.842, 88.3909}</string> + <string>{454.158, 72.6091}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>HeadArrow</key> + <string>FilledArrow</string> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>4</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{62, 131}, {54, 31}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>9</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 Client}</string> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{62, 58}, {54, 31}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>8</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 Client}</string> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{454, 128}, {54, 31}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>7</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 B3}</string> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{454, 86}, {54, 31}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>6</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 B2}</string> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{454, 44}, {54, 31}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>5</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 B1}</string> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{368, 86}, {54, 31}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>4</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 XR}</string> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{355, 35}, {166, 133}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>3</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf330 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 Cluster\ +\ +\ +\ +\ +\ +\ +\ +\ +\ +\ +\ +\ +}</string> + </dict> + </dict> + </array> + <key>GridInfo</key> + <dict/> + <key>GuidesLocked</key> + <string>NO</string> + <key>GuidesVisible</key> + <string>YES</string> + <key>HPages</key> + <integer>1</integer> + <key>ImageCounter</key> + <integer>1</integer> + <key>IsPalette</key> + <string>NO</string> + <key>KeepToScale</key> + <false/> + <key>Layers</key> + <array> + <dict> + <key>Lock</key> + <string>NO</string> + <key>Name</key> + <string>Layer 1</string> + <key>Print</key> + <string>YES</string> + <key>View</key> + <string>YES</string> + </dict> + </array> + <key>LayoutInfo</key> + <dict> + <key>LayoutTarget</key> + <integer>3</integer> + </dict> + <key>LinksVisible</key> + <string>NO</string> + <key>MagnetsVisible</key> + <string>NO</string> + <key>MasterSheet</key> + <string>Master 1</string> + <key>MasterSheets</key> + <array> + <dict> + <key>ActiveLayerIndex</key> + <integer>0</integer> + <key>AutoAdjust</key> + <true/> + <key>CanvasColor</key> + <dict> + <key>w</key> + <string>1</string> + </dict> + <key>CanvasOrigin</key> + <string>{0, 0}</string> + <key>CanvasScale</key> + <real>1</real> + <key>ColumnAlign</key> + <integer>1</integer> + <key>ColumnSpacing</key> + <real>36</real> + <key>DisplayScale</key> + <string>1 cm = 1 cm</string> + <key>GraphicsList</key> + <array/> + <key>GridInfo</key> + <dict/> + <key>HPages</key> + <integer>1</integer> + <key>IsPalette</key> + <string>NO</string> + <key>KeepToScale</key> + <false/> + <key>Layers</key> + <array> + <dict> + <key>Lock</key> + <string>NO</string> + <key>Name</key> + <string>Layer 1</string> + <key>Print</key> + <string>YES</string> + <key>View</key> + <string>YES</string> + </dict> + </array> + <key>LayoutInfo</key> + <dict> + <key>LayoutTarget</key> + <integer>3</integer> + </dict> + <key>Orientation</key> + <integer>2</integer> + <key>OutlineStyle</key> + <string>Basic</string> + <key>RowAlign</key> + <integer>1</integer> + <key>RowSpacing</key> + <real>36</real> + <key>SheetTitle</key> + <string>Master 1</string> + <key>UniqueID</key> + <integer>1</integer> + <key>VPages</key> + <integer>1</integer> + </dict> + </array> + <key>ModificationDate</key> + <string>2008-07-15 11:15:13 +0200</string> + <key>Modifier</key> + <string>Karel Kubat</string> + <key>NotesVisible</key> + <string>NO</string> + <key>Orientation</key> + <integer>2</integer> + <key>OriginVisible</key> + <string>NO</string> + <key>OutlineStyle</key> + <string>Basic</string> + <key>PageBreaks</key> + <string>YES</string> + <key>PrintInfo</key> + <dict> + <key>NSBottomMargin</key> + <array> + <string>coded</string> + <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string> + </array> + <key>NSLeftMargin</key> + <array> + <string>coded</string> + <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string> + </array> + <key>NSPaperName</key> + <array> + <string>string</string> + <string>A4</string> + </array> + <key>NSPaperSize</key> + <array> + <string>size</string> + <string>{595.2, 841.8}</string> + </array> + <key>NSRightMargin</key> + <array> + <string>coded</string> + <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string> + </array> + <key>NSTopMargin</key> + <array> + <string>coded</string> + <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string> + </array> + </dict> + <key>ReadOnly</key> + <string>NO</string> + <key>RowAlign</key> + <integer>1</integer> + <key>RowSpacing</key> + <real>36</real> + <key>SheetTitle</key> + <string>Canvas 1</string> + <key>SmartAlignmentGuidesActive</key> + <string>YES</string> + <key>SmartDistanceGuidesActive</key> + <string>YES</string> + <key>UniqueID</key> + <integer>1</integer> + <key>UseEntirePage</key> + <true/> + <key>VPages</key> + <integer>1</integer> + <key>WindowInfo</key> + <dict> + <key>CurrentSheet</key> + <string>0</string> + <key>DrawerOpen</key> + <false/> + <key>DrawerTab</key> + <string>Outline</string> + <key>DrawerWidth</key> + <real>209</real> + <key>FitInWindow</key> + <false/> + <key>Frame</key> + <string>{{537, 57}, {593, 939}}</string> + <key>ShowRuler</key> + <false/> + <key>ShowStatusBar</key> + <true/> + <key>VisibleRegion</key> + <string>{{0, 0}, {578, 825}}</string> + <key>Zoom</key> + <string>1</string> + </dict> +</dict> +</plist> diff --git a/doc/main/benchmarking.yo b/doc/main/benchmarking.yo @@ -1,170 +0,0 @@ -This section shows how crossroads affects the -transmitting of HTML data when used as an intermediate 'station' -through which all data travels. - - -subsect(Benchmark 1: Accessing a proxy via crossroads or directly) - -The benchmark was run on a system where the following was varied: - -enumeration( - eit() A website was recursively spidered through a local squid - proxy. The spidering was repeated 10 times, the total was recorded. - - eit() Crossroads was placed in front of the squid proxy, and - the website was again recursively spidered. Again, the - spidering was repeated 10 times and the total was recorded.) - -The crossroads configuration of the second alternative is shown below: - -verb(\ -service HttpProxy { - port 8080; - verbosity on; - backend LocalSquid { - server 127.0.0.1; - port 3128; - verbosity on; - } -}) - - -subsubsect(Results) - -The results of this test are that crossroads causes a negligible -delay, if it is statistically relevant at all. Without crossroads, the -timing results are: - -verb(\ -real 0m8.146s -user 0m0.130s -sys 0m0.253s) - -When using crossroads as a middle station, the results are: - -verb(\ -real 0m9.481s -user 0m0.141s -sys 0m0.230s) - - -subsubsect(Discussion) - -The above shown results are quite favorable to crossroads. However, -one should know that situations will exist where crossroads leans -towards the 'worst case' scenario, causing up to 50% -delay. - -E.g., imagine a test where a tt(wget) command retrieves a -HTML document from an Apache server on tt(localhost). Now we have -(almost) no overhead due to network throttling, hostname lookups and -so on. When this test would be run either with or without crossroads -in between, then theoretically, crossroads would cause a much larger -delay, because it has to read from the server, and then write the same -information to tt(wget). Each read/write occurs twice when crossroads -sits in between. - -This worst case scenario will however (fortunately) occur only very -seldom in the real world: - -itemization( - it() Normally network issues, such as the above mentioned host - name lookups or throughput restrictions, will add - significantly to the duration of a request. The 'twice as - many' read/writes caused by crossroads are then relatively - irrelevant. - - it() Normally a significant amount of time will be spent in a - back end, due to processing (e.g., when calling a servlet on a - back end). Again, this processing time will weigh much heavier - than the multiple read/writes.) - - -subsect(Benchmark 2: Crossroads versus Linux Virtual Server (LVS)) - -LVS is a kernel-based balancer that acts like a masquerading -firewall: TCP packets that arrive at the balancer are sent to one of -the configured back ends. LVS has the advantage over crossroads that -there is no stop-and-go in the transmission; in contrast, crossroads -needs to send data via an internal buffer. Crossroads has the -advantage that it offers instantaneous failover because it tries to -contact the back end for upon each new TCP connection; in contrast, -LVS isn't aware of downtime of back ends (unless one implements an -external heartbeat). Also, crossroads offers more complex balancing -than LVS. - -subsubsect(Environment) - -On the balancer, LVS was run on port 80, its forwarding set up for two -equally weighted back ends, using tt(ipvsadm): - -verb(\ -ipvsadm -a -t 192.168.1.250:http -r 10.1.1.100:http -m -w 1 -ipvsadm -a -t 192.168.1.250:http -r 10.1.1.101:http -m -w 1) - -Crossroads was run on port 81. The configuration file is shown below: - -verb(\ -service http { - port 81; - dispatchmode roundrobin; - revivinginterval 5; - backend one { - server 10.1.1.100; - port 80; - } - backend two { - server 10.1.1.101; - port 80; - } -}) - -subsubsect(Tests and results) - -In the first test, ports 80 and 81 on the balancer were 'bombed' with -50 concurrent clients, each requesting a small page 50 times. The -following timings where measured: - -itemization( - it() How long it takes to establish a connection; - it() How long it takes to retrieve the page.) - -The results of this test were: - -itemization( - it() On average, each client took 0.12 seconds to connect - to LVS, and each page was retrieved in 0.14 seconds; - it() On average, each client took 0.11 seconds to connect to - crossroads, and each page was retrieved in 0.13 seconds.) - -In this setup there seems to be no difference between the performance -of LVS and crossroads! - -In a second test, the size of the retrieved page was varied from 2.000 -to 2.000.000 bytes. This test was taken to see whether crossroads would -show performance degradation when transferring larger amounts of data. - -For each page size, 30 concurrent clients were started, that retrieved -the page 50 times. Again, the connect times and processing times where -recorded. - -The results of the total time (connect time + retrieval time) -are shown in the below table: - -table(3)(rrr)( - rowline() - row( - cell(bf(Bytes)) cell(bf(LVS timing)) cell(bf(Crossroads timing))) - row( - cell(2000) cell(0.130741688) cell(0.12739582)) - row( - cell(20000) cell(0.490916224) cell(0.50376901)) - row( - cell(200000) cell(3.799440328) cell(4.33125273)) - row( - cell(2000000) cell(45.25090855) cell(45.9600728)) - rowline()) - -Again, the results show that crossroads performs just as effectively -as LVS, even with large data chunks! - -\ No newline at end of file diff --git a/doc/main/compiling.yo b/doc/main/compiling.yo @@ -1,226 +0,0 @@ -subsect(Prerequisites) - -The creation of crossroads requires: - -itemization( - it() Standard Unix tools, such as tt(sed), tt(awk); - - it() Perl (5.00 or better). The web interface - tt(crossroads-mgr) requires specific modules; - - it() A POSIX-compliant C compiler; - - it() Support for SYSV IPC, networking and so on. -) - -Basically a Linux or Apple MacOSX box will do nicely. To compile and install -crossroads, follow these steps. - - -subsect(Compiling and installing) - -itemization( - it() Obtain the source distribution. It can be found on - lurl(http://crossroads.e-tunity.com). The distribution comes as an - archive tt(crossroads-)em(type)tt(.tar.gz), where em(type) is - tt(stable) or tt(devel). - - it() Unpack the archive in a sources directory using tt(tar - xzf crossroads-)em(X.YY)tt(.tar.gz). The contents spill into a - subdirectory tt(crossroads-)em(X.YY/). - - it() Change-dir into the directory. - - it() Next, edit tt(etc/Makefile.def) and verify that all - compilation settings are to your likings. The settings are - explained in the file. bf(Note that) the default distribution - of tt(Makefile.def) is suited for Linux or Apple MacOSX - systems. On other Unices, or on non-Unix systems, you must - particularly pay attention to tt(SET_PROC_TITLE_BY...). When - in doubt, set the tt(SET_PROC_TITLE...) - settings to 0. Crossroads will work nevertheless, but it won't show - nice titles in tt(ps) listings. Also there's a macro - tt(EXTRA_LIBS) to add linkage flags (an example for a Solaris - build is included). - - it() Now crossroads is ready for compilation. Do a tt(make - local) followed by tt(make install). The latter step may have - to be done by the user tt(root) if the tt(BINDIR) setting of - tt(etc/Makefile.def) points to a root-owned directory. - - it() The manual pages on the basic usage of Crossroads and on - the layout of the configuration file are installed as - well, if a suitable tt(man/) directory is present under the - installation prefix directory. If these pages are not - installed, then you can always copy tt(doc/crossroads.1) to a - suitable tt(man/man1/) directory, and - tt(doc/crossroads.conf.7) to a suitable tt(man/man7/) - directory. After this, tt(man crossroads) and tt(man - crossroads.conf) would show the appropriate manual pages. - - it() The full documentation doesn't install in this process. If you - want to install the documentation, then proceed as follows: - - itemization( - it() Optionally, tt(cp doc/crossroads.html) - em(htmldirectory/); where em(htmldirectory) is the destination - directory for your HTML manuals; - - it() Optionally, tt(cp doc/crossroads.pdf) - em(pdfdirectory/); where em(pdfdirectory) is the - destination directory for your PDF manuals.) - - it() In order to use the web interface tt(crossroads-mgr), - make sure that the following Perl modules are available: - tt(HTTP::Daemon), tt(Getopt::Std), - tt(MIME::Base64). When in doubt, start the commandline tool - tt(cpan) and enter tt(install HTTP::Daemon), which will - install the named module if necessary. You can repeat this for - all listed modules. - -) - - -subsect(Configuring crossroads)label(configuring) - -Now that the binary is available on your system, you need to create a -configuration tt(crossroads.conf). Use this manual to get started. You -can locate the configuration file in any of the following locations on -your file system: - -itemization( - it() Along an 'etc' directory that matches the path where - Crossroads is installed. E.g, when the Crossroads binary is - tt(/usr/local/bin/crossroads), then the configuration can be - put in tt(/usr/local/etc/crossroads.conf). Alternatively, if - you install Crossroads in tt(/opt/crossroads/bin/), then you - can install the configuration into tt(/opt/crossroads/etc/). - - it() As the file tt(/etc/crossroads.conf). When Crossroads - fails to locate a configuration file near its path, it tries - this name.) - -Once you have the configuration ready, start crossroads with -tt(crossroads start). Test the availability of your services and back -ends. Monitor how crossroads is doing with: - -itemization( - it() In one terminal, run the script: - verb(\ -while [ 1 ] ; do - tput clear - crossroads status - sleep 3 -done) - - bf(Note) that depending on your system you might need - tt(sleep 3s), i.e., with an tt(s) appended. - - it() In another terminal, run: - verb(\ -while [ 1 ] ; do - tput clear - ps ax | grep crossroads | grep -v grep - sleep 3 -done) - - bf(Note) that depending on your system you might need - tt(ps -ef) instead of tt(ps ax). - - it() In yet another terminal, run tt(tail -f - /var/log/messages) (supply the appropriate system log file if - tt(/var/log/messages) doesn't work for you).) - -Now thoroughly test the availability of your back ends through -crossroads. The status display will show an updated view of which back -ends are selected and how busy they are. The process list will show -which crossroads daemons are running. Finally, the tailing of -tt(/var/log/messages) shows what's going on -- especially if you have -tt(verbosity true) statements in the configuration. - - -subsect(A boot script) - -Finally, you may want to create a boot-time startup script. The exact -procedure depends on the used Unix flavor. - -subsubsect(SysV Style Startup) - -On SysV style systems, there's a startup script directory -tt(/etc/init.d) where bootscripts for all utilities are located. -You may have the tt(chkconfig) utility to automate the task of -inserting scripts into the boot sequence, but -otherwise the steps will resemble the following. - -itemization( - it() Create a script tt(crossroads) in tt(/etc/init.d) similar to the - following: - -verb(\ -#!/bin/sh -/usr/local/bin/crossroads -v $1 -/usr/local/bin/crossroads-mgr $1 1000) - - tt(init) will supply the right value for tt($1): "start" - during startup, and "stop" during shutdown. Note that - the stated directory tt(/usr/local/bin) must correspond with - the installation path. - - In the first line, The flag tt(-v) causes the startup to be - more 'verbose'. However, once daemonized, the verbosity of - Crossroads is controlled by the appropriate statements in the - configuration. - - The second line starts the web interface tt(crossorads-mgr) on - TCP port 1000. Access to the web interface is free for all; - add your own flags tt(-b) or tt(-B) to enforce basic - authentication (see the manual page of tt(crossroads-mgr) and - section ref(tips/webinterface)). - - it() Determine your 'runlevel': usually 3 when your system is - running in text-mode only, or 5 when you are using a graphical - interface. If your runlevel is 3, then: - -verb(\ -root> cd /etc/rc.d/rc3.d -root> ln -s /etc/init.d/crossroads S99crossroads -root> ln -s /etc/init.d/crossroads K99crossroads) - - This creates startup (tt(S*)) and stop (tt(K*)) links that - will be run when the system enters or leaves a given runlevel. - - If your runlevel is 5, then the right tt(cd) command is to - tt(/etc/rc.d/rc5.d). Alternatively, you can create the - symlinks in both runlevel directories.) - -subsubsect(BSD Style Startup) - -On BSD style systems, daemons are booted directly from tt(/etc/rc) and -related scripts. Incase you have a file tt(/etc/rc.local), edit it, -and add the statement: - -verb(\ -/usr/local/bin/crossroads start -/usr/local/bin/crossroads-mgr start 1000) - -If your BSD system lacks tt(/etc/rc.local), then you may need to start -Crossroads from tt(/etc/rc). Your mileage may vary. - - -subsubsect(Linux-related) - -When using Crossroads on Linux, the following may be relevant: - -itemization( - - it() Crossroads supports flag tt(-p), which causes Crossroads not - to modify the process name. In the absence of the flag, Crossroads - alters its process name to something like tt(crossroads) tt(-) - tt(Service) em(myservice:) tt(listening). - - Flag tt(-p) may be handy if you want to suppress this - behavior. The process name is then tt(crossroads-daemon). Some - boot scripts use the tt(/proc) filesystem to find PID's by - filename; in those cases, flag tt(-p) can be used. - -) diff --git a/doc/main/conf/addclientheader.yo b/doc/main/conf/addclientheader.yo @@ -1,151 +0,0 @@ -conf(HTTP Header Modification Directives) - (Crossroads understands the following - header modification directives: tt(addclientheader), - tt(appendclientheader), tt(setclientheader), tt(addserverheader), - tt(appendserverheader), tt(setserverheader). - - The directive names always consist of - em(Action)em(Destination)tt(header), where: - - itemization( - it() The action is tt(add), tt(append) or tt(insert). - - itemization( - it() Action tt(add) adds a header, even when headers with - the same name already are present in an HTTP - message. Adding headers is useful for e.g. tt(Set-Cookie) - headers; a message may contain several of such headers. - - it() Action tt(append) adds a header if it isn't present - yet in an HTTP message. If such a header is already - present, then the value is appended to the pre-existing - header. This is useful for e.g. tt(Via) headers. Imagine - an HTTP message with a header tt(Via: someproxy). Then the - directive tt(appendclientheader "Via: crossroads") will - rewrite the header to tt(Via: someproxy; crossroads). - - it() Action tt(set) overwrites headers with the same - name; or adds a new header if no pre-existing is found. - This is useful for e.g. tt(Host) headers.) - - it() The destination is one of tt(client) or tt(server). When - the destination is tt(server), then Crossroads will apply such - directives to HTTP messages that originate from the browser - and are being forwarded to back ends. When the destination is - tt(client), then Crossroads will apply such directives to - backend responses that are shuttled to the browser.) - - The format of the directives is e.g. tt(addclientheader - "X-Processed-By: Crossroads"). The directives expect one - argument; a string, consisting of a header name, a colon, and a - header value. As usual, the directive must end with a semicolon. - - The header value may contain one of the following formatting - directives: - - INCLUDEFILE(formattable.yo) - - The following examples show common uses of header modifications. - - description( - dit(Enforcing session stickiness:) By combining - tt(stickycookie) and tt(addclientheader), HTTP session - stickiness is enforced. Consider the following configuration: - - verb(\ -service ... { - ... - backend one { - ... - addclientheader "Set-Cookie: BalancerID=first; path=/"; - stickycookie "BalancerID=first"; - } - backend two { - ... - addclientheader "Set-Cookie: BalancerID=second; path=/"; - stickycookie "BalancerID=second"; - } -}) - - The first request of an HTTP session is balanced to either - backend tt(one) or tt(two). The server response is enriched - using tt(addclientheader) with an appropriate cookie. A - subsequent request from the same browser now has that cookie - in place; and is therefore sent to the same back end where the - its predecessors went. - - The header which is sent to the client to inject a cookie, can - furthermore be expanded to specify a timeout: - - verb(\ -addclientheader "Set-Cookie: BalancerID=second; path=/; expires=%1800g";) - - The format specifier tt(%1800g) outputs a GMT-string date 1800 - seconds in the future (half an hour from now). - - dit(Hiding the server software version:) Many servers - (e.g. Apache) advertize their version, as in tt(Server: Apache - 1.27). This potentially provides information to attackers. The - following configuration hides such information: - - verb(\ -service ... { - ... - backend one { - ... - setclientheader "Server: WWW-Server"; - } -}) - - dit(Informing the server of the clients' IP address:) Since - Crossroads sits 'in the middle' between a client and a back - end, the back end perceives Crossroads as its client. The - following sends the true clients' IP address to the server, in - a header tt(X-Real-IP): - - verb(\ -service ... { - ... - backend one { - ... - setserverheader "X-Real-IP: %r"; - } -}) - - dit(Keep-Alive Downgrading:) The directives - tt(setclientheader) and tt(setserverheader) also play a key - role in downgrading Keep-Alive connections to - 'single-shot'. E.g., the following configuration makes sure - that no Keep-Alive connections occur. - - verb(\ -service ... { - ... - backend one { - ... - setserverheader "Connection: close"; - setclientheader "Connection: close"; - } -}))) - (itemization( - it() tt(addclientheader) em(Headername: headervalue) to add a - header in the traffic towards the client, even when another - header em(Headername) exists; - it() tt(appendclientheader) em(Headername: headervalue) to - append em(headervalue) to an existing header em(Headername) - in the traffic towards the client, - or to add the whole header alltogether; - it() tt(setclientheader) em(Headername: headervalue) to - overwrite an existing header in the traffic towards the - client, or to add such a header; - it() tt(addserverheader) em(Headername: headervalue) to add a - header in the traffic towards the server, even when another - header em(Headername) exists; - it() tt(appendserverheader) em(Headername: headervalue) to - append em(headervalue) to an existing header em(Headername) - in the traffic towards the server, - or to add the whole header alltogether; - it() tt(setserverheader) em(Headername: headervalue) to - overwrite an existing header in the traffic towards the - server, or to add such a header.)) - (There is no default.) diff --git a/doc/main/conf/allow.yo b/doc/main/conf/allow.yo @@ -1,84 +0,0 @@ -conf(allow* and deny* - Allowing or denying connections) - (Crossroads can allow or deny - connections based on the IP address of a client. There are four - directives that are relevant: tt(allowfrom), tt(allowfile), - tt(denyfrom) and tt(denyfile). - - When using tt(allowfrom) and - tt(denyfrom) then the IP addresses to allow or deny connections are - stated in the configuration. When using tt(allowfile) and - tt(denyfile) the allow or deny connections are stated in a - separate file. - - When tt(allow*) directives are used, then all connections are denied - unless they match the stated allowed IP's. When tt(deny*) directives - are used, then all connections are allowed unless they match the - stated disallowed IP's. When denying and allowing is both used, - then the Crossroads checks the deny list first. - - The statements tt(allowfrom) and tt(denyfrom) are followed by a - list of filter specifications. The statements tt(allowfile) and - tt(denyfile) are followed by a filename; Crossroads will read - filter specifications from those external files. In both cases, - Crossroads obtains filter specifications and places them in its - lists of allowed or denied IP addresses. The difference between - specifying filters in the configuration file or in external - files, is that Crossroads will reload the external files when it - receives signal 1 (tt(SIGHUP)), as in tt(killall -1 crossroads). - - The filter specifications must obey the following syntax: they - are series of space-separated strings, consisting of up to - four numbers ranging from 0 to 255 and separated by a decimal - sign. Optionally a slash follows, with a bitmask which is also a - decimal number. For example, tt(127.0.0/24 10/8 192.168.2.1) is a - setting that consists of three specifiers. - - This is probably best explained by a few examples: - - itemization( - it() tt(allowfrom 10/8;) will allow connections from - tt(10.*.*.*) (a full Class A network). The mask tt(/8) means - that the first 8 bits of the number (ie., only the tt(10)) are - significant. On the last 3 positions of the IP address, all - numbers are allowed. Given this directive, client connections - from e.g. 10.1.1.1 and 10.2.3.4 will be allowed. - - it() tt(allowfrom 10.3/16;) will allow all IP addresses that - start with tt(10.3). The first 16 bits (i.e., the first 2 - numbers) are significant, the rest doesn't matter. - - it() tt(allowfrom 10.3.1/16;) is the same as above. The third - byte of the IP address is superfluous because the netmask - specifies that only the first 16 bits (2 numbers) are taken - into account. - - it() tt(allowfrom 10.3.1.15;) allows traffic from only the - specified IP address. There is no bitmask; all four numbers - are relevant. - - it() tt(allowfrom 10.3.1.15 10.2/16;) allows traffic from one - IP address tt(10.3.1.15) or from a complete Class B network - tt(10.2.*.*) - - it() tt(allowfile /tmp/myfile.txt;) in combination with a file - tt(/tmp/myfile.txt), with the contents tt(10.3.1.15 10.2/16), - is the same as above.) - - When using ttt(allowfrom) and tt(denyfrom), separate specifiers - can be stated in one statement (separated by whitespace), or the - whole statement can be repeated. E.g., the following two - alternatives have the same effect: - - verb(\ -/* Alternative 1: */ -allowfrom 10/8 192.168.1/24; - -/* Alternative 2: */ -allowfrom 10/8; -allowfrom 192.168.1.24;)) - (itemization( - it() tt(allowfrom) em(filter-specificication(s)) - it() tt(denyfrom) em(filter-specificication(s)) - it() tt(allowfile) em(filename) - it() tt(denyfile) em(filename))) - (In absence of these statements, all client IP's are accepted.) diff --git a/doc/main/conf/backendport.yo b/doc/main/conf/backendport.yo @@ -1,8 +0,0 @@ -conf(port - Specifying a back end port) - (Back ends must be known by their host name and a port. Both can - be simultaneously specified in a tt(server) statement. When the - tt(server) statement specifies a host name only, then a tt(port) - statement must be used to specify the port.) - (tt(port) em(number)) - (There is no default for the port. It must be specified either - using tt(server) or using tt(port).) diff --git a/doc/main/conf/backlog.yo b/doc/main/conf/backlog.yo @@ -1,12 +0,0 @@ -conf(backlog - The TCP Back Log size) - (The TCP back log size is a number that controls how many - 'waiting' network connections may be queued, before a client simply - cannot connect. The syntax is e.g. tt(backlog 5) to cause crossroads - to have 5 waiting connections for 1 active connection. - The backlog queue shouldn't be too - high, or clients will experience timeouts before they can actually - connect. The queue shouldn't be too small either, because clients - would be simply rejected. Your mileage may vary.) - (tt(backlog) em(number)) - (0, which takes the operating system's default - value for socket back log size.) diff --git a/doc/main/conf/bindto.yo b/doc/main/conf/bindto.yo @@ -1,11 +0,0 @@ -conf(bindto - Binding to a specific IP address) - (The tt(bindto) statement is used in situations where crossroads - should only listen to the stated port at a given IP address. E.g., - tt(bindto 127.0.0.1) causes crossroads to 'bind' the service only to - the local IP address. Network connections from other hosts won't be - serviced. By default, crossroads binds a service to all presently - active IP addresses at the invoking host.) - (tt(bindto) em(address), where em(address) is a numeric IP - address, such as 127.0.0.1, or the keyword tt(any).) - (tt(any)) - -\ No newline at end of file diff --git a/doc/main/conf/checkinterval.yo b/doc/main/conf/checkinterval.yo @@ -1,22 +0,0 @@ -conf(checkinterval - Periodic back end checks) - (When a check interval is stated, Crossroads will periodically - probe back ends to determine whether available back ends are - still there, and to see whether unavailable back ends have woken - up yet. - - An example is tt(checkinterval 10). When this is stated, - Crossroads will probe all back ends each 10 seconds.) - (itemization( - it() tt(checkinterval) em(number); - it() tt(checkinterval) em(number) tt(externalhandler) - em(program arguments);) - The first form checks bhy connecting to the back end server. - If the connection succeeds, then the back end is considered - available; otherwise the back end is considered unavailable. - - The second form activates an external program (see section - ref(tips/periodic) for a description). The back end is considered - available if the program's exit status is zero; otherwise it is - considered unavailable.) - (0 (no periodic checks)) - diff --git a/doc/main/conf/connectiontimeout.yo b/doc/main/conf/connectiontimeout.yo @@ -1,11 +0,0 @@ -conf(connectiontimeout - Specifying client time outs) - (Sometimes, clients simply won't close a network - connection which leads to unnecessary resource usage. To avoid this, - one might state e.g. tt(connectiontimeout 300). This instructs - crossroads to consider a connection where nothing has happened for - 300 seconds as - 'finished'. Crossroads will terminate the connection when this timeout - is exceeded.) - (tt(connectiontimeout) em(number), where em(number) specifies the - timeout in seconds.) - (0, meaning that crossroads will not cut connections.) diff --git a/doc/main/conf/decay.yo b/doc/main/conf/decay.yo @@ -1,18 +0,0 @@ -conf(decay - Levelling out activity of a back end) - (To make sure that a 'spike' of activity doesn't - influence the perceived load of a back end forever, you may - specify a certain decay. E.g, the statement tt(decay 10) makes - sure that the load that crossroads computes for this back end (be - it in seconds or in bytes) is decreased by 10% each time that - bf(an other) back end is hit. Decays are not applied to the count - of concurrent connections. - - This means that when a given back end is hit, then its usage data - of the transferred bytes and the connection duration are updated - using the actual number of bytes and actual duration. However, - when a different back end is hit, then the usage data are - decreased by the specified decay. ) - (tt(decay) em(number), where em(number) is a percentage that - decreases the back end usage data when other back ends are - hit.) - (0, meaning that no decay is applied to usage statistics.) diff --git a/doc/main/conf/dispatchmode.yo b/doc/main/conf/dispatchmode.yo @@ -1,84 +0,0 @@ -conf(dispatchmode - How are back ends selected) - (The dispatch mode controls how crossroads selects a back end from - a list of active back ends. The below text shows the bare - syntax. See section ref(howselected) for a textual explanation. - - The settings can be: - - itemization( - it() tt(dispatchmode roundrobin): Simply the 'next in line' is - chosen. E.g, when 3 back ends are active, then the usage - series is 1, 2, 3, 1, 2, 3, and so on. - - Roundrobin dispatching is the default method, when no - tt(dispatchmode) statement occurs. - - it() tt(dispatchmode random): Random selection. Probably only - for stress testing, though when used with weights (see below) - it is a good distributor of new connections too. - - it() tt(dispatchmode bysize [ over) em(connections) tt(]): - The next back end is the one - that has transferred the least number of bytes. This - selection mechanism assumes that the more bytes, the heavier - the load. - - The modifier tt(over) em(connections) is optional. (The square - brackets shown above are not part of the statement but - indicate optionality.) When given, - the load is computed as an average of the last stated number of - connections. When this modifier is absent, then the load is - computed over all connections since startup. - - it() tt(dispatchmode byduration [ over) em(connections) tt(]): - The next back end is the one - that served connections for the shortest time. This mechanism - assumes that the longer the connection, the heavier the load. - - it() tt(dispatchmode byconnections): The next back end is the one - with the least active connections. This mechanism assumes that - each connection to a back end represents load. It is usable - for e.g. database connections. - - it() tt(dispatchmode byorder): The first back end is selected - every time, unless it's unavailable. In that case the second - is taken, and so on. - - it() tt(dispatchmode byclientip): The client's IP address is - 'hashed' into a number, which is used to pick a back end. The - same client IP address will therefore always be dispatched to - the same back end. When the back end of choice is down, - tt(dispatchmode byconnections) is used. - - it() tt(dispatchmode externalhandler) em(program arguments): - This is a special mode, where an external program is delegated - the responsibility to say which back end should be used - next. In this case, Crossroads will call the external program, - and this will of course be slower than one of the 'built-in' - dispatch modes. However, this is the ultimate escape when - custom-made dispatch modes are needed. - - The dispatch mode that uses an tt(externalhandler) is - discussed separately in section ref(externalhandler).) - - The selection algorithm is only used when clients are serviced that - aren't part of a sticky HTTP session. This is the case during: - - itemization( - it() all client requests of a service type tt(any); - it() new sessions of a service type tt(http).) - - When type tt(http) is in effect and a session is underway, then the - previously used back end is always selected -- regardless of - dispatching mode. - - Your 'right' dispatch mode will depend on the type of service. Given - the fact that crossroads doesn't know (and doesn't care) how to - estimate load from a network traffic stream, you have to choose an - appropriate dispatch mode to optimize load balancing. In most cases, - tt(roundrobin) or tt(byconnections) will do the job just fine.) - (tt(dispatchmode) em(mode) (see above for the modes), optionally - followed by tt(over) em(number), or when the em(mode) is - tt(externalhandler), followed by em(program).) - (tt(roundrobin)) - diff --git a/doc/main/conf/headerinspection.yo b/doc/main/conf/headerinspection.yo @@ -1,13 +0,0 @@ -conf(headerinspection - are all HTTP headers inspected) - (The tt(headerinspection) directive defines whether Crossroads -must inspect all HTTP headers that are seen on one TCP connection, or -only the first ones. There are two possible values for this directive: -tt(deep) and tt(shallow). In tt(deep) mode, all information that is -seen on the TCP link is monitored -and parsed, and all HTTP header blocks are analyzed and subject to -directives such as tt(addclientheader). In tt(shallow) mode, only the -first header block that the server sends, and the first header block -that forms the server's answer, are analyzed.) - (tt(headerinspection) em(specifier), where em(specifier) is -tt(deep) or tt(shallow)) - (tt(deep)) diff --git a/doc/main/conf/httptiminglog.yo b/doc/main/conf/httptiminglog.yo @@ -1,12 +0,0 @@ -conf(httptiminglog - Timing debugging in HTTP mode) - (The directive tt(httptiminglog) turns on logging of HTTP mode - timing. There must be one argument, the filename where the timings - are written to. Turning on this option will slow down processing, - but may be helpful in finding out where delays are caused.) - (The syntax is: - - itemization( - it() tt(httptiminglog) em(filename) tt(;) - it() Where em(filename) is the output file, e.g. - tt(/tmp/timings.log))) - (none) diff --git a/doc/main/conf/maxcon-client.yo b/doc/main/conf/maxcon-client.yo @@ -1,13 +0,0 @@ -conf(maxconnections - Limiting the connections to a back end) - (The directive tt(maxconnections) limits the number of allowed - connections to this client. - Note that this directive can also occur on the level of a service - block, in which case it limits the overall number of connections. - - bf(Futhermore note) that this directive cannot be used when an external - dispatcher is in effect. In such cases, the external dispatcher has - full control over backend selection.) - (tt(maxconnections) em(number) tt(;)) - (0; meaning no limit) - - diff --git a/doc/main/conf/maxconnections.yo b/doc/main/conf/maxconnections.yo @@ -1,16 +0,0 @@ -conf(maxconnections - Limiting concurrent clients at service level) - (The maximum number of connections is specified using - tt(maxconnections). There is one argument; the number of concurrent - established connections that may be active within one service. - - 'Throttling' the number of connections is a way of preventing Denial of - Service (DOS) attacks. Without a limit, numerous network connections - may spawn so many server instances, that the service ultimately breaks - down and becomes unavailable. - - Note that tt(maxconnections) is also allowed in a backend - description block, in which case it limits the number of TCP - connections to that particular back end.) - (tt(maxconnections) em(number), where the number specifies the - maximum of concurrent connections to the service.) - (0, meaning that all connections will be accepted.) diff --git a/doc/main/conf/onfailure.yo b/doc/main/conf/onfailure.yo @@ -1,10 +0,0 @@ -conf(onfailure and onsuccess - Action Hooks) - (As special 'hooks' for actions, two triggers - are available: tt(onfailure) and tt(onsuccess). The argument to - the triggers is a system command that is executed when a connection - with the back end either fails or succeeds.) - (itemization( - it() tt(onfailure) em(commandline) - it() tt(onsuccess) em(commandline))) - (There is no default. Normally no external programs are run upon - success or failure of a back end.) diff --git a/doc/main/conf/onhooks.yo b/doc/main/conf/onhooks.yo @@ -1,27 +0,0 @@ -conf(onstart, onend, onfail - Action Hooks) - (The three directives tt(onstart), tt(onend) and tt(onfail) can be - specified to start system commands (external programs) when a - connection to a back end starts, fails or ends: - itemization( - it() tt(onstart) commands will be run when Crossroads - successfully connects to a back end, and starts servicing; - it() tt(onend) commands will be run when a (previously - established) connection stops; - it() tt(onfail) commands will be run when Crossroads tries to - contact a back end to serve a client, but the back end can't - be reached.) - - The format is always tt(on)em(type) em(command). The em(command) - is an external program, optionally followed by arguments. The - command is expanded according to the following table: - - INCLUDEFILE(formattable)) - (The syntax of the commands is as follows. - - itemization( - it() tt(onstart) em(commandline) - it() tt(onend) em(commandline) - it() tt(onfail) em(commandline) - it() tt(onsuccess) em(commandline))) - (There is no default. Normally no external programs are run upon - connection, success or failure of a back end.) diff --git a/doc/main/conf/port.yo b/doc/main/conf/port.yo @@ -1,11 +0,0 @@ -conf(port - Specifying the listen port) - (The tt(port) statement defines to which TCP port a service - 'listens'. E.g. tt(port 8000) says that this service will accept - connections on port 8000. - - Multiple services in one configuration cannot use the same port - number, unless they both bind to specific (and different) IP - addresses. See also the tt(bindto) statement.) - (tt(port) em(number)) - (There is no default. This is a required setting.) - diff --git a/doc/main/conf/retries.yo b/doc/main/conf/retries.yo @@ -1,11 +0,0 @@ -conf(retries - Specifying allowed failures) - (Back ends that are 'flaky' or on a less reliable network can be - marked as unavailable after not just one failure, but after - e.g. three. You can use this configuration if you suspect that - spurious errors cause otherwise 'good' back ends to be marked as - unavailable, while they in fact still could be used.) - (tt(retries) em(number); where em(number) is the threshold of bad - connections. Once exceeded, Crossroads will mark a back end as - unavailable.) - (1; a back end is assumed to be unavailable after the first bad - connection.) diff --git a/doc/main/conf/revivinginterval.yo b/doc/main/conf/revivinginterval.yo @@ -1,26 +0,0 @@ -conf(revivinginterval - Back end wakeup calls) - (A reviving interval definition is used when Crossroads - determines that a back end is temporarily unavailable. This will - happen when: - - itemization( - it() The back end cannot be reached (network connection - fails); - it() The network connection to the back end suddenly dies.) - - Once a reviving interval is set, Crossroads will periodically - check the unavailable back end(s) to see whether they have woken up. - - An example of the definition is tt(revivinginterval 10). When this - reviving interval is given, crossroads will check each 10 seconds - whether unavailable back ends have woken up yet.) - (itemization( - it() tt(revivinginterval) em(number); - it() tt(revivinginterval) em(number) tt(externalhandler) - em(program arguments);) - The first form connects to a back end server. If the connection - succeeds, then the back end is considered available. The second - form activates an external program (see section - ref(tips/periodic) for a description). The back end is marked - available if the program's exit status is zero.) - (0 (no wakeup calls)) diff --git a/doc/main/conf/server.yo b/doc/main/conf/server.yo @@ -1,14 +0,0 @@ -conf(server - Specifying the back end address) - (Each back end must be identified by the network name - (server name) where it is located. For example: tt(server - 10.1.1.23), or tt(server web.mydomain.org). A TCP port specifier - can follow the server name, as in tt(server - web.mydomain.org:80). bf(Note that) resolved host names can be - cached by Crossroads. (The DNS cache timeout can be - controlled using the invocation flag tt(-d).)) - (itemization( - it() tt(server) em(servername), where em(servername) is a - network name or IP address. In this case a separate tt(port) - statement must be used to define the TCP port; - it() tt(server) em(servername:port))) - (There is no default. This is a required setting.) diff --git a/doc/main/conf/shmkey.yo b/doc/main/conf/shmkey.yo @@ -1,18 +0,0 @@ -conf(shmkey - Shared Memory Access) - (Different Crossroads - invocations must 'know' of each others activity. E.g, tt(crossroad - status) must be able to get to the actual state information of all - running services. This is internally implemented through shared - memory, which is reserved using a key. - - Normally crossroads will supply a shared memory key, based on the - service name. In situations where this conflicts with existing - keys (of other programs, having their own keys), you may supply a - chosen value. - - The actual key value doesn't matter much, as long as it's unique - and as long as each invocation of crossroads uses it.) - (tt(shmkey) em(number)) - (0, which means that crossroads will 'guess' its - own key, based on the service name.) - diff --git a/doc/main/conf/state.yo b/doc/main/conf/state.yo @@ -1,13 +0,0 @@ -conf(state - Setting an initial back end state) - (Using the tt(state) directive a back end can be 'primed' with an - initial state. The keyword tt(state) can be followed by - tt(available), tt(unavailable) or tt(down). Back ends marked - tt(unavailable) are excluded as candidates, but are checked when - a tt(revivinginterval) or a tt(checkinterval) is used. Back ends - marked tt(down) are excluded and never re-checked.) - (itemization( - it() tt(state) em(specifier) tt(;) - it() where em(specifier) is one of tt(available), - tt(unavailable) or tt(down))) - (tt(available), meaning that a back end is a candidate for - initial dispatching.) diff --git a/doc/main/conf/stickycookie.yo b/doc/main/conf/stickycookie.yo @@ -1,35 +0,0 @@ -conf(stickycookie - Back end selection with an HTTP cookie) - (The directive tt(stickycookie) em(value) - causes Crossroads to unpack clients' requests, to check for - em(value) in the cookies. When found, the message is routed to the - back end having the appropriate tt(stickycookie) directive. - - E.g., consider the following configuration: - - verb(\ -service ... { - ... - backend one { - ... - stickycookie "BalancerID=first"; - } - backend two { - ... - stickycookie "BalancerID=second"; - } -}) - - When clients' messages contain cookies named tt(BalancerID) with - the value tt(first), then such messages are routed to backend - tt(one). When the value is tt(second) then they are routed to the - backend tt(two). - - There are basically to provide such cookies to a browser. First, a - back end can insert such a cookie into the HTTP response. E.g., - the webserver of back end tt(one) might insert a cookie named - tt(BalancerID), having value tt(first). - Second, Crossroads can insert such cookies using a carefully - crafted directive tt(addclientheader).) - (tt(stickycookie) em(cookievalue)) - (There is no default.) - diff --git a/doc/main/conf/trafficlog.yo b/doc/main/conf/trafficlog.yo @@ -1,20 +0,0 @@ -conf(trafficlog and throughputlog - Debugging and Performance Aids) - (Two directives are available - to log network traffic to files. They are tt(trafficlog) and - tt(throughputlog). - - The tt(trafficlog) statement causes all traffic to be logged in - hexadecimal format. Each line is prefixed by tt(B) or tt(C), - depending on whether the information was received from the back - end or from the client. - - The tt(throughputlog) statement writes shorthand transmissions to - its log, accompanied by timings and the number of bytes each - transmission was able to read or write in one chunk.) - (The syntax is: - - itemization( - it() tt(trafficlog) em(filename) tt(;) - it() tt(throughputlog) em(filename) tt(;))) - (none) - diff --git a/doc/main/conf/type.yo b/doc/main/conf/type.yo @@ -1,18 +0,0 @@ -conf(type - Defining the service type) - (The tt(type) statement defines how crossroads handles the stated - service. There are currently two types: tt(any) and - tt(http). The type tt(any) means that crossroads doesn't - interpret the contents of a TCP stream, but only distributes streams - over back ends. The type tt(http) means that crossroads has to - analyze what's in the messages, does magical HTTP header tricks, and - so on -- all to ensure that multiple connections are treated as one - session, or that the back end is notified of the client's IP - address. - - Unless you really need such special features, use the type tt(any) (the - default), even for HTTP protocols.) - (tt(type) em(specifier), where em(specifier) is tt(any) or - tt(http)) - (tt(any)) - - diff --git a/doc/main/conf/useraccount.yo b/doc/main/conf/useraccount.yo @@ -1,14 +0,0 @@ -conf(useraccount - Limiting the effective ID of external processes) - (Using the directive tt(useraccount), the effective user and group - ID can be restricted. This comes into effect when Crossroads runs - external commands, such as: - itemization( - it() Hooks for tt(onstart), tt(onend) or tt(onfail); - it() External dispatchers, when tt(dispatchmode - externalhandler) is in effect.) - Once a user name for external commands is specified, Crossroads - assumes the associated user ID and group ID before running those - commands.) - (tt(useraccount) em(username)) - (None; when unspecified, external commands are run with the - ID that was in effect when Crossroads was started.) -\ No newline at end of file diff --git a/doc/main/conf/verbose-backend.yo b/doc/main/conf/verbose-backend.yo @@ -1,10 +0,0 @@ -conf(verbosity - Controlling verbosity at the back end level) - (Similar to tt(service) specifications, a - tt(backend) can have its own verbosity (tt(on) or tt(off)). When - tt(on), traffic to and fro this back end is reported.) - (itemization( - it() tt(verbosity) em(setting), or - it() tt(verbose) em(setting), where em(setting) is tt(true), - tt(yes) or tt(on), or tt(false), tt(no), tt(off) to turn it - off.)) - (tt(off)) diff --git a/doc/main/conf/verbose.yo b/doc/main/conf/verbose.yo @@ -1,14 +0,0 @@ -conf(verbosity - Controlling debug output) - (Verbosity statements come in two forms: tt(verbosity on) or - tt(verbosity off). When 'on', log messages to tt(/var/log/messages) - are generated that show what's going on.footnote(Actually, the - messages go to tt(syslog(3)), using facility tt(LOG_DAEMON) and - priority tt(LOG_INFO). In most (Linux) cases this will mean: output to - tt(/var/log/messages). On Mac OSX the messages go to - tt(/var/log/system.log).) The keyword tt(verbose) is an alias for - tt(verbosity).) - (tt(verbosity) em(setting) or tt(verbose) em(setting), where - em(setting) is tt(true), tt(yes) or tt(on) to turn - verbosity on; or tt(false), tt(no), tt(off) to turn it off.) - (tt(off)) - -\ No newline at end of file diff --git a/doc/main/conf/weight.yo b/doc/main/conf/weight.yo @@ -1,15 +0,0 @@ -conf(weight - When a back end is more equal than others) - (To influence how backends are selected, a backend can specify its - 'weight' in the process. The higher the weight, the less likely a - back end will be chosen. The default is 1. - - The weighing mechanism only applies to the dispatch modes - tt(random), tt(byconnections), tt(bysize) and tt(byduration). - The weight is in fact a penalty factor. E.g., if backend A has - tt(weight 2) and backend B has tt(weight 1), then backend B will - be selected all the time, until its usage parameter is twice as - large as the parameter of A. Think of it as a 'sluggishness' - statement.) - (tt(weight) em(number); the higher the number, the more 'sluggish' - a back end is) - (1; all back ends have equal weight.) diff --git a/doc/main/config.yo b/doc/main/config.yo @@ -1,278 +0,0 @@ -The configuration that crossroads uses is normally stored in the file -tt(crossroads.conf), either in an 'etc' directory near the binary, or -in tt(/etc/). See section ref(configuring) for details. - -The name of the configuration file can be overruled using the -command line flag tt(-c). - -This section explains the syntax of the configuration file, and what -all settings do. - -confsect(General language elements) - -This section describes the general elements of the crossroads -configuration language. - - -confsubsect(Empty lines, indentation and comments) - -Empty lines are of course allowed in the configuration. Also, -indentation is irrelevant as far as processing is concerned, but is -loosely inspired on a C-like style. So the following two examples are -identical as far as Crossroads is concerned (thought the first one -will be typically more readable): - -verb(/* Example one */ -options { - tcpbuffersize 10240; - logactivity on; -} - -/* Example two */ -options {tcpbuffersize 10240 -;logactivity on;}) - -Crossroads recognizes the following comment formats: - -itemization( - it() C-style, between tt(/*) and tt(*/), - it() C++-style, starting with tt(//) and ending with the end - of the text line.) - -Simply choose your favorite editor and use the comment that 'looks -best'.footnote(I favor C or C++ comment. My favorite editor em(emacs) -can be put in tt(cmode) and nicely highlight what's comment and what's -not. And as a bonus it will auto-indent the configuration!) - - -confsubsect(Preprocessor directives) - -Similar to bf(C) or bf(C++), the Crossroads grammar knows tt(#include) -and tt(#define). Both directives must start at the first column of the -line (ie., the tt(#) sign must occur at the leftmost line -position). - -itemization( - it() tt(#include) tt(")em(filename)tt(") includes the stated file - name at the place of the statement. - it() tt(#define) em(SYMBOL) em(DEFINITION) defines em(SYMBOL) - as placeholder for em(DEFINITION).) - -For example, one may use the configuration: - -verb(\ -#define SERVICEPORT 80 -service web { - port SERVICEPORT; - . - . /* More statements follow here */ - . -}) - -The tt(port) statement is then read as tt(port 80). - -The statement tt(#define) can also be very nicely used when trying out -Crossroads configurations. Crossroads has a statement tt(verbosity) -tt(true) that causes debugging information to be logged. Once a -configuration has proven to work, you'll most likely want -tt(verbosity) tt(false) so that overhead due to logging is -avoided. This can be easily implemented using tt(#define): - -verb(\ -/* Set DEBUG to true or false; - * true is for testing purposes, - * false is for production */ -#define DEBUG true - -service web { - verbosity DEBUG; - . - . /* More statements follow here */ - . -}) - - -confsubsect(Keywords, numbers, identifiers, generic strings) - -In a configuration file, statements are identified by em(keywords), -which are reserved words. The list of the keywords of the Crossroads -grammar is: -includefile(keywords.yo) - -Many keywords require an em(identifier) as the argument. E.g, a -service has a unique name, which must start with a letter or -underscore, followed by zero or more letters, underscores, or -digits. E.g., in the statement tt(service myservice), the keyword is -tt(service) and the identifier is tt(myservice). - -Other keywords require a numeric argument. Crossroads knows only -non-negative integer numbers, as in tt(port 8000). Here, tt(port) is -the keyword and tt(8000) is the number. Octal numbers are specified by -prefixing a zero; e.g., tt(0644) is an octal number. - -Yet other keywords require 'generic strings', such as hostname -specifications or system commands. Such generic strings contain any -characters (including white space) up to the terminating statement -character tt(;). If a string must contain a semicolon, then it must -be enclosed in single or double quotes: - -itemization( - it() tt(This is a string;) is a string that starts at tt(T) - and ends with tt(g) - it() tt("This is a string";) is the same, the double quotes - are not necessary - it() tt("This is ; a string";) has double quotes to protect - the inner ;) - -Finally, an argument can be a 'boolean' value. Crossroads knows -tt(true), tt(false), tt(yes), tt(no), tt(on), tt(off). The keywords -tt(true), tt(yes) and tt(on) all mean the same and can be used -interchangeably; as can the keywords tt(false), tt(no) and tt(off). - - -confsect(Daemon options)label(daemonoptions) - -Crossroads supports an optional block that defines run options for the -daemon process. These options can be also be specified on the command line -using flags. - -The syntax of the options block is: - -verb(options { - . - . option statements (see below) - . -}) - -In the curly-brace block the following option statements can occur: - -itemization( - it() tt(logactivity on;) or tt(off) Turns activity logging on - or off. This is also controlled using the flag tt(-a). The - default is not to log network activity. - - it() tt(tcpbuffersize) em(number) tt(;) This defines the size - of network buffers. The default is 5120 bytes. This is also - controlled using the flag tt(-B). - - it() tt(dnscachettl) em(number) tt(;) This controls the - time-to-live of cached DNS entries, in seconds. Value zero - means no caching and is the default. This is also controlled - using the flag tt(-d). - - it() tt(logfacility) em(number) tt(;) This controls the - logging facility. Values 0 to 7 select tt(LOG_LOCAL0) to - tt(LOG_LOCAL7). Any other value selects tt(LOG_DAEMON), which - is also the default. This is also controlled using the flag tt(-l). - - it() tt(shmpermissions) tt(number) tt(;) This defines the - permissions (for user, group and other) of the shared memory - block. The number is most often specified as an octal value, - e.g. tt(0644), which is also the default. This is also - controlled using the flag tt(-m). - - it() tt(sloppyportbind on;) or tt(off) Turns on 'sloppy' port - binding of the listener. When tt(on), Crossroads will not - treat port-busy conditions as fatal, but will wait and retry. - The default is tt(off). This is also controlled using flag - tt(-s). - - it() tt(leaveprocesstitle) tt(on;) or tt(off) When tt(on), the - process title of Crossroads is not modified, so that tt(ps) - will show tt(crossroads-daemon). The default is tt(off): - Crosroads will modify its process name into something like - em(crossroads - Sevice web: listening) or em(crossroads - - Service web: serving).) - - -confsect(Service definitions) label(servicedef) - -Service definitions are blocks in the configuration file that -state what is for each service. A service definition starts with -tt(service), followed by a unique identifier, and by statements in -tt({) and tt(}). For example: - -verb(\ -// Definition of service 'www': -service www { - ... - ... // statements that define the - ... // service named 'www' - ... -}) - -The configuration file can contain many service blocks, as long as the -identifying names differ. The following list shows possible -statements. Each statement must end with a semicolon, except for the -tt(backend) statement, which has is own block (more on this later). - -redef(conf)(4)(\ - confsubsect(ARG1) label(confARG1) - startdit() - dit(Description:) ARG2 - dit(Syntax:) ARG3 - dit(Default:) ARG4 - enddit()) - - -includefile(conf/port) -includefile(conf/type) -includefile(conf/headerinspection) -includefile(conf/bindto) -includefile(conf/verbose) -includefile(conf/dispatchmode) -includefile(conf/revivinginterval) -includefile(conf/checkinterval) -includefile(conf/maxconnections) -includefile(conf/backlog) -includefile(conf/shmkey) -includefile(conf/allow) -includefile(conf/useraccount) - - -confsect(Backend definitions) - -Inside the service definitions as are described in the previous -section, em(backend definitions) must also occur. Backend definitions -are started by the keyword tt(backend), followed by an identifier -(the back end name) , and statements inside tt({) and tt(}): - -verb(\ -service myservice { - ... - ... // statements that define the - ... // service named 'myservice' - ... - - backend mybackend { - ... - ... // statements that define the - ... // backend named 'mybackend' - ... - } -}) - -Each service definition must have at least one backend -definition. There may be more (and probably will, if you want -balancing and fail over) as long as the backend names differ. -The statements in the backend definition blocks are described in the -following sections. - -Some directives (tt(stickycookie) etc.) only have effect when -Crossroads treats the network traffic as a stream of HTTP messages; -i.e., when the service is declared with tt(type http). Incase of -tt(type any), the HTTP-specific directives have no effect. - -includefile(conf/server.yo) -includefile(conf/backendport.yo) -includefile(conf/verbose-backend.yo) -includefile(conf/retries.yo) -includefile(conf/maxcon-client.yo) -includefile(conf/weight) -includefile(conf/decay) -includefile(conf/state) -includefile(conf/onhooks) -includefile(conf/trafficlog) -includefile(conf/httptiminglog) -includefile(conf/stickycookie) -includefile(conf/addclientheader) diff --git a/doc/main/crossroads.yo b/doc/main/crossroads.yo @@ -1,44 +0,0 @@ -includefile(../crossroads-defs) -tocclearpage() -titleclearpage() -includefile(defs) -abstract(Crossroads is a load balance and fail over utility for TCP - based services. It is a daemon program running in user - space, and features extensive configurability, polling of - back ends using 'wakeup calls', detailed status reporting, - 'hooks' for special actions when backend calls fail, and much - more. Crossroads is service-independent: it is usable for - HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP - balancing, Crossroads can modify HTTP headers, e.g. to - provide 'session stickiness' for back end processes that need - sessions, but aren't session-aware of other - back ends. Crossroads is configured via a file and accessible - via a web interface.) -article(Crossroads VER()) - (AUTHORNAME() nl() - Maintained by MAINTAINERNAME() (MAINTAINEREMAIL())) - (YEARS(), ff.) - -sect(Introduction) -includefile(intro) - -sect(Installation for the impatient) -includefile(impatient) - -sect(Using Crossroads) -includefile(using) - -redef(confsect)(1)(subsect(ARG1)) -redef(confsubsect)(1)(subsubsect(ARG1)) -sect(The configuration) -includefile(config) - -sect(Tips, Tricks and Random Remarks) -includefile(tips) - -sect(Benchmarking) -includefile(benchmarking) - -sect(Compiling and Installing) label(installation) -includefile(compiling) - diff --git a/doc/main/defs.yo b/doc/main/defs.yo @@ -1,84 +0,0 @@ -COMMENT( - - Local Yodl Macros - ================= - -Here's a sample "local config" file that gets installed into -/usr/e/share/yodl. You can use this as an example to cook your -own. E.g., you'll want to a use different affiliation, mailto, and so -on. - -Also, the shown latexlayoutcmds() probably won't be what you like to -see... Good luck! --- [KK 2004-11-17] - -) - -COMMENT(General document modifiers) -htmlstylesheet(http://www.e-tunity.com/css/yodl.css) -affiliation(e-tunity) -mailto(info@e-tunity.com) -latexlayoutcmds( - \usepackage{a4wide} - \usepackage{fancyhdr} - \usepackage{graphicx} - \usepackage{moreverb} - \usepackage{palatino} - \setlength{\textheight}{21cm} - \setlength{\parindent}{0pt} - \setlength{\parskip}{1ex plus 0.5ex minus 0.2ex} - \renewcommand{\headrulewidth}{.6pt} - \renewcommand{\footrulewidth}{.6pt} - \fancypagestyle{plain}{ - \fancyhf{} - \fancyhead[LE,RO]{} - \fancyhead[LO]{\small\slshape \leftmark} - \fancyhead[RE]{\small\slshape \rightmark} - \fancyfoot[LE,RO]{\small\thepage} - %\fancyfoot[RE,LO]{\small\textbf{e-tunity}} - } - \AtBeginDocument{\pagestyle{plain}}) -setfigureext(.png) -sethtmlfigureext(.png) - -COMMENT(pngfig (basename-without-extension) (caption) (label) - A-la figure() but includes the true png. We can do that - because we use pdflatex.) - -redef(pngfig)(3)(\ - whenhtml(figure(ARG1)(ARG2)(ARG3))\ - whenlatex(\ - latexcommand(\begin{figure}[htbp])\ - latexcommand(\centerline{)\ - latexcommand(\includegraphics[scale=.5]{)\ - ARG1.png+\ - latexcommand(}})\ - latexcommand(\caption{\small )ARG2+latexcommand(})\ - latexcommand(\label{)ARG3+latexcommand(})\ - latexcommand(\end{figure})) - whenman(verb( - ===================================================== - Insert figure ARG1 about here - ARG2 - =====================================================)\ - label(ARG3))) - -COMMENT(listing(text) - A-la verb() but nicely formatted in LaTeX.) - -redef(listing)(1)(\ - whenhtml(verb(ARG1))\ - whenlatex(\ - PUSHCHARTABLE() - latexcommand(\begin{listing}{1})\ - XXnl()\ - NOEXPAND(ARG1)\ - XXnl()\ - latexcommand(\end{listing}) - POPCHARTABLE())\ - whenman(\ - XXroffcmd(.nf)()()()\ - INTERNALINDEX(verb on)\ - NOEXPAND(ARG1)\ - INTERNALINDEX(verb off)\ - XXroffcmd(.fi)()()())) diff --git a/doc/main/formattable.yo b/doc/main/formattable.yo @@ -1,59 +0,0 @@ -itemization( - - it() tt(%a) is the availability of the current back end, when - a current back end is established. tt(%1a) is the - availability of the first back end (0 when unavailable, 1 if - available); tt(%2a) is the availability of the second back - end, and so on. - - it() tt(%b) is the name of the current back end, when one is - established. tt(%1b) is the name of the first back end, - tt(%2b) of the second back end, and so on. - - it() tt(%e) is the count of seconds since start of epoch - (January 1st 1970 GMT). tt(%60e) is the count since start of - epoch plus 60, so this is a 1 minute offset into the future. - - it() tt(%g) is a "GMT-string" representation of the current - time, in the format em(monthname, day year hh:mm:ss). This - format is used in e.g. cookie expiry. tt(%600g) is the same - representation but of a moment 600 seconds in the future (10 - minutes). - - it() tt(%h) is the host name of the current back end. tt(%1h) - is the host name of the first back end, tt(%2h) of the second - back end, and so on. - - it() tt(%l) is the duration of the last successful connection - in seconds, concerning the current back end. tt(%1l) is the - duration of the connection to back end one, and so on. Note - that tt(%l) refers only to the last successful connections. - Unsuccessful connection attempts to back ends do not change - the value. - - it() tt(%p) is the TCP port of the current back end. tt(%1p) - is the TCP port of the first back end, tt(%2p) of the second - back end, and so on. - - it() tt(%P) is the process ID of the Crossroads handler that - handled this request. For debugging only. - - it() tt(%r) is the IP address of the connecting client. - - it() tt(%s) is the name of the current service that the client - connected to. - - it() tt(%t) is the current local time in ANSI format, in - em(YYYY-MM-DD/hhh:mm:ss). tt(%1800s) is an ANSI stamp of a - moment 1800 seconds in the future (half an hour). - - it() tt(%T) is the current GMT time in ANSI format. tt(%10T) - offsets this 10 seconds into the future. - - it() tt(%v) is the Crossroads version. - - it() tt(%w) is the weight factor of the current back - end. tt(%1w) is the weight factor of the first back end, etc.. - - it() Any other chararacter following a tt(%) sign is taken - literally; e.g. tt(%z) is just a z.) diff --git a/doc/main/impatient.yo b/doc/main/impatient.yo @@ -1,73 +0,0 @@ - -For the impatient, here's the very-quick-but-very-superficial recipy -for getting Crossroads up and running: - -itemization( - - it() If you don't have SVN or don't want to use it: - - itemization( - it() Obtain the Crossroads source archive at - lurl(http://crossroads.e-tunity.com). - - it() Change-dir to a 'sources' directory on your system and - unpack the archive. - - it() Change-dir into the create directory tt(crossroads/).) - - it() If you have SVN and want to go for the newest snapshot: - - itemization( - - it() Get the latest sources and snapshots using SVN from nl() - tt(svn://svn.e-tunity.com/crossroads). - - it() You'll find the newest alpha version under - tt(crossroads/trunk) and the stable versions under - tt(crossroads/tags), - e.g. tt(crossroads/tags/release-1.00). - - it() Choose which you want to use: the latest stable - release, or the bleeding edge alpha? In the former case, - change-dir to tt(crossroads/tags/release-)em(X.YY), where - em(X.YY) is a release ID. In the latter case, change-dir to - tt(crossroads/trunk).) - - it() Type tt(make install). This installs the Crossroads - binary into tt(/usr/local/bin/). If the compilation doesn't - work on your system, check tt(etc/Makefile.def) for hints. - - it() Create a configuration file as e.g. - tt(/etc/crossroads.conf). (Other locations are permitted. See - section ref(configuring) for details.) In it state something - like: - - verb(\ -service www { - port 80; - revivinginterval 15; - backend one { - server 10.1.1.100:80; - } - backend two { - server 10.1.1.101:80; - } -}) - - That's off course assuming that you want to balance HTTP on - port 80 to two back ends at 10.1.1.100 and 10.1.1.101. - - it() Type tt(crossroads start). - - it() Surf to the machine where Crossroads is running. You will - see the pages served by the back ends 10.1.1.100 or - 10.1.1.101. - - it() To monitor the status of Crossroads from the command - line, type tt(crossroads status). - - it() If you want to monitor Crossroads via a web front end, - start the manager: type tt(crossroads-mgr start 1000). Next - point your browser to the machine where Crossroads is - running, port 1000. -) diff --git a/doc/main/intro.yo b/doc/main/intro.yo @@ -1,294 +0,0 @@ -Crossroads is a daemon that basically accepts TCP connections at -preconfigured ports, and given a list of 'back ends' distributes each -incoming connection to one of the back ends, so that a client request -is served. Additionally, crossroads maintains an internal -administration of the back end connectivity: if a back end isn't -usable, then the client request is handled using another back -end. Crossroads will then periodically check whether a previously not -usable back end has come to life yet. Also, crossroads can select back -ends by estimating the load, so that balancing is achieved. - -Using this approach, crossroads serves as load balancer and fail over -utility. Crossroads will very likely not be as reliable as hardware -based balancers, since it always will require a server to run on. This -server, in turn, may become a new Single Point of Failure (SPOS). -However, in situations where cost efficiency is an issue, crossroads -may be a good choice. Furthermore, crossroads can be deployed in -situations where a hardware based balancing already exists and -augmenting service reliability is needed. Or, crossroads may be run -off a diskless system, which again improves reliability of the -underlying hardware. - -This document describes how to use crossroads, how to configure it in -order to increase the reliability of your systems, and how to compile -the program from its sources. whenhtml(This document is also available -in url(PDF)(crossroads.pdf) format.) - - -subsect(Obtaining Crossroads) - -As quick reference, here are some important URL's for Crossroads: - -itemization( - it() lurl(http://crossroads.e-tunity.com) is the site that serves - Crossroads. You can browse this at leisure - for documentation, sources, and so on. - - it() lurl(http://freshmeat.net/projects/crossr) is the - Freshmeat announcement page. - - it() lurl(svn://svn.e-tunity.com/crossroads) is the SVN - repository; anonymous reading (fetching) is allowed. In order - to commit changes, mail the author or maintainer for credentials.) - - -subsect(Reporting bugs) - -Crossroads was thoroughly tested and proven to work. However, on -particular Unices with particular needs and network particularities, -bugs may occur. - -In such cases you can contact the maintainer to ask for -assistance. Visit the site url(http://crossroads.e-tunity.com) -(http://crossroads.e-tunity.com) for contact information. In questions -or bug reports, always include the following: - -itemization( - it() A description of the bug: when does it occur, what are - the circumstances, what is the expected behavior, what is the - observed behavior. - - it() The Crossroads configuration in the file tt(crossroads.conf) - - it() The version of Crossroads and all compile-time settings, - obtained via tt(crossroads -C).) - - -subsect(Copyright and Disclaimer) - -Crossroads is distributed as-is, without assumptions of fitness or -usability. You are free to use crossroads to your liking. It's free, -and as with everything that's free: there's also no -warranty. Crossroads is distibuted under the GNU General Public -Licence, version 3. See lurl(http://crossroads.e-tunity.com) for more -information. - -You are allowed to make modifications to the source code of -crossroads, and you are allowed to (re)distribute crossroads, as long -as you include this text, all sources, and if applicable: all your -modifications, with each distribution. - -While you are allowed to make any and all changes to the sources, I -would appreciate hearing about them. If the changes concern new -functionality or bugfixes, then I'll include them in a next release, -stating full credits. If you want to seriously contribute (to which -you are heartily encouraged), then mail me and I'll get you access to -the Crossroads SVN repository, so that you can update and commit as -you like. - -subsect(Terminology) - -Throughout this document, the following terms are used: footnote(Many -more meanings of the terms will exist - yes, I am aware of that. I'm -using the terms here in a very strict sense.) - -description( - dit(A client) is a process that initiates a network connection - to get contact with some service. - dit(A service) or bf(server process) or bf(listener) - is a central application - that accepts network connections from clients and sevices - them. - dit(Back ends) are locations where crossroads looks in - order to service its clients. Crossroads sits 'in between' - and does its tricks. Therefore, as far as the back ends - are concerned, crossroads behaves like a client. As far as - the true client is concerned, crossroads behaves like the - service. The communication is however transparent: neither - client nor back end are aware of the middle position of - crossroads. - dit(A connection) is a network conversation between client and service, - where data are transferred to and fro. As - far as crossroads is concerned, success means that a - connection can be established without errors on - the network level. Crossroads isn't aware of service - pecularities. E.g., when a webserver answers tt(HTTP/1.0 - 500 Server Error) then crossroads will see this as a - succesful connection, though the user behind a browser may - think otherwise. - dit(Back end selection algorithms) are methods by which - crossroads determines which back end it will talk to - next. Crossroads has a number of built-in algorithms, - which may be configured per service. - dit(Back end states) are the statusses of each back end that - is known to crossroads. A back end may be available, - (temporarily) unavailble or truly down. When a back end is - temporarily unavailable, then crossroads will periodically - check whether the back end has come to life yet (that is, - if configured so). - dit(A spike) is a sudden increase in activity, leading to - extra load on a given service. When crossroads is in - effect and when the spike occurs in one connection, - then obviously the spike will also appear at one - of the back ends. However, crossroads will see the spike - and will make sure that a subsequent request goes to an - other back end. In contrast, when several connections - arrive simultaneously and cause a spike, then crossroads - will be able to distribute the connections over several - back ends, thereby 'flattening out' the increase. - dit(Load balancing) means that incoming client requests are - distributed over more than just one back end (which wouldn't be the - case if you wouldn't be running crossroads). Enabling load - balancing is nothing more than duplicating services over - more than one back end, and having something (in this - case: crossroads) distribute the requests, so that per - back end the load doesn't get too high. - dit(An HTTP session) is a series of separate network connections - that originate from one browser. E.g., to fill the display - with text and images, the browser hits a website several times. - An HTTP session may even span several - screens. E.g., a website registration dialog may involve 3 - screens that when called from the same browser, - form a logical group of some sort. - dit(Headers) or bf(header lines) are specific parts of an HTTP - message. Crossroads has directives to add or modify - headers that are part of the request that a browser sends - to server, or those that are part of the server. - dit(Session stickiness) means that when a browser starts an - HTTP dialog, the balancer makes sure that it 'sticks' to - the same back end (i.e., subsequent requests from the - browser are forced to go to the same back end, instead of - being balanced to other ones). - dit(Back end usage) is measured by crossroads in order to be - able to determine back end selection. Crossroads stores - information about the number of active connections, the - transferred bytes and - about the connection duration. These numbers can be used to - estimate which back end is the least used -- and - therefore, presumably, the best candidate for a new - request. - dit(Fail over) is almost always used when load balancing is in - effect. The distributor of client requests (crossroads of - course) can also monitor back ends, so that incase a back - end is 'down', it is no longer accessed. - dit(Service downtime) normally occurs when a service is - switched off. Downtime is obviously avoided when fail over - is in effect: a back end can be taken out of service in a - controlled manner, without any client noticing it. -) - -subsect(Porting Issues) - -This section lists some caveats when converting Crossroads -configurations to new versions. Given the changes of the syntax of the -configuration file of Crossroads, existing configuration files may -need to be made suitable for new versions. - -subsubsect(Porting issues for pre-1.80 installations) - - Please be aware that as of version 1.80, the Crossroads - configuration file may be located in an 'etc' directory along the - path where Crossroads was started. E.g., if the Crossroads binary - is tt(/usr/local/bin/crossroads), then Crossroads will read the - configuration file tt(/usr/local/etc/crossroads.conf) before - defaulting to tt(/etc/crossroads.conf). When installing - configurations to such 'etc' directories along the path, be sure - to remove stale configuration files. Crossroads will only parse - one primary configuration file. - -subsubsect(Porting issues for pre-1.63 installations) - - The parser of Crossroads 1.63 has become even - stricter. HTTP-related commands, such as tt(addclientheader), may - only occur when the service type is tt(http). - - Previously such directives were allowed, albeit they would be - ineffective. - -subsubsect(Porting issues for pre-1.50 installations) - - As of version 1.50, the command tt(crossroads tell) has been - changed. To set a state, the command must be used as follows: - - center(tt(crossroads) tt(tell) em(service) em(backend) tt(state) - em(newstate)) - - The keyword tt(state) was added. This is because tt(crossroads - tell) can also set a new server address; in that case the keyword - tt(server) is required. If you have automatic health checkers or - the like that change the back end states, please modify the - commands to suit this. - -subsubsect(Porting issues for pre-1.43 installations) - - As of version 1.43, the shared memory key calculations are based - on a different algorithm. This key is e.g. necessary when starting - and stopping Crossroads; in both actions, the key must be computed - in the same way. - - Therefore, when upgrading Crossroads, make sure that you stop a - running Crossroads daemon using the same binary that started - it. After this, install a new binary, and start the daemon using - the new binary. - - (Incidentally, this is always a good idea, but especially so when - the 'old' binary is pre-1.43 and the 'new' one is post-1.43.) - - Furthermore, shell-style comment is no longer supported as of - 1.43. The reason is that 1.43 introduces the hash mark as a - preprocessor token (in tt(#include), tt(#define)). Therefore, if - your configuration files use shell-style comment, please convert - this to bf(C) or bf(C++) style. - -subsubsect(Porting issues for pre-1.21 installations) - - As of version 1.21, the event-hook directives tt(onsuccess) and - tt(onfailure) no longer exists. - - itemization( - it() Please replace tt(onsuccess) by tt(onstart); - it() Please replace tt(onfailure) bu tt(onfail); - it() Note that there is a new hook tt(onend).) - - The commands that are run via tt(onstart), tt(onend) or tt(onfail) - are subject to format expansion; e.g., tt(%1w) is expanded to the - weight of the first back end, etc.. See section ref(config) for details. - -subsubsect(Porting issues for pre-0.26 installations) - - As of version 0.26 the syntax of the configuration file has - changed. In particular: - - itemization( - it() The keyword tt(maxconnections) is now used instead of - tt(maxclients); - it() The keyword tt(connectiontimeout) is now used instead of - tt(sessiontimeout).) - - Therefore when converting configuration files to the new syntax, - the above keywords must be changed. (The reason for these changes - is that 0.26 introduces em(sticky HTTP sessions) that span - multiple TCP connections, and the term - em(session) is used strictly in that sense -- and no longer for a - TCP connection.) - -subsubsect(Porting issues for pre-1.08 installations) - - As of version 1.08, the following directives no longer are - supported: - - itemization( - it() tt(insertstickycookie) was replaced by the more generic - directive tt(addclientheader). E.g., instead of nl() - tt(insertstickycookie "XRID=100; Path=/";) nl() - the syntax is now nl() - tt(addclientheader "Set-Cookie: XRID=100; Path=/";) - - it() tt(insertrealip) was replaced by the more generic - directive tt(setserverheader). E.g., instead of nl() - tt(insertrealip on;) nl() - the syntax is now nl() - tt(setserverheader "XR-Real-IP: %r";) nl() - This incidentally also makes it possible to change the header - name (here: tt(XR-Real-IP)).) - diff --git a/doc/main/keywords.yo b/doc/main/keywords.yo @@ -1,63 +0,0 @@ -tt(service) -tt(port) -tt(verbosity) -tt(maxconnections) -tt(type) -tt(any) -tt(http) -tt(backend) -tt(server) -tt(bindto) -tt(connectiontimeout) -tt(on) -tt(off) -tt(dispatchmode) -tt(roundrobin) -tt(random) -tt(byduration) -tt(bysize) -tt(byconnections) -tt(byorder) -tt(byclientip) -tt(over) -tt(decay) -tt(revivinginterval) -tt(checkinterval) -tt(retries) -tt(shmkey) -tt(weight) -tt(onstart) -tt(onfail) -tt(backlog) -tt(throughputlog) -tt(trafficlog) -tt(httptiminglog) -tt(stickycookie) -tt(addclientheader) -tt(setclientheader) -tt(appendclientheader) -tt(addserverheader) -tt(setserverheader) -tt(appendserverheader) -tt(allowfrom) -tt(denyfrom) -tt(allowfile) -tt(denyfile) -tt(externalhandler) -tt(useraccount) -tt(onend) -tt(headerinspection) -tt(deep) -tt(shallow) -tt(state) -tt(available) -tt(unavailable) -tt(down) -tt(options) -tt(logactivity) -tt(tcpbuffersize) -tt(dnscachettl) -tt(logfacility) -tt(shmpermissions) -tt(sloppyportbind) -tt(leaveprocesstitle) diff --git a/doc/main/tips.yo b/doc/main/tips.yo @@ -1,54 +0,0 @@ -The following sections elaborate on the directives as described in -section ref(config) to illustrate how crossroads works and to help you -achieve the "optimal" balancing configuration. - -subsect(Configuration examples) -includefile(tips/examples) - -subsect(How back ends are selected in load balancing) label(howselected) -includefile(tips/howselected) - -subsect(Periodic probes and wake up calls) -includefile(tips/periodic) - -subsect(Throttling the number of concurrent connections) -includefile(tips/throttling) - -subsect(TCP Session Stickiness) -includefile(tips/tcpstickiness) - -subsect(HTTP Session Stickiness) -includefile(tips/httpstickiness) - -subsect(Passing the client's IP address) -includefile(tips/clientip) - -subsect(Deep or shallow HTTP header inspection) -includefile(tips/deeporshallow) - -subsect(Debugging network traffic) -includefile(tips/debugging) - -subsect(IP filtering: Limiting Access by Client IP Address) -includefile(tips/ipfiltering) - -subsect(Using an external program to dispatch) label(externalhandler) -includefile(tips/externalhandler) - -subsect(Linux and ip_conntrack_max) -includefile(tips/ipconntrackmax) - -subsect(Marking back ends as bad after more than one try) -includefile(tips/retries) - -subsect(Using the Web Interface crossroads-mgr) -includefile(tips/webinterface) - -subsect(Rendering Crossroads' status in a web page) label(xmlstatus) -includefile(tips/rendering) - -subsect(Crossroads and DNS caching) -includefile(tips/dnscaching) - -subsect(Managing a Pool of Virtual Back Ends) -includefile(tips/virtual) diff --git a/doc/main/tips/clientip.yo b/doc/main/tips/clientip.yo @@ -1,78 +0,0 @@ -Since Crossroads just shuttles bytes to and fro, meta-information of -network connections is lost. As far as the back ends are concerned, -their connections originate at the Crossroads junction. -For example, standard Apache access logs will show the IP address of -Crossroads. - -In order to compensate for this, Crossroads can insert a special -header in HTTP connections, to inform the back end of the original -client's IP address. In order to enable this, the Crossroads -configuration must state the following: - -itemization( - it() The service type must be tt(http), and not tt(any); - it() Make sure that the tt(headerinspection) is tt(deep) (or that - there is no tt(headerinspection) statement, since tt(deep) is - the default, see section ref(tips/deeporshallow)); - it() In the back end definition, the following statement must - occur: nl() - tt(addserverheader "X-Real-IP: %r";) nl() - You are of course free to choose the header name; the here - used tt(X-Real-IP) is a common name for this purpose.) - -After this, HTTP traffic that arrives at the back ends has a new -header: tt(X-Real-IP), holding the client's IP address. -bf(Note that) once the type is set to tt(http), Crossroads' -performance will be hampered -- all passing messages will have to be -unpacked and analyzed. - -subsubsect(Sample Crossroads configuration) - -The below sample configuration shows two HTTP back ends that receive -the client's IP address: - -verb( -service www { - port 80; - type http; - revivinginterval 5; - dispatchmode roundrobin; - - backend one { - server 10.1.1.100:80; - addserverheader "X-Real-IP: %r"; - } - - backend two { - server 10.1.1.200:80; - addserverheader "X-Real-IP: %r"; - } -}) - - -subsubsect(Sample Apache configuration) - -The method by which each back end analyzes the header tt(X-Real-IP) -will obviously be different per server implementations. However, a -common method with the Apache webserver is to log the client's IP -address into the access log. - -Often this is accomplished using the log format tt(custom), defined as -follows: - -verb(\ -LogFormat "%h %l %u %t %D \"%r\" %>s %b" common -CustomLog logs/access_log common) - -The first line defines the format tt(common), with the remote host -specified by tt(%h). The second line sends access information to a log -file tt(logs/access_log), using the previously defined format -tt(common). - -Furtunately, Apache's tt(LogFormat) allows one to log contents of -headers. By replacing the tt(%h) with tt(%{X-Real-IP}i), the desired -information is sent to the log. Therefore, normally you can simply -redefine the tt(common) format to - -verb(\ -LogFormat "%{X-Real-IP}i %l %u %t %D \"%r\" %>s %b" common) diff --git a/doc/main/tips/debugging.yo b/doc/main/tips/debugging.yo @@ -1,107 +0,0 @@ - Incase the traffic between client and backend must be debugged, - the statement tt(trafficlog) em(filename) can be issued. This - causes the traffic to be dumped in hexadecimal format to the - stated filename. - - Traffic sent by the client is prefixed by a bf(C), traffic sent by - the back end is prefixed by a bf(B). Below is a sample traffic - dump of a browser trying to get a HTML page. The server replies - that the page was not modified. - - verb(\ -C 0000 47 45 54 20 68 74 74 70 3a 2f 2f 77 77 77 2e 63 GET http://www.c -C 0010 73 2e 68 65 6c 73 69 6e 6b 69 2e 66 69 2f 6c 69 s.helsinki.fi/li -C 0020 6e 75 78 2f 6c 69 6e 75 78 2d 6b 65 72 6e 65 6c nux/linux-kernel -C 0030 2f 32 30 30 31 2d 34 37 2f 30 34 31 37 2e 68 74 /2001-47/0417.ht -C 0040 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a 43 6f 6e ml HTTP/1.1..Con -C 0050 6e 65 63 74 69 6f 6e 3a 20 63 6c 6f 73 65 0d 0a nection: close.. -. -. etcetera -. -B 0000 48 54 54 50 2f 31 2e 30 20 33 30 34 20 4e 6f 74 HTTP/1.0 304 Not -B 0010 20 4d 6f 64 69 66 69 65 64 0d 0a 44 61 74 65 3a Modified..Date: -B 0020 20 54 75 65 2c 20 31 32 20 4a 75 6c 20 32 30 30 Tue, 12 Jul 200 -B 0030 35 20 30 39 3a 34 39 3a 34 37 20 47 4d 54 0d 0a 5 09:49:47 GMT.. -B 0040 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 Content-Type: te -B 0050 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset -. -. etcetera -.) - - Turning on traffic dumps will em(significantly) slow down - crossroads. - - Besides tt(trafficlog), there is also a directive - tt(throughputlog). This directive also takes one argument, a - filename. The file is appended, and the following information is - logged: - - itemization( - it() The process ID of the crossroads image that serves the - TCP connection; - it() The time of the request, in seconds and microseconds - since start of the run; - it() A bf(C) when the request originated at the client, or - bf(B) when the request originated at the back end; - it() The first 100 bytes of the request.) - - As an example, consider the following (the lines are shortened for - brevity and prefixed by line numbers for clarity): - - verb( -1 0027566 0.000000 C 462 GET http://public.e-tunity.com/ ... -2 0027566 0.052974 B 394 HTTP/1.1 200 OK..Via: 1.1 tinyp ... -3 0027566 0.053413 B 1073 <html>.. <head>.. <titl ... -4 0027566 0.053589 B 1448 1> </td>.. </tr>.. </table ... -5 0027566 0.065679 B 725 for more. info.... <li> To ...) - - This tells us that: - - itemization( - it() Line 1: PID 27566 served a request that originated at the - client (C). The corresponding timing is 0 seconds, which means - that this is the start of the run. The request was 462 bytes - long and started with tt(GET http://public...) - - it() Line 2: 0.052974 seconds later, the backend (B) replied - with tt(HTTP/1.1 200 OK). - - it() Lines 3, 4 and 5: The backend (B) proceeded by sending - chunks.) - - The buffer sizes (462, 394 and so on) also tell us that the - network sends relatively small chunks. In this case we might save - some processing memory by setting the TCP buffer size to say 2K, - instead of the default 5K. In this situation, most of the default - 5K buffers will be unused. Setting the TCP buffer size to 2K can be done - using the flag tt(-B 2048) or by using the statement - tt(tcpbuffersize) tt(2048;) in an tt(options) block of the configuration. - - It is also worth while remembering that the start time of a bf(C) - request is the time that crossroads sees the activity. Any latency - between the true client and crossroads is obviously not - included. This is illustrated by the below simple ASCII art: - - verb( -client ---->---->---->--->*crossroads ====>====>====> - \ - back end - / -client ----<----<----<---< crossroads ====<====<====< -) - - This simple picture shows a typical request that originates - at a client, travels to crossroads, and is relayed via the back - end. The bf(C) entry in a throughput log is the time when - crossroads sees the request, indicated by an asterisk. The bf(B) - entries are the times that it takes the back end to answer, - indicated by tt(===) style lines. Therefore, the true roundtrip - time will be longer than the number of seconds that are logged in - the throughput log: the latency between client and crossroads - isn't included in that measurement. - - Summarizing, the throughput times and buffer sizes of a - client-back end connection can be analyzed using the directive - tt(throughputlog). In a real-world analysis, you'd probably want - to write up a script to analyze the output and to compute round - trip times. Such scripts are not (yet) included in Crossroads. diff --git a/doc/main/tips/deeporshallow.yo b/doc/main/tips/deeporshallow.yo @@ -1,103 +0,0 @@ -The service-level directive tt(headerinspection) defines which HTTP -headers Crossroads will analyze. Often, several HTTP requests and -responses will be served over one network -link. Browsers and servers will try to keep a TCP link open so that it -may be re-used; this is a measure to increase efficiency and to shorten load -times of pages. E.g., a typical HTML page -will require a style sheet and a few images - and these can be -retrieved over the same link that originally served the HTML page. - -Re-using the TCP link occurs more often than not. It is the default in -HTTP/1.1 (unless tt(Connection: close) is specified as one of the HTTP -headers). The older HTTP protocol, HTTP/1.0, by default passes just -one request and response over a TCP link, after which the link is -closed (unless tt(Connection: keep-alive) is specified as one of the -HTTP headers). - -If you define your service as tt(type http), then by default -Crossroads will inspect all HTTP headers that it sees: not only the -first browser request and server answer, but also subsequent requests -and answers that travel over the same link. This is called 'deep -inspection mode', in which Crossroads applies -directives such as tt(addclientheader) to all header -blocks. Inspecting the full TCP stream to catch header blocks can be -resource consuming. You can optionally make Crossroads's resource -consumption 'lighter' by instructing it to inspect only the first -HTTP header block, and to simply pass-through over all subsequent information -(which might well include next header blocks of a re-used TCP -connection) This is done using the directive tt(headerinspection): - -verb(\ -service web { - type http; - headerinspection shallow; - backend a { .... } - backend b { .... } -}) - -The situations where tt(shallow) mode can be used, depends on what you -need to do: - -itemization( - it() tt(shallow) mode can be used when the inspection and - modification of the first HTTP header block suffices. For example, - HTTP session stickiness is a good example: - -verb(\ -service web { - type http; - headerinspection shallow; - backend a { - server 10.1.1.1:80; - stickycookie "BalancerID=1"; - addclientheader "Set-Cookie: BalancerID=1"; - } - . - . other back ends are defined here - . -}) - - In this case, Crossroads will inspect only the first header block that - the client sends for the presence of a cookie tt(BalancerID). If the - cookie has value 1, then the request will be routed to back end - tt(a). Similarly, Crossroads will inspect only the first header block - that the server sends, and will inject a tt(Set-Cookie) - instruction. - - In this example, deep mode is not necessary because Crossroads - will use the first header that the client sends to route the - information to a given back end. - If more than one HTTP transactions follow over - the same TCP link, then by definition the link will go to the - same back end - even without inspecting all HTTP headers that - follow the first ones. - - it() tt(deep) mode is necessary in situations where all header - blocks must be inspected and modified. E.g., if you want to hide your - HTTP server identification, you might use: - -verb(\ -service web { - type http; - headerinspection deep; - backend a { - server 10.1.1.1:80; - setclientheader "Server: MyWebServer"; - } - . - . other back ends are defined here - . -}) - - Here, all HTTP header blocks that come from the server will be parsed, - and tt(Server) headers will be overwritten. - In a similar vein, if you want to pass the client's IP address, you - will also need deep mode. - - In these examples, tt(shallow) mode is not usable, because the - outbound header modifications should apply to all headers of a - given series. Imagine that one would use tt(shallow) mode - here: then, in a series of 5 HTTP transactions that pass over - the same TCP link, only the first transaction would hide the - HTTP server signature. All subsequent transactions would still - show the HTTP server signature to the world.) diff --git a/doc/main/tips/dnscaching.yo b/doc/main/tips/dnscaching.yo @@ -1,21 +0,0 @@ - -The option tt(-d) allows you to control Crossroads' built in DNS -caching mechanism. Most often you will not need to use this: -DNS lookups in Crossroads occur only to find back ends; and back ends -are usually 'near' to the balancer. - -You might want or need to use DNS caching if: - -itemization( - it() Back end servers are specified as hostnames, and not as - IP addresses; - it() DNS resolving of those host names is perceptibly slow.) - -You can test DNS resolving from the command line using e.g. the -commands tt(nslookup) and tt(host). - -If DNS resolving is an issue, then you can specify the flag tt(-d) -em(nsec) on the command line while invoking Crossroads. This instructs -Crossroads to use its DNS cache to store results. Each result is -stored for up to em(nsec) seconds - after that, a new request for the -back end will lead to a new DNS lookup. diff --git a/doc/main/tips/examples.yo b/doc/main/tips/examples.yo @@ -1,236 +0,0 @@ - -subsubsect(A load balancer for three webserver back ends) - -The following configuration example binds crossroads to port 80 of the -current server, and distributes the load over three back ends. This -configuration shows most of the possible settings. - -verb(\ -service www { - /* We don't need session stickyness. */ - type any; - - /* Port on which we'll listen in this service: required. */ - port 8000; - - /* What IP address should this service listen? Default is 'any'. - * Alternatively you can state an explicit IP address, such as - * 127.0.0.1; that would bind the service only to 'localhost'. */ - bindto any; - - /* Verbose reporting or not. Default is off. */ - verbosity on; - - /* Dispatching mode, or: How to select a back end for an incoming - * request. Possible values: - * roundrobin: just the next back end in line - * random: like roundrobin, but at random to make things more - * confusing. Probably only good for testing. - * bysize: The backend that transferred the least nr of bytes - * is the next in line. As a modifier you can say e.g. - * bysize over 10, meaning that the 10 last connections will - * be used to compute the transfer size, instead of all - * transfers. - * byduration: The backend that was active for the shortest time - * is the next in line. As a modifier you can say e.g. - * byduration of 10 to compute over the last 10 connections. - * byconnections: The back end with the least active connections - * is the next ine line. - * byorder: The first available back end is always taken. - */ - dispatchmode byduration over 5; - - /* Interval at which we'll check whether a temporarily unavailable - * backend has woken up. - */ - revivinginterval 5; - - /* TCP backlog of connections. Default is 0 (no backlog, one - * connection may be active). - */ - backlog 5; - - /* For status reporting: a shared memory key. Default is the same - * as the port number, OR-ed by a magic number. - */ - shmkey 8000; - - /* This controls when crossroads should consider a connection as - * finished even when the TCP sockets weren't closed. This is to - * avoid hanging connections that don't do anything. NOTE THAT when - * crossroads cuts off a connection due to timeout exceed, this is - * not marked as a failure, but as a success. Default is 0: no timeout. - */ - connectiontimeout 300; - - /* The max number of allowed client connections. When present, connections - * won't be accepted if the max is about to be exceeded. When - * absent, all connections will be accepted, which might be misused - * for a DOS attack. - */ - maxconnections 300; - - /* Now let's define a couple of back ends. Number 1: */ - backend www_backend_1 { - /* The server and its port, the minimum configuration. */ - server httpserver1; - port 9010; - /* The 'decay' of usage data of this back end. Only relevant - * when the whole service has 'dispatchmode bysize' or - * 'byduration'. The number is a percentage by which the usage - * parameter is decreased upon each connection of an other back - * end. - */ - decay 10; - - /* To see what's happening in /var/log/messages: */ - verbosity on; - } - - /* The second one: */ - backend www_backend_2 { - /* Server and port */ - server httpserver2; - port 9011; - - /* Verbosity of reporting when this back end is active */ - verbosity on; - - /* Decay */ - decay 10; - - /* This back end is twice as weak as the first one */ - weight 2; - - /* Event triggers for system commands upon succesful activation - * and upon failure. - */ - onsuccess echo 'success on backend 2' | mail root; - onfailure echo 'failure on backend 2' | mail root; - } - - /* And yet another one.. this time we will dump the traffic - * to a trace file. Furthermore we don't want more than 10 concurrent - * connections here. Note that there's also a total maxconnections for the - * whole service. - */ - backend www_backend_3 { - server httpserver3; - verbosity on; - port 9000; - verbosity on; - decay 10; - trafficlog /tmp/backend.3.log; - maxconnections 10; - } -}) - -subsubsect(An HTTP forwarder when travelling) - -As another example, here's my configuration tt(crossroads.conf) that I -use on my Unix laptop. The problem that I face is that I need many -HTTP proxy configurations (at home, at customers' sites and so on) but -I'm too lazy to reconfigure browsers all the time. - -Here's how it used to be before crossroads: - -itemization( - it() At home, I would surf through a squid proxy on my local - machine. The browser proxy setting is then - tt(http://localhost:3128). - - it() Sometimes I start up an SSH tunnel to our offices. The - tunnel has a local port 3129, and connects to a squid proxy on - our e-tunity server. Hence, the browser proxy is then - tt(http://localhost:3129). - - it() At a customer's location I need the proxy - tt(http://10.120.34.113:8080), because they have configured it - so. - - it() And in yet other instances, I use a HTTP diagnostic tool - url(Charles)(http://www.xk72.com/charles) - that sits between browser and website and shows me - what's happening. I run charles on my own machine and it - listens to port 8888, behaving like a proxy. The browser - configuration for the proxy is then - tt(http://localhost:8888).) - -Here's how it works with a crossroads configuration: - -itemization( - it() I have configured my browsers to use - tt(http://localhost:8080) as the proxy. For all situations. - - it() I use the following crossroads configuration, and let - crossroads figure out which proxy backend works, and which - doesn't. Note two particularities: - - itemization( - it() The statement tt(dispatchmode byorder). This - makes sure that once crossroads determines which - backend works, it will stick to it. This usage of - crossroads doesn't need to balance over more than one - back end. - - it() The statement tt(bindto 127.0.0.1) makes sure - that requests from other interfaces than loopback - won't get serviced.) - -verb(\ -service HttpProxy { - port 8080; - bindto 127.0.0.1; - verbosity on; - dispatchmode byorder; - revivinginterval 15; - - backend Charles { - server localhost:8888; - verbosity on; - } - - backend CustomerProxy { - server 10.120.34.113:8080; - verbosity on; - } - - backend SshTunnel { - server localhost:3129; - } - - backend LocalSquid { - server localhost:3128; - } -})) - -As a final note, the commandline argument tt(tell) can be used to -influence crossroad's own detection mechanism of back end availability -detection. E.g., if in the above example the back ends tt(SshTunnel) -and tt(LocalSquid) are both active, then tt(crossroads tell httpproxy -sshtunnel down) will 'take down' the back end tt(SshTunnel) -- and -will automatically cause crossroads to switch to tt(LocalSquid). - - -subsubsect(SSH login with enforced idle logout) - -The following example shows how crossroads 'throttles' SSH -logins. Connections are accepted on port -22 (the normal SSH port) and forwarded to the actual SSH daemon -which is running on port 2222. - -Note the usage of the -tt(connectiontimeout) directive. This makes sure that users are logged -out after 10 minutes of inactivity. Note also the tt(maxconnections) -setting, this makes sure that no more than 10 concurrent logins occur. - -verb(\ -service Ssh { - port 22; - backlog 5; - maxconnections 10; - connectiontimeout 600; - backend TrueSshDaemon { - server localhost:2222; - } -}) diff --git a/doc/main/tips/externalhandler.yo b/doc/main/tips/externalhandler.yo @@ -1,448 +0,0 @@ -As mentioned before, Crossroads supports several built-in dispatch -modes. However, you are always free to hook-in your own dispatch mode -that determines the next back end using your own specific -algorithm. This section explains how to do it. - -subsubsect(Configuring the external handler) - -First, the tt(dispatchmode) statement needs to inform Crossroads that -an external program will do the job. The syntax is: tt(dispatchmode -externalhandler) em(program arguments). The em(program) must point to -an executable program that will be started by Crossroads. The -specifier em(arguments) can be anything you want; those will be the -arguments to your dispatch helper. You use the following special -format specifiers in the argument list: - -INCLUDEFILE(formattable) - -Note that the format specifiers such as tt(%b) don't make sense in the -phase in which an external handler is called, since there is no -current back end yet (the job of the handler is to supply one, so at -the time of calling, tt(%b) is undefined). - -subsubsect(Writing the external handler) - -The external handler is activated using the arguments that are -specified in the configuration file. The external handler can do -whatever it wants, but ultimately, it must write a back end name on -its em(stdout). Crossroads reads this, and if the back end is -available, uses that back end for the connection. - -subsubsect(Examples of external handlers) - -This section shows some examples of Crossroads configurations -vs. external handlers. The sample handlers that are shown here, are -also included in the Crossroads distribution, under the directory -tt(etc/). Also note that the examples shown here are just -quick-and-dirty Perl scripts, meant to illustrate only. Your -applications may need other external handlers, but you can use the -shown scripts as a starting point. - -subsubsubsect(Round-robin dispatching) - -This example is trivial in the sense that round-robin dispatching is -already built into Crossroads, so -that using an external handler for this purpose only slows down -Crossroads. However, it's a good starting example. - -The Crossroads configuration is shown below: - -verb(\ -service test { - port 8001; - verbosity on; - revivinginterval 5; - - dispatchmode externalhandler - /usr/local/src/crossroads/etc/dispatcher-roundrobin - %1b %1a %2b %2a; - - backend testone { - server localhost:3128; - verbosity on; - } - backend testtwo { - server locallhost:3128; - verbosity on; - } -}) - -The relevant tt(dispatchmode) statement invokes the external program -tt(dispatcher-roundrobin) with four arguments: the name of the first -back end (tt(testone)), its availability (0 or 1), the name of the -second back end (tt(testtwo)) and its availability (0 or 1). - -The external handler, which is also included in the Crossroads -distribution, is shown below. It is a Perl script. - -verb(\ -#!/usr/bin/perl - -use strict; - -# Example of a round-robin external dispatcher. This is totally -# superfluous, Crossroads has this on-board; if you use the external -# program for determining round-robin dispatching, then you'll only -# slow things down. This script is just meant as an example. - -# Globals / configuration -# ----------------------- -my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress -my $statefile = '/tmp/rr.last'; # Where we keep the last used - -# Logging -# ------- -sub msg { - return if ($log eq '/dev/null' or $log eq ''); - open (my $of, ">>$log") or return; - print $of (scalar(localtime()), ' ', @_); -} - -# Read the last used back end -# --------------------------- -sub readlast() { - my $ret; - - if (open (my $if, $statefile)) { - $ret = <$if>; - chomp ($ret); - close ($if); - msg ("Last used back end: $ret\n"); - return ($ret); - } - msg ("No last-used back end (yet)\n"); - return (undef); -} - -# Write back the last used back end, reply to Crossroads and stop -# --------------------------------------------------------------- -sub reply ($) { - my $last = shift; - - if (open (my $of, ">$statefile")) { - print $of ("$last\n"); - } - print ("$last\n"); - exit (0); -} - -# Main starts here -# ---------------- - -# Collect the cmdline arguments. We expect pairs of backend-name / -# backend-availablility, and we'll store only the available ones. -msg ("Dispatch request received\n"); -my @backend; -for (my $i = 0; $i <= $#ARGV; $i += 2) { - push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]); -} -msg ("Available back ends: @backend\n"); - -# Let's see what the last one is. If none found, then we return the -# first available back end. Otherwise we need to go thru the list of -# back ends, and return the next one in line. -my $last = readlast(); -if ($last eq '') { - msg ("Returning first available back end $backend[0]\n"); - reply ($backend[0]); -} - -# There **was** a last back end. Try to match it in the list, -# then return the next-in-line. -for (my $i = 0; $i < $#backend; $i++) { - if ($last eq $backend[$i]) { - msg ("Returning next back end ", $backend[$i + 1], "\n"); - reply ($backend[$i + 1]); - } -} - -# No luck.. run back to the first one. -msg ("Returning first back end $backend[0]\n"); -reply ($backend[0]);) - -The working of the script is basically as follows: - -itemization( - it() The argument list is scanned. Back ends that are - available are collected in an array tt(@backend). - - it() The script queries a state file tt(/tmp/rr.last). If a - back end name occurs there, then the next back end is looked - up in tt(@backend) and returned to Crossroads. If no last back - is unknown or can't be matched, then the first available back - end (first element of tt(@backend)) is returned to Crossroads. - - it() Informing Crossroads is done via the subroutine - tt(reply()). This code writes the selected back end to file - tt(/tmp/rr.last) (for future usage) and prints the back end - name to em(stdout). - - it() The script logs its actions to a file - tt(/tmp/exthandler.log). This log file can be inspected for - the script's actions.) - - -subsubsubsect(Dispatching by the client IP address) - -The following example shows a useful real-life situation that -illustrates how dispatching by client IP address works. bf(Note that) -as of Crossroads 1.31, tt(dispatchmode byclientip) is implemented -- -so that the below description is somewhat superfluous. The code -snippets however can help you in modelling your own specific dispatch -modes, aided by external helpers. (Incidentally, the tt(dispatchmode) -tt(byclientip) was modeled after the shown script. The functionality -proved so useful that it was embedded into Crossroads.) - -Our hypothetical dispatching situation is as follows: - -itemization( - it() Crossroads is used as a single-address point to forward - Remote Desktop requests to a farm of Windows systems, where - users can work via remote access; - - it() However, users may stop their session, and when they - re-connect, they expect to be sent to the Windows system that - they had worked on previously; - - it() Client PC's have their distinct IP addresses, which - distinguish them. - - it() Of four windows systems, two are large servers, and two - are small ones. We'll want to assign large servers to clients - when we have a choice.) - -The requirements resemble session stickiness in HTTP, except that the remote -desktop protocol doesn't support stickiness. This situation is a -perfect example of how an external handler can help: -the potential delay due to the calling of an external -handler won't even be noticed. Remote Desktop is a network service where -the connection time isn't critical; users won't notice a slightly -larger connection time due to the fact that Crossroads invokes an -external program. We expect only a few (albeit lengthy) TCP connections. - -The approach to the solution of this problem uses several external -program hooks: - -itemization( - it() An external dispatcher handler will be responsible for - suggesting a back end, given a client IP and given the current - timestamp. This handler will consult an internal - administration to see whether the stated IP address should - re-use a back end, or to determine which back end is free for usage. - it() An external hook tt(onstart) will be responsible for - updating the internal administration; i.e., to flag a back end - as 'occupied'. - it() The external hooks tt(onfailure) and tt(onend) will be - responsible for flagging a back end as 'free' again; i.e., for - erasing any previous information that states that the back end - was occupied.) - -The Crossroads configuration is shown below. Only four Windows back -ends are shown. Each back end is configured on a -given IP address, port 3389, and is limited to one concurrent connection -(otherwise a new user might 'steal' a running desktop session). - -verb(\ -service rdp { - port 3389; - revivinginterval 5; - - /* rdp-helper dispatch IP STAMP ... will suggest a back end to use, - * arguments are for all back ends: name, availability, weight */ - dispatchmode externalhandler - /usr/local/src/crossroads/etc/rdp-helper dispatch %r %e - %1b %1a %1w - %2b %2a %2w - %3b %3a %3w - %4b %4a %4w; - - backend win1 { - server 10.1.1.1:3389; - maxconnections 1; - /* rdp-helper start IP STAMP BACKEND will log the actual start - * of a connection; - * rdp-helper end IP will log the ending of a connection */ - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } - backend win2 { - server 10.1.1.2:3389; - maxconnections 1; - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } - backend win3 { - server 10.1.1.3:3389; - maxconnections 1; - weight 2; - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } - backend win4 { - server 10.1.1.4:3389; - maxconnections 1; - weight 3; - onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b; - onend /usr/local/src/crossroads/etc/rdp-helper end %r; - onfail /usr/local/src/crossroads/etc/rdp-helper end %r; - } -}) - -Depending on the dispatcher stage, the exernal handler tt(rdp-helper) -is invoked in different ways: - -description( - dit(During dispatching) the helper is called to suggest a back - end. The arguments are an action indicator tt(dispatch), the - client's IP address, the timestamp, and four triplets that - represent back ends: per back end its name, its availability, - and its weight. The purpose of the helper is to tell - Crossroads which back end to use. - - dit(During connection start) the helper will be invoked to - inform it of the start of a connection, given a client IP - address. - - dit(When a connection terminates) the helper will be invoked - to inform it that the connection has ended.) - -Here's the external handler as Perl script. It uses the module -tt(GDBM_File) which most likely will not be part of standard Perl -distributions, but can be added using CPAN. (Alternatively, any other -database module can be used.) - -verb(\ -#!/usr/bin/perl - -use strict; -use GDBM_File; - -# Global variables and configuration -# ---------------------------------- -my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress -my $cdb = '/tmp/client.db'; # GDBM database of clients -my %db; # .. and memory representation of it -my $timeout = 24*60*60; # Timeout of a connection in secs - -# Logging -# ------- -sub msg { - return if ($log eq '/dev/null' or $log eq ''); - open (my $of, ">>$log") or return; - print $of (scalar(localtime()), ' ', @_); - close ($of); -} - -# Reply a back end to the caller and stop processing. -# --------------------------------------------------- -sub reply ($) { - my $b = shift; - msg ("Suggesting $b to Crossroads.\n"); - print ("$b\n"); - exit (0); -} - -# Is a value in an array -# ---------------------- -sub inarray { - my $val = shift; - for my $other (@_) { - return (1) if ($other eq $val); - } - return (0); -} - -# A connection is starting -# ------------------------ -sub start { - my ($ip, $stamp, $backend) = @_; - msg ("Logging START of connection for IP $ip on stamp $stamp, ", - "back end $backend\n"); - $db{$ip} = "$backend:$stamp"; -} - -# A connection has ended -# ---------------------- -sub end { - my $ip = shift; - msg ("Logging END of connection for IP $ip\n"); - $db{$ip} = undef; -} - -# Request to determine a back end -# ------------------------------- -sub dispatch { - my $ip = shift; - my $stamp = shift; - - msg ("Request to dispatch IP $ip on stamp $stamp\n"); - - # Read the next arguments. They are triplets of - # backend-name / availability / weight. Store if the back end is - # available. - my (@backends, @weights); - for (my $i = 0; $i < $#_; $i += 3) { - if ($_[$i + 1] != 0) { - push (@backends, $_[$i]); - push (@weights, $_[$i + 2]); - msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n"); - } - } - - # See if this is a reconnect by a previously seen client IP. We'll - # treat this as a reconnect if the timeout wasn't yet exceeded. - if ($db{$ip} ne '') { - my ($last_backend, $last_stamp) = split (/:/, $db{$ip}); - msg ("IP $ip had last connected on $last_stamp to $last_backend\n"); - if ($stamp < $last_stamp + $timeout) { - msg ("Timeout not yet exceeded, this may be a reconnect\n"); - # We'll allow a reconnect only if the stated last_backend is - # free (sanity check). - if (inarray ($last_backend, @backends)) { - msg ("Last back end $last_backend is available, ", - "letting through\n"); - reply ($last_backend); - } else { - msg ("Last used back end isn't free, suggesting a new one\n"); - } - } else { - msg ("Timeout exceeded, suggesting a new back end\n"); - } - } else { - msg ("Np preveious connection data, suggesting a new back end\n"); - } - - my $bestweight = -1; - my $bestbackend; - for (my $i = 0; $i <= $#weights; $i++) { - if ($bestweight == -1 or $bestweight > $weights[$i]) { - $bestweight = $weights[$i]; - $bestbackend = $backends[$i]; - } - } - - msg ("Best back end: $bestbackend (given weight $bestweight)\n"); - reply ($bestbackend); -} - -# Main starts here -# ---------------- -msg ("Start of run, attaching GDBM database '$cdb'\n"); -tie (%db, 'GDBM_File', $cdb, &GDBM_WRCREAT, 0600); - -# The first argument must be an action 'dispatch', 'start' or 'end'. -# Depending on the action, we do stuff. -my $action = shift (@ARGV); -if ($action eq 'dispatch') { - dispatch (@ARGV); -} elsif ($action eq 'start') { - start (@ARGV); -} elsif ($action eq 'end') { - end (@ARGV); -} else { - print STDERR ("Usage: rdp-helper {dispatch|start|end} args\n"); - exit (1); -}) diff --git a/doc/main/tips/howselected.yo b/doc/main/tips/howselected.yo @@ -1,179 +0,0 @@ -In order to tune your load balancing, you'll need to understand how -crossroads computes usage, how weighing works, and so on. In this -section we'll focus on the dispatching modes tt(bysize), tt(byduration) -and tt(byconnections) only. The other dispatching types are -self-explanatory. - - -subsubsect(Bysize, byduration or byconnections?) - -As stated before, crossroads doesn't know 'what a service does' and -how to judge whether a given back end is very busy or not. You -must therefore give the right hints: - -itemization( - it() In general, a service which is CPU bound, will be more - busy when it takes longer to process a request. The dispatch - mode tt(byduration) is appropriate here. - - it() In contrast, a service which is filesystem bound, will be - more busy when more data are transferred. The dispatch mode - tt(bysize) is apppropriate. - - it() The dispatch mode tt(byduration) can also be used when - network latency is an issue. E.g., if your balancer has back - ends that are geograpically distributed, then tt(byduration) - would be a good way to select best available back ends. - - it() Furthermore it is noteworthy that tt(dispatchmode - byduration) is not usable for interactive processes such as - SSH logins. Idle time of a - login adds to the duration, while causing (almost) no - load. Mode tt(byduration) should only be used for automated - processes that don't wait for user interaction (e.g., SOAP - calls and other HTTP requests). - - it() As a last remark, the dispatching mode tt(byconnections) can - be used if you don't have other clues for load - estimations. - - E.g., consider a database connection. What's - heavier on the back end, time-consuming connections, or connections - where loads of bytes are transferred? Well, that depends. A - tough tt(select) query that joins multiple tables can be very - heavy on the back end, though the response set can be quite - small - and hence the number of - transferred bytes. That would suggest - dispatching by duration. However, tt(byduration) - balancing doesn't respresent the true world, when interactive - connections can occur where users have an idle TCP connection to - the database: - this consumes time, but no bytes (see the SSH login example - above). In this case, the dispatch mode tt(byconnections) may be - your best bet. - - ) - - -subsubsect(Averaging size and duration) - -The configuration statement tt(dispatchmode bysize) or tt(byduration) -allows an optional modifier tt(over) em(number), where the stated -number represents a connection count. When this modifier is present, then -crossroads will use a moving average over the last em(n) connections to -compute duration and size figures. - -In the real world you'll always want this modifier. E.g., consider two -back ends that are running for years now, and one of them is suddenly -overloaded and very busy (it experiences a 'spike' in activity). -When the tt(over) modifier is absent, then -the sudden load will hardly show up in the usage figures -- it will -flatten out due to the large usage figures already stored in the years -of service. - -In contrast, when e.g. tt(over 3) is in effect, then a sudden load -does show up -- because it highly contributes to the average of three -connections. - - -subsubsect(Specifying decays) - -Decays are also only relevant when crossroads computes the 'next best -back end' by size (bytes) or duration (seconds). E.g., imagine two -back ends A and B, both averaged over say 3 connections. - -Now when back end A is suddenly hit by a spike, -its average would go up accordingly. But the back end would never -again be used, unless B also received a similar spike, because A's -'usage data' over its last three connections would forever be larger than -B's data. - -For that reason, you should in real situations probably always -specify a decay, so that the backend selection algorithm recovers from -spikes. Note that the usage data of the back end where a decay is -specified, decay when bf(other) back ends are hit. The decay parameter -is like specifying how fast your body regenerates when someone else -does the work. - -The below configuration illustrates this: - -verb(\ -/* Definition of the service */ -service soap { - /* Local TCP port */ - port 8080; - - /* We'll select back ends by the processing - * duration - */ - dispatchmode byduration over 3; - - /* First back end: */ - backend A { - /* Back end IP address and port */ - server 10.1.1.1:8080; - - /* When this back end is NOT hit because - * the other one was less busy, then the - * usage parameters decay 10% per connection - */ - decay 10; - } - - /* Second back end: */ - backend B { - server 10.1.1.2:8080; - decay 10; - } -}) - -subsubsect(Adjusting the weights) - -The back end modifier tt(weight) is useful in situations where your -back ends differ in respect to performance. E.g,. your back ends may -be geographically distributed, and you know that a given back end is -difficult to reach and often experiences network lag. - -Or you may have -one primary back end, a system with a fast CPU and enough memory, and a -small fall-back back end, with a slow CPU and short on memory. In that -case you know in advance that the second back end should be used only -rarely. Most requests should go to the big server, up to a certain load. - -In such cases you will know in advance that the best performing back ends -should be selected the most often. Here's where the tt(weight) -statement comes in: you can simply increase the weight of the back -ends with the least performance, so that they are selected less -frequently. - -E.g., consider the following configuration: - -verb(\ -service soap { - port 8080; - dispatchmode byduration over 3; - backend A { - server 10.1.1.1:8080; - decay 20; - } - backend B { - server 10.1.1.2:8080; - weight 2; - decay 10; - } - backend C { - server 10.1.1.3:8080; - weight 4; - decay 5; - } -}) - -This will cause crossroads to select back ends by the processing time, -averaging over the last three connections. However, backend B will kick -in only when its usage is half of the usage of A (back end B is -probably only half as fast as A). Backend C will kick in only when its -usage is a quarter of the usage of A, which is half of the usage of B -(back end C is probably very weak, and just a fall-back system incase -both A and B crash). Note also that A's usage data decay much faster -than B's and C's: we're assuming that this big server recovers quicker -than its smaller siblings. diff --git a/doc/main/tips/httpstickiness.yo b/doc/main/tips/httpstickiness.yo @@ -1,118 +0,0 @@ -This section focuses on HTTP session stickiness. This term refers to -the ability of a balancer to route a conversation between browser and -a backend farm with webservers always to the same back end. In other -words: once a back end is selected by the balancer, it will remain the -back end of choice, even for subsequent connections. - -subsubsect(Don't use stickiness!) - -The rule of thumb as far as the balancer is concerned, is: bf(Do not -use HTTP session stickiness unless you really have to.) Enabling -session stickiness hampers failover, balancing and performance: - -itemization( - it() Failover is hampered because during the session, - the balancer has to assign new connections to the same back - end that was selected at the start of a session. If the back - end suddenly goes 'down', then the session will most likely - crash. (Actually, when a back end becomes unreachable in the - middle of a session, Crossroads will assign a new back end to - that session. This will most likely result in a malfunction - of the underlying application.) - it() Balancing is hampered because at the start of the session, - the balancer has selected the next-best back end. But during - the session, that back end may well become overloaded. The - balancer however must continue to send the requests there. - it() Performance is hampered because crossroads needs to 'unpack' - messages as they are passed to and fro. That's because - crossroads needs to check the HTTP headers in the messages - for persistence cookies.) - -There is a number of measures that you can take to avoid using session -stickiness. E.g., session data can be 'shared' between web back -ends. PHP offers functionality to store session data in a database, so -that all PHP applications have access to these data. Application -servers such as Websphere can be configured to replicate session data -between nodes. - -subsubsect(But if you must..) - -If you really need stickiness, think first whether you might use TCP -stickiness (using the client's IP address to dispatch). If you can, -then this is the preferred method, since Crossroads won't have to -unpack TCP streams. Below is a short configuration example: - -verb(\ -service www { - port 80; - type any; - revivinginterval 15; - dispatchmode byclientip; - - backend one { - server 10.1.1.100:80; - } - - backend two { - server 10.1.1.101:80; - } -}) - - -However, if you bf(must) use HTTP-base session stickiness, then -proceed as follows: - -itemization( - it() At the level of a tt(service) description, set the type to - tt(http). - it() Furthermore, set the tt(headerinspection) to tt(shallow) - (unless of course you also want to modify other HTTP headers, - see section ref(tips/deeporshallow)). - it() At the level of each back end description, configure the - tt(stickycookie) and a tt(addclientheader) directives.) - -Once crossroads sees that, it will examine each HTTP message that it -shuttles between client and back end: - -itemization( - it() If there is no persistence cookie in the HTTP headers of a - client's request, then the message must be the first one and - a new session should be established. - Crossroads selects an appropriate back - end, sends the message to that back end, catches the reply, - and inserts a tt(Set-Cookie) directive. - it() If there is a persistence cookie in the HTTP headers of a - client's request, then the request is part of an already - established session. Crossroads analyzes the cookie and - forwards the request to the appropriate back end.) - -Below is a short example of a configuration. - -verb(\ -service www { - port 80; - type http; - headerinspection shallow; - revivinginterval 15; - dispatchmode byconnections; - - backend one { - server 10.1.1.100:80; - stickycookie XRID=100; - addclientheader "Set-Cookie: XRID=100; Path=/"; - /* or: XRID=100; Path=/; Expires=%600g - * This would make the session cookie expire in 600 sec (10 mins) - */ - } - - backend two { - server 10.1.1.101:80; - stickycookie XRID=101; - addclientheader "Set-Cookie: XRID=101; Path=/"; - } -}) - -Note how the cookie names and values in the directives -tt(stickycookie) and tt(addclientheader) match. That is obviously a -prerequisite for stickiness. - diff --git a/doc/main/tips/ipconntrackmax.yo b/doc/main/tips/ipconntrackmax.yo @@ -1,34 +0,0 @@ -The kernel value of tt(ip_conntrack_max) is important for routers and -balancers under Linux. Basically it's the maximum number of tracked -connections. Felix A.W.O. describes the following situation: - -itemization( - it() Crossroads seems to mark back ends as unavailable, while - in fact nothing is afoot. - it() This happens under heavy load. - it() In tt(/var/log/messages) one may see the message: - tt(kernel: ip_conntrack: table full, dropping packet).) - -The reason for Crossroad's behavior is that the kernel refuses to -build up a requested network connection. For Crossroads, this looks -just as a non-responding back end. Crossroads therefore marks the back -end as unavailable. - -The solution is as follows: - -itemization( - it() Try tt(cat /proc/sys/net/ipv4/ip_conntrack_max) to see - the current value. - it() Add something to the shown value (e.g., multiply by two), - and inform - the kernel of the new value, using tt(echo) em(new-value) tt(> - /proc/sys/net/ipv4/ip_conntrack_max) - it() Make sure that the same step occurs somewhere in your - boot sequence as well, or that the new value is stated in a - configuration file such as tt(/etc/sysctl.conf).) - -The value for em(new-value) is something that you'll have to figure -out yourself. Note however that each count will cause the kernel to -reserve 350 bytes. So if you set tt(ip_conntrack_max) to 100.000, then -you're already taking 33.3Mb off the total available memory. - diff --git a/doc/main/tips/ipfiltering.yo b/doc/main/tips/ipfiltering.yo @@ -1,122 +0,0 @@ -subsubsect(General Examples) - -The directives tt(allowfrom), tt(denyfrom), tt(allowfile) and -tt(denyfile) can be used to instruct Crossroads to specifically allow -access by using a "whitelist" of IP addresses, or to specifically deny -access by using a "blacklist". E.g., the following configuration -allows access to service tt(webproxy) only to em(localhost): - -verb(\ -service webproxy { - port 8000; - allowfrom 127.0.0.1; - backend one { - . - . Back end definitions occur here - . - } - . - . Other back ends or other service directives - . may occur here - . -}) - -In this example there is a "whitelist" having only one entry: IP -address 127.0.0.1, or em(localhost). (Incidentally, the same behaviour -could be accomplished by stating em(bindto 127.0.0.1), in which case -Crossroads would only listen to the local network device.) - -In the same vein, the directive tt(allowfrom 127.0.0.1 192.168.1/24) -would allow access to em(localhost) and to all IP addresses that start -with 192.168.1. The specifier tt(192.168.1/24) states that there are -three network bytes (192, 168 and 1), and 24 bits (or 3 bytes) are -relevant; so that the fourth network byte doesn't matter. - -subsubsect(Using External Files) - -The directives tt(allowfile) and tt(denyfile) allow you to specify IP -addresses in external files. The Crossroads configuration states -e.g. tt(allowfile /tmp/allow.txt), and the IP addresses are then in -tt(/tmp/allow.txt). The format of tt(/tmp/allow.txt) is as follows: - -itemization( - it() The specifications follow again em(p.q.r.s/mask), where - p, q, r and s are network bytes which can be left out on the - right hand side when the mask allows it; - - it() The specifications must be separated by white space - (spaces, tabs or newlines).) - -E.g., the following is a valid example of an external specification -file: - -verb(\ -127.0.0.1 -192.168.1/24 -10/8) - -When external files are in effect, then the signal tt(SIGHUP) (1) -causes Crossroads to reload the external file. E.g., while Crossroads -is running, you may edit tt(/tmp/allow.txt), and then issue tt(killall --1 crossroads). The new contents of tt(/tmp/allow.txt) will be -reloaded. - -subsubsect(Mixing Directives) - -Crossroads allows to mix all directives in one service -description. However, some mixes are less meaningful than others. It's -up to you to take this into account. - -The following rules apply: - -itemization( - it() Blacklisting and whitelisting can be used together. When - combined, the blacklist will always be interpreted - first. E.g., consider the following directives: - - verb(\ -allowfrom 192.168.1/24 -denyfrom 192.168.1.100) - - Given the fact that the deny list is checked first, client - 192.168.1.100 won't be able to access Crossroads. Then the - allow list will be checked, stating that all clients whose IP - address starts with 192.168.1 may connect. The effect will be - that e.g., client 192.168.1.1 may connect, 192.168.1.2 may - connect too, 192.168.1.100 will be blocked, and 10.1.1.1 will - be blocked as well. - - Now consider the following directives: - - verb(\ -allowfrom 192.168.1.100 127.0.0.1 -denyfrom 192.168.1/24) - - This will first of all deny access to all IP addresses that - start with 192.168.1. So the rule that allows 192.168.1.100 - won't ever be effective. The net result will be that access - will be granted to 127.0.0.1. - - it() Blacklisting or whitelisting can be left out. A list is - considered empty when no appropriate directives occur in the - configuration file tt(crossroads.conf), or when the directive - points to an empty or non-existent external file. - - it() Using tt(*from) and tt(*file) statements is allowed, but - doesn't make sense. E.g., the following configuration sample - is such a case: - - verb(\ -allowfrom 127.0.0.1 192.168.1/24 -allowfile /tmp/allow.txt) - - There is a technical reason for this. Once Crossroads - processes the tt(allowfile) directive, then the whole - whitelist is cleared (thereby removing the entries 127.0.0.1 - and 192.168.1/24), and new entries are reloaded from the - file. The net result is that the tt(allowfrom) specification - is overruled.) - -Crossroads only performs syntactic checking of the configuration. Some -of the above samples are syntactically correct, but make no semantic -sense: Crossroads doesn't warn for such situations. diff --git a/doc/main/tips/periodic.yo b/doc/main/tips/periodic.yo @@ -1,89 +0,0 @@ -Crossroads has two methods of periodic back end verifications: - -itemization( - - it() The first method only checks unavailable back ends. It is - configured using the directive tt(revivinginterval). - The way tt(revivinginterval) works is as follows: - - enumeration( - eit() Crossroads determines that a back end is - unavailable. This happens when a new network connection to - the back end fails, or when an existing connection - suddenly dies. - - eit() The back end is the periodically checked to see - if it has woken up yet.) - - it() The second method periodically checks all back ends, - available ones and unavailable ones. This is configured using - tt(checkinterval). During each check, back ends can be marked - as available or as unavailable.) - - -subsubsect(Syntax) - -The syntax of both verifications is: - -center(em(method)tt(interval) em(seconds) tt([) tt(externalhandler) -em(program) em(arguments) tt(])) - -In this syntax, the method is tt(reviving) or tt(check). The seconds -specifier defines the delay between consecutive checks. - -When no specification tt(externalhandler) em(program) em(arguments) is -given, then Crossroads runs the verification by trying to connect to -the back end. A simple successful connection suffices to cause -Crossroads to consider the back end available. However, when the -tt(externalhandler) specifier is given, then Crossroads runs the -specified external program. This program must exit with status 0 to -inform Crossroads that the back end is available. All other exit -statuses will mark the back end as unavailable. - -The em(arguments) are expanded according to the following table: - -INCLUDEFILE(formattable) - - -subsubsect(Security Considerations) - -When tt(externalhandler) is in effect, Crossroads spawns an external -process. For security reasons, you may want to run this process under -a restricted user account. - -The directive tt(useraccount) can be used to accomplish this. - - -subsubsect(An example) - -The following configuration balances SMTP requests to two back -ends. The connectivity is checked by retrieving output from each back -end. The back end is available when the standard greeting of a mail -server is seen; this greeting must contain the word SMTP. During each -check the probe is terminated by sending tt(quit). - -verb(\ -/* Use either DEBUG on or off */ -#define DEBUG off - -/* SMTP balancer */ -service smtp { - /* Standard stuff */ - port 25; - verbosity DEBUG; - /* Check back ends every 30 seconds. User 'nobody' - * will run the external handler. */ - useraccount nobody; - checkinterval 30 - externalhandler "echo quit | netcat -w1 %h %p | grep SMTP"; - - /* Two back ends to handle mail traffic. */ - backend mailone { - server smtp1.local.network:25; - verbosity DEBUG; - } - backend second { - server smtp2.local.network:25; - verbosity DEBUG; - } -}) diff --git a/doc/main/tips/rendering.yo b/doc/main/tips/rendering.yo @@ -1,66 +0,0 @@ -The Crossroads flag tt(-x) causes the status output to be generated as -an XML document. This format can be nicely used to render the output -as an HTML page. - -E.g., the following sample shows how Crossroads reports its status in -XML format (the actual XML structure that Crossroads outputs may be -different from this example, depending on the version): - -verb(\ -<?xml version="1.0" encoding="UTF-8"?> -<status> - <service id="1" name="smtp"> - <connections>1</connections> - <lastbackend>0</lastbackend> - <backend id="0" name="first"> - <availability id="0">available</availability> - <clients>0</clients> - <failures>0</failures> - <connections>2</connections> - <duration sec="0.559882">0.56s</duration> - <throughput bytes="3564">3.48Kb</throughput> - </backend> - <backend id="1" name="second"> - <availability id="0">available</availability> - <clients>0</clients> - <failures>0</failures> - <connections>2</connections> - <duration sec="23.7636">23.76s</duration> - <throughput bytes="9055">8.84Kb</throughput> - </backend> - </service> -</status>) - -A custom-made XSLT transformation stylesheet can be used to convert -this to any output format - and also to HTML. Such a style sheet is -included in the Crossroads distribution as -tt(etc/xml-status-to-html.xslt). The sheet is lengthy, and is -therefore not included in this document. (You're welcome to modify it -to suit your specific needs. If you have cool tips, send them along to -me and I'll include them in the next distribution!) - -If you want to show this output in a webpage which is generated on -demand by a webserver, then you might run into the following -problem. The status reporter (tt(crossroads -x status)) - must be able to access the shared memory segment of the running -Crossroads instance. By default, the shared memory is protected for -the user that started Crossroads, which will often not be the user who -runs the webserver. Under the auspices of the webserver user, -tt(crossroads status) might abort with a message: "ERROR: Cannot get -shared memory for service em(name), key em(number): Permission denied." - -The solution for this problem is to make the shared memory access -somewhat more liberal. There are basically two options: - -itemization( - it() Start Crossroads with the flag tt(-m 0666), which makes - the shared memory segment available to all. The octal number - 0666 works just like a file permission setting under - Unix. Now, any user can run tt(crossroads status). - - it() If you want to make the access to Crossroads' shared - memory somewhat stricter, then make the user who starts - Crossroads and the user who runs the webserver member of a new - Unix group. Start Crossroads with the flag tt(-m 0664). Now, - users belonging to the same group as the Crossroads starter - can run tt(crossroads status).) diff --git a/doc/main/tips/retries.yo b/doc/main/tips/retries.yo @@ -1,41 +0,0 @@ -Crossroads allows you to specify on a per-back end basis how many -retries are needed before a back end is considered unavailable. The -default is just one, meaning that after one failed connection, -Crossroads will mark a back end as unavailable (the back end may be -'revived', if you use tt(revivinginterval) or tt(checkinterval)). - -Increasing the number is specified using the keyword tt(retries). The -following configuration defines two back ends; the one on the IP -address 5.6.7.8 is somehow 'flaky', and Crossroads should try -connecting 3 times before crossing it off: - -verb(\ -service www { - port 80; - backend plugh { - server 1.2.3.4:80; - } - backend xyzzy { - server 5.6.7.8:80; - retries 3; - } -}) - -There may be several reasons for increasing the retries number: - -itemization( - it() The network connections to the server may spuriously - hamper, but such rare errors don't mean that the back end server is - down. - - it() The back end server is a 'slow starter' and can't handle spikes - very well. E.g., it may be a webserver which starts with only - one daemon; extra capacity is added as network connections - arrive, but adding capacity take a little time.) - -Whatever the reason, the keyword tt(retries) might be of help -here. This keyword should however be used carefully: Crossroads will -retry connecting with a small one-second delay in between. A high -tt(retries) number means also lots of one-second delays, in which time -a client is kept waiting. - diff --git a/doc/main/tips/tcpstickiness.yo b/doc/main/tips/tcpstickiness.yo @@ -1,22 +0,0 @@ -If you need to make sure that a client that once gets dispatched to a -given back end keeps re-visiting the back end, then Crossroads offers -the dispatch mode tt(byclientip). This mode will only work when each -client is seen by Crossroads with its own specific IP address; ie., -this method won't work when clients reach Crossroads through a -masquerading firewall (in which case all clients would be seen as -having the firewall's IP address). - -The tt(dispatchmode byclientip) works as follows: - -itemization( - it() The client's IP address is taken in its string - representation and 'hashed' into a number. - - it() The number is brought back to the number of available - back ends (using a modulo-operation). - - it() The result defines the back end of choice.) - -If the preferred back end is unavailable, then the action that -Crossroads takes is to dispatch as if tt(byconnections): of the -available back ends, the one with the least connections is taken. diff --git a/doc/main/tips/throttling.yo b/doc/main/tips/throttling.yo @@ -1,27 +0,0 @@ -If you suspect that your service may occasionally receive 'spikes' of -activity+footnote(which you should always assume), then it might be a -good idea to protect your service by specifying a maximum number of -concurrent connections. This protection can be specified on two levels: - -description( - dit(On the service level) a statement like tt(maxconnections - 100;) states that the service as a whole will never - service more than 100 concurrent connections. This means that - all your back ends and the crossroads balancer itself - will be protected from being overloaded. - dit(On the back end level) a statement like tt(maxconnections 10;) - states that this particular back end will never have more - than 10 concurrent connections; regardless of the overall - setting on the service level. This means that this - particular back end will be protected from being - overloaded (regardless of what other back ends may - experience).) - -The tt(maxconnections) statement, combined with a back end selection -algorithm, allows very fine granularity. The tt(maxconnections) statement -on the back end level is like a hand brake: even when you specify a -back end algorithm that would protect a given back end from being used -too much, a situation may occur where that back end is about to be -hit. A tt(maxconnections) statement on the level of that back may then -protect it. - diff --git a/doc/main/tips/virtual.yo b/doc/main/tips/virtual.yo @@ -1,77 +0,0 @@ -Crossroads can be used as a central dispatcher for computing on -demand. E.g., imagine a situation where a web service exists on one or -two servers, with the option of adding new servers in case of a high -load. The "other" servers, besides the first two ones, are a virtual -pool - the pool size and the IP addresses may not be known in advance. - -The following Crossroads' features are used in this example: - -itemization( - - it() The back ends of Crossroads can have an initial state. For - the two real servers which are already present this would be - tt(up) (the default). For the non-present ones this would be - tt(down): Crossroads excludes back ends that are down from the set - of candidate workers, and does not check their presence using - tt(revivalinterval) or tt(checkinterval). - - it() The back ends must be configured to have a server address and - port, but this can be later reconfigured runtime. E.g., the - virtual pool servers would be initially configured on - tt(localhost:80), and later the addresses would be filled in.) - -A sample Crossroads configuration is shown below. It defines 20 back -ends: two are always present, and 18 are in the virtual pool. - -verb(\ -service vpool { - port 80; - revivinginterval 3; - dispatchmode byconnections; - - /* The two back ends that are always present: */ - backend real_01 { server 10.1.1.1:80; } - backend real_02 { server 10.1.1.2:80; } - - /* The virtual pool: */ - backend virt_01 { server localhost:80; state down; } - backend virt_02 { server localhost:80; state down; } - backend virt_03 { server localhost:80; state down; } - backend virt_04 { server localhost:80; state down; } - backend virt_05 { server localhost:80; state down; } - backend virt_06 { server localhost:80; state down; } - backend virt_07 { server localhost:80; state down; } - backend virt_08 { server localhost:80; state down; } - backend virt_09 { server localhost:80; state down; } - backend virt_10 { server localhost:80; state down; } - backend virt_11 { server localhost:80; state down; } - backend virt_12 { server localhost:80; state down; } - backend virt_13 { server localhost:80; state down; } - backend virt_14 { server localhost:80; state down; } - backend virt_15 { server localhost:80; state down; } - backend virt_16 { server localhost:80; state down; } - backend virt_17 { server localhost:80; state down; } - backend virt_18 { server localhost:80; state down; } - -}) - -New back ends can be enabled using tt(crossroads tell). E.g., to -enable back end tt(virt_01) on IP address 10.100.1.1:80, the commands -are: - -verb(\ - crossroads tell vpool virt_01 server 10.100.1.1:80 - crossroads tell vpool virt_01 up) - -To disable a back end, its state is reset to tt(down). The IP address -doesn't even have to be erased: - -verb(\ - crossroads tell vpool virt_01 down) - -A script to monitor the total number of connections, and to add a back -end or to remove one, is left to the reader. The output of -tt(crossroads -x status) can be very helpful here: it reports on the -states of all back ends, their connections, the total number of -available or down back ends, etc.. - diff --git a/doc/main/tips/webinterface.yo b/doc/main/tips/webinterface.yo @@ -1,118 +0,0 @@ -The mini-webserver tt(crossroads-mgr) provides an intuitive web -interface to the state of Crossroads. Once started, an administrator -may view the state of the balancer, and may influence the state of all -back ends. - -As an example, a procedure is described here where a given back end is -gracefully taken out of service. Below is a sample Crossroads -configuration, which distributes requests over three back ends: - -verb(\ -service www { - port 80; - type http; - backend one { - server 10.1.1.1:80; - stickycookie "BalancerID=one"; - addclientheader "Set-Cookie: BalancerID=one"; - } - backend two { - server 10.1.1.1:80; - stickycookie "BalancerID=two"; - addclientheader "Set-Cookie: BalancerID=two"; - } - backend three { - server 10.1.1.1:80; - stickycookie "BalancerID=three"; - addclientheader "Set-Cookie: BalancerID=three"; - } -}) - -In order to take a given back end gracefully offline (say back end -tt(two)), either of the following procedures can be followed: - -enumeration( - eit() Back end tt(two) is taken down via the commandline, using - tt(crossroads tell www two down). Next we wait for say one hour to - let existing sessions die out. After that, the back end can be turned - off. - - eit() Back end tt(two) is taken down via the web interface, by - switching the status from tt(available) to tt(down). Next we wait - for say one our, and turn off the back end.) - -The web interface doesn't offer extra functionality over the command -line tools; but all information is available at one glance, and -accessible without a shell access to the balancer. - - -subsubsect(Starting crossroads-mgr) - -The basic command to start tt(crossroads-mgr) is - -center(tt(crossroads-mgr start) em(portnumber)) - -where the port number specifies to which TCP port the manager will -listen. There is however one important security aspect that needs -attention. Unless specified otherwise, anyone who points their browser -to the balancer and the given port, can view back end states and even -change them. This may be a too lax policy. - -The web interface daemon has two methods to limit access: a listening -address can be specified via the command line, and basic -authentication (username / password protection) can be turned on. - -Specifying a listening address is done using the flag tt(-a). E.g., -after - -center(tt(crossroads-mgr -a 127.0.0.1 start 10000)) - -the manager is started to listen to port 10000, but only on the -address 127.0.0.1 which is localhost. Requests from other addresses -will not be served. - -Enforcing basic authentication is turned on using two command line -flags: - -itemization( - it() Flag tt(-b) turns on basic authentication to view the status. - it() Flag tt(-B) turns on basic authentication to change back end - states.) - -Both flags must be followed by the required credentials. The most -simple way to state the credentials, is to postfix the flag with the -required user name, a colon, and the required password. E.g., -tt(-Buser:secret) says that anyone who tries to change back end -states, must supply the user name tt(user) and the password -tt(secret). - -In this example the full invocation would be e.g.: - -center(tt(crossroads-mgr -Buser:secret start 1000)) - -The disadvantage is here that the credentials are visible for anyone -who has shell access to the balancer. A process overview, generated -with say tt(ps ax | grep crossroads-mgr), would show the required -username and password. In order to avoid such a leak, -tt(crossroads-mgr) can be started as follows: - -center(tt(crossroads-mgr -BPROMPT start 1000)) - -The 'magic' word tt(PROMPT) instructs tt(crossroads-mgr) to read the -username and password from em(stdin). The invocation can be further -scripted, using something like: - -center(tt(echo user:secret | crossroads-mgr -BPROMPT start 1000)) - -The same trick can be used with the flag tt(-b). When both flags are -present, and both 'magic' words tt(PROMPT) occur, then -tt(crossroads-mgr) will first ask for the credentials of the 'viewer', -and next for the credentials of the 'modifier' (even when flag tt(-B) -is stated first). So the following example starts tt(crossroads-mgr) -and requires the user name tt(viewer), with password tt(showme) to view the -status, and it requires the user name tt(modifier), with password -tt(changeit) to change states: - -verb(\ -echo -e 'viewer:showme\nmodifier:changeit' | - crossroads-mgr -bPROMPT -BPROMPT start 1000) diff --git a/doc/main/using.yo b/doc/main/using.yo @@ -1,161 +0,0 @@ -subsect(The Balancer: crossroads) - -The Crossroads balancer is started from the commandline, and highly -depends on the configuration file tt(crossroads.conf) (the default -configuration file). It supports a number of flags (e.g., to overrule -the location of the configuration file). The actual usage information -is always obtained by typing tt(crossroads) without any arguments. -Crossroads then displays the allowed arguments. - -The installation (see section ref(installation)) installs also a -second program called tt(crossroads-daemon). This program is not meant -to be started from the command line; it is administered through the -front end tt(crossroads). - -subsubsect(General Commandline Syntax) - -This section shows the most basic usage. As said above, start -tt(crossroads) without arguments to view the full listing of options. - -itemization( - it() tt(crossroads start) and tt(crossroads stop) are typical - actions that are run from system startup scripts. The - meaning is self-explanatory. - it() tt(crossroads restart) is a combination of the former - two. Beware that a restart may cause discontinuity in - service; it is just a shorthand for typing the 'stop' and - 'start' actions after one another. The reason is that in - the split second where the listener is stopped and a new one - is not yet started, a new network connection won't be - accepted. Running connections are however not hindered by a - restart. - it() tt(crossroad status) reports on each running - service. Per service, the state of each back end is - reported. - it() tt(crossroads tell) em(service backend) tt(state) - em(newstate) is a - command line way of telling crossroads that a given back - end, of a given service, is in a given state. Normally - crossroads maintains state information itself, but by - using tt(crossroads tell), a back end can be e.g. taken - 'off line' for servicing. - it() tt(crossroads tel) em(service backend) tt(server) - em(hostname:port) redefines the back end address of a - running back end. - it() tt(crossroads configtest) tells you whether the - configuration is syntactially correct. - it() tt(crossroads services) reports on the configured - services. In contrast to tt(crossroads status), this - option only shows what's configured -- not what's up and - running. Therefore, tt(crossroads services) doesn't - report on back end states. -) - -subsubsect(Status Reporting from the Command Line) - -The command tt(crossroads status) shows a verbose human-readable -report of how Crossroads is doing. When many services are configured, -this can be a somewhat lengthy output. If you're interested in the -overview of only one service, you can use tt(crossroads status) -em(servicename), in which case the report will only be shown for the -stated service. - -Similarly, if you're interested only in the status of a given back end -of a given service, use tt(crossroads service) em(servicename) -em(backendname). - -The flag tt(-t) causes the status overview to be abbreviated and -displayed in a parseable format. This flag can be used in automated -scripts that check how Crossroads is doing; e.g., in health checking -scripts. When tt(-t) is used, the format of the status reporter is as -follows: - -itemization( - it() Service health is reported on one line per service. - it() The first string on the line is the service name. - it() After this, a series follows of em(backendname=state), - where the back end name is the configured name of the back - end, and where state is e.g. tt(available), tt(down), - tt(unavailable), tt(waking). The statuses may occur in caps. - The series is repeated for all back ends of the given service.) - -The flag tt(-x) causes the overview to be presented as an XML -document. This can be handy if you want to further automate the -control over Crossroads, or if you want to show the status via the -web. (See section ref(xmlstatus) for more information.) - -The status reporter shows the number of running connections per back -end, and the total of connections of the entire service. Due to -technical reasons the total count can be one off. (There is a -technical reason for this: the total counter gets updated at a -different rate than separate back end counts. This may be fixed in a -future release.) - - -subsubsect(Logging-related options) - -Two 'flags' of Crossroads are specifically logging-related. This -section elaborates on these flags. - -First, there's flag tt(-a). When present, the start and end of -activity is logged using statements like - -center(em(YYYY-MM-DD HH/MM/SS starting http from 61.45.32.189 to 10.1.1.1)) - -Similarly, there are 'ending' statements. Using this flag and -scanning your logs for these statements may be helpful in quickly -determining your system load. - -Second, there's flag tt(-l). This flag selects the 'facility' of -logging and defaults to tt(LOG_DAEMON). You can supply a number -between 0 and 7 to flag tt(-l) to select tt(LOG_LOCAL0) to -tt(LOG_LOCAL7). This would separate the Crossroads-related logging -from other streams. Here's a very short guide; please read your Unix -manpages of tt(syslogd) for more information. - -itemization( - it() First edit tt(/etc/syslog.conf) and add a line: - - verb(local7.* /var/log/crossroads.log) - - That instructs tt(syslogd) to send tt(LOG_LOCAL7) requests to the - logfile tt(/var/log/crossroads.log). - - it() Next, restart tt(syslogd). On most Unices that's done by - issuing tt(killall -1 syslogd). (As a side-note, I tried this once - on an Bull/AIX system, and the box just shut down. The tt(killall) - command killed every process...) - - it() Now start tt(crossroads) with the flag tt(-l7). - - it() Finally, monitor tt(/var/log/crossroads.log) for Crossroads' - messages.) - - -subsubsect(Reloading Configurations) - -Crossroads doesn't support the reloading of a configuration while -running (such as other programs, e.g. Apache do). There are various -technical reasons for this. - -However, external lists of allowed or denied IP addresses can be -reloaded by sending a signal -1 (tt(SIGHUP)) to Crossroads. See -section ref(servicedef) for the details. - - -subsect(The Web Frontend: crossroads-mgr) - -A web front end for Crossroads is tt(crossroads-mgr). Type -tt(crossroads-mgr) without arguments to see the usage. - -Normally the web interface is started like: - -center(tt(crossroads-mgr start 1000)) - -where 1000 is a free TCP port. Note that tt(crossroads-mgr) must be -started on the system where the balancer tt(crossroads) itself is -running. - -There are lots of other options that can be used with -tt(crossroads-mgr), see section ref(tips/webinterface). - diff --git a/doc/man/crossroads-mgr.yo b/doc/man/crossroads-mgr.yo @@ -1,106 +0,0 @@ -includefile(../crossroads-defs) -manpage(crossroads-mgr)(1)(YEARS())(crossroads)() -manpagename(crossroads-mgr)(Web interface for Crossroads) - -manpagesynopsis() - itemization( - it() bf(crossroads-mgr) em([flags]) bf(start) em(port): starts - the web interface server - it() bf(crossroads-mgr) bf(stop): stops the server - it() bf(crossroads-mgr) bf(status): shows whether the server - is running) - -manpagedescription() - - bf(crossroads-mgr) is a self-contained web server, written in - Perl, providing a web interface to control Crossroads. The web - interface allows one to: - - itemization( - it() View the states of back ends, and their usage; - it() Set the states of back ends (e.g., to take a back end out - of service).) - - The web interface cannot be used to fully configure Crossroads (e.g., to - add new back ends). This must be done via the configuration file - bf(crossroads.conf). Note furthermore that bf(crossroads status) and - bf(crossroads tell) are commandline tools that achieve the same - functionality as bf(crossroads-mgr). - -manpageoptions() - - bf(crossroads-mgr) recognizes the following flags: - - itemization( - - it() bf(-a ADDRESS): Specifies the address to which the - manager binds. E.g., one may specify bf(-a 127.0.0.1), after - which only requests originating from localhost will be served. - - it() bf(-b UN:PW): Viewing the status is protected using - simple basic authentication, requiring username bf(UN) and - password bf(PW). - - For example one may specify: bf(-b user:secret), after which - visitors to the web interface are prompted for a username and - password. - - Instead of the username, a colon, and a password, the special - word bf(PROMPT) may be used (in caps). In this case, - bf(crossroads-mgr) prompts for the entry of a username and a - password. Using bf(PROMPT) will have the effect that the - username and password are not shown in a process listing. - - it() bf(-B UN:PW): Setting the status of a back end is - protected using simple basic authentication, with username - bf(UN) and password bf(PW). The special word bf(PROMPT) may be - used. - - it() bf(-f): bf(crossroads-mgr) stays in the foreground - instead of daemonizing. Useful for debugging. - - it() bf(-l FILE): Verbose activity (when bf(-v) is given) is - written to the stated file. Useful for debugging. Also, error - messages are written to the file. The default is - bf(/tmp/crossroads-mgr.log). Note that file logging is not - used in 'foreground' mode (flag bf(-f); in that case all - logging goes to terminal). - - it() bf(-v): Verbosity is increased. - - it() bf(-x CMD): This specifies how bf(crossroads-mgr) should - invoke bf(crossroads). The default is just bf(crossroads). An - example is: bf(/usr/local/bin/crossroads -c - /alternative/configuration) which overrules the default - configuration file name. Note that once bf(-x) is given, - bf(crossroads-mgr) will no longer search along the PATH. When - using bf(-x), the full access path to bf(crossroads) must be - specified. - - it() bf(-X XSLT): Causes bf(crossroads-mgr) to send the - specified XSLT file, instead of the built-in. Useful for - debugging.) - - -manpagebugs() - - itemization( - - it() Basic authentication headers are logged when bf(-v) - is active. Make sure that the log file is not in a - world-readable place, or turn off bf(-v). - - it() When using basic authentication in the form bf(-b - UN:PW) and bf(-B UN:PW), the required credentials are - visible in the process overview list. Use bf(PROMPT) to - hide these. - - it() bf(crossroads-mgr stop) and bf(crossroads-mgr status) - use the process list to see which daemons are active. If - you start more than one daemon, then bf(crossroads-mgr - stop) will stop all of them.) - -manpageauthor() - itemization( - it() Author: AUTHORNAME() - it() Maintainer: MAINTAINERNAME() MAINTAINEREMAIL()) diff --git a/doc/man/crossroads.conf.yo b/doc/man/crossroads.conf.yo @@ -1,29 +0,0 @@ -includefile(../crossroads-defs) - -manpage(crossroads.conf)(7)(YEARS())(crossroads)() -manpagename(crossroads.conf)(Crossroads configuration) - -manpagesynopsis() -This manpage describes the configuration of -Crossroads, usually the file bf(crossroads.conf). This file is often -located in an 'etc' directory near the path where Crossroads is -installed. E.g., when Crossroads is installed as -bf(/usr/local/bin/crossroads), then the configuration file can be -tt(/usr/local/etc/crossroads.conf). Alternatively the configuration -can always be put in bf(/etc/crossroads.conf). Crossroads can however -be started with a flag bf(-c) to specify an entirely different name.. - -manpagedescription() -redef(confsect)(1)(manpagesection(ARG1)) -redef(confsubsect)(1)(bf(ARG1)) -includefile(../main/config) - -manpageseealso() - itemization( - it() crossroads (1) - it() The full Crossroads documentation, in pdf and html - format, distributed with the sources.) - -manpageauthor() - Crossroads is written by AUTHORNAME() and is maintained by - MAINTAINERNAME() (MAINTAINEREMAIL()). diff --git a/doc/man/crossroads.yo b/doc/man/crossroads.yo @@ -1,120 +0,0 @@ -includefile(../crossroads-defs) -manpage(crossroads)(1)(YEARS())(crossroads)() -manpagename(crossroads)(Load balancer and fail over utility) - -manpagesynopsis() - itemization( - it() bf(crossroads) em([flags]) bf(start): starts Crossroads - it() bf(crossroads) em([flags]) bf(stop): stops Crossroads - it() bf(crossroads) em([flags]) bf(restart): restarts Crossroads - it() bf(crossroads) em([flags]) bf(configtest): verifies - configuration - it() bf(crossroads) em([flags]) bf(services): shows configured - services - it() bf(crossroads) em([flags]) bf(tell) em(service) - em(backend) bf(state) em(newstate): sets a back end state - it() bf(crossroads) em([flags]) bf(tell) em(service) - em(backend) bf(server) em(hostname:port): redefines the - address of a back end) - -manpagedescription() - - bf(crossroads) is the main control program of Crossroads, a - balancing and fail over utility. The Crossroads utility consists - of two binaries: bf(crossroads) and bf(crossroads-daemon). The - latter is controlled by the front end bf(crossroads). - - Recognized states in bf(crossroads tell) are: - - itemization( - it() bf(available) or bf(up) when a backend is working; - it() bf(unavailable) when a back end is temporarily down, but - can be woken up; - it() bf(down) when a back end is permanently down (e.g., for - maintenance). Crossroads itself will never activate a - backend in this state. - it() bf(waking) when the live status of a back end is being checked.) - -manpageoptions() - - Crossroads recognizes the following flags: - - itemization( - it() bf(-a): Logs start and end times of activity to - em(syslog). - - it() bf(-b): Writes a binary representation of the - configuration to em(stdout). For debugging only. - - it() bf(-B) em(size): Sets the size of network buffers. Larger - buffers will mean a less number of tt(read()) or - tt(write()) operations, but a larger memory - footprint. Usually the default value will do fine. - - it() bf(-C): Writes the compile time configuration to - em(stdout) and stops. Useful when submitting bug reports. - - it() bf(-c) em(config): Uses em(config) as the configuration - file. The default is bf(/some/path/etc/crossroads.conf) - or bf(/etc/crossroads.conf). The first alternative - applies when Crossroads is installed as - bf(/some/path/bin/crossroads); in that case, Crossroads - tries to find its configuration by trying an 'etc' - diretory 'near' its own path. - - it() bf(-d) em(sec): Sets the DNS cache time-to-live to - em(sec) seconds. Value 0 suppresses DNS entry - caching. The default is bf(DNSCACHETTL()). - - it() bf(-l) em(facility): Specifies the openlog (3) facility, - default is bf(LOG_DAEMON). Values can be 0 to 7, meaning - bf(LOG_LOCAL0) to bf(LOG_LOCAL9). - - it() bf(-m) em(permissions): Specifies shared memory - permissions, default is bf(0644). - - it() bf(-s): Requests 'sloppy' binding to TCP ports. When a - port is busy, Crossroads will retry periodically to get - it. Default is to stop when a listening port cannot be - obtained. - - it() bf(-t): Output of bf(crossroads status) is shown as a - tabular view, in one or more lines of: - - em(service) em(backend=state) em([backend=state...]) - - it() bf(-x): Output of bf(crossroads status) is shown as an - XML document. - - it() bf(-v): Initial actions of Crossroads are more - 'verbose'. (Once daemonized, the verbosity is controlled - using bf(verbosity) statements in the configuration.) - - it() bf(-V): Shows the version ID and stops. - - it() bf(-?): Shows an overview of the usage.) - -manpagefiles() - bf(crossroads.conf), the default configuration file - -manpageseealso() - itemization( - it() crossroads.conf (7) - it() The full Crossroads documentation in html or pdf format, - distributed with the sources.) - -manpagebugs() - Crossroads is of course bug free (haha). When submitting bugs to - the maintainer. please always include the following information: - - itemization( - it() The configuration file that Crossroads uses; - it() The compile-time configuration, which is the output of - bf(crossroads -C); - it() A good description of the bug, and of the conditions - under which it occurs.) - -manpageauthor() - itemization( - it() Author: AUTHORNAME() - it() Maintainer: MAINTAINERNAME() MAINTAINEREMAIL()) diff --git a/doc/xr.odt b/doc/xr.odt Binary files differ. diff --git a/doc/xr.pdf b/doc/xr.pdf Binary files differ. diff --git a/etc/Makefile.conf b/etc/Makefile.conf @@ -1,44 +0,0 @@ -# The used C compiler. -CC := $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache c-compiler) - -# Link time libraries. -LIBS := $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache lib \ - ucb nsl pthread socket m alf 2>/dev/null) - -# Compiletime defines. Remember to update show_config() when changing -# this list! -DEFS := -DSLEEP_TIME=$(SLEEP_TIME) -DDNS_CACHESIZE=$(DNS_CACHESIZE) \ - -DDNS_CACHETTL=$(DNS_CACHETTL) -DDEF_MAX=$(DEF_MAX) \ - -DCONNECT_TIMEOUT=$(CONNECT_TIMEOUT) -DVER='"$(VER)/$(REVVER)"' \ - -DSET_PROC_TITLE_BY_ARGV=$(SET_PROC_TITLE_BY_ARGV) \ - -DDEFAULT_TCP_BUFSZ=$(DEFAULT_TCP_BUFSZ) -DRETRY_WAIT=$(RETRY_WAIT) \ - -DREVVER='"$(REVVER)"' -DBINDIR='"$(BINDIR)"' \ - -DPREFIX='"$(PREFIX)"' -DYEARS='"$(YEARS)"' \ - -DEXTRALIBS='"$(EXTRALIBS)"' -DLIBS='"$(LIBS)"' \ - -DAUTHORNAME='"$(AUTHORNAME)"' -DMAINTAINERNAME='"$(MAINTAINERNAME)"' \ - -DMAINTAINEREMAIL='"$(MAINTAINEREMAIL)"' -DFQDN_LENGTH=$(FQDN_LENGTH) \ - -DDEFAULT_SPT_BUFSIZE=$(DEFAULT_SPT_BUFSIZE) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - ifheader01 malloc.h HAVE_MALLOC_H) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - ifheader01 stdint.h HAVE_STDINT_H) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 flock HAVE_FLOCK) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 lockf HAVE_LOCKF) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 strlcat HAVE_STRLCAT) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 sranddev HAVE_SRANDDEV) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 vsyslog HAVE_VSYSLOG) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 strcasestr HAVE_STRCASESTR) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 setproctitle HAVE_SETPROCTITLE) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 strupr HAVE_STRUPR) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libfunction01 strnstr HAVE_STRNSTR) \ - $(shell $(BASE)/tools/c-conf -vc $(BASE)/src/c-conf.cache \ - libvariable01 __progname_full HAVE_PROGNAME_FULL) diff --git a/etc/Makefile.def b/etc/Makefile.def @@ -1,78 +0,0 @@ -# Definitions for the making of... Crossroads! -# -------------------------------------------- -# Tweak the below values to your liking. Just don't uncomment anything; -# if you don't need the setting, set it to zero (for numbers) or to an -# emtpy value (for strings). - -# Versioning. This defines the overall version ID and must match the topmost -# entry in the ChangeLog. -VER = 1.81 - -# Years that Crossroads has been 'round. -YEARS = 2005-2008 - -# Author and maintainer -AUTHORNAME = Karel Kubat -MAINTAINERNAME = Karel Kubat -MAINTAINEREMAIL = karel@kubat.nl - -# Revision version, auto-detected. -REVVER = $(shell $(BASE)/etc/svnrev $(BASE)/ChangeLog $(BASE)/etc/svnrev.txt) - -# The length of a fully qualified domain name. See RFC2181. -FQDN_LENGTH = 256 - -# Sleep time (sec). When we detect that there are too many clients, or -# no back ends, then we'll wait during this period. -SLEEP_TIME = 10 - -# How long before an attempt to connect to a back end fails (secs)? -# This is NOT the sessiontimeout specifier of the config file; it affects -# trying to connect to a back end. If your back ends are nearby -# (as they should be) then keep this number small. -CONNECT_TIMEOUT = 2 - -# When retrying connecting to the same back end (config: retries), how -# long should we wait between trying again? -RETRY_WAIT = 1 - -# The default size of buffers for the copying of data between sockets. -# Can be overruled using the flag -B. -DEFAULT_TCP_BUFSZ = 5120 - -# The max size of the DNS cache (nr of entries). Crossroads will cache -# gethostbyname() results until the max nr is exceeded; then the latest hits -# will be killed off. -DNS_CACHESIZE = 20 - -# The max age of a DNS cache entry, before it is considered obsolete. -# Value is in seconds; so e.g. 900 is 15 minutes, 3600 is 1 hour. -# Value 0 suppresses DNS caching. This can always be overruled using the -# flag -d. -DNS_CACHETTL = 0 - -# The number of #define expansions before Crossroads thinks it's a -# circular definition. Also the max nr of #defines that's allowed in one file. -DEF_MAX = 1000 - -# Prefix directory for the install processs. -PREFIX = /usr/local - -# Installation directory for the binaries. -BINDIR = $(PREFIX)/bin - -# Default size of a commandline buffer. This is used in juggling the -# process name. If your headers define SPT_BUFSIZE then that will be used -# instead. -DEFAULT_SPT_BUFSIZE = 2048 - -# If you're on Linux, then one can set the program title for 'ps' output -# by modifying argv directly. If you're on Linux or if your system supports -# this too, then define as value 1. Otherwise set to zero. If your system -# has setproctitle(3) then that will be used instead. -SET_PROC_TITLE_BY_ARGV = 1 - -# Set to some meaningful value if you're on Solaris and don't want to -# fiddle with LD_LIBRARY_PATH or crle (yuk!) -# EXTRALIBS = -R/usr/ucblib -EXTRALIBS = diff --git a/etc/Makefile.help b/etc/Makefile.help @@ -1,37 +0,0 @@ -============================================================================ - The Making Of.... Crossroads. -============================================================================ - -Make what? Choose: - -** Local Construction ** - make compile-only Local program construction (src/crossroads/crossroads - and src/crossroads-daemon/crossroads-daemon) - make local Local program construction and unit tests - make local-g Like 'make local' but wigh 'gdb' symbols - make local-pg Like 'make local-g' but with 'gprof' profiling - make local-alf Like 'make local' but with allocation leak finder - (requires 'alf', for debugging only) - make documentation Creates documentation under doc/. Requires Yodl - and pdflatex. - -** Installing ** - make install Locally built programs are installed, and the - manpages are installed, see etc/Makefile.def for - the destination location. - NOTE: Don't use 'make local-pg' and 'make install' - in production environments! - make dbginstall Debug-able programs are installed (for testing only) - make pginstall Debug-able and profile-able programs are installed - (for testing/profiling only) - -** Maintenance ** - make clean Cleanup of stale files - make distclean Even cleaner still - make dist Creates a distribution as - /tmp/crossroads-VERSION.tar.gz, Yodl etc. required - make commit Creates documentation, cleans up, commits into SVN. - -(If your GNU make is called 'gmake', just type with 'gmake <whatever>'.) - -============================================================================ diff --git a/etc/dispatcher-roundrobin b/etc/dispatcher-roundrobin @@ -1,84 +0,0 @@ -#!/usr/bin/perl - -use strict; - -# Example of a round-robin external dispatcher. This is totally -# superfluous, Crossroads has this on-board; if you use the external -# program for determining round-robin dispatching, then you'll only -# slow things down. This script is just meant as an example. - -# Globals / configuration -# ----------------------- -my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress -my $statefile = '/tmp/rr.last'; # Where we keep the last used - -# Logging -# ------- -sub msg { - return if ($log eq '/dev/null' or $log eq ''); - open (my $of, ">>$log") or return; - print $of (scalar(localtime()), ' ', @_); -} - -# Read the last used back end -# --------------------------- -sub readlast() { - my $ret; - - if (open (my $if, $statefile)) { - $ret = <$if>; - chomp ($ret); - close ($if); - msg ("Last used back end: $ret\n"); - return ($ret); - } - msg ("No last-used back end (yet)\n"); - return (undef); -} - -# Write back the last used back end, reply to Crossroads and stop -# --------------------------------------------------------------- -sub reply ($) { - my $last = shift; - - if (open (my $of, ">$statefile")) { - print $of ("$last\n"); - } - print ("$last\n"); - exit (0); -} - -# Main starts here -# ---------------- - -# Collect the cmdline arguments. We expect pairs of backend-name / -# backend-availablility, and we'll store only the available ones. -msg ("Dispatch request received\n"); -my @backend; -for (my $i = 0; $i <= $#ARGV; $i += 2) { - push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]); -} -msg ("Available back ends: @backend\n"); - -# Let's see what the last one is. If none found, then we return the -# first available back end. Otherwise we need to go thru the list of -# back ends, and return the next one in line. -my $last = readlast(); -if ($last eq '') { - msg ("Returning first available back end $backend[0]\n"); - reply ($backend[0]); -} - -# There **was** a last back end. Try to match it in the list, -# then return the next-in-line. -for (my $i = 0; $i < $#backend; $i++) { - if ($last eq $backend[$i]) { - msg ("Returning next back end ", $backend[$i + 1], "\n"); - reply ($backend[$i + 1]); - } -} - -# No luck.. run back to the first one. -msg ("Returning first back end $backend[0]\n"); -reply ($backend[0]); - diff --git a/etc/hdr.template b/etc/hdr.template @@ -1,5 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads __VER__, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ diff --git a/etc/healthcheck b/etc/healthcheck @@ -1,63 +0,0 @@ -#!/usr/bin/perl - -use strict; -use LWP::UserAgent; - -# Show usage and stop -sub usage () { - die <<"ENDUSAGE"; - -Usage: healthcheck URL SERVICE BACKEND - -Performs a health check of the stated url and if necessary flips the -state of the stated back end between 'available' and 'unavailable'. -Back ends that are 'down' are not affected. - -ENDUSAGE -} - -# Test an URL. Return whether it's a success. -sub testurl() { - my $ua = LWP::UserAgent->new(); - my $req = HTTP::Request->new(GET => $ARGV[0]); - my $res = $ua->request($req); - - if ($res->is_success()) { - print ("URL $ARGV[0] indicates success\n"); - return (1); - } else { - print ("URL $ARGV[0] indicates failure\n"); - return (0); - } -} - -# Main starts here -usage() unless ($#ARGV == 2); - -# Get the current state of the back end -open (my $if, "crossroads -t status '$ARGV[1]' '$ARGV[2]' |") - or die ("Cannot run 'crossroads status'\n"); -my $status = <$if>; -close ($if) or die ("'crossroads status' terminated with error\n"); -chomp ($status); -$status =~ s{.*=}{}; -$status =~ s{\s+}{}; - -print ("Back end $ARGV[2] has status $status\n"); -if ($status eq 'UNAVAILABLE') { - if (testurl()) { - print ("Back end $ARGV[2] is now available\n"); - system ("crossroads tell $ARGV[1] $ARGV[2] available") - and die ("'crossroads tell' terminated with error\n"); - } -} elsif ($status eq 'available') { - if (!testurl()) { - print ("Back end $ARGV[2] is is now UNAVAILABLE\n"); - system ("crossroads tell $ARGV[1] $ARGV[2] UNAVAILABLE") - and die ("'crossroads tell' terminated with error\n"); - } -} else { - print ("No action given status '$status'\n"); -} - - diff --git a/etc/rdp-helper b/etc/rdp-helper @@ -1,140 +0,0 @@ -#!/usr/bin/perl - -use strict; -use GDBM_File; - -# Global variables and configuration -# ---------------------------------- -my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress -my $cdb = '/tmp/client.db'; # GDBM database of clients -my %db; # .. and memory representation of it -my $timeout = 24*60*60; # Timeout of a connection in secs - -# Logging -# ------- -sub msg { - return if ($log eq '/dev/null' or $log eq ''); - open (my $of, ">>$log") or return; - print $of (scalar(localtime()), ' ', @_); - close ($of); -} - -# Reply a back end to the caller and stop processing. -# --------------------------------------------------- -sub reply ($) { - my $b = shift; - msg ("Suggesting $b to Crossroads.\n"); - print ("$b\n"); - exit (0); -} - -# Is a value in an array -# ---------------------- -sub inarray { - my $val = shift; - for my $other (@_) { - return (1) if ($other eq $val); - } - return (0); -} - -# A connection is starting -# ------------------------ -sub start { - my ($ip, $stamp, $backend) = @_; - msg ("Logging START of connection for IP $ip on stamp $stamp, ", - "back end $backend\n"); - $db{$ip} = "$backend:$stamp"; -} - -# A connection has ended -# ---------------------- -sub end { - my $ip = shift; - msg ("Logging END of connection for IP $ip\n"); - $db{$ip} = undef; -} - -# A back end has failed -# --------------------- -sub fail { - my $backend = shift; - msg ("Back end $backend failed, thanks for notifying me\n"); -} - -# Request to determine a back end -# ------------------------------- -sub dispatch { - my $ip = shift; - my $stamp = shift; - - msg ("Request to dispatch IP $ip on stamp $stamp\n"); - - # Read the next arguments. They are triplets of - # backend-name / availability / weight. Store if the back end is - # available. - my (@backends, @weights); - for (my $i = 0; $i < $#_; $i += 3) { - if ($_[$i + 1] != 0) { - push (@backends, $_[$i]); - push (@weights, $_[$i + 2]); - msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n"); - } - } - - # See if this is a reconnect by a previously seen client IP. We'll - # treat this as a reconnect if the timeout wasn't yet exceeded. - if ($db{$ip} ne '') { - my ($last_backend, $last_stamp) = split (/:/, $db{$ip}); - msg ("IP $ip had last connected on $last_stamp to $last_backend\n"); - if ($stamp < $last_stamp + $timeout) { - msg ("Timeout not yet exceeded, this may be a reconnect\n"); - # We'll allow a reconnect only if the stated last_backend is - # free (sanity check). - if (inarray ($last_backend, @backends)) { - msg ("Last back end $last_backend is available, ", - "letting through\n"); - reply ($last_backend); - } else { - msg ("Last used back end isn't free, suggesting a new one\n"); - } - } else { - msg ("Timeout exceeded, suggesting a new back end\n"); - } - } else { - msg ("Np preveious connection data, suggesting a new back end\n"); - } - - my $bestweight = -1; - my $bestbackend; - for (my $i = 0; $i <= $#weights; $i++) { - if ($bestweight == -1 or $bestweight > $weights[$i]) { - $bestweight = $weights[$i]; - $bestbackend = $backends[$i]; - } - } - - msg ("Best back end: $bestbackend (given weight $bestweight)\n"); - reply ($bestbackend); -} - -# Main starts here -# ---------------- -msg ("Start of run, attaching GDBM database '$cdb'\n"); -tie (%db, 'GDBM_File', $cdb, &GDBM_WRCREAT, 0600); - -# The first argument must be an action 'dispatch', 'start' or 'end'. -# Depending on the action, we do stuff. -my $action = shift (@ARGV); -if ($action eq 'dispatch') { - dispatch (@ARGV); -} elsif ($action eq 'start') { - start (@ARGV); -} elsif ($action eq 'end') { - end (@ARGV); -} elsif ($action eq 'fail') { - fail (@ARGV); -} else { - print STDERR ("Usage: rdp-helper {dispatch|start|end|fail} args\n"); - exit (1); -} diff --git a/etc/svncheck b/etc/svncheck @@ -1,21 +0,0 @@ -#!/usr/bin/perl - -use strict; - -my $startdir = $ENV{BASE} - or die ("BASE not set.\n", - "You should only run this from the top level level Makefile!\n"); -chdir ($startdir) - or die ("Start dir '$startdir' not available!\n"); - -open (my $if, "svn status |") - or die ("Cannot run 'svn status'\n"); -my $unknowns = 0; -while (my $line = <$if>) { - if ($line =~ /^\?/) { - print STDERR ("ERROR - unknown: $line"); - $unknowns++; - } -} -die ("ERROR - $unknowns unknown files, svn not up to date?\n") - if ($unknowns); diff --git a/etc/svnrev b/etc/svnrev @@ -1,42 +0,0 @@ -#!/usr/bin/perl -use strict; - -die ("Usage: svnrev REFERENCEFILE STATEFILE\n", - "The revision number of the reference file is retrieved and shown.\n", - "The state file is updated.\n") - if ($#ARGV != 1); - -die ("No such file $ARGV[0]\n") - unless (-f $ARGV[0]); - -my $revno; - -# Can we slurp in "svn info"? -if (open (my $if, "svn info $ARGV[0] 2>&1 |")) { - while (defined (my $line = <$if>)) { - chomp ($line); - next unless ($line =~ /Last Changed Rev:/); - $line =~ s/Last Changed Rev: //; - $revno = $line; - } - close ($if); - - if ($revno ne '') { - open (my $of, ">$ARGV[1]") - or die ("Cannot write $ARGV[2]: $!\n"); - print $of ("$revno\n"); - close ($of); - } -} - -# If we failed to retrieve the rev no, get it from the state file. -if ($revno eq '') { - open (my $if, $ARGV[1]) - or die ("Cannot read $ARGV[1]: $!\n"); - $revno = <$if>; - chomp ($revno); - close ($if); -} - -# Finally.. display it -print ("$revno\n"); diff --git a/etc/svnrev.txt b/etc/svnrev.txt @@ -1 +0,0 @@ -233 diff --git a/etc/xml-status-short.xslt b/etc/xml-status-short.xslt @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xsl:stylesheet version="1.0" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> - -<xsl:template match="/"> - <html> - <head> - <title>Crossroads Status Overview</title> - </head> - <body> - <h1>Crossroads Status Overview</h1> - <xsl:apply-templates/> - </body> - </html> -</xsl:template> - -<xsl:template match="/status"> - <table> - <xsl:apply-templates/> - </table> -</xsl:template> - -<xsl:template match="/status/service"> - <tr> - <td> <b> Service <xsl:value-of select="@name"/> </b> </td> - <td align="right"> <xsl:value-of select="connections"/> </td> - <td> connections </td> - </tr> - <xsl:apply-templates/> - <tr> </tr> -</xsl:template> - -<xsl:template match="/status/service/backend"> - <tr> - <td> </td> - <td> Backend </td> - <td> <b> <xsl:value-of select="@name"/> </b> </td> - <td> - is <xsl:value-of select="availability"/> - </td> - <td> </td> - </tr> - <tr> - <td> </td> - <td> </td> - <td> Clients: </td> - <td> - total <xsl:value-of select="connections"/>, - <xsl:value-of select="failures"/> failures - </td> - </tr> - <tr> - <td> </td> - <td> </td> - <td> Usage: </td> - <td> - <xsl:value-of select="throughput"/> in - <xsl:value-of select="duration"/>, - <xsl:value-of select="clients"/> active connections - </td> - </tr> - <xsl:apply-templates/> -</xsl:template> - -<xsl:template match="*"/> - -</xsl:stylesheet> diff --git a/etc/xml-status-to-html.xslt b/etc/xml-status-to-html.xslt @@ -1,131 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xsl:stylesheet version="1.0" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> - -<!-- Sample XSLT stylesheet to convert "crossroads -x status" - into HTML. This is only a VERY BASIC example, you would - want to add a CSS, or to display information in a different - way. Good luck! - Karel <karel@e-tunity.com> --> - -<xsl:template match="/"> - <html> - <head> - <title>Crossroads Status Overview</title> - </head> - <body> - <h1>Crossroads Status Overview</h1> - <xsl:apply-templates/> - </body> - </html> -</xsl:template> - -<xsl:template match="/status"> - <table> - <xsl:apply-templates/> - </table> -</xsl:template> - -<xsl:template match="/status/service"> - <tr> - <td> <b> Service <xsl:value-of select="@name"/> </b> </td> - <td> </td> - <td> </td> - <td> </td> - </tr> - <xsl:apply-templates/> -</xsl:template> - -<xsl:template match="/status/service/connections"> - <tr> - <td> </td> - <td> Connections: </td> - <td> </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/lastbackend"> - <tr> - <td> </td> - <td> Last back end: </td> - <td> </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend"> - <tr> - <td> </td> - <td> <b> Backend <xsl:value-of select="@name"/> </b> </td> - <td> </td> - <td> </td> - </tr> - <xsl:apply-templates/> -</xsl:template> - -<xsl:template match="/status/service/backend/availability"> - <tr> - <td> </td> - <td> </td> - <td> Availability: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend/clients"> - <tr> - <td> </td> - <td> </td> - <td> Connections: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend/connections"> - <tr> - <td> </td> - <td> </td> - <td> Total successes: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend/failures"> - <tr> - <td> </td> - <td> </td> - <td> Total failures: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend/duration"> - <tr> - <td> </td> - <td> </td> - <td> Duration: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend/throughput"> - <tr> - <td> </td> - <td> </td> - <td> Throughput: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="/status/service/backend/sessions"> - <tr> - <td> </td> - <td> </td> - <td> Sessions: </td> - <td> <xsl:value-of select="."/> </td> - </tr> -</xsl:template> - -<xsl:template match="*"/> - -</xsl:stylesheet> diff --git a/src/Makefile b/src/Makefile @@ -1,45 +0,0 @@ -include ../etc/Makefile.def - -DIRS = lib crossroads crossroads-daemon crossroads-mgr - -all: prep - for d in ${DIRS}; do \ - echo "Making all in $$d"; \ - echo BASE=$(BASE) PG=$(PG) G=$(G) $(MAKE) -C $$d all; \ - BASE=$(BASE) PG=$(PG) G=$(G) ALF=$(ALF) \ - $(MAKE) -C $$d all || exit 1; \ - done - -install: prep - for d in ${DIRS}; do \ - echo "Making install in $$d"; \ - echo BASE=$(BASE) PG=$(PG) G=$(G) $(MAKE) -C $$d install; \ - BASE=$(BASE) $(MAKE) -C $$d install || exit 1; \ - done - -dbginstall: prep - for d in ${DIRS}; do \ - echo "Making dbginstall in $$d"; \ - echo BASE=$(BASE) PG=$(PG) G=$(G) $(MAKE) -C $$d dbginstall; \ - BASE=$(BASE) $(MAKE) -C $$d dbginstall || exit 1; \ - done - -clean: - for d in ${DIRS}; do \ - echo "Making clean in $$d"; \ - echo BASE=$(BASE) $(MAKE) -C $$d clean; \ - BASE=$(BASE) $(MAKE) -C $$d clean || exit 1; \ - done - rm -f c-conf.cache - -distclean: - for d in ${DIRS}; do \ - echo "Making distclean in $$d"; \ - echo BASE=$(BASE) $(MAKE) -C $$d distclean; \ - BASE=$(BASE) $(MAKE) -C $$d distclean || exit 1; \ - done - rm -f c-conf.cache - -prep: - ../tools/gettools e-ver e-txt2c c-conf - ../tools/e-ver ../ChangeLog $(VER) diff --git a/src/crossroads-daemon/Makefile b/src/crossroads-daemon/Makefile @@ -1,28 +0,0 @@ -include $(BASE)/etc/Makefile.def -include $(BASE)/etc/Makefile.conf - -BIN = crossroads-daemon -all: - BASE=$(BASE) $(MAKE) $(BIN) - -dbginstall: all $(BINDIR)/$(BIN) - -install: all $(BINDIR)/$(BIN) - strip $(BINDIR)/$(BIN) - -distclean: clean -clean: - rm -f *.o $(BIN) - -$(BINDIR)/$(BIN): $(BIN) - cp $(BIN) $(BINDIR)/$(BIN) - -main.o: main.c - $(CC) -c -g -W -Wall $(DEFS) $(PG) main.c - -$(BIN): main.o ../lib/libcrossroads.a - $(CC) $(PG) -o $(BIN) main.o -L$(BASE)/src/lib -lcrossroads \ - $(LIBS) $(EXTRALIBS) - -# Extra deps: -main.o: main.c $(BASE)/src/crossroads.h $(BASE)/etc/Makefile.def diff --git a/src/crossroads-daemon/main.c b/src/crossroads-daemon/main.c @@ -1,61 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int main (int argc, char **argv) { - int i; - - /* Remember original ac/av/ep */ - org_argc = argc; - org_argv = argv; - - /* Inherit program options and configuration */ - options_read (0); - config_read (0); - - /* Convert the log facility number into a syslog constant. */ - switch (opt.log_facility) { - case 0: - opt.log_facility = LOG_LOCAL0; - break; - case 1: - opt.log_facility = LOG_LOCAL1; - break; - case 2: - opt.log_facility = LOG_LOCAL2; - break; - case 3: - opt.log_facility = LOG_LOCAL3; - break; - case 4: - opt.log_facility = LOG_LOCAL4; - break; - case 5: - opt.log_facility = LOG_LOCAL5; - break; - case 6: - opt.log_facility = LOG_LOCAL6; - break; - case 7: - opt.log_facility = LOG_LOCAL7; - break; - default: - opt.log_facility = LOG_DAEMON; - break; - } - - /* We're the daemon executable. We know what to do -- serve. */ - msg ("Starting services"); - for (i = 0; i < nservice; i++) { - /* Start the service. */ - activeservice = service + i; - runservice (); - } - - /* All done. */ - return (0); -} diff --git a/src/crossroads-mgr/Makefile b/src/crossroads-mgr/Makefile @@ -1,19 +0,0 @@ -include $(BASE)/etc/Makefile.def - -all: crossroads-mgr -crossroads-mgr: crossroads-mgr.in Makefile $(BASE)/tools/patch-mgr \ - crossroads-mgr.xslt $(BASE)/etc/Makefile.def \ - crossroads-mgr.usage - $(BASE)/tools/patch-mgr $(VER)/$(REVVER) \ - crossroads-mgr.xslt crossroads-mgr.usage < $< > $@ - chmod +x $@ - -dbginstall: install -install: $(BINDIR)/crossroads-mgr -$(BINDIR)/crossroads-mgr: crossroads-mgr - cp crossroads-mgr $(BINDIR)/crossroads-mgr - chmod +x $(BINDIR)/crossroads-mgr - -distclean: clean -clean: - rm -f crossroads-mgr diff --git a/src/crossroads-mgr/crossroads-mgr.in b/src/crossroads-mgr/crossroads-mgr.in @@ -1,460 +0,0 @@ -#!/usr/bin/perl - -__WARNING__ - -use strict; -use HTTP::Daemon; -use Getopt::Std; -use POSIX qw(:sys_wait_h setsid); -use MIME::Base64; - -# The built in XSLT. -my $builtin_xslt = <<'END_XSLT'; -__XSLT__ -END_XSLT - -# The version ID -my $VER = "__VER__"; - -# Main starts here -my $prog; -$prog = Program->new(); -my $daemon = Daemon->new (); -$daemon->start ($prog->port()); # doesn't return - - -# All program-related functions -{ - package Program; - use strict; - - # Instantiate program handler. Parse the commandline. - sub new { - my $proto = shift; - my $self = {}; - my %opts; - - # Get the commandline options. - ::getopts ('vfl:x:X:b:B:a:', \%opts) or usage(); - - # Act according to argument; 'start' is handled here. - if ($ARGV[0] eq 'stop') { - stop(); - exit (0); - } elsif ($ARGV[0] eq 'status' and $#ARGV == 0) { - status(); - exit (0); - } elsif ($ARGV[0] ne 'start' or $#ARGV != 1) { - usage(); - exit (0); - } - - # ARGV[0] was 'start' and there is one more argument $ARGV[1], port - $self->{port} = sprintf ("%d", $ARGV[1]); - $self->{verbose} = $opts{v} ? 1 : 0; - $self->{foreground} = $opts{f} ? 1 : 0; - $self->{isdaemon} = 0; - $self->{xsltfile} = $opts{X} if ($opts{X}); - $self->{address} = $opts{a} if ($opts{a}); - if ($opts{b}) { - if ($opts{b} eq 'PROMPT') { - print ("Enter username:password for status authentication: ") - if (-t STDIN); - $opts{b} = <STDIN>; - chomp ($opts{b}); - } - $self->{statuscred} = ::encode_base64($opts{b}); - chomp ($self->{statuscred}); - } - if ($opts{B}) { - if ($opts{B} eq 'PROMPT') { - print ("Enter username:password for updating authentication: ") - if (-t STDIN); - $opts{B} = <STDIN>; - chomp ($opts{B}); - } - $self->{setcred} = ::encode_base64($opts{B}); - chomp ($self->{setcred}); - } - if ($opts{x}) { - $self->{call} = $opts{x}; - } else { - $self->{call} = ''; - for my $p (split /:/, $ENV{PATH}) { - if (-x "$p/crossroads") { - $self->{call} = "$p/crossroads"; - last; - } - } - die ("Failed to locate crossroads program on PATH\n") - if ($self->{call} eq ''); - } - die ("Argument $ARGV[0] is not a port number\n") unless ($self->{port}); - $self->{logfile} = "/tmp/crossroads-mgr.log"; - $self->{logfile} = $opts{l} if ($opts{l}); - - bless ($self, $proto); - return ($self); - } - - # Return the necessary un/pw for status viewing, in Base64 encoding - sub authstatus { - return ($_[0]->{statuscred}); - } - - # Return the necessary un/pw for status setting, in Base64 encoding - sub authset { - return ($_[0]->{setcred}); - } - - # Show usage and stop. - sub usage { - die <<"ENDUSAGE"; - -__USAGE__ - -ENDUSAGE - } - - # Stop the daemon. NOTE: Class-less, called from the constructor. - sub stop { - my @pids = pids() - or die ("crossroads-mgr is not running\n"); - kill (2, @pids); - } - - # Report on the status. NOTE: Class-less, called from the constructor. - sub status { - my @pids; - if (@pids = pids()) { - print ("crossroads-mgr is running (pids @pids)\n"); - } else { - print ("crossroads-mgr is NOT running\n"); - } - } - - # Return the PID's where the manager is running. NOTE: Class-less. - sub pids { - my %ps = (Darwin => 'ps ax', - linux => 'ps ax', - Linux => 'ps ax', - SunOS => 'ps -eo \'pid args\''); - my $uname = `uname`; - chomp ($uname); - die ("Failed to get uname of this system\n") if ($uname eq ''); - die ("No 'ps' command configured for uname '$uname'\n") - unless ($ps{$uname}); - open (my $if, "$ps{$uname} |") - or die ("Failed to start '$ps{$uname}'\n"); - my @ret; - while (my $line = <$if>) { - next unless ($line =~ /perl/ and $line =~ /crossroads-mgr/); - my $pid = sprintf ("%d", $line); - next if ($pid == $$); - push (@ret, $pid); - } - return (@ret); - } - - # Get or set the listening port. - sub port { - $_[0]->{port} = $_[1] if ($_[1]); - return ($_[0]->{port}); - } - - # Get or set the listening address. - sub address { - $_[0]->{address} = $_[1] if ($_[1]); - return ($_[0]->{address}); - } - - # Issue an error - sub error { - my $self = shift; - if (! $self->{isdaemon}) { - die (@_, "\n"); - } else { - $self->sendlog ('ERROR', @_); - die (@_); - } - } - - # Return a Crossroads command - sub call { - my $self = shift; - my $ret = $self->{call}; - for my $a (@_) { - $ret .= " $a"; - } - $ret .= " 2>&1"; - $self->msg ("External call: [$ret]"); - return ($ret); - } - - # Log a verbose message. - sub msg { - my $self = shift; - return unless ($self->{verbose}); - if (! $self->{isdaemon}) { - print (@_, "\n"); - } else { - $self->sendlog ('DEBUG', @_); - } - } - - # Log a line - sub sendlog { - my $self = shift; - my $type = shift; - open (my $of, '>>' . $self->{logfile}) or return; - print $of (scalar (localtime()), ' ', $type, ' ', @_, "\n"); - } - - # Get/set verbosity - sub verbose { - $_[0]->{verbose} = $_[1] if ($_[1]); - return ($_[0]->{verbose}); - } - - # Get/set foreground mode - sub foreground { - $_[0]->{foreground} = $_[1] if ($_[1]); - return ($_[0]->{foreground}); - } - - # Daemonize - sub daemonize { - my $self = shift; - return if ($self->{foreground}); - if (my $pid = fork()) { - $self->msg ("Daemon is running as pid $pid"); - sleep (2); - exit (0); - } - $self->msg ("Daemonizing."); - $self->{isdaemon} = 1; - open (STDIN, "/dev/null") - or $self->error ("Cannot read /dev/null: $!"); - open (STDOUT, ">/dev/null") - or $self->error ("Cannot write /dev/null: $!"); - open (STDERR, ">&STDOUT") - or $self->error ("Cannot dup stdout: $!"); - } - - # The XSLT stylesheet - sub xslt { - my $self = shift; - if (! $self->{xsltfile}) { - $self->msg ("Returning built-in XSLT"); - return ($builtin_xslt); - } else { - $self->msg ("Returning contents of ", $self->{xsltfile}, - " as XSLT"); - open (my $if, $self->{xsltfile}) - or die ("Cannot read ", $self->{xsltfile}, ": $!"); - my $ret = ''; - while (my $line = <$if>) { - $ret .= $line; - } - return ($ret); - } - } -} - -# Daemon-related stuff -{ - package Daemon; - use strict; - - # Instantiate a daemon and start serving. - sub new { - my $proto = shift; - my $self = {}; - $self->{request} = ''; - $self->{conn} = ''; - bless ($self, $proto); - $prog->msg ("HTTP daemon instantiated."); - return ($self); - } - - sub start { - my ($self, $port) = @_; - $prog->msg ("Starting HTTP daemon on port $port"); - my $daemon = HTTP::Daemon->new (LocalPort => $port, - LocalAddr => $prog->address(), - ReuseAddr => 1) - or $prog->error ("Failed to start HTTP daemon. ", - "Maybe the address is in use?"); - $SIG{CHLD} = \&reaper; - $prog->daemonize(); - $prog->msg ("Open for business"); - while (1) { - my ($conn, $peer) = $daemon->accept() or next; - $prog->msg ("Accepted new connection, spawning handler"); - next if (my $pid = fork()); - $prog->msg ("Handler spawned as pid $$"); - my $req = Request->new(); - eval { $req->serve ($conn); }; - $conn->send_error (500, $@) if ($@); - exit (0); - } - } - - # Reaper of stopped child processes. NOTE: Class-less! - sub reaper { - my $child; - while (($child = waitpid (-1, 'WNOHANG')) > 0){ - $prog->msg ("Child process $child stopped with status $?"); - } - $SIG{CHLD} = \&reaper; - } -} - -# Request related stuff -{ - package Request; - use strict; - - sub new { - my $proto = shift; - my $self = {}; - bless ($self, $proto); - return ($self); - } - - # Serve a request. - sub serve { - my ($self, $conn) = @_; - - # Receive the request. - $self->receive ($conn); - } - - # Check authentication. - sub checkauth { - my ($self, $required, $desc) = @_; - return (1) if ($required eq ''); - my $h = $self->{request}->header ('Authorization') - or return ($self->prompt_auth($desc)); - $h =~ s/^\s*Basic\s*//i; - if ($h eq $required) { - $prog->msg ("Required authentication '$required' matched"); - return (1); - } - $prog->msg ("Required authentication '$required' does not ", - "match passed '$h'"); - return ($self->prompt_auth($desc)); - } - - # Prompt for authentication - sub prompt_auth { - my ($self, $desc) = @_; - $prog->msg ("Prompting for authentication ($desc)"); - my $r = HTTP::Response->new (401, 'Authentication Required'); - $r->header ('WWW-Authenticate' => - "Basic realm=\"Crossroads Load Balancer: $desc\""); - $self->{conn}->send_response ($r); - return (undef); - } - - # Receive client's request. - sub receive { - my ($self, $conn) = @_; - $self->{conn} = $conn; - $self->{request} = $conn->get_request() - or die ("Failed to receive request"); - die ("Bad request, only GET supported") - if ($self->{request}->method() ne 'GET'); - my $uri = $self->{request}->uri(); - $prog->msg ("Requested URI: ", $uri); - - # Take action depending on the URI. - if ($uri eq '/') { - $self->act_status (); - } elsif ($uri eq '/xslt') { - $self->act_xslt (); - } elsif ($uri =~ m{^/set/}) { - $self->act_set($uri); - } else { - die ("No action on URI ", $uri, "\n"); - } - } - - # Status setter - sub act_set { - my ($self, $uri) = @_; - - # The uri is /set/{service}/{backend}/state/{numericalstate} or - # /set/{service}/{backend}/server/{hostname}/{port} - # Get the variables, and run crossroads tell. - $self->checkauth ($prog->authset(), "Status Modification") - or return; - my (undef, undef, $service, $backend, $action) = split (/\//, $uri); - my ($nr, $hostname, $port, $if); - if ($action eq 'state') { - (undef, undef, undef, undef, undef, $nr) = split (/\//, $uri); - $prog->msg ("Setting service $service, backend $backend ", - "to state $nr"); - open ($if, $prog->call ("tell $service $backend state $nr") . '|') - or die ("Cannot start crossroads\n"); - } elsif ($action eq 'server') { - (undef, undef, undef, undef, undef, $hostname, $port) = - split (/\//, $uri); - $prog->msg ("Setting service $service, backend $backend ", - "to address $hostname:$port"); - open ($if, $prog->call ("tell $service $backend " . - "server $hostname:$port") . '|') - or die ("Cannot start crossroads\n"); - } else { - die ("Action '$action' not supported in 'set'\n"); - } - - my $resp = ''; - while (<$if>) { - $resp .= $_; - } - close ($if); - die ("Failed to modify runtime environment: $resp\n") - if ($resp ne ''); - - # Runtime environment is updated. Redirect to the overview page. - $prog->msg ("Redirecting to status overview page"); - my $r = HTTP::Response->new (302, 'Moved Temporarily'); - $r->header ('Location' => '/'); - $r->header ('Cache-Control' => 'no-cache'); - $self->{conn}->send_response ($r); - } - - # Status overview actor - sub act_status { - my $self = shift; - - $prog->msg ("Serving status overview page"); - $self->checkauth ($prog->authstatus(), "Status Retrieval") - or return; - open (my $if, $prog->call ('-x -Xxslt status') . '|') - or die ("Failed to run status retrieval"); - my $buf = ''; - while (my $line = <$if>) { - $buf .= $line; - } - - my $r = HTTP::Response->new (200, 'OK'); - $r->header ('Content-Type' => 'text/xml'); - $r->header ('Cache-Control' => 'no-cache'); - $r->content ($buf); - $self->{conn}->send_response ($r); - } - - # XSLT server - sub act_xslt { - my $self = shift; - - $prog->msg ("Serving XSLT"); - my $r = HTTP::Response->new (200, 'OK'); - $r->header ('Content-Type' => 'text/xml'); - $r->header ('Cache-Control' => 'no-cache'); - $r->content ($prog->xslt()); - $self->{conn}->send_response ($r); - } -} diff --git a/src/crossroads-mgr/crossroads-mgr.usage b/src/crossroads-mgr/crossroads-mgr.usage @@ -1,25 +0,0 @@ -This is crossroads-mgr V$VER, a web interface for Crossroads. -Usage: crossroads-mgr [flags] start PORT (starts on tcp port) - or: crossroads-msg stop (stops the manager) - or: crossroads-msg status (shows if it's running) - -Controls a mini-webserver to monitor Crossroads. -Supported flags during 'start' are: - -a ADDR States the binding address for the monitor. Default is to listen - to all addresses. - -b UN:PW Viewing the Crossroads status will be protected using basic - authentication, UN is the username, PW is the password. - Use PROMPT to be prompted for the username/password. - -B UN:PW Modifying back end states will be protected using authentication, - use PROMPT to be prompted for the username/password. - -f crossroads-mgr stays in the foreground instead of daemonizing - (for debugging). - -l FILE States the log file of the daemon, default is - /tmp/crossroads-mgr.log. - -v Verbosity is increased, either to the log file or to stdout - (in -f mode). - -x CMD States how crossroads should be invoked, default is 'crossroads'. - Set to e.g. '/opt/xr/bin/crossroads -c /opt/xr/conf/xr.conf' if - your Crossroads is in an atypical location and if we should not use - configuration /etc/crossroads.conf. - -X XSLT Serves XSLT file, instead of built-in xslt. diff --git a/src/crossroads-mgr/crossroads-mgr.xslt b/src/crossroads-mgr/crossroads-mgr.xslt @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xsl:stylesheet version="1.0" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> -<xsl:output method="html"/> - -<xsl:template match="/"> - <html> - <head> - <title>Crossroads Status Overview</title> - <script type="text/javascript"> - function setstate (target, index) { - document.location = '/set/' + target + '/state/' + index; - } - function setaddress (target) { - var hostname = document.getElementById (target + '/hostname').value; - var port = document.getElementById (target + '/port').value; - document.location = '/set/' + target + '/server/' + - hostname + '/' + port; - } - </script> - <style type="text/css"> - h1 { - font-family: Verdana,Helvetica; - font-size: 12pt; - color: blue; - } - body { - font-family: Verdana,Helvetica; - font-size: 8pt; - } - td { - font-family: Verdana,Helvetica; - font-size: 10pt; - } - .service { background-color: #ffff33; } - .backend { background-color: #ffff99; } - .info { font-size: 8pt; } - .footer { color: gray; } - </style> - </head> - <body> - <h1>Crossroads Status Overview</h1> - <hr/> - <form> - <xsl:apply-templates/> - </form> - <hr/> - <div class="footer"> - <i> - Generated by Crossroads __VER__. - Visit - <a href="http://crossroads.e-tunity.com"> - http://crossroads.e-tunity.com - </a> - for documentation and downloads. <br/> - </i> - </div> - </body> - </html> -</xsl:template> - -<xsl:template match="/status"> - <table width="100%"> - <xsl:apply-templates/> - </table> -</xsl:template> - -<xsl:template match="/status/service"> - <tr> - <td class="service" valign="top"> - <b> Service <xsl:value-of select="@name"/> </b> - </td> - <td class="service" colspan="3"> - <table> - <tr> - <td class="service">Total connections:</td> - <td class="service"><xsl:value-of select="connections"/></td> - </tr> - <xsl:if test="backends_available &gt; 0"> - <tr> - <td class="service">Available back ends:</td> - <td class="service"><xsl:value-of select="backends_available"/></td> - </tr> - </xsl:if> - <xsl:if test="backends_unavailable &gt; 0"> - <tr> - <td class="service">Unvailable back ends:</td> - <td class="service"><xsl:value-of select="backends_unavailable"/></td> - </tr> - </xsl:if> - <xsl:if test="backends_down &gt; 0"> - <tr> - <td class="service">Down back ends:</td> - <td class="service"><xsl:value-of select="backends_down"/></td> - </tr> - </xsl:if> - <xsl:if test="backends_waking &gt; 0"> - <tr> - <td class="service">Waking back ends:</td> - <td class="service"><xsl:value-of select="backends_waking"/></td> - </tr> - </xsl:if> - <tr> - <td class="service">Listener process PID:</td> - <td class="service"><xsl:value-of select="pid"/></td> - </tr> - </table> - </td> - </tr> - <xsl:apply-templates/> - <tr> </tr> -</xsl:template> - -<xsl:template match="/status/service/backend"> - <tr> - <td width="15%"> </td> - <td class="backend" width="15%"> Back end </td> - <td class="backend" width="15%"> <b> <xsl:value-of select="@name"/> </b> </td> - <td class="backend"> - <select onchange="setstate('{../@name}/{@name}', this.selectedIndex);"> - <xsl:choose> - <xsl:when test="availability/@id = 0"> - <option value="available" selected="1">available</option> - </xsl:when> - <xsl:otherwise> - <option value="available">available</option> - </xsl:otherwise> - </xsl:choose> - <xsl:choose> - <xsl:when test="availability/@id = 1"> - <option value="unavailable" selected="1">unavailable</option> - </xsl:when> - <xsl:otherwise> - <option value="unavailable">unavailable</option> - </xsl:otherwise> - </xsl:choose> - <xsl:choose> - <xsl:when test="availability/@id = 2"> - <option value="down" selected="1">down</option> - </xsl:when> - <xsl:otherwise> - <option value="down">down</option> - </xsl:otherwise> - </xsl:choose> - <xsl:choose> - <xsl:when test="availability/@id = 3"> - <option value="waking" selected="1">waking</option> - </xsl:when> - <xsl:otherwise> - <option value="waking">waking</option> - </xsl:otherwise> - </xsl:choose> - <xsl:choose> - <xsl:when test="availability/@id = 4"> - <option value="intermediate" selected="1">intermediate</option> - </xsl:when> - <xsl:otherwise> - <option value="intermediate">intermediate</option> - </xsl:otherwise> - </xsl:choose> - <xsl:choose> - <xsl:when test="availability/@id = 5"> - <option value="unknown" selected="1">unknown</option> - </xsl:when> - <xsl:otherwise> - <option value="unknown">unknown</option> - </xsl:otherwise> - </xsl:choose> - </select> - </td> - </tr> - <tr> - <td class="info"/> - <td class="info"/> - <td class="info"> Address: </td> - <td class="info"> - <input name="{../@name}/{@name}/hostname" type="text" class="info" - id="{../@name}/{@name}/hostname" value="{server}" maxlength="255"/> - : - <input name="{../@name}/{@name}/port" type="text" value="{port}" - id="{../@name}/{@name}/port" size="7" class="info"/> - <input type="button" class="info" value="Set" - onclick="setaddress('{../@name}/{@name}');"/> - </td> - </tr> - <tr> - <td class="info"/> - <td class="info"/> - <td class="info"> Connections: </td> - <td class="info"> - Total <xsl:value-of select="connections"/> - (<xsl:value-of select="failures"/> failures, - <xsl:value-of select="clients"/> active) - </td> - </tr> - <tr> - <td class="info"/> - <td class="info"/> - <td class="info"> Usage: </td> - <td class="info"> - Throughput - <b><xsl:value-of select="throughput"/></b> in - <b><xsl:value-of select="duration"/></b>, - <b><xsl:value-of select="lastaccess"/></b> ago - </td> - </tr> - <xsl:apply-templates/> -</xsl:template> - -<xsl:template match="*"/> - -</xsl:stylesheet> diff --git a/src/crossroads.h b/src/crossroads.h @@ -1,534 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -/* Actually used commandline size. */ -#ifndef SPT_BUFSIZE -#define SPT_BUFSIZE DEFAULT_SPT_BUFSIZE -#endif - -/* Includes. */ -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <netdb.h> -#if HAVE_MALLOC_H == 1 -#include <malloc.h> -#endif -#include <math.h> -#include <pwd.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#if HAVE_STDINT_H == 1 -#include <stdint.h> -#endif -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <sys/file.h> -#include <sys/ipc.h> -#include <sys/resource.h> -#include <sys/sem.h> -#include <sys/shm.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <sys/times.h> -#include <sys/types.h> - -/* Allocation leak finding (requires 'alf' package) */ -#ifdef ALF -#include <alf.h> -#endif - -/* flock() macros, incase they aren't present on your system. */ -#ifndef LOCK_SH -#define LOCK_SH 1 -#endif -#ifndef LOCK_EX -#define LOCK_EX 2 -#endif -#ifndef LOCK_NB -#define LOCK_NB 4 -#endif -#ifndef LOCK_UN -#define LOCK_UN 8 -#endif - -/* inet_addr() macros, incase they aren't present on your system. */ -#ifndef INADDR_NONE -#define INADDR_NONE ((unsigned long) -1) -#endif - -/* Types. */ - -/* NOTE: Adapt optionsread.c and data.c and parser.y when changing! - * -------------------------------------------------------------- */ -typedef struct { /* Operating options in daemon */ - int log_activity; /* -a: log TCP timings? */ - unsigned tcp_bufsz; /* -B: TCP buffer size */ - int dns_cachettl; /* -d: DNS cache timeout */ - int log_facility; /* -l: logging facility */ - int shmperm; /* -m: SHM permissions */ - int sloppyportbind; /* -s: sloppy port bindings? */ - int leave_proctitle; /* -p: leave process title? */ - int log_http_activity; /* -h: log HTTP activity? */ -} Options; - -typedef struct { /* Symbol table for #define */ - char *symbol; - char *redef; -} Symtab; - -typedef struct { /* Bison/Flex state given files */ - char *name; - FILE *file; - int lineno; - char *buf; -} ParserState; - -typedef enum { /* Config parsing related */ - cf_portspec, - cf_backlogspec, - cf_verbosityspec, - cf_backendspec, - cf_serverspec, - cf_dispatchspec, - cf_revivespec, - cf_checkspec, - cf_shmkeyspec, - cf_weightspec, - cf_decayspec, - cf_onstartspec, - cf_onfailspec, - cf_onendspec, - cf_connectiontimeoutspec, - cf_maxconnectionsspec, - cf_typespec, - cf_inspectionspec, - cf_dumpspec, - cf_thruspec, - cf_httptimingspec, - cf_bindspec, - cf_stickycookiespec, - cf_setclientheaderspec, - cf_addclientheaderspec, - cf_appendclientheaderspec, - cf_setserverheaderspec, - cf_addserverheaderspec, - cf_appendserverheaderspec, - cf_allowfromspec, - cf_denyfromspec, - cf_allowfilespec, - cf_denyfilespec, - cf_useraccountspec, - cf_retriesspec, - cf_statespec, -} Conftype; - -typedef union { /* Integer of string value */ - int ival; /* passed around by the parser */ - char *sval; -} IntOrString; - -typedef struct { - Conftype cf; - IntOrString v; -} Confset; - -typedef struct { - int n; - Confset *set; -} Confsets; - -typedef enum { /* Dispatching types */ - ds_roundrobin, - ds_random, - ds_bysize, - ds_byduration, - ds_byorder, - ds_byconnections, - ds_byclientip, - ds_externalhandler -} Dispatchtype; - -typedef enum { /* Service types */ - type_any, - type_http -} Servicetype; - -typedef enum { /* Backend availability */ - st_available, /* .. all OK */ - st_unavailable, /* .. temporarily unavailable */ - st_down, /* .. permanently unavailable */ - st_waking, /* .. scheduled for wake up */ - st_intermediate, /* .. bogus, for mark_activity() */ - st_unknown /* .. no idea (terminator) */ -} Backendavail; -#define NR_OF_STATES 6 /* Make sure this matches the above */ - /* possible states! */ - -typedef struct { /* Backend description */ - char *name; /* .. back end identifier */ - int initial_port; /* .. TCP port */ - char *initial_server; /* .. server name */ - int verbosity; /* .. debugging on/off */ - int weight; /* .. weight in backend selection */ - int decay; /* .. decay in % */ - char *onstart; /* .. system() on success */ - char *onfail; /* .. or on failure */ - char *onend; /* .. or on ending */ - char *dumpfile; /* .. traffic dump file */ - char *thruputfile; /* .. traffic throughput file */ - char *timinglog; /* .. HTTP timings log */ - unsigned maxconnections; /* .. max # of allowed connections */ - char *stickycookie; /* .. cookie selector */ - int retries; /* .. # retries before unavailable */ - char **addclientheader; /* .. client hdrs to ADD */ - int naddclientheader; /* .. table size */ - char **setclientheader; /* .. client hdrs to SET */ - int nsetclientheader; /* .. table size */ - char **appendclientheader; /* .. client hdrs to APPEND */ - int nappendclientheader; /* .. table size */ - char **addserverheader; /* .. server hdrs to ADD */ - int naddserverheader; /* .. table size */ - char **setserverheader; /* .. server hdrs to SET */ - int nsetserverheader; /* .. table size */ - char **appendserverheader; /* .. server hdrs to APPEND */ - int nappendserverheader; /* .. table size */ - Backendavail initial_avail; /* .. initial back end state */ -} Backend; - -typedef struct { /* Filtering information: */ - unsigned ip; /* .. IP address (or part of it) */ - unsigned mask; /* .. netmask */ -} IpFilter; - -typedef enum { /* HTTP header inspection mode */ - ins_deep, /* .. inspect all headers */ - ins_shallow, /* .. inspect only first */ -} HttpInspection; - -/* The configuration in internal representation. When changing, remember - * to change config_write() and config_read() and config_msg()! */ -typedef struct { /* Service description */ - /* Basic stuff */ - char *name; /* .. service name */ - char *bind; /* .. address to bind to */ - unsigned port; /* .. listening port */ - unsigned verbosity; /* .. message generation */ - Dispatchtype dispatchtype; /* .. backend selection method */ - unsigned dispatchover; /* .. selection over # connections */ - char *dispatchext; /* .. external handler */ - unsigned rev_interval; /* .. dead backend revival interval */ - unsigned check_interval; /* .. backend recheck interval */ - char *check_cmd; /* .. rechecking command */ - unsigned backlog; /* .. # pending TCP connections */ - unsigned shmkey; /* .. key for SysV IPC */ - unsigned connectiontimeout; /* .. # secs for timeout handling */ - unsigned maxconnections; /* .. max # of allowed connections */ - Servicetype type; /* .. type of the service */ - HttpInspection inspection; /* .. header inspection mode */ - char *allowfile; /* .. file with allowed IP filters */ - char *denyfile; /* .. and denied */ - int uid; /* .. UID to assume for system() */ - int gid; /* .. GID to assume for system() */ - /* Deny chain */ - int ndenychain; /* .. size of chain */ - IpFilter *denychain; /* .. chain of denied IP's */ - /* Allow chain */ - int nallowchain; /* .. size of chain */ - IpFilter *allowchain; /* .. chain of allowed IP's */ - /* Back ends */ - int nbackend; /* .. size of backend array */ - Backend *backend; /* .. the back ends */ -} Service; - -typedef struct { /* Backend state */ - Backendavail actual_avail; /* .. availability */ - unsigned long totuses; /* .. times hit */ - unsigned long failures; /* .. times failed */ - double nbytes; /* .. transferred bytes */ - unsigned long sessions; /* .. # of http sessions */ - unsigned long avg_nbytes; /* .. averaged over # connections */ - double nsec; /* .. connection durations */ - double avg_nsec; /* .. averaged over # connections */ - unsigned nclients; /* .. active clients */ - unsigned fail; /* .. failures so far */ - unsigned laststamp; /* .. last usage */ - int actual_port; /* .. currently used port */ - char actual_server[FQDN_LENGTH]; /* .. currently used server */ - double last_con_sec; /* .. last connect duration */ -} Backendstate; - -typedef struct { /* Service reporting (shmem) */ - int pid; /* .. PID of service daemon */ - int rev_pid; /* .. PID of revival handler */ - unsigned nclients; /* .. active clients */ - int last_backend; /* .. last used back end */ - Backendstate /* .. states of the back ends */ - backendstate[1]; /* .. actual shmem array will be >1 */ -} Servicereport; - -typedef enum { /* Stage of the program: */ - stage_main, /* .. commandline */ - stage_waiting, /* .. waiting for connections */ - stage_serving, /* .. servicing a connection */ - stage_retrying /* .. waking up a backend */ -} Programstage; /* Update stagetostring.c when */ - /* modifying! */ - -typedef struct { /* An HTTP Message */ - char **header; /* Headers table, excluding first */ - int nheader; /* Headers table size */ -} HttpHeader; - -typedef enum { /* Time representation format */ - tm_localtime, /* .. local */ - tm_gmtime /* .. UTC */ -} TmType; - -typedef enum { /* TCP copying direction */ - dir_client_to_server, - dir_server_to_client -} CopyDirection; - -typedef enum { /* HTTP version */ - con_unknown, - con_keepalive, - con_close -} HttpConnectionType; - -typedef struct { /* Numeric state to string map */ - Backendavail av; - char *nm; -} StateStringMap; - -typedef struct { /* DNS cache related */ - char *hostname; /* resolved name */ - struct in_addr hostaddr; /* resolved host info entry */ - time_t stamp; /* date of cache entry */ -} DnsCache; - -/* Globals. */ -#ifndef EXTERN -#define EXTERN extern -#endif - -EXTERN Service *activeservice; /* target service of a daemon */ -EXTERN unsigned char *clbuf; /* client socket input buffer */ -EXTERN int clbufpos, clbufmax; /* .. position & bytes */ -EXTERN char *client_ip; /* connected client */ -EXTERN unsigned client_ip_nr; /* .. and in reversed octets form */ -EXTERN char *config_file; /* config to parse */ -EXTERN int current_backend; /* of a given service */ -EXTERN int daemonized; /* are we forked off yet */ -EXTERN DnsCache dnscache[]; /* DNS cache */ -EXTERN int dns_cachettl; /* TTL of DNS cache entries */ -EXTERN int flag_verbose; /* flag: verbosity */ -EXTERN int gid_org, gid_set; /* original gid, flag: changed? */ -EXTERN int interrupted; /* got a signal? */ -EXTERN char *laststring; /* semantic lexer value */ -EXTERN int listen_sock; /* servicer listening socket */ -EXTERN int logstarted; /* was syslog() called yet? */ -EXTERN int nservice; /* # service descriptions */ -EXTERN Options opt; /* processing options */ -EXTERN int org_argc; /* original argc */ -EXTERN char **org_argv; /* and original argv */ -EXTERN int parser_debug; /* lexer/parser debugging */ -EXTERN ParserState *parserstate; /* parser state */ -EXTERN int nparserstate; /* size of array */ -EXTERN char *program_name; /* fully expanded argv[0] */ -EXTERN Programstage program_stage; /* stage of the program */ -EXTERN int relevant_sigs[]; /* relevant signals */ -EXTERN int reload_allow_deny; /* flag to reload spec files */ -EXTERN int semid; /* semaphore ID */ -EXTERN Service *service; /* service descriptions */ -EXTERN Servicereport *servicereport; /* reporter in shared mem */ -EXTERN unsigned char *srbuf; /* server socket input buffer */ -EXTERN int srbufpos, srbufmax; /* .. position & bytes */ -EXTERN StateStringMap /* state/string map */ - statestringmap[]; /* .. and vv */ -EXTERN Symtab *symtab; /* symbol table of #defines */ -EXTERN int nsymtab; /* table size */ -EXTERN int tabular_status; /* -t: tabular status overview */ -EXTERN int uid_org, uid_set; /* original uid, flag: changed? */ -EXTERN int xml_status; /* -x: XML status overview */ -EXTERN char *xml_xslt; /* -X: XSLT refrence for XML output */ -EXTERN FILE *yyin; /* config file handle */ -EXTERN int yylineno; /* input line number */ -EXTERN char *yyerrmsg; /* parsing error msg */ -EXTERN char *yytext; /* lexical buffer */ - -/* Functions. To suppress the __attribute__ macro (if you're on a - * non-gcc compiler), uncomment the following. */ -// #undef __attribute__ -// #define __attribute(x) -extern void alloc_reporter (Service *active, int first); -extern char *ansistamp (TmType t, int offset); -extern int backend_available (int target); -extern int backend_count(void); -extern int backend_connect (void); -extern void check_service (Service s); -extern void choose_backend (void); -extern int cmd_restart (int ac, char **av); -extern int cmd_services (int ac, char **av); -extern int cmd_status (int ac, char **av); -extern int cmd_start (int ac, char **av); -extern int cmd_stop (int ac, char **av); -extern int cmd_tell (int ac, char **av); -extern int cmd_configtest (int ac, char **av); -extern void config_msg (void); -extern void config_read (int fd); -extern void config_write (int fd); -extern void copysockets (int clientsock, int serversock); -extern void decr_client_count (void); -extern void dealloc_reporter (Service *s); -extern struct in_addr *dns (char const *hostname); -extern void error (char const *fmt, ...) - __attribute__ ((format (printf, 1, 2))); -extern void expand_program_name (char const *av0); -extern char *gmtstamp (int offset); -extern unsigned hashpjw (char const *s); -extern void http_copy (HttpHeader *h, int src_sock, int dst_sock, - CopyDirection dir); -extern void http_error (int clientsock); -extern void http_header_addheader (HttpHeader *m, char const *hdr); -extern void http_header_appendheader (HttpHeader *m, char const *hdr); -extern HttpConnectionType http_header_connectiontype (HttpHeader *h); -extern void http_header_free (HttpHeader *msg); -extern int http_header_hascookie (HttpHeader *m, char const *cookie); -extern double http_header_httpver (HttpHeader *h); -extern void http_header_removeheader (HttpHeader *h, char const *hdr); -extern unsigned char const *http_header_val (HttpHeader *h, char const *hdr); -extern HttpHeader *http_header_new (void); -extern void http_header_read (HttpHeader *m, int srcsock, CopyDirection dir); -extern void http_header_setheader (HttpHeader *m, char const *hdr); -extern void http_header_write (HttpHeader *m, int sock, CopyDirection dir); -extern void http_log_activity (double start, double end, char const *fmt, ...); -extern void http_serve (int clientsock); -extern int http_serversocket (HttpHeader *m, int *is_continuation); -extern int http_write (int sock, CopyDirection dir, unsigned char const *buf, - int buflen); -extern void incr_client_count (void); -extern int init_sockaddr (struct sockaddr_in *name, - const char *hostname, int port); -extern void interrupt (int sig); -extern int ipf_add_allow (Service *s, char const *val); -extern int ipf_add_deny (Service *s, char const *val); -extern int ipf_allowed (void); -extern int ipf_denied (void); -extern int ipf_loadfile (char const *fname, IpFilter **chain, int *nchain); -extern int ipf_match (IpFilter f); -extern int ipf_parse (char const *val, IpFilter *res); -extern int is_hex_digit (char ch); -extern int is_space (char ch); -extern void limits_msg (void); -extern void lock_reporter (void); -extern void log_activity_any (char const *action); -extern void log_activity_start (void); -extern void log_activity_end (void); -extern void log_activity_continuation (void); -extern int make_socket (int port, char const *ipaddr); -extern void mark_activity (double nbytes, double nsec, Backendavail newstate); -extern void msg (char const *fmt, ...) - __attribute__ ((format (printf, 1, 2))); -extern void msgdumpbuf (unsigned char const *buf, int buflen); -extern int net_copy (int clientsock, int serversock, int maxbytes, - unsigned char *buf); -extern int net_read (int sock, int maxbytes, unsigned char *buf, - int is_client); -extern void net_allocbufs (void); -extern unsigned char *net_buffer (CopyDirection dir, unsigned *sz); -extern unsigned char *net_bufread (int sock, int maxbytes, - int *nreadp, int is_client); -extern int net_write (int sock, unsigned char const *buf, int len, - int is_client); -extern void options_read (int fd); -extern void options_write (int fd); -extern int parser_close(); -extern char const *parser_filename(void); -extern void parser_open (char const *name); -extern int parser_input (char *buf, int max); -extern void parser_run(void); -extern void parser_skipchar(void); -extern void parser_skipline(void); -extern void runservice (void); -extern void set_program_title (char const *fmt, ...) - __attribute__ ((format (printf, 1, 2))); -extern void show_config (void); -extern char *stage_to_string (Programstage stage); -extern char *state_to_string (Backendavail avail); -extern Backendavail string_to_state (char const *str); -extern char *str_expand_format (char const *h); -extern char *str_printf (char const *fmt, ...) - __attribute__ ((format (printf, 1, 2))); -extern char *str_vprintf (char const *fmt, va_list args); -extern void symtab_end (void); -extern char const *symtab_lookup (char const *txt); -extern void symtab_set (char const *symbol); -extern void symtab_start (char const *symbol); -extern int sysrun (char const *cmd); -extern void tcpserve (int sock); -extern double timeofday(void); -extern void thruputlog (unsigned char const *buf, int len, CopyDirection dir); -extern void trafficlog (unsigned char const *buf, int len, CopyDirection dir); -extern void unlock_reporter (void); -extern void uid_assume (void); -extern void uid_restore (void); -extern void usage (void); -extern void wakeup_handler (void); -extern void warning (char const *fmt, ...) - __attribute__ ((format (printf, 1, 2))); -extern void writelog (int prio, char const *fmt, ...) - __attribute__ ((format (printf, 2, 3))); -extern int yyparse (void); -extern void *xcalloc (unsigned sz); -extern void *xmalloc (unsigned sz); -extern void *xrealloc (void *what, unsigned sz); -extern char *xstrcat (char *what, char const *rest); -extern char *xstrcatch (char *what, char ch); -extern char *xstrdup (char const *what); - -/* strlcat() if it's missing on this system */ -#if HAVE_STRLCAT == 0 -extern size_t strlcat (char *dst, char const *src, size_t size); -#endif - -/* vsyslog() if it's missing on this system */ -#if HAVE_VSYSLOG == 0 -extern void vsyslog (int fac, char const *fmt, va_list args); -#endif - -/* strcasestr() if it's missing on this system */ -#if HAVE_STRCASESTR == 0 -extern char *strcasestr (char const *big, char const *little); -#endif - -/* setproctitle() if it's missing on this system */ -#if HAVE_SETPROCTITLE == 0 -extern void setproctitle (char const *fmt, ...); -#endif - -/* strupr() if it's missing on this system */ -#if HAVE_STRUPR == 0 -extern char *strupr (char *buf); -#endif - -/* strnstr() if it's missing on this system */ -#if HAVE_STRNSTR == 0 -extern char *strnstr (char const *buf, char const *little, size_t len); -#endif - -/* __progname_full on systems that have it */ -#if HAVE_PROGNAME_FULL == 1 -extern char *__progname_full; -#endif - diff --git a/src/crossroads/Makefile b/src/crossroads/Makefile @@ -1,29 +0,0 @@ -include $(BASE)/etc/Makefile.def -include $(BASE)/etc/Makefile.conf - -BIN = crossroads -all: - BASE=$(BASE) $(MAKE) $(BIN) - -dbginstall: all $(BINDIR)/$(BIN) - -install: all $(BINDIR)/$(BIN) - strip $(BINDIR)/$(BIN) - -distclean: clean -clean: - rm -f *.o $(BIN) - -$(BINDIR)/$(BIN): $(BIN) - cp $(BIN) $(BINDIR)/$(BIN) - -main.o: main.c - $(CC) -c -g -W -Wall $(DEFS) $(PG) main.c - -$(BIN): main.o ../lib/libcrossroads.a - $(CC) $(PG) -o $(BIN) main.o -L$(BASE)/src/lib -lcrossroads \ - $(LIBS) $(EXTRALIBS) - -# Extra deps: -main.o: main.c $(BASE)/src/crossroads.h $(BASE)/etc/Makefile.def - diff --git a/src/crossroads/main.c b/src/crossroads/main.c @@ -1,164 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -typedef struct { - char *arg; /* cmdline argument */ - int narg; /* required remaining args */ - int needconf; /* configuration file required? */ - int (*handler)(int ac, char **av); /* action handler */ -} Handler; - -int main (int argc, char **argv) { - int o; - unsigned i; - int dumpconf = 0; - char *cp; - static Handler handler[] = { - /* Argument Nr.args Needconf Handler function */ - /* -1=no check */ - { "services", 0, 1, cmd_services }, - { "status", -1, 1, cmd_status }, - { "stop", 0, 1, cmd_stop }, - { "start", 0, 1, cmd_start }, - { "restart", 0, 1, cmd_restart }, - { "tell", 4, 1, cmd_tell }, - { "configtest", 0, 1, cmd_configtest }, - }; - - /* Expand argv[0] to a full program path */ - expand_program_name (argv[0]); - - /* Remember original ac/av/ep */ - org_argc = argc; - org_argv = argv; - org_argv[0] = program_name; - - /* Find the configuration file, relative to this program or as - * /etc/crossroads.conf. - */ - config_file = xstrdup (program_name); - if ( (cp = strstr (config_file, "/bin/")) ) { - *cp = 0; - config_file = xstrcat (config_file, "/etc/crossroads.conf"); - } else if ( (cp = strstr (config_file, "/sbin/")) ) { - *cp = 0; - config_file = xstrcat (config_file, "/etc/crossroads.conf"); - } else { - free (config_file); - config_file = xstrdup ("/etc/crossroads.conf"); - } - if (access (config_file, R_OK) && - strcmp (config_file, "/etc/crossroads.conf")) { - free (config_file); - config_file = xstrdup ("/etc/crossroads.conf"); - } - - /* Parse the command line. - * NOTE: When changing the options list, remember to update: - * - src/lib/usage.txt - * - doc/main/using.yo - * - doc/man/crossroads.yo - */ - while ( (o = getopt (argc, argv, "bB:?c:fhvVal:stCm:xd:X:pP")) > 0 ) - switch (o) { - case 'a': - opt.log_activity++; - break; - case 'b': - dumpconf++; - break; - case 'B': - opt.tcp_bufsz = atoi (optarg); - break; - case 'c': - config_file = optarg; - break; - case 'd': - opt.dns_cachettl = atoi (optarg); - break; - case 'v': - flag_verbose++; - break; - case 'l': - opt.log_facility = atoi(optarg); - break; - case 'm': - opt.shmperm = strtol (optarg, 0, 8); - break; - case 's': - opt.sloppyportbind++; - break; - case 't': - tabular_status++; - break; - case 'x': - xml_status++; - break; - case 'X': - xml_xslt = optarg; - break; - case 'C': - show_config(); - exit (0); - case 'p': - opt.leave_proctitle++; - break; - case 'P': - parser_debug++; - break; - case 'V': - puts (VER); - exit (0); - case '?': - case 'h': - default: - usage (); - } - - /* Request to dump binary configuration? */ - if (dumpconf) { - msg ("Parsing configuration '%s'", config_file); - parser_open (config_file); - parser_run(); - parser_close(); - options_write (1); - config_write (1); - exit (0); - } - - /* We need at last one argument: the action */ - if (optind >= argc) - usage(); - - /* More sanity checks */ - if (xml_status && tabular_status) - error ("Flags -t and -x are mutually exclusive"); - - /* Act on the argument. */ - for (i = 0; i < sizeof(handler) / sizeof(Handler); i++) { - /* Match the handler action string with argv, and match the - * required remaining arguments. If they match.. - * scan the configuration (if needed) and GO! - */ - if (!strcmp (argv[optind], handler[i].arg) && - (handler[i].narg == -1 || - handler[i].narg == argc - optind - 1)) { - if (handler[i].needconf) { - parser_open (config_file); - parser_run(); - parser_close(); - } - if (!handler[i].handler (argc - optind, argv + optind + 1)) - error ("'%s' failed", argv[optind]); - return (0); - } - } - - usage(); - return (1); -} diff --git a/src/lib/Makefile b/src/lib/Makefile @@ -1,56 +0,0 @@ -include $(BASE)/etc/Makefile.def -include $(BASE)/etc/Makefile.conf - -LIB = libcrossroads.a -SRC := $(wildcard *.c) -OBJ := $(patsubst %.c, %.o, $(SRC)) - -all: - BASE=$(BASE) $(MAKE) textconv - BASE=$(BASE) $(MAKE) grammar - BASE=$(BASE) $(MAKE) $(LIB) - -dbginstall: install -install: all - -$(LIB): $(OBJ) - ar rs $(LIB) $(OBJ) - ranlib $(LIB) - -textconv: usage.h proxyerror.h -usage.h: usage.txt - $(BASE)/tools/untab usage.txt - $(BASE)/tools/e-txt2c USAGETEXT <usage.txt >usage.h -proxyerror.h: proxyerror.txt - $(BASE)/tools/untab proxyerror.txt - $(BASE)/tools/e-txt2c ERRORTEXT <proxyerror.txt >proxyerror.h - -grammar: parser.c lexer.c -lexer.c: lexer.l - flex -I -o$@ $< -parser.c: parser.y - bison -d -o $@ $< - -objects: $(OBJ) - - -parser.o: parser.c - $(CC) $(DEFS) $(PG) -c -g $< - -lexer.o: lexer.c - $(CC) $(DEFS) $(PG) -c -g $< - -%.o: %.c - $(CC) $(DEFS) $(PG) $(ALF) -c -g -W -Wall $< - -distclean: clean -clean: - rm -f $(OBJ) libcrossroads.a usage.h proxyerror.h - -# Extra deps: -strexpandformat.o: strexpandformat.c $(BASE)/etc/Makefile.def -showconfig.o: showconfig.c $(BASE)/etc/Makefile.def Makefile -data.o: data.c $(BASE)/src/crossroads.h -usage.o: usage.c usage.h -optionswrite.o: optionswrite.c ../crossroads.h -optionsread.o: optionsread.c ../crossroads.h diff --git a/src/lib/allocreporter.c b/src/lib/allocreporter.c @@ -1,69 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* Here's the calculation of the required shm block size. The Servicereport - * has already one Backendstate in it, so we need to add (n-1) blocks to - * make block->backendstate a linear array. */ -#define REPORTSIZE(s) \ - sizeof(Servicereport) + ((s)->nbackend - 1) * sizeof(Backendstate) - -void alloc_reporter (Service *s, int first) { - int shmid; - struct shmid_ds shmbuf; - - /* Get an ID to work on, depending on the create mode. - Get a corresponding semaphore too. */ - msg ("Obtaining reporting memory for service %s (key %d)", - s->name, s->shmkey); - - if (first) { - /* First time 'round. We're allocating for the first time here.*/ - if ( (shmid = shmget (s->shmkey, REPORTSIZE(s), - opt.shmperm | IPC_CREAT) ) >= 0) - msg ("Created new shm at id %d", shmid); - else - error ("Cannot create shared memory for service %s (key %d: %s)", - s->name, s->shmkey, strerror(errno)); - - /* Make sure that we're the owner of the segment. */ - if (shmctl (shmid, IPC_STAT, &shmbuf) == -1) - error ("Failed to get status of shared memory: %s", - strerror(errno)); - shmbuf.shm_perm.uid = getuid(); - shmbuf.shm_perm.gid = getgid(); - if (shmctl (shmid, IPC_SET, &shmbuf) == -1) - error ("Failed to set status of shared memory: %s", - strerror(errno)); - - if ( (semid = semget (s->shmkey, 1, opt.shmperm | IPC_CREAT)) >= 0 ) - msg ("Created new sem at id %d", semid); - else - error ("Cannot create semaphore for service %s (key %d: %s)", - s->name, s->shmkey, strerror(errno)); - } else { - /* Second time 'round. We're getting a handle on previously - * created shared memory and semaphores. */ - if ( (shmid = shmget (s->shmkey, REPORTSIZE(s), opt.shmperm) ) >= 0 ) - msg ("Got existing shm at id %d", shmid); - else - error ("Cannot get shared memory for service %s, key %d: %s. " - "Maybe crossroads isn't running, or the configuration " - "has changed.", - s->name, s->shmkey, strerror(errno)); - if ( (semid = semget (s->shmkey, 1, opt.shmperm)) >= 0 ) - msg ("Got existing sem at id %d", semid); - else - error ("Cannot get semaphore for service %s (key %d: %s)", - s->name, s->shmkey, strerror(errno)); - } - - /* Attach to our memory segment. */ - if ( ((servicereport = shmat (shmid, 0, 0))) == (void *) -1 ) - error ("Cannot attach shared memory for service %s: %s", - s->name, strerror(errno)); -} diff --git a/src/lib/ansistamp.c b/src/lib/ansistamp.c @@ -1,27 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -char *ansistamp (TmType t, int offset) { - static char buf[80]; - time_t now; - struct tm *tmp; - - time (&now); - now += offset; - if (t == tm_localtime) - tmp = localtime (&now); - else - tmp = gmtime (&now); - - snprintf (buf, sizeof(buf), - "%4.4d-%2.2d-%2.2d/%2.2d:%2.2d:%2.2d", - tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - - return (buf); -} diff --git a/src/lib/backendavailable.c b/src/lib/backendavailable.c @@ -1,25 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int backend_available (int target) { - /* Backend is available when: - * (a) The state indicates availability; - * (b) Either, there is no max to the active connections, or - * the # of connections is below the max. - */ - if (servicereport->backendstate[target].actual_avail == st_available && - (activeservice->backend[target].maxconnections == 0 || - activeservice->backend[target].maxconnections > - servicereport->backendstate[target].nclients)) { - msg ("Backend %s is available", activeservice->backend[target].name); - return (1); - } - - msg ("Bckend %s is unavailable", activeservice->backend[target].name); - return (0); -} diff --git a/src/lib/backendconnect.c b/src/lib/backendconnect.c @@ -1,114 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -static int timed_out; - -static void alarmhandler (int sig) { - timed_out++; -} - -int backend_connect () { - int sock; - struct sockaddr_in servername; - int val = 1; - struct timeval con_start, con_end; - - /* Create the socket. */ - if ( (sock = socket (PF_INET, SOCK_STREAM, 0)) < 0 ) { - if (program_stage != stage_retrying) { - error ("failed to create socket for backend communication: %s", - strerror(errno)); - } else { - warning ("failed to create socket for backend communication: %s", - activeservice->name, strerror(errno)); - return (-1); - } - } - -#ifdef SO_NOSIGPIPE - /* Make sure we don't get SIGPIPE's on it. Not supported on all - * platforms. */ - if (setsockopt (sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) { - warning ("cannot set backend socket options (nosigpipe): %s", - activeservice->name, strerror(errno)); - close (sock); - return (-2); - } -#endif - - /* Assign to back end server address. */ - if (init_sockaddr ( - &servername, - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port)) { - /* The hostname is unusable.. */ - warning ("unknown host %s", - servicereport->backendstate[current_backend].actual_server); - close (sock); - mark_activity (0, 0, st_unavailable); - return (-3); - } - if (program_stage != stage_retrying) - msg ("Network socket to %s:%d created", - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port); - - /* If retrying, delay a bit, unless we're in the wakeup handler. */ - if (program_stage != stage_retrying && - servicereport->backendstate[current_backend].fail > 0) { - msg ("Seen %d retries, waiting a bit", - servicereport->backendstate[current_backend].fail); - sleep (RETRY_WAIT); - } - - /* Set up the connect interruption */ - signal (SIGALRM, alarmhandler); - timed_out = 0; - alarm (CONNECT_TIMEOUT); - - /* Try connecting. Remember the time we try this. */ - gettimeofday (&con_start, 0); - if (connect (sock, (struct sockaddr *) &servername, - sizeof(servername)) < 0) { - /* This backend is unusable. - * Either connect() has failed (connection refused) or it got - * interrupted by the SIGALRM, or another signal went off. - */ - close (sock); - alarm(0); - signal (SIGALRM, SIG_DFL); - - if (program_stage != stage_retrying) { - if (timed_out) - warning ( - "server %s:%d not usable due to timeout", - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port); - else - warning ( - "server %s:%d: cannot connect: %s", - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port, - strerror(errno)); - } - mark_activity (0, 0, st_unavailable); - return (-3); - } - alarm(0); - signal (SIGALRM, SIG_DFL); - - /* Log the last connect time. Store the duration of this connect. */ - gettimeofday (&con_end, 0); - servicereport->backendstate[current_backend].last_con_sec = - ( ((double)con_end.tv_sec * 1000000 + con_end.tv_usec) - - ((double)con_start.tv_sec * 1000000 + con_start.tv_usec) ) - / 1000000; - servicereport->backendstate[current_backend].laststamp = time(0); - - return (sock); -} diff --git a/src/lib/backendcount.c b/src/lib/backendcount.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int backend_count () { - int n = 0, i; - - for (i = 0; i < activeservice->nbackend; i++) - if (backend_available(i)) - n++; - msg ("%d backend(s) available", n); - return (n); -} diff --git a/src/lib/checkservice.c b/src/lib/checkservice.c @@ -1,83 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void check_service (Service s) { - int i; - - /* Check for port duplicates, bindto duplicates */ - for (i = 0; i < nservice; i++) { - if (service[i].port == s.port) { - if (service[i].bind && s.bind && - !strcmp(service[i].bind, s.bind)) - error ("Services %s and %s cannot bind to same IP %s and " - "port %d", - service[i].name, s.name, - s.bind, s.port); - if (!service[i].bind && s.bind) - error ("Services %s and %s use the same port %d, but " - "%s does not bind to a specific IP", - service[i].name, s.name, - s.port, service[i].name); - if (service[i].bind && !s.bind) - error ("Services %s and %s use the same port %d, but " - "%s does not bind to a specific IP", - service[i].name, s.name, - s.port, s.name); - if (!service[i].bind && !s.bind) - error ("Services %s and %s use the same port without " - "binding to an IP", - service[i].name, s.name); - } - } - - /* Verify the service description, port */ - if (!s.port) - error ("Service %s lacks a port", - s.name); - if (!s.nbackend) - error ("Service %s lacks back ends", - s.name); - if (s.rev_interval && s.check_interval) - error ("Service %s has both a revivinginterval and " - "a checkinterval", s.name); - - /* HTTP modification directives only with "type http" */ - if (s.type != type_http) { - for (i = 0; i < s.nbackend; i++) { - if (s.backend[i].stickycookie && - *s.backend[i].stickycookie) - error ("Backend %s of service %s: sticky cookies " - "only allowed in 'type http'", - s.backend[i].name, s.name); - if (s.backend[i].naddclientheader) - error ("Backend %s of service %s: addclientheader " - "only allowed in 'type http'", - s.backend[i].name, s.name); - if (s.backend[i].nsetclientheader) - error ("Backend %s of service %s: setclientheader " - "only allowed in 'type http'", - s.backend[i].name, s.name); - if (s.backend[i].nappendclientheader) - error ("Backend %s of service %s: appendclientheader " - "only allowed in 'type http'", - s.backend[i].name, s.name); - if (s.backend[i].naddserverheader) - error ("Backend %s of service %s: addserverheader " - "only allowed in 'type http'", - s.backend[i].name, s.name); - if (s.backend[i].nsetserverheader) - error ("Backend %s of service %s: setserverheader " - "only allowed in 'type http'", - s.backend[i].name, s.name); - if (s.backend[i].nappendserverheader) - error ("Backend %s of service %s: appendserverheader " - "only allowed in 'type http'", - s.backend[i].name, s.name); - } - } -} diff --git a/src/lib/choosebackend.c b/src/lib/choosebackend.c @@ -1,393 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void choose_backend () { - int *backends = 0, nbackends = 0, h, i, j, k, - target_set = 0, flat_weights = 1, *weights = 0, *sel_weights, - tot_weights, lo_val, hi_val, done; - double *values = 0, nbest; - unsigned nclients; - char *exthandler; - FILE *f; - char buf[80]; -# if HAVE_SRANDDEV == 0 - struct timeval tv; -# endif - - /* Check that we're allowed to accept at all. */ - if (activeservice->maxconnections && - servicereport->nclients >= activeservice->maxconnections) { - warning ("max clients %d exceeded", activeservice->maxconnections); - current_backend = -1; - return; - } - - /* Incase of dispatching by client IP, check that the back end - * is up. If it is, take that target. */ - if (activeservice->dispatchtype == ds_byclientip) { - h = hashpjw (client_ip) % activeservice->nbackend; - msg ("Client IP %s hashed to back end index %d", client_ip, h); - if (servicereport->backendstate[h].actual_avail == st_available && - (activeservice->backend[h].maxconnections == 0 || - activeservice->backend[h].maxconnections > - servicereport->backendstate[h].nclients)) { - msg ("Hashed back end %d is acceptable", h); - current_backend = h; - return; - } else - msg ("Hashed back end %d is not available, redispatching", h); - } - - /* Populate the array of selectable backends. */ - for (i = 0; i < activeservice->nbackend; i++) { - /* Backend i is a candidate if: - * (a) it is available, AND - * (b) there's no max of connections, or the actual nr of connections - * is below max - */ - if (servicereport->backendstate[i].actual_avail == st_available && - (activeservice->backend[i].maxconnections == 0 || - activeservice->backend[i].maxconnections > - servicereport->backendstate[i].nclients)) { - msg ("candidate back end: %d (max clients %d, active %d, " - "state %s)", - i, activeservice->backend[i].maxconnections, - servicereport->backendstate[i].nclients, - state_to_string(servicereport->backendstate[i].actual_avail)); - backends = xrealloc (backends, (nbackends + 1) * sizeof(int) ); - backends[nbackends++] = i; - } - } - - /* When we don't have any active back ends, then we sleep a bit. - * Abort choosing a back end, unless there is an external dispatcher - - * it may overrule our assumptions. */ - if (!nbackends) { - sleep (RETRY_WAIT); - if (activeservice->dispatchtype != ds_externalhandler) { - current_backend = -1; - warning ("No active backends to select!"); - return; - } - } - - /* Only one backend? Then it's always the first one, whatever - * you do. Exception is when we have an external dispatcher. In that - * case we want to call it, even when there's just one back end. - * Thanks Nicolas Prochazka for suggesting this. */ - if (nbackends == 1 && activeservice->dispatchtype != ds_externalhandler) { - current_backend = backends[0]; - servicereport->last_backend = current_backend; - msg ("only 1 backend to select (%d)", current_backend); - free (backends); - return; - } - - /* More than 1 backends available, or external dispatching. - Make a wise choice. */ - switch (activeservice->dispatchtype) { - - case ds_roundrobin: - /* Find the currently used backend, go one forward. */ - msg ("last roundrobin back end was %d", servicereport->last_backend); - for (i = 0; i < nbackends; i++) - if (backends[i] == servicereport->last_backend) { - i++; - i %= nbackends; - current_backend = backends[i]; - servicereport->last_backend = current_backend; - msg ("next roundrobin back end is %d", current_backend); - free (backends); - return; - } - /* None found.. try the first one (run to momma). */ - current_backend = backends[0]; - servicereport->last_backend = current_backend; - msg ("chosen backend (roundrobin, without historical info): %d", - current_backend); - free (backends); - return; - - case ds_random: - /* Re-randomize. */ -# if HAVE_SRANDDEV == 1 - sranddev(); - msg ("randomizer seeded with randdev"); -# else - gettimeofday (&tv, 0); - srand ((unsigned) tv.tv_usec); - msg ("randomizer seeded with %u", (unsigned) tv.tv_usec); -# endif - - /* First of all let's see if all the weights are the same. */ - values = xmalloc (nbackends * sizeof(double)); - weights = xmalloc (nbackends * sizeof(int)); - tot_weights = 0; - for (i = 0; i < nbackends; i++) { - values[i] = activeservice->backend[backends[i]].weight; - if (activeservice->backend[backends[i]].weight != 1) - flat_weights = 0; - } - - if (! flat_weights) { - /* Non-flat weight random balancing. Determine the chance - * to hit a given back end. First we populate the weights array. - * Eg imagine we have 4 back ends with weights 1,2,3,4. Then - * ultimately we need to get a selection array of indices: - * 0000111223, meaning that the chance to hit index 0 is 4/10 - * and so on */ - - /* First of all we reverse the values vector, to get 4,3,2,1 */ - done = 0; - while (!done) { - done = 1; - lo_val = 9999999; - hi_val = 0; - /* Find highest/lowest weights in values vector. */ - for (i = 0; i < nbackends; i++) { - if (!values[i]) - continue; - done = 0; - if (lo_val >= values[i]) - lo_val = values[i]; - if (hi_val <= values[i]) - hi_val = values[i]; - } - - if (done) - break; - - /* Stick the values into the duplicate 'weights' array, - but the other way 'round. */ - for (i = 0; i < nbackends; i++) - if (values[i] == lo_val) - weights[i] = hi_val; - else if (values[i] == hi_val) - weights[i] = lo_val; - - for (i = 0; i < nbackends; i++) - if (values[i] == lo_val) { - tot_weights += hi_val; - values[i] = 0; - } else if (values[i] == hi_val) { - tot_weights += lo_val; - values[i] = 0; - } - } - - /* The 'weights' vector is now the reverse of 'values'. - * Create a selection array. */ - sel_weights = xmalloc (tot_weights * sizeof(int)); - k = 0; - for (i = 0; i < nbackends; i++) - for (j = 0; j < weights[i]; j++) { - sel_weights[k] = i; - k++; - } - - /* Debugging: - * for (k = 0; k < tot_weights; k++) - * msg ("random: sel_weight[%d] = %d", k, sel_weights[k]); - */ - - /* Select the next random back end. Algorithm is: - * i = rand() % tot_weights; - * j = sel_weights[i]; - * k = backends[j]; - * current_backend = k; - * msg ("i=%d j=%d, k=%d", i, j, k); - */ - i = rand() % tot_weights; - current_backend = backends[sel_weights[i]]; - servicereport->last_backend = current_backend; - free (sel_weights); - msg ("chosen backend (weighted random): %d at index %d", - current_backend, i); - free (backends); - free (values); - free (weights); - return; - } - - /* ELSE: Choose a random one of the availables. */ - current_backend = backends[rand() % nbackends]; - servicereport->last_backend = current_backend; - msg ("chosen backend (flat-weight random): %d", current_backend); - free (backends); - free (values); - return; - - case ds_bysize: - /* Fill the values */ - values = xmalloc (nbackends * sizeof(double)); - for (i = 0; i < nbackends; i++) { - /* When dispatch over #-connections is in place, then we - * compute using the averaged value. However if the averaged - * value is 0 (not yet computed), we take the true value. */ - if (activeservice->dispatchover && - servicereport->backendstate[backends[i]].avg_nbytes) - values[i] = - servicereport->backendstate[backends[i]].avg_nbytes * - activeservice->backend[backends[i]].weight; - else - values[i] = servicereport->backendstate[backends[i]].nbytes * - activeservice->backend[backends[i]].weight; - msg ("by size weighing backend %d has weight %d, value %u" - " (bytes=%u, avgbytes=%u)", - backends[i], - activeservice->backend[backends[i]].weight, - (unsigned) values[i], - (unsigned) servicereport->backendstate[backends[i]].nbytes, - (unsigned) - servicereport->backendstate[backends[i]].avg_nbytes); - } - /* Get the backend that has transported the least bytes */ - for (i = 0; i < nbackends; i++) - if (!target_set++ || values[i] < nbest) { - nbest = values[i]; - current_backend = backends[i]; - servicereport->last_backend = current_backend; - msg ("by size weighing: best so far is %d (value %g)", - current_backend, nbest); - } - msg ("chosen backend (by size): %d", current_backend); - free (backends); - free (values); - return; - - case ds_byduration: - /* Fill the values */ - values = xmalloc (nbackends * sizeof(double)); - for (i = 0; i < nbackends; i++) { - /* When dispatch over #-connections is in place, then we - * compute using the averaged value. However if the averaged - * value is 0 (not yet computed), we take the true value. */ - if (activeservice->dispatchover && - servicereport->backendstate[backends[i]].avg_nsec) - values[i] = - servicereport->backendstate[backends[i]].avg_nsec * - 1000000 * - activeservice->backend[backends[i]].weight; - else - values[i] = servicereport->backendstate[backends[i]].nsec * - 1000000 * - activeservice->backend[backends[i]].weight; - msg ("By duration weighing: backend %d has value %g" - " (sec=%g, avgsec=%g, weight=%d)", - backends[i], values[i], - servicereport->backendstate[backends[i]].nsec, - servicereport->backendstate[backends[i]].avg_nsec, - activeservice->backend[backends[i]].weight); - } - /* Get the backend that has transported the least connection secs */ - for (i = 0; i < nbackends; i++) - if (!target_set++ || values[i] < nbest) { - nbest = values[i]; - current_backend = backends[i]; - servicereport->last_backend = current_backend; - msg ("by duration weighing: best so far is %d (value %g)", - current_backend, nbest); - } - msg ("chosen backend (by duration): %d", current_backend); - free (backends); - free (values); - return; - - case ds_byorder: - /* Get the first available back end in line. */ - current_backend = backends[0]; - servicereport->last_backend = current_backend; - msg ("chosen backend (by order): %d", current_backend); - free (backends); - return; - - case ds_byconnections: - case ds_byclientip: - /* Note: this serves byconnections dispatching, plus byclientip - * when the actual back end has gone down. */ - values = xmalloc (nbackends * sizeof(double)); - for (i = 0; i < nbackends; i++) { - values[i] = - servicereport->backendstate[backends[i]].nclients * - activeservice->backend[backends[i]].weight; - msg ("by connections weighing: backend %d has value %g", - backends[i], values[i]); - } - /* Find the minimum in the values array. */ - for (i = 0; i < nbackends; i++) - if (!target_set++ || values[i] < nclients) { - nclients = values[i]; - current_backend = backends[i]; - servicereport->last_backend = current_backend; - msg ("by connections weighing: best so far is %d (value %u)", - current_backend, nclients); - } - msg ("chosen backend (by connections): %d", current_backend); - free (backends); - free (values); - return; - - case ds_externalhandler: - /* External handler to be called. Assume selection will fail. */ - current_backend = -1; - exthandler = str_expand_format (activeservice->dispatchext); - msg ("calling external handler '%s'", exthandler); - signal (SIGCHLD, SIG_DFL); - uid_assume(); - if (! (f = popen (exthandler, "r")) ) { - warning ("failed to start external handler '%s': %s", - exthandler, strerror(errno)); - if (nbackends > 0) { - current_backend = backends[0]; - servicereport->last_backend = current_backend; - } - - uid_restore(); - signal (SIGCHLD, SIG_IGN); - free (backends); - free (exthandler); - return; - } - while (1) { - if (fscanf (f, " %80s ", buf) < 1) { - msg ("external handler signals end"); - pclose (f); - uid_restore(); - signal (SIGCHLD, SIG_IGN); - break; - } - msg ("external handler says '%s'", buf); - for (i = 0; i < activeservice->nbackend; i++) { - if (!strcmp (buf, activeservice->backend[i].name)) { - msg ("selecting back end %s due to external handler", buf); - current_backend = i; - servicereport->last_backend = current_backend; - pclose (f); - uid_restore(); - signal (SIGCHLD, SIG_IGN); - free (backends); - free (exthandler); - return; - } - } - } - warning ("external handler '%s' didn't reply with a selectable back end", - exthandler); - if (nbackends > 0) - current_backend = backends[0]; - servicereport->last_backend = current_backend; - free (backends); - free (exthandler); - return; - - default: - /* Internal fry.. */ - error ("internal error: unhandled dispatch type %d", - activeservice->dispatchtype); - } -} diff --git a/src/lib/cmdconfigtest.c b/src/lib/cmdconfigtest.c @@ -1,12 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int cmd_configtest (int ac, char **av) { - /* If we got this far, after reading the config: */ - printf ("Configuration '%s' OK.\n", config_file); - return (1); -} diff --git a/src/lib/cmdrestart.c b/src/lib/cmdrestart.c @@ -1,22 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int cmd_restart (int ac, char **av) { - - /* First run the 'stop' action. */ - if (!cmd_stop (ac, av)) - error ("Daemon stop failed, restart failure"); - - sleep (2); - - /* Now run 'start' */ - if (!cmd_start (ac, av)) - error ("Daemon start failed, restart failure"); - - /* All ok.. */ - return (1); -} diff --git a/src/lib/cmdservices.c b/src/lib/cmdservices.c @@ -1,26 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int cmd_services (int ac, char **av) { - int i, j; - - for (i = 0; i < nservice; i++) { - printf ("Service %s (on port %d) %d backends\n", - service[i].name, service[i].port, service[i].nbackend); - if (service[i].allowfile) - printf (" Allow file: %s\n", service[i].allowfile); - if (service[i].denyfile) - printf (" Deny file: %s\n", service[i].denyfile); - for (j = 0; j < service[i].nbackend; j++) - printf (" Backend %s (%s:%d)\n", - service[i].backend[j].name, - service[i].backend[j].initial_server, - service[i].backend[j].initial_port); - } - return (1); -} diff --git a/src/lib/cmdstart.c b/src/lib/cmdstart.c @@ -1,36 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int cmd_start (int ac, char **av) { - FILE *f; - char *cmd; - - /* Prepare the command to run. When the process title may be modified, - * reserve space. */ - cmd = xstrdup (BINDIR "/crossroads-daemon"); - if (!opt.leave_proctitle) - cmd = xstrcat (cmd, " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - /* - for (i = 1; i < org_argc; i++) { - cmd = xstrcatch (cmd, ' '); - cmd = xstrcat (cmd, org_argv[i]); - } - msg ("Starting daemon (%s), cmd"); - */ - - if (! (f = popen (cmd, "w")) ) - error ("Failed to start %s: %s", cmd, strerror(errno)); - options_write (fileno(f)); - config_write (fileno(f)); - if (pclose (f) == -1) - error ("Crossroads daemon '%s' indicates error", cmd); - - return (1); -} diff --git a/src/lib/cmdstatus.c b/src/lib/cmdstatus.c @@ -1,192 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -static char *timestr (double nsec) { - static char *buf; - char *tmp; - - free (buf); - buf = 0; - - if (nsec > 3600) { - buf = str_printf ("%uh", (unsigned) (nsec / 3600)); - nsec = fmod (nsec, 3600); - } - if (nsec > 60) { - tmp = str_printf ("%um", (unsigned) (nsec / 60)); - nsec = fmod (nsec, 60); - buf = xstrcat (buf, tmp); - free (tmp); - } - tmp = str_printf ("%.2fs", nsec); - if (!strcmp (tmp + strlen(tmp) - 4, ".00s")) - strcpy (tmp + strlen(tmp) - 4, "s"); - buf = xstrcat (buf, tmp); - free (tmp); - - return (buf); -} - -static char *bytestr (double nbytes) { - static char *buf; - - free (buf); - - if (nbytes > (double)1024*1024*1024*1024) - buf = str_printf ("%.2fTb", - nbytes / ((double) 1024*1024*1024*1024)); - else if (nbytes > 1024*1024*1024) - buf = str_printf ("%.2fGb", - nbytes / ((double) 1024*1024*1024)); - else if (nbytes > 1024*1024) - buf = str_printf ("%.2fMb", - nbytes / ((double) 1024*1024)); - else if (nbytes > 1024) - buf = str_printf ("%.2fKb", - nbytes / (double) 1024); - else - buf = str_printf ("%gb", nbytes); - - return (buf); -} - -int cmd_status (int ac, char **av) { - int i, j, services_shown = 0; - unsigned sec_ago; - int state_count[NR_OF_STATES]; - - if (xml_status) { - printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - if (xml_xslt) - printf ("<?xml-stylesheet type=\"text/xsl\" " - "href=\"%s\"?>\n", xml_xslt); - printf ("<status>\n"); - } - - for (i = 0; i < nservice; i++) { - if (ac > 1 && strcmp (service[i].name, av[0])) - continue; - if (services_shown++) { - if (xml_status) - printf (" </service>\n"); - else - putchar ('\n'); - } - alloc_reporter (service + i, 0); - msg ("Reporting service %s (pid = %d)", - service[i].name, servicereport->pid); - - /* Count the back ends by their state. */ - for (j = 0; j < NR_OF_STATES; j++) - state_count[j] = 0; - for (j = 0; j < service[i].nbackend; j++) - state_count[servicereport->backendstate[j].actual_avail]++; - - if (tabular_status) - printf ("%s ", service[i].name); - else if (xml_status) { - printf (" <service id=\"%d\" name=\"%s\">\n" - " <connections>%d</connections>\n" - " <lastbackend>%d</lastbackend>\n" - " <pid>%u</pid>\n", - i, service[i].name, servicereport->nclients, - servicereport->last_backend, - servicereport->pid); - for (j = 0; j < NR_OF_STATES; j++) - printf (" <backends_%s>%d</backends_%s>\n", - state_to_string ( (Backendavail)j ), - state_count[j], - state_to_string ( (Backendavail)j )); - } else - printf ("Service : %s, %d live connections, " - "last backend %d, at pid %u\n", - service[i].name, servicereport->nclients, - servicereport->last_backend, servicereport->pid); - for (j = 0; j < service[i].nbackend; j++) { - if (ac > 2 && strcmp (service[i].backend[j].name, av[1])) - continue; - if (! servicereport->backendstate[j].laststamp) - sec_ago = 0; - else - sec_ago = time(0) - servicereport->backendstate[j].laststamp; - if (tabular_status) - printf ("%s=%s ", service[i].backend[j].name, - state_to_string(servicereport->backendstate[j].actual_avail)); - else if (xml_status) { - printf (" <backend id=\"%d\" name=\"%s\">\n" - " <availability id=\"%d\">%s</availability>\n" - " <clients>%u</clients>\n" - " <failures>%lu</failures>\n" - " <connections>%lu</connections>\n" - " <duration sec=\"%g\">%s</duration>\n" - " <throughput bytes=\"%g\">%s</throughput>\n", - j, service[i].backend[j].name, - servicereport->backendstate[j].actual_avail, - state_to_string( - servicereport->backendstate[j].actual_avail), - servicereport->backendstate[j].nclients, - servicereport->backendstate[j].failures, - servicereport->backendstate[j].totuses, - servicereport->backendstate[j].nsec, - timestr (servicereport->backendstate[j].nsec), - servicereport->backendstate[j].nbytes, - bytestr (servicereport->backendstate[j].nbytes)); - printf (" <lastaccess sec=\"%u\">%s</lastaccess>\n" - " <server>%s</server>\n" - " <port>%d</port>\n", - sec_ago, timestr( (double) sec_ago), - servicereport->backendstate[j].actual_server, - servicereport->backendstate[j].actual_port); - if (service[i].type == type_http) - printf (" <sessions>%lu</sessions>\n", - servicereport->backendstate[j].sessions); - printf (" </backend>\n"); - } - else { - printf (" Backend %2d : %s is %s, %u live connections\n" - " Stats : %lu failures out of %lu connections", - j, - service[i].backend[j].name, - state_to_string ( - servicereport->backendstate[j].actual_avail), - servicereport->backendstate[j].nclients, - servicereport->backendstate[j].failures, - servicereport->backendstate[j].totuses); - if (service[i].type == type_http) - printf (", %lu sessions,", - servicereport->backendstate[j].sessions); - printf ("\n" - " usage %s, %s", - timestr (servicereport->backendstate[j].nsec), - bytestr (servicereport->backendstate[j].nbytes)); - if (service[i].dispatchover) - printf (", avg %s, %s", - timestr(servicereport->backendstate[j].avg_nsec), - bytestr(servicereport->backendstate[j].avg_nbytes)); - printf (", last hit %s ago\n" - " address %s:%d\n", - timestr ( (double) sec_ago), - servicereport->backendstate[j].actual_server, - servicereport->backendstate[j].actual_port); - } - } - - if (shmdt (servicereport) < 0) - error ("Failure releasing reporter memory: %s", - strerror(errno)); - } - if (tabular_status) - putchar ('\n'); - else if (xml_status) { - if (services_shown) - printf (" </service>\n"); - printf ("</status>\n"); - } - - return (1); -} diff --git a/src/lib/cmdstop.c b/src/lib/cmdstop.c @@ -1,35 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int cmd_stop (int ac, char **av) { - int i, result = 0; - - for (i = 0; i < nservice; i++) { - alloc_reporter (service + i, 0); - msg ("Stopping service %s (daemon pid %d, wakeup handler pid %d)", - service[i].name, servicereport->pid, servicereport->rev_pid); - if (kill (servicereport->pid, SIGINT)) { - warning ("Failed to stop service %s (%s)", - service[i].name, strerror(errno)); - result++; - } else if (servicereport->rev_pid && - kill (servicereport->rev_pid, SIGINT)) { - warning ("Failed to stop wakeup handler of service %s (%s)", - service[i].name, strerror(errno)); - result++; - } - - /* Note: We run the deallocator of shmem here anyway, though the - * signalled listener does it too.. just to make sure.. We do this - * only if above kills have succeeded. - */ - if (!result) - dealloc_reporter (service + i); - } - - return (1); -} diff --git a/src/lib/cmdtell.c b/src/lib/cmdtell.c @@ -1,73 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int cmd_tell (int ac, char **av) { - Service *target_service = 0; - int target_backend = -1, i; - Backendavail avail; - int newstate, port; - char *hostname, *cp; - - /* Locate service */ - for (i = 0; i < nservice; i++) - if (!strcasecmp (service[i].name, av[0])) { - target_service = service + i; - break; - } - if (!target_service) - error ("Service '%s' isn't known. " - "Maybe the configuration has changed?", av[0]); - - /* Locate back end */ - for (i = 0; i < target_service->nbackend; i++) - if (!strcasecmp (target_service->backend[i].name, av[1])) { - target_backend = i; - break; - } - if (target_backend == -1) - error ("Backend '%s' of service '%s' isn't known. " - "Maybe the configuration has changed?", av[1], av[0]); - - /* Let's see what we need to tell the back end of that service. */ - if (!strcmp (av[2], "state")) { - /* Locate new state: either the direct number, or alpha name */ - if (sscanf (av[3], "%d", &newstate) > 0) { - avail = (Backendavail) newstate; - if (avail > st_unknown) - error ("Bad state number '%s'", av[3]); - } else if ( (avail = string_to_state (av[3])) == st_unknown ) - error ("Incorrect state '%s'.", av[3]); - - /* Got all data. Poke the new state into shared memory. */ - alloc_reporter (target_service, 0); - servicereport->backendstate[target_backend].actual_avail = avail; - servicereport->backendstate[target_backend].fail = 0; - msg ("Marked backend %s of service %s as %s.", - av[1], av[0], state_to_string (avail)); - } else if (!strcmp (av[2], "server")) { - /* Get the hostname and port part. */ - hostname = av[3]; - if (! (cp = strchr (hostname, ':')) ) - error ("Bad address '%s' (hostname:port expected)", av[3]); - *cp = 0; - if (sscanf (cp + 1, "%d", &port) < 1) - error ("Bad port part in address"); - /* Got all data. Poke the new state into shared memory. */ - alloc_reporter (target_service, 0); - strncpy (servicereport->backendstate[target_backend].actual_server, - hostname, FQDN_LENGTH - 1); - servicereport->backendstate[target_backend].actual_port = port; - msg ("Address for backend %s of service %s set to %s:%d.", - av[1], av[0], hostname, port); - } else { - usage(); - } - /* All done.. success */ - return (1); -} - diff --git a/src/lib/configmsg.c b/src/lib/configmsg.c @@ -1,144 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -#define MSGSTR(x,y) msg(x ": [%s]", y ? y : "<none>") -#define MSGUNS(x,y) msg(x ": [%u]", y) -#define MSGINT(x,y) msg(x ": [%d]", y) - -#define BMSGSTR(x,y,z) msg("Backend %d: " y " [%s]", x + 1, z ? z : "<none>") -#define BMSGINT(x,y,z) msg("Backend %d: " y " [%d]", x + 1, z) -#define BMSGUNS(x,y,z) msg("Backend %d: " y " [%u]", x + 1, z) - -static char *dispatchstr (Dispatchtype t) { - switch (t) { - case ds_roundrobin: - return ("roundrobin"); - case ds_random: - return ("random"); - case ds_bysize: - return ("bysize"); - case ds_byduration: - return ("byduration"); - case ds_byorder: - return ("byorder"); - case ds_byconnections: - return ("byconnections"); - case ds_byclientip: - return ("byclientip"); - case ds_externalhandler: - return ("externalhandler"); - default: - return ("*** cannot display ***"); - } -} - -static char *servicestr (Servicetype t) { - switch (t) { - case type_any: - return ("any"); - case type_http: - return ("http"); - default: - return ("*** cannot display ***"); - } -} - -static char *inspectionstr (HttpInspection t) { - switch (t) { - case ins_deep: - return ("deep"); - case ins_shallow: - return ("shallow"); - default: - return ("*** cannot display ***"); - } -} - -static void headers (int nr, char **hdr, int nhdr, char *desc) { - int i; - - msg ("Backend %d: %d %s header(s)", nr + 1, nhdr, desc); - for (i = 0; i < nhdr; i++) - msg ("Backend %d: %s #%d: %s", nr + 1, desc, i + 1, hdr[i]); -} - -void config_msg () { - int i; - - if (!activeservice->verbosity) - return; - - msg ("*** Overview of initial configuration of service '%s' ***", - activeservice->name); - MSGSTR ("Bind address", activeservice->bind); - MSGUNS ("Bind port", activeservice->port); - MSGUNS ("Verbosity", activeservice->verbosity); - MSGSTR ("Dispatch type", dispatchstr (activeservice->dispatchtype)); - MSGSTR ("External handler", activeservice->dispatchext); - MSGUNS ("Reviving interval", activeservice->rev_interval); - MSGUNS ("Check interval", activeservice->check_interval); - MSGSTR ("External checker", activeservice->check_cmd); - MSGUNS ("TCP backlog", activeservice->backlog); - MSGUNS ("SHM key", activeservice->shmkey); - MSGUNS ("Connection timeout", activeservice->connectiontimeout); - MSGUNS ("Max connections", activeservice->maxconnections); - MSGSTR ("Service type", servicestr (activeservice->type)); - MSGSTR ("HTTP inspection", inspectionstr (activeservice->inspection)); - MSGSTR ("Allow file", activeservice->allowfile); - MSGSTR ("Deny file", activeservice->denyfile); - MSGINT ("UID", activeservice->uid); - MSGINT ("GID", activeservice->gid); - MSGINT ("Initial deny chain", activeservice->ndenychain); - for (i = 0; i < activeservice->ndenychain; i++) - msg ("Deny #%d: %4.4x/%4.4x", i + 1, activeservice->denychain[i].ip, - activeservice->denychain[i].mask); - MSGINT ("Initial allow chain", activeservice->nallowchain); - for (i = 0; i < activeservice->nallowchain; i++) - msg ("Allow #%d: %4.4x/%4.4x", i + 1, activeservice->allowchain[i].ip, - activeservice->allowchain[i].mask); - MSGINT ("Back ends", activeservice->nbackend); - - for (i = 0; i < activeservice->nbackend; i++) { - BMSGSTR (i, "name", activeservice->backend[i].name); - BMSGINT (i, "port", activeservice->backend[i].initial_port); - BMSGSTR (i, "server", activeservice->backend[i].initial_server); - BMSGINT (i, "verbosity", activeservice->backend[i].verbosity); - BMSGINT (i, "weight", activeservice->backend[i].weight); - BMSGINT (i, "decay", activeservice->backend[i].decay); - BMSGSTR (i, "onstart cmd", activeservice->backend[i].onstart); - BMSGSTR (i, "onfail cmd", activeservice->backend[i].onfail); - BMSGSTR (i, "onend cmd", activeservice->backend[i].onend); - BMSGSTR (i, "dumplog", activeservice->backend[i].dumpfile); - BMSGSTR (i, "thruputlog", activeservice->backend[i].thruputfile); - BMSGSTR (i, "timinglog", activeservice->backend[i].timinglog); - BMSGUNS (i, "maxconnections", activeservice->backend[i].maxconnections); - BMSGSTR (i, "stickycookie", activeservice->backend[i].stickycookie); - BMSGINT (i, "retries", activeservice->backend[i].retries); - headers (i, activeservice->backend[i].addclientheader, - activeservice->backend[i].naddclientheader, - "addclientheader"); - headers (i, activeservice->backend[i].setclientheader, - activeservice->backend[i].nsetclientheader, - "setclientheader"); - headers (i, activeservice->backend[i].appendclientheader, - activeservice->backend[i].nappendclientheader, - "appendclientheader"); - headers (i, activeservice->backend[i].addserverheader, - activeservice->backend[i].naddserverheader, - "addserverheader"); - headers (i, activeservice->backend[i].setserverheader, - activeservice->backend[i].nsetserverheader, - "setserverheader"); - headers (i, activeservice->backend[i].appendserverheader, - activeservice->backend[i].nappendserverheader, - "appendserverheader"); - BMSGSTR (i, "availability", - state_to_string (activeservice->backend[i].initial_avail)); - } -} - diff --git a/src/lib/configread.c b/src/lib/configread.c @@ -1,164 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -static void rd (int fd, void *buf, unsigned sz) { - int res; - int toread = (int) sz; - - if ( (res = read (fd, buf, sz)) != toread ) - error ("Failed to fetch %d bytes (%d)", toread, res); -} - -static int rd_int (int fd) { - int res; - - rd (fd, &res, sizeof(int)); - return (res); -} - -static int rd_uns (int fd) { - unsigned res; - - rd (fd, &res, sizeof(unsigned)); - return (res); -} - -static char *rd_str (int fd) { - char *buf; - int len; - - len = rd_int (fd); - if (!len) - return (0); - if (len > 1024) - error ("Maximum string length exceeded, attempt for %d", len); - buf = xcalloc (len + 1); - rd (fd, buf, len); - return (buf); -} - - -void config_read (int fd) { - int i, j, k; - - msg ("Reading binary service definition from fd %d", fd); - - nservice = rd_int (fd); - msg ("Configured services: %d", nservice); - service = xcalloc (nservice * sizeof(Service)); - for (i = 0; i < nservice; i++) { - /* Basic stuff */ - service[i].name = rd_str (fd); - msg (" Scanning service %d (%s)", i + 1, service[i].name); - service[i].bind = rd_str (fd); - service[i].port = rd_uns (fd); - service[i].verbosity = rd_uns (fd); - service[i].dispatchtype = (Dispatchtype)rd_int (fd); - service[i].dispatchover = rd_uns (fd); - service[i].dispatchext = rd_str (fd); - service[i].rev_interval = rd_uns (fd); - service[i].check_interval = rd_uns (fd); - service[i].check_cmd = rd_str (fd); - service[i].backlog = rd_uns (fd); - service[i].shmkey = rd_uns (fd); - service[i].connectiontimeout = rd_uns (fd); - service[i].maxconnections = rd_uns (fd); - service[i].type = (Dispatchtype)rd_int (fd); - service[i].allowfile = rd_str (fd); - service[i].denyfile = rd_str (fd); - service[i].uid = rd_int (fd); - service[i].gid = rd_int (fd); - service[i].inspection = rd_int (fd); - - /* Deny chain */ - service[i].ndenychain = rd_int (fd); - service[i].denychain = xcalloc (service[i].ndenychain * - sizeof(IpFilter)); - for (j = 0; j < service[i].ndenychain; j++) { - service[i].denychain[j].ip = rd_int (fd); - service[i].denychain[j].mask = rd_int (fd); - } - - /* Allow chain */ - service[i].nallowchain = rd_int (fd); - service[i].allowchain = xcalloc (service[i].nallowchain * - sizeof(IpFilter)); - for (j = 0; j < service[i].nallowchain; j++) { - service[i].allowchain[j].ip = rd_int (fd); - service[i].allowchain[j].mask = rd_int (fd); - } - - /* Back ends */ - service[i].nbackend = rd_int (fd); - msg (" Configured back ends: %d", service[i].nbackend); - service[i].backend = xcalloc (service[i].nbackend * - sizeof(Backend)); - for (j = 0; j < service[i].nbackend; j++) { - service[i].backend[j].name = rd_str (fd); - msg (" Backend %d: %s", j + i, service[i].backend[j].name); - service[i].backend[j].initial_port = rd_int (fd); - service[i].backend[j].initial_server = rd_str (fd); - service[i].backend[j].verbosity = rd_int (fd); - service[i].backend[j].weight = rd_int (fd); - service[i].backend[j].decay = rd_int (fd); - service[i].backend[j].onstart = rd_str (fd); - service[i].backend[j].onend = rd_str (fd); - service[i].backend[j].dumpfile = rd_str (fd); - service[i].backend[j].thruputfile = rd_str (fd); - service[i].backend[j].timinglog = rd_str (fd); - service[i].backend[j].maxconnections = rd_uns (fd); - service[i].backend[j].stickycookie = rd_str (fd); - service[i].backend[j].retries = rd_int (fd); - service[i].backend[j].initial_avail = rd_int (fd); - - service[i].backend[j].naddclientheader = rd_int (fd); - service[i].backend[j].addclientheader = - xcalloc (service[i].backend[j].naddclientheader * - sizeof(char*)); - for (k = 0; k < service[i].backend[j].naddclientheader; k++) - service[i].backend[j].addclientheader[k] = rd_str (fd); - - service[i].backend[j].nsetclientheader = rd_int (fd); - service[i].backend[j].setclientheader = - xcalloc (service[i].backend[j].nsetclientheader * - sizeof(char*)); - for (k = 0; k < service[i].backend[j].nsetclientheader; k++) - service[i].backend[j].setclientheader[k] = rd_str (fd); - - service[i].backend[j].nappendclientheader = rd_int (fd); - service[i].backend[j].appendclientheader = - xcalloc (service[i].backend[j].nappendclientheader * - sizeof(char*)); - for (k = 0; k < service[i].backend[j].nappendclientheader; k++) - service[i].backend[j].appendclientheader[k] = rd_str (fd); - - service[i].backend[j].naddserverheader = rd_int (fd); - service[i].backend[j].addserverheader = - xcalloc (service[i].backend[j].naddserverheader * - sizeof(char*)); - for (k = 0; k < service[i].backend[j].naddserverheader; k++) - service[i].backend[j].addserverheader[k] = rd_str (fd); - - service[i].backend[j].nsetserverheader = rd_int (fd); - service[i].backend[j].setserverheader = - xcalloc (service[i].backend[j].nsetserverheader * - sizeof(char*)); - for (k = 0; k < service[i].backend[j].nsetserverheader; k++) - service[i].backend[j].setserverheader[k] = rd_str (fd); - - service[i].backend[j].nappendserverheader = rd_int (fd); - service[i].backend[j].appendserverheader = - xcalloc (service[i].backend[j].nappendserverheader * - sizeof(char*)); - for (k = 0; k < service[i].backend[j].nappendserverheader; k++) - service[i].backend[j].appendserverheader[k] = rd_str (fd); - } - } - - msg ("Configuration scanned."); -} diff --git a/src/lib/configwrite.c b/src/lib/configwrite.c @@ -1,153 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* For debugging, uncomment: */ -#define DEBUG - -static void wr (int fd, void const *buf, unsigned len) { - int res; - int towrite = (int)len; - - if ( (res = write (fd, buf, len)) != towrite ) - error ("Failed to transmit %d bytes (%d)", towrite, res); -} - -static void wr_int (int fd, int x, char const *desc) { - #ifdef DEBUG - msg ("Configwrite: int %d (%u bytes), %s", x, (unsigned)sizeof(int), desc); - #endif - wr (fd, &x, sizeof(int)); -} - -static void wr_uns (int fd, unsigned x, char const *desc) { - #ifdef DEBUG - msg ("Configwrite: unsigned %u (%u bytes), %s", x, (unsigned)sizeof(int), - desc); - #endif - wr (fd, &x, sizeof(unsigned)); -} - -static void wr_str (int fd, char const *s, char const *desc) { - if (!s || !*s) { - #ifdef DEBUG - msg ("Configwrite: empty string, %s", desc); - #endif - wr_int (fd, 0, "zero string length"); - } else { - #ifdef DEBUG - msg ("Configwrite: string %s (%u bytes), %s", s, (unsigned)strlen(s), - desc); - #endif - wr_int (fd, strlen(s), "nonzero string length"); - wr (fd, s, strlen(s)); - } -} - -void config_write (int fd) { - int i, j, k; - - msg ("Writing binary service definition to fd %d", fd); - wr_int (fd, nservice, "number of services"); - for (i = 0; i < nservice; i++) { - /* Basic stuff */ - wr_str (fd, service[i].name, "servicename"); - wr_str (fd, service[i].bind, "servicebind"); - wr_uns (fd, service[i].port, "serviceport"); - wr_uns (fd, service[i].verbosity, "serviceverbosity"); - wr_int (fd, (int)service[i].dispatchtype, "dispatchtype"); - wr_uns (fd, service[i].dispatchover, "dispatchover"); - wr_str (fd, service[i].dispatchext, "dispatchext"); - wr_uns (fd, service[i].rev_interval, "revivinginterval"); - wr_uns (fd, service[i].check_interval, "checkinterval"); - wr_str (fd, service[i].check_cmd, "checkcommand"); - wr_uns (fd, service[i].backlog, "backlog"); - wr_uns (fd, service[i].shmkey, "shmkey"); - wr_uns (fd, service[i].connectiontimeout, "connectiontimeout"); - wr_uns (fd, service[i].maxconnections, "maxconnections"); - wr_int (fd, (int)service[i].type, "type"); - wr_str (fd, service[i].allowfile, "allowfile"); - wr_str (fd, service[i].denyfile, "denyfile"); - wr_int (fd, service[i].uid, "uid"); - wr_int (fd, service[i].gid, "gid"); - wr_int (fd, service[i].inspection, "inspectiontype"); - - /* Deny chain */ - wr_int (fd, service[i].ndenychain, "sizeof denychain"); - for (j = 0; j < service[i].ndenychain; j++) { - wr_uns (fd, service[i].denychain[j].ip, "denied ip"); - wr_uns (fd, service[i].denychain[j].mask, "denied mask"); - } - - /* Allow chain */ - wr_int (fd, service[i].nallowchain, "sizeof allowchain"); - for (j = 0; j < service[i].nallowchain; j++) { - wr_uns (fd, service[i].allowchain[j].ip, "allowed ip"); - wr_uns (fd, service[i].allowchain[j].mask, "allowed mask"); - } - - /* Back ends */ - wr_int (fd, service[i].nbackend, "nr of backends"); - for (j = 0; j < service[i].nbackend; j++) { - wr_str (fd, service[i].backend[j].name, "backend name"); - wr_int (fd, service[i].backend[j].initial_port, "backend port"); - wr_str (fd, service[i].backend[j].initial_server, "backend server"); - wr_int (fd, service[i].backend[j].verbosity, "backend verbosity"); - wr_int (fd, service[i].backend[j].weight, "backend weight"); - wr_int (fd, service[i].backend[j].decay, "backend decay"); - wr_str (fd, service[i].backend[j].onstart, "backend onstart"); - wr_str (fd, service[i].backend[j].onend, "backend onend"); - wr_str (fd, service[i].backend[j].dumpfile, "backend dumpfile"); - wr_str (fd, service[i].backend[j].thruputfile, - "backend thruputfile"); - wr_str (fd, service[i].backend[j].timinglog, "backend timinglog"); - wr_uns (fd, service[i].backend[j].maxconnections, - "backend maxcconnections"); - wr_str (fd, service[i].backend[j].stickycookie, - "backend stickycookie"); - wr_int (fd, service[i].backend[j].retries, "backend retries"); - wr_int (fd, service[i].backend[j].initial_avail, - "backend availability"); - - wr_int (fd, service[i].backend[j].naddclientheader, - "nr of clientheaders to add"); - for (k = 0; k < service[i].backend[j].naddclientheader; k++) - wr_str (fd, service[i].backend[j].addclientheader[k], - "client header to add"); - - wr_int (fd, service[i].backend[j].nsetclientheader, - "nr of client headers to set"); - for (k = 0; k < service[i].backend[j].nsetclientheader; k++) - wr_str (fd, service[i].backend[j].setclientheader[k], - "client header to set"); - - wr_int (fd, service[i].backend[j].nappendclientheader, - "nr of client headers to append"); - for (k = 0; k < service[i].backend[j].nappendclientheader; k++) - wr_str (fd, service[i].backend[j].appendclientheader[k], - "client header to append"); - - wr_int (fd, service[i].backend[j].naddserverheader, - "nr of server headers to add"); - for (k = 0; k < service[i].backend[j].naddserverheader; k++) - wr_str (fd, service[i].backend[j].addserverheader[k], - "server header to add"); - - wr_int (fd, service[i].backend[j].nsetserverheader, - "nr of server headers to set"); - for (k = 0; k < service[i].backend[j].nsetserverheader; k++) - wr_str (fd, service[i].backend[j].setserverheader[k], - "server header to set"); - - wr_int (fd, service[i].backend[j].nappendserverheader, - "nr of server headers to append"); - for (k = 0; k < service[i].backend[j].nappendserverheader; k++) - wr_str (fd, service[i].backend[j].appendserverheader[k], - "server header to append"); - } - } -} diff --git a/src/lib/copysockets.c b/src/lib/copysockets.c @@ -1,17 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void copysockets (int clientsock, int serversock) { - unsigned char *buf = xmalloc (opt.tcp_bufsz); - - /* Copy between client and server. Function net_copy() will - * stop itself when either an error is seen, or when no more - * data are there. - * So there isn't even a reason to free(buf). */ - while (1) - net_copy (clientsock, serversock, opt.tcp_bufsz, buf); -} diff --git a/src/lib/data.c b/src/lib/data.c @@ -1,46 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#define EXTERN -#include "../crossroads.h" - -/* Global data for all Crossroads programs */ - -Options opt = { - 0, /* log_activity */ - DEFAULT_TCP_BUFSZ, /* tcp_bufsz */ - DNS_CACHETTL, /* dns_cachettl */ - LOG_DAEMON, /* log_facility */ - - 0644, /* shmperm */ - 0, /* sloppyportbind */ - 0, /* leave_proctitle */ - 0, /* log_http_activity */ -}; - -StateStringMap statestringmap[] = { - { st_available, "available" }, - { st_available, "up" }, - { st_unavailable, "unavailable" }, - { st_down, "down" }, - { st_waking, "waking" }, - { st_intermediate, "intermediate" }, - { st_unknown, "unknown" }, - { st_unknown, 0 } -}; - -int relevant_sigs[] = { /* signals relevant to this app */ - SIGHUP, /* either caught or ignored, */ - SIGINT, /* depending on the stage */ - SIGQUIT, - SIGABRT, - SIGKILL, - SIGPIPE, - SIGTERM, - 0, -}; - -DnsCache dnscache[DNS_CACHESIZE]; /* The DNS cache */ diff --git a/src/lib/deallocreporter.c b/src/lib/deallocreporter.c @@ -1,39 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void dealloc_reporter (Service *s) { - int shmid; - - msg ("Freeing reporting memory for service %s (key %d)", - s->name, s->shmkey); - - /* Get the shared memory ID. If this fails and we're in stage 0, - * then ignore the error. The true handler will have already released it. - */ - if ( (shmid = shmget (s->shmkey, sizeof(Servicereport), - 0644) ) >= 0) { - msg ("Got existing shm at id %d", shmid); - /* Mark to be released. If this fails then warn. */ - if (shmctl (shmid, IPC_RMID, 0) < 0 && program_stage != stage_main) - warning ("WARNING: Cannot mark shared memory as destructable: %s " - "Root might need to 'ipcrm -m %d' and " - "'ipcrm -s %d' to clean up!", - strerror(errno), shmid, semid); - if (semctl (semid, 0, IPC_RMID) < 0 && program_stage != stage_main) - warning ("WARNING: Cannot remove semaphore: %s " - "Root might need to 'ipcrm -s %d' to clean up!", - strerror(errno), semid); - } - else if (program_stage != stage_main) { - /* Errno 2 means that the shm block was already removed (by a - * different listener) Don't warn if that's the case. - */ - if (errno != 2) - warning ("Cannot get shared memory for service %s (key %d: %d/%s)", - s->name, s->shmkey, errno, strerror(errno)); - } -} diff --git a/src/lib/decrclientcount.c b/src/lib/decrclientcount.c @@ -1,43 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void decr_client_count () { - unsigned totclients = 0; - int i; - - if (program_stage != stage_serving) - return; - - lock_reporter(); - - for (i = 0; i < activeservice->nbackend; i++) - totclients += servicereport->backendstate[i].nclients; - servicereport->nclients = totclients; - msg ("total active clients now %u", totclients); - - /* Warning: Use current_backend as index only if >= 0; it - * may be -1 when still searching for an HTTP back end */ - if (current_backend >= 0 && - servicereport->backendstate[current_backend].nclients > 0) - servicereport->backendstate[current_backend].nclients--; - - unlock_reporter(); - - if (current_backend >= 0) { - msg ("extcmd-onend: " - "current_backend = %d, clients = %d, " - "totclients = %d, cmd = %s", - current_backend, - servicereport->backendstate[current_backend].nclients, - servicereport->nclients, - activeservice->backend[current_backend].onend ? - activeservice->backend[current_backend].onend : "<none>"); - - sysrun (activeservice->backend[current_backend].onend); - } -} diff --git a/src/lib/dns.c b/src/lib/dns.c @@ -1,101 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* #define DEBUG */ - -static int noncache_resolve (char const *hostname, struct in_addr *res) { - struct hostent *hent; - double tick; - - tick = timeofday(); - if (! (hent = gethostbyname(hostname)) ) - return (0); - *res = *(struct in_addr *)hent->h_addr; - http_log_activity (tick, 0, "hostname resolved"); - return (1); -} - -struct in_addr *dns (char const *hostname) { - int i, target = -1; - time_t now, age, min; - - /* No cache TTL? No caching. We use slot 0 for storage of the - * intermediate result. */ - if (!dns_cachettl) { - if (noncache_resolve (hostname, &dnscache[0].hostaddr)) - return (&dnscache[0].hostaddr); - return (0); - } - - /* Current time */ - now = time (0); - - #ifdef DEBUG - if (program_stage != stage_retrying) { - msg ("dnscache: now %d", now); - for (i = 0; i < DNS_CACHESIZE; i++) - msg ("dnscache[%d].hostname: %s, dnscache[%d].stamp: %d", - i, dnscache[i].hostname, i, (int)dnscache[i].stamp); - } - #endif - - /* See if we still have the hostname. If so, if the TTL isn't - * exceeded, return that. */ - for (i = 0; i < DNS_CACHESIZE && dnscache[i].hostname; i++) { - if (!strcmp (dnscache[i].hostname, hostname)) { - age = now - dnscache[i].stamp; - if (age <= dns_cachettl) { - if (program_stage != stage_retrying) - msg ("host %s resolved from cache slot %d (age: %d sec)", - hostname, i, (int)age); - return (&(dnscache[i].hostaddr)); - } else { - if (program_stage != stage_retrying) - msg ("host entry for %s is too old at slot %d " - "(age: %d sec, ttl %d sec)", - hostname, i, (int)age, - dns_cachettl); - target = i; - break; - } - } - } - - /* We failed to return from cache. If target isn't set yet, set it. */ - if (target == -1) { - if (i < DNS_CACHESIZE) { - target = i; - if (program_stage != stage_retrying) - msg ("host entry '%s' at slot %d will be added", - hostname, target); - } else { - for (i = 0; i < DNS_CACHESIZE; i++) - if (i == 0 || dnscache[i].stamp < min) { - target = i; - min = dnscache[i].stamp; - - } - if (program_stage != stage_retrying) - msg ("host entry '%s' at slot %d " - "will be overwritten", - hostname, target); - } - } - - /* Get the name, and stick it in the cache if it's a resolved entry, - * return the result.*/ - if (noncache_resolve (hostname, &dnscache[i].hostaddr)) { - free (dnscache[i].hostname); - dnscache[i].hostname = xstrdup (hostname); - dnscache[i].stamp = now; - return (&dnscache[i].hostaddr); - } - - /* Failed, return a NULL ptr. */ - return (0); -} diff --git a/src/lib/error.c b/src/lib/error.c @@ -1,32 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void error (char const *fmt, ...) { - va_list args; - char *str; - - if (fmt && *fmt) { - va_start (args, fmt); - str = str_vprintf (fmt, args); - if (!daemonized) { - if (activeservice) - fprintf (stderr, "ERROR, Service %s: %s\n", - activeservice->name, str); - else - fprintf (stderr, "ERROR: %s\n", str); - } else { - if (activeservice) - writelog (LOG_ERR, "ERROR, Service %s: %s", - activeservice->name, str); - else - writelog (LOG_ERR, "ERROR: %s", str); - } - free (str); - } - exit (1); -} diff --git a/src/lib/expandprogamname.c b/src/lib/expandprogamname.c @@ -1,30 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void expand_program_name (char const *av0) { - char *str, *cp, *last = (char*)expand_program_name; - - /* If we have path separators, then assume it's already expanded. */ - if (strchr (av0, '/')) { - program_name = xstrdup (av0); - return; - } - - /* Try to locate av0 along the PATH */ - str = xstrdup(getenv("PATH")); - for (cp = strtok_r (str, ":", &last); cp; cp = strtok_r (0, ":", &last)) { - free (program_name); - program_name = str_printf ("%s/crossroads", cp); - if (!access (program_name, R_OK | X_OK)) - return; - } - - /* All PATHs failed. Run back to av0. */ - free (program_name); - program_name = xstrdup(av0); -} diff --git a/src/lib/gmtstamp.c b/src/lib/gmtstamp.c @@ -1,26 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -char *gmtstamp (int offset) { - static char buf[80]; - time_t now; - struct tm *tmp; - static char *monthname[] = { - "January", "February", "March", "April", - "May", "June", "July", "August", "September", - "October", "November", "December" }; - - time (&now); - now += offset; - tmp = gmtime (&now); - - snprintf (buf, sizeof(buf), "%s, %d %4.4d %2.2d:%2.2d:%2.2d", - monthname[tmp->tm_mon], tmp->tm_mday, tmp->tm_year + 1900, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - return (buf); -} diff --git a/src/lib/hashpjw.c b/src/lib/hashpjw.c @@ -1,25 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -unsigned hashpjw (char const *str) { - unsigned h = 0, g; - - while (str && *str) { - h <<= 4; - h += *str; - if ( (g = h & 0xf0000000) ) { - h ^= g >> 24; - h ^= g; - } - - str++; - } - - return (h); -} - diff --git a/src/lib/httpcopy.c b/src/lib/httpcopy.c @@ -1,122 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -static void copy (int src, int dst, int dir, int tot) { - int ncopied = 0, to_copy, bytes; - unsigned char *buf; - - msg ("about to copy %d bytes from %s", - tot, dir == dir_client_to_server ? "client" : "server"); - msg ("%d bytes in client buffer, %d in server buffer", - clbufmax ? clbufmax - clbufpos : 0, - srbufmax ? srbufmax - srbufpos : 0); - - while (ncopied < tot) { - /* Here's how many bytes we should shuttle to and fro. - * This is the remaining count, or if the buffer is too small - * for that, the buffer size. */ - to_copy = tot - ncopied; - if (to_copy > (int)opt.tcp_bufsz) - to_copy = (int)opt.tcp_bufsz; - - buf = net_bufread (src, to_copy, &bytes, dir == dir_client_to_server); - net_write (dst, buf, bytes, dir == dir_client_to_server); - ncopied += bytes; - - /* msg ("Copied 0x%x (%d) bytes of 0x%x (%d) total", - ncopied, ncopied, tot, tot); - */ - } -} - -unsigned getchunk (int sock, int dest, CopyDirection dir) { - unsigned ret = 0; - unsigned char *cp; - char *hexbuf = 0; - int in_number = 1; - - /* We match - * <hex-chunksize> <any-mush> [\r]\n - * while copying all to the other socket, and return the - * chunk size */ - while (1) { - cp = net_bufread (sock, 1, 0, dir == dir_client_to_server); - /* msg ("Got character %c (%d) while scanning chunk size", - *cp, *cp); */ - net_write (dest, cp, 1, dir == dir_client_to_server); - if (*cp == '\n') { - sscanf (hexbuf, "%x", &ret); - free (hexbuf); - msg ("Chunk size 0x%x (%d)", ret, ret); - /* If the chunk size is zero, then match final [\r]\n */ - if (ret == 0) { - cp = net_bufread (sock, 1, 0, dir == dir_client_to_server); - net_write (dest, cp, 1, dir == dir_client_to_server); - if (*cp == '\r') { - cp = net_bufread (sock, 1, 0, dir == dir_client_to_server); - net_write (dest, cp, 1, dir == dir_client_to_server); - if (*cp != '\n') - warning ("Chunks not terminated by \\r\\n " - "but by \\r <decimal>%d", *cp); - } else if (*cp != '\n') - warning ("Chunks not terminated by \r\n or \n " - "but by <decimal>%d", *cp); - } - return (ret); - } - if (in_number) { - if (is_hex_digit (*cp)) - hexbuf = xstrcatch (hexbuf, *cp); - else - in_number = 0; - } - } -} - -void http_copy (HttpHeader *h, int src, int dst, CopyDirection dir) { - unsigned char const *mode, *cp; - unsigned nbytes; - - msg ("copying HTTP body from %s", - dir == dir_client_to_server ? - "client to server" : "server to client"); - - /* Check in what 'mode' we are. - * We know the following modes: - * - Transfer-Encoding is chunked: we copy chunks - * - Content-Length is supplied: we copy that # of bytes - * - None of the above: simply return, the caller http_serve() - * will enter copy-thru mode - */ - - if ( (mode = http_header_val (h, "transfer-encoding")) && *mode ) { - /* *** CHUNKED TRANSFER ENCODING *** */ - if (strncasecmp ( (char const *) mode, "chunked", 7)) - error ("can't handle transfer-encoding '%s'", mode); - msg ("copying chunks from %s", - dir == dir_client_to_server ? - "client to server" : "server to client"); - while (1) { - /* See how large the chunk will be. */ - nbytes = getchunk (src, dst, dir); - msg ("Next chunk is 0x%x (%d) bytes", nbytes, nbytes); - /* Copy the chunk. Add 2 bytes for the \r\n that follows. */ - copy (src, dst, dir, nbytes + 2); - /* Last chunksize of 0 signals the end. */ - if (!nbytes) - break; - } - } else if ( (mode = http_header_val (h, "content-length")) && *mode ) { - /* *** CONTENT-LENGTH PRESENT *** */ - nbytes = atoi ( (char const *) mode ); - msg ("copying %u bytes of content from %s", - nbytes, - dir == dir_client_to_server ? - "client to server" : "server to client"); - copy (src, dst, dir, nbytes); - } -} diff --git a/src/lib/httperror.c b/src/lib/httperror.c @@ -1,18 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" -#include "proxyerror.h" - -void http_error (int clientsock) { - char *buf = str_printf ("HTTP/1.0 502 Internal Server Error\r\n" - "Content-Length: %u\r\n" - "\r\n" - ERRORTEXT, - (unsigned) strlen(ERRORTEXT)); - write (clientsock, buf, strlen(buf)); - decr_client_count(); - error ("No back end could be selected. Client received error page."); -} diff --git a/src/lib/httpheaderaddheader.c b/src/lib/httpheaderaddheader.c @@ -1,33 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* #define DEBUG */ - -#ifdef DEBUG -static void debughdr (HttpHeader *h) { - int i; - msg ("http header add: %d headers so far", h->nheader); - for (i = 0; i < h->nheader; i++) - msg ("http header add: header[%d] '%s'", - i, h->header[i]); -} - -#define SHOWHEADERS(h) debughdr((h)) -#else -#define SHOWHEADERS(h) -#endif - -void http_header_addheader (HttpHeader *m, char const *h) { - char *exp; - - exp = str_expand_format (h); - msg ("adding header '%s'", exp); - m->header = xrealloc (m->header, (m->nheader + 1) * sizeof(char*)); - m->header[m->nheader++] = exp; - SHOWHEADERS(m); -} diff --git a/src/lib/httpheaderappendheader.c b/src/lib/httpheaderappendheader.c @@ -1,43 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void http_header_appendheader (HttpHeader *m, char const *h) { - char *hname, *cp, *exp; - int i; - - hname = xstrdup (h); - if (! (cp = strchr (hname, ':')) ) { - free (hname); - return; - } - *cp = 0; - cp++; - - while (is_space (*cp) || *cp == ':') - cp++; - exp = str_expand_format (cp); - - for (i = 0; i < m->nheader; i++) { - if (!m->header[i] || !*m->header[i]) - continue; - /* msg ("Append header: comparing '%s' to '%s'", - hname, m->header[i]); */ - - if (!strncasecmp (m->header[i], hname, strlen(hname))) { - m->header[i] = xstrcat (m->header[i], "; "); - m->header[i] = xstrcat (m->header[i], exp); - /* msg ("appending (after existing) header, now '%s'", - m->header[i]); */ - free (hname); - return; - } - } - - /* msg ("appending (setting) header '%s'", h); */ - - http_header_addheader (m, h); -} diff --git a/src/lib/httpheaderconnectiontype.c b/src/lib/httpheaderconnectiontype.c @@ -1,27 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -HttpConnectionType http_header_connectiontype (HttpHeader *h) { - unsigned char const *val; - - if (! (val = http_header_val (h, "proxy-connection")) ) - val = http_header_val (h, "connection"); - - if (!val) { - msg ("Cannot determine connection type, no such header"); - return (con_unknown); - } - if (strcasestr ((char const *)val, "close")) { - msg ("Connection-Type is CLOSE"); - return (con_close); - } else if (strcasestr ((char const *)val, "keep-alive")) { - msg ("Connection-Type is KEEP-ALIVE"); - return (con_keepalive); - } - warning ("Unsupported Connection-Type '%s'", val); - return (con_unknown); -} diff --git a/src/lib/httpheaderfree.c b/src/lib/httpheaderfree.c @@ -1,18 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void http_header_free (HttpHeader *m) { - int i; - - if (!m) - return; - for (i = 0; i < m->nheader; i++) - free (m->header[i]); - m->header = 0; - m->nheader = 0; -} diff --git a/src/lib/httpheaderhascookie.c b/src/lib/httpheaderhascookie.c @@ -1,32 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int http_header_hascookie (HttpHeader *m, char const *cookie) { - unsigned char const *val; - char *cp, *buf; - - if (!cookie || !*cookie) - return (0); - if (! (val = http_header_val (m, "cookie")) ) { - msg ("no cookies in HTTP message"); - return (0); - } - - buf = xstrdup ((char const *) val); - for (cp = buf; cp && *cp; cp = strchr (cp, ';')) { - while (*cp == ';' || *cp == ' ') - cp++; - if (!strncmp (cp, cookie, strlen (cookie))) { - msg ("found cookie '%s' in '%s'", cookie, buf); - free (buf); - return (1); - } - } - msg ("cookie '%s' not present in '%s'", cookie, buf); - free (buf); - return (0); -} diff --git a/src/lib/httpheaderhttpver.c b/src/lib/httpheaderhttpver.c @@ -1,33 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -double http_header_httpver (HttpHeader *h) { - double ret; - - /* We need at least a header line */ - if (h->nheader < 1) { - warning ("cannot get HTTP version, no headers"); - return (0.9); - } - - /* We need HTTP/1.? length */ - if (strlen (h->header[0]) < 8) { - warning ("cannot get HTTP version, header line '%s'", h->header[0]); - return (0.9); - } - - /* If the line starts with HTTP then it's a server line. - * Otherwise it's a client line. */ - if (!strncmp (h->header[0], "HTTP/", 5)) - ret = atof (h->header[0] + 5); - else - ret = atof (h->header[0] + strlen(h->header[0]) - 3); - - msg ("HTTP version is %g (headerline %s)", ret, h->header[0]); - return (ret); -} - diff --git a/src/lib/httpheadernew.c b/src/lib/httpheadernew.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -HttpHeader *http_header_new () { - HttpHeader *ret; - - /* Alloc & zero out */ - ret = xmalloc (sizeof (HttpHeader)); - memset (ret, 0, sizeof(HttpHeader)); - - return (ret); -} diff --git a/src/lib/httpheaderread.c b/src/lib/httpheaderread.c @@ -1,119 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* For more debugging information, undefine the following and set - * "verbosity on" in the configuration file. */ -/* #define DEBUG */ - -#ifdef DEBUG - -static void debughdr (HttpHeader *h) { - int i; - msg ("http_header_read: %d headers so far", h->nheader); - for (i = 0; i < h->nheader; i++) - msg ("http header read: header[%d] '%s'", - i, h->header[i]); -} - -#define SHOWHEADERS(h) debughdr((h)) -#define MSG(m) m; - -#else - -#define SHOWHEADERS(h) -#define MSG(m) - -#endif - -void http_header_read (HttpHeader *h, int sock, CopyDirection dir) { - unsigned char *hdrbuf = 0, *start, *buf, *cp; - int hdrbuflen = 0, usedlen, *bufpos, *bufmax, i; - - msg ("reading HTTP headers from %s", - dir == dir_client_to_server ? "client" : "server"); - - /* Read from network until we match [\r]\n[\r]\n */ - if (dir == dir_client_to_server) { - buf = clbuf; - bufpos = &clbufpos; - bufmax = &clbufmax; - } else { - buf = srbuf; - bufpos = &srbufpos; - bufmax = &srbufmax; - } - while (1) { - if (! *bufmax) { - *bufmax = net_read (sock, opt.tcp_bufsz, buf, - dir == dir_client_to_server); - MSG(msg("Got %d network bytes (%s)", *bufmax, buf)); - } - hdrbuf = xrealloc (hdrbuf, hdrbuflen + *bufmax); - memcpy (hdrbuf + hdrbuflen, buf + *bufpos, *bufmax); - hdrbuflen += *bufmax; - MSG(msg("Headers buffer so far: %d bytes (%s)", hdrbuflen, hdrbuf)); - - *bufmax = 0; - - /* Try to find header ending sequence */ - if ( (cp = strnstr ((char const *)hdrbuf, "\r\n\r\n", - (unsigned)hdrbuflen)) || - (cp = strnstr ((char const *)hdrbuf, "\n\n", - (unsigned)hdrbuflen)) ) { - break; - } - } - - MSG(msg("Headers done, end at %s", cp)); - - /* Skip over the ending sequence and save remainder of buffer - * for next usage */ - for (i = 0; i < 2; i++) { - if (*cp == '\r') - cp++; - if (*cp == '\n') - cp++; - } - usedlen = cp - hdrbuf; - if (usedlen < hdrbuflen) { - /* NOTE: We can always poke back the remainder, this is by definition - * not longer than opt.tcp_bufsize */ - memcpy (buf, cp, hdrbuflen - usedlen); - *bufpos = 0; - *bufmax = hdrbuflen - usedlen; - MSG(msg("Pushed back %d bytes (%s)", *bufmax, buf)); - } else { - *bufmax = 0; - MSG(msg("No remainding network for next read")); - } - - /* Split up the header buffer into separate headers and store them */ - start = hdrbuf; - while (1) { - if (! (cp = memchr (start, '\r', usedlen - (start - hdrbuf))) ) - cp = memchr (start, '\n', usedlen - (start - hdrbuf)); - if (!cp) - break; - MSG(msg("Header %d: (%d bytes) at %s", h->nheader + 1, - cp - start, start)); - if (cp - start) { - h->header = xrealloc (h->header, (h->nheader + 1) * sizeof(char*)); - h->header[h->nheader] = xmalloc (cp - start + 1); - memcpy (h->header[h->nheader], start, cp - start); - h->header[h->nheader][cp - start] = 0; - h->nheader++; - } - start = cp + 1; - if (*start == '\n') - start++; - } - - free (hdrbuf); - - SHOWHEADERS(h); -} diff --git a/src/lib/httpheaderremoveheader.c b/src/lib/httpheaderremoveheader.c @@ -1,21 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void http_header_removeheader (HttpHeader *h, char const *hdr) { - int i; - char *cp; - - for (i = 0; i < h->nheader; i++) - if (h->header[i] && !strncasecmp (h->header[i], hdr, strlen(hdr))) { - cp = h->header[i] + strlen(hdr); - if (*cp == ':') { - msg ("removing header '%s'", hdr); - free (h->header[i]); - h->header[i] = 0; - } - } -} diff --git a/src/lib/httpheadersetheader.c b/src/lib/httpheadersetheader.c @@ -1,36 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void http_header_setheader (HttpHeader *m, char const *h) { - int i; - char *hname, *cp, *exp; - - hname = xstrdup (h); - if (! (cp = strchr (hname, ':')) ) { - free (hname); - return; - } - *cp = 0; - - exp = str_expand_format (h); - for (i = 0; i < m->nheader; i++) { - if (!m->header[i] || !*m->header[i]) - continue; - if (!strncasecmp (m->header[i], hname, strlen(hname))) { - free (m->header[i]); - msg ("setting (replacing) header '%s'", exp); - m->header[i] = exp; - free (hname); - return; - } - } - - msg ("setting (adding) header '%s'", exp); - m->header = xrealloc (m->header, (m->nheader + 1) * sizeof(char*)); - m->header[m->nheader++] = exp; - free (hname); -} diff --git a/src/lib/httpheaderval.c b/src/lib/httpheaderval.c @@ -1,27 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -unsigned char const *http_header_val (HttpHeader *m, char const *what) { - unsigned char const *ret; - - int i; - - for (i = 0; i < m->nheader; i++) { - /* msg ("Scanning header %s for %s", m->header[i], what); */ - if (m->header[i] && !strncasecmp (m->header[i], what, strlen(what))) { - ret = (unsigned char const *) m->header[i] + strlen(what); - if (*ret == ':') { - ret++; - while (is_space (*ret)) - ret++; - return (ret); - } - } - } - - return (0); -} diff --git a/src/lib/httpheaderwrite.c b/src/lib/httpheaderwrite.c @@ -1,27 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void http_header_write (HttpHeader *m, int sock, CopyDirection dir) { - int i; - - msg ("Sending %d HTTP headers to %s", - m->nheader, - dir == dir_server_to_client ? "client" : "server"); - - for (i = 0; i < m->nheader; i++) { - if (! m->header[i] || ! *m->header[i]) - continue; - /* - msg ("sending header %d: '%s'", - i, m->header[i]); - */ - http_write (sock, dir, (unsigned char const *) m->header[i], - strlen(m->header[i])); - http_write (sock, dir, (unsigned char const *) "\r\n", 2); - } - http_write (sock, dir, (unsigned char const *) "\r\n", 2); -} diff --git a/src/lib/httpinsertheader.c b/src/lib/httpinsertheader.c @@ -1,55 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void http_insert_header (unsigned char **bufp, unsigned *buflen, - char const *header) { - unsigned char const *buf, *cp; - unsigned char *newbuf; - char *instruction; - int crseen = 0, atend = 0; - - cp = buf = *bufp; - msg ("Pasting header '%s' in '%s'", header, buf); - while (1) { - if (*cp == '\n') - cp++; - if (! (cp = (unsigned char const *) - strchr ( (char const *) cp, '\n')) ) { - msg ("Failed to find end of headers while inserting header"); - return; - } - cp++; - - if (*cp == '\r') { - crseen++; - if (*(cp + 1) == '\n') - atend++; - } else if (*cp == '\n') - atend++; - - if (atend) { - instruction = str_printf ("%s%s", - header, - crseen ? "\r\n" : "\n"); - - newbuf = xmalloc (*buflen + strlen(instruction) + 1); - - memcpy (newbuf, buf, cp - buf); - memcpy (newbuf + (cp - buf), instruction, strlen(instruction)); - memcpy (newbuf + (cp - buf) + strlen(instruction), - cp, *buflen - (cp - buf)); - - msg ("Modified buffer: '%s'", newbuf); - - free (*bufp); - *bufp = newbuf; - *buflen += strlen(instruction); - - return; - } - } -} diff --git a/src/lib/httplogactivity.c b/src/lib/httplogactivity.c @@ -1,49 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void http_log_activity (double start, double now, char const *fmt, ...) { - FILE *f; - va_list args; - - if (activeservice->type != type_http || program_stage == stage_retrying || - current_backend < 0 || - !activeservice->backend[current_backend].timinglog || - ! *activeservice->backend[current_backend].timinglog) - return; - - if (!now) - now = timeofday(); - if (! (f = fopen (activeservice->backend[current_backend].timinglog, - "a")) && - ! (f = fopen (activeservice->backend[current_backend].timinglog, - "w")) ) { - warning ("cannot write HTTP timings log %s: %s", - activeservice->backend[current_backend].timinglog, - strerror(errno)); - return; - } - -#if HAVE_FLOCK == 1 - flock (fileno(f), LOCK_EX); -#elif HAVE_LOCKF == 1 - lockf (fileno(f), F_LOCK, 0); -#endif - - fprintf (f, "%7.7d %15f ", getpid(), now - start); - va_start (args, fmt); - vfprintf (f, fmt, args); - fputc ('\n', f); - -#if defined HAVE_FLOCK - flock (fileno(f), LOCK_UN); -#elif defined HAVE_LOCKF - lockf (fileno(f), F_ULOCK, 0); -#endif - - fclose (f); -} diff --git a/src/lib/httpserve.c b/src/lib/httpserve.c @@ -1,228 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void http_serve (int clientsock) { - int serversock = -1, is_continuation, i; - HttpHeader *clientreq, *serverresp; - int client_persisting, server_persisting; - double cl_header_start, cl_header_end, - con_start, con_end, start, tcptotal, tick; - - /* Set relevant sigs etc. */ - program_stage = stage_serving; - for (i = 0; relevant_sigs[i]; i++) - signal (relevant_sigs[i], interrupt); - - set_program_title ("crossroads - Service %s: serving %s", - activeservice->name, client_ip); - - /* We're the child branch now of fork_tcp_servicer(). - * Here's the logic of the HTTP services. - * - Get the client's request; - * - If we're not yet connected to a back end: determine the back - * end and connect to it - * - Apply all server-directed headers - * - Send client's request to the server - * - Read back the server's response - * - Apply all client-directed headers - * - Send the server's response to the client - * - Redo from start; this might be a Keep-Alive connection. - * Alternatively, if deep inspection is off, go to cpio-thru mode. - */ - - net_allocbufs(); - while (1) { - start = timeofday(); - tcptotal = 0; - /* Reset network IO buffers. */ - clbufpos = clbufmax = srbufpos = srbufmax = 0; - - /* Assume both client and server want to persist. */ - client_persisting = server_persisting = 1; - - /* Get the initial client headers. We store the times but do - * not log yet, that can be done once we assign a back end. */ - clientreq = http_header_new(); - cl_header_start = timeofday(); - http_header_read (clientreq, clientsock, dir_client_to_server); - cl_header_end = timeofday(); - tcptotal += (cl_header_end - cl_header_start); - - /* Determine the back end if necessary. */ - if (serversock == -1) { - con_start = timeofday(); - if ( (serversock = http_serversocket (clientreq, - &is_continuation)) < 1 ) - http_error (clientsock); - con_end = timeofday(); - http_log_activity (cl_header_start, cl_header_end, - "client headers received"); - http_log_activity (con_start, con_end, "connected to backend"); - tcptotal += (con_end - con_start); - flag_verbose = activeservice->backend[current_backend].verbosity; - - incr_client_count(); - if (is_continuation) - log_activity_continuation(); - else - log_activity_start (); - } - - /* Put all server-directed headers in place. */ - for (i = 0; - i < activeservice->backend[current_backend].naddserverheader; - i++) - http_header_addheader (clientreq, - activeservice->backend[current_backend].addserverheader[i]); - for (i = 0; - i < activeservice->backend[current_backend].nsetserverheader; - i++) - http_header_setheader (clientreq, - activeservice->backend[current_backend].setserverheader[i]); - for (i = 0; - i < activeservice->backend[current_backend].nappendserverheader; - i++) - http_header_appendheader (clientreq, - activeservice->backend[current_backend].appendserverheader[i]); - - /* See RFC2616. We're actually a proxy here. Clients that talk - * HTTP/1.0 MUST be treated as single shot connection clients, even - * when they specify Connection: Keep-Alive. */ - if (http_header_httpver (clientreq) < 1.1) { - msg ("Client talks HTTP < 1.1, forcing closing connections"); - http_header_setheader (clientreq, "Connection: close"); - http_header_setheader (clientreq, "Proxy-Connection: close"); - client_persisting = 0; - } else if (http_header_connectiontype (clientreq) == con_close) { - msg ("Client asks for connection closing"); - client_persisting = 0; - } - - /* Send client's headers to the back end. */ - tick = timeofday(); - http_header_write (clientreq, serversock, dir_client_to_server); - tcptotal += timeofday() - tick; - http_log_activity (tick, 0, "sent clientheaders to backend"); - - /* Copy body from client to server */ - tick = timeofday(); - http_copy (clientreq, clientsock, serversock, dir_client_to_server); - tcptotal += timeofday() - tick; - http_log_activity (tick, 0, "sent clientbody to backend"); - - /* Get server response. */ - serverresp = http_header_new(); - tick = timeofday(); - http_header_read (serverresp, serversock, dir_server_to_client); - tcptotal += timeofday() - tick; - http_log_activity (tick, 0, "received headers from backend"); - - /* Put client-directed headers in place. */ - for (i = 0; - i < activeservice->backend[current_backend].naddclientheader; - i++) - http_header_addheader (serverresp, - activeservice->backend[current_backend].addclientheader[i]); - for (i = 0; - i < activeservice->backend[current_backend].nsetclientheader; - i++) - http_header_setheader (serverresp, - activeservice->backend[current_backend].setclientheader[i]); - for (i = 0; - i < activeservice->backend[current_backend].nappendclientheader; - i++) - http_header_appendheader (serverresp, - activeservice->backend[current_backend].appendclientheader[i]); - - /* According to RFC1616, we should convert chunked output from - * the server to one big message. We won't do that here, it breaks - * streaming. But we will close connections in the case of chunked - * transfer. */ - if (client_persisting && - http_header_val (serverresp, "transfer-encoding")) { - msg ("Server is sending chunks, forcing singleshot"); - server_persisting = 0; - client_persisting = 0; - http_header_setheader (serverresp, "Connection: close"); - http_header_setheader (serverresp, "Proxy-Connection: close"); - } - - /* If the server wants to close the connection, then we must - * inform the client. */ - if (client_persisting) { - if (http_header_httpver (serverresp) < 1.1) { - msg ("Server talks HTTP < 1.1, forcing closing connections"); - http_header_setheader (serverresp, "Connection: close"); - http_header_setheader (serverresp, "Proxy-Connection: close"); - server_persisting = 0; - } else if (http_header_connectiontype (serverresp) == con_close) { - msg ("Server asks for connection closing"); - http_header_setheader (serverresp, "Connection: close"); - http_header_setheader (serverresp, "Proxy-Connection: close"); - server_persisting = 0; - } - } - - /* Send server headers to the client. */ - tick = timeofday(); - http_header_write (serverresp, clientsock, dir_server_to_client); - tcptotal += timeofday() - tick; - http_log_activity (tick, 0, "sent serverheaders to client"); - - /* Copy body from server to client and flush network buffers if any */ - tick = timeofday(); - http_copy (serverresp, serversock, clientsock, dir_server_to_client); - tcptotal += timeofday() - tick; - http_log_activity (tick, 0, "sent serverbody to client"); - - /* Free up info from this request/response chat. */ - http_header_free (clientreq); - http_header_free (serverresp); - - /* Overall activity reporting */ - http_log_activity (start, 0, "total processing (%g in TCP)", tcptotal); - - /* Should we loop around? Go to copy-thru mode if either the - * client or the server wants to stop. */ - if (!client_persisting || !server_persisting) { - msg ("non-persistent connection"); - break; - } - - if (activeservice->inspection == ins_deep) - msg ("Will analyze following requests over the same TCP link"); - else { - msg ("Shallow analysis - won't analyze following " - "requests of the same TCP link"); - break; - } - } - - /* We're done analyzing. If there's leftovers in the network buffers, - * then we flush them first. */ - if (clbufmax) { - msg ("Flushing client-side buffer to server, %u bytes", - clbufmax - clbufpos); - http_write (serversock, dir_client_to_server, - clbuf + clbufpos, clbufmax - clbufpos); - clbufmax = clbufpos = 0; - } - if (srbufmax) { - msg ("Flushing server-side buffer to client, %u bytes", - srbufmax - srbufpos); - http_write (clientsock, dir_server_to_client, - srbuf + srbufpos, srbufmax - srbufpos); - srbufmax = srbufpos = 0; - } - - /* Copy through all that arrives on back end or client. - * This doesn't return... */ - msg ("HTTP service done analyzing, entering copy-thru mode"); - - /* This doesn't return */ - copysockets (clientsock, serversock); -} diff --git a/src/lib/httpserversocket.c b/src/lib/httpserversocket.c @@ -1,72 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int http_serversocket (HttpHeader *m, int *is_continuation) { - int i, sock; - - msg ("searching for back end for HTTP request"); - - /* Try to find a sticky cookie in the request. */ - for (i = 0; i < activeservice->nbackend; i++) { - if (servicereport->backendstate[i].actual_avail == st_unavailable) - continue; - if (activeservice->backend[i].maxconnections > 0 && - activeservice->backend[i].maxconnections <= - servicereport->backendstate[i].nclients) - continue; - if (http_header_hascookie (m, - activeservice->backend[i].stickycookie)) { - msg ("HTTP backend %d selected due to cookie '%s'", - i, - activeservice->backend[i].stickycookie); - - /* Got the target back end. Try to connect to it. - * If it doesn't succeed, then we'll do a failover to - * any other back end. */ - current_backend = i; - if ( (sock = backend_connect ()) >= 0 ) { - *is_continuation = 1; - set_program_title ("crossroads - Service %s: " - "serving %s to %s", - activeservice->name, - client_ip, - activeservice->backend[current_backend].name); - return (sock); - } - } - } - - /* We got here because this is the first time 'round or because - * a previously established back end went dead in the middle of a - * session. So: Loop 'till we find a back end, or until we fail. */ - while (1) { - /* No back end? Nogo. */ - if (! backend_count()) { - msg ("out of back ends while scanning for HTTP back end"); - return (-1); - } - - /* Try one. */ - choose_backend(); - if (current_backend < 0) { - warning ("No back end for request."); - return (-2); - } - - msg ("trying back end %s for new HTTP connection", - activeservice->backend[current_backend].name); - if ( (sock = backend_connect ()) >= 0 ) { - servicereport->backendstate[current_backend].sessions++; - *is_continuation = 0; - set_program_title ("crossroads - Service %s: serving %s to %s", - activeservice->name, - client_ip, - activeservice->backend[current_backend].name); - return (sock); - } - } -} diff --git a/src/lib/httpwrite.c b/src/lib/httpwrite.c @@ -1,18 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int http_write (int sock, CopyDirection dir, unsigned char const *buf, - int buflen) { - int nwritten, totwritten = 0; - - while (totwritten < buflen) { - nwritten = net_write (sock, buf + totwritten, buflen - totwritten, - dir == dir_client_to_server); - totwritten += nwritten; - } - return (0); -} diff --git a/src/lib/incrclientcount.c b/src/lib/incrclientcount.c @@ -1,41 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void incr_client_count () { - unsigned totclients = 0; - int i; - - if (program_stage != stage_serving) - return; - - if (current_backend >= 0) { - lock_reporter(); - servicereport->backendstate[current_backend].nclients++; - servicereport->backendstate[current_backend].totuses++; - for (i = 0; i < activeservice->nbackend; i++) - totclients += - servicereport->backendstate[i].nclients; - servicereport->nclients = totclients; - unlock_reporter(); - - msg ("extcmd-onstart: " - " current_backend = %d, clients = %d, totclients = %d, cmd = %s", - current_backend, - servicereport->backendstate[current_backend].nclients, - servicereport->nclients, - activeservice->backend[current_backend].onstart ? - activeservice->backend[current_backend].onstart : "<none>"); - - sysrun (activeservice->backend[current_backend].onstart); - - msg ("Activity on back end %d now %d, total %d", - current_backend, - servicereport->backendstate[current_backend].nclients, - totclients); - } -} diff --git a/src/lib/initsockaddr.c b/src/lib/initsockaddr.c @@ -1,21 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int init_sockaddr (struct sockaddr_in *name, - const char *hostname, int port) { - struct in_addr *hostaddr; - - if (program_stage != stage_retrying) - msg ("trying to connect to backend %s:%d", hostname, port); - name->sin_family = AF_INET; - name->sin_port = htons (port); - if (! (hostaddr = dns (hostname)) ) - return (1); - name->sin_addr = *hostaddr; - return (0); -} diff --git a/src/lib/interrupt.c b/src/lib/interrupt.c @@ -1,54 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* Signal handler. NOTE - don't use non-reentrant functions in code - * that doesn't exit! */ -void interrupt (int sig) { - - switch (sig) { - - case SIGHUP: - /* Reload allow/deny files upon next loop */ - reload_allow_deny++; - break; - - case SIGPIPE: - /* SIGPIPE shouldn't happen, the sockets are set up not to - * generate this. Incase it does: ignore, the next read/write - * will stop. */ - break; - - default: - /* In any case, stop the listening socket. */ - interrupted++; - shutdown (listen_sock, SHUT_RDWR); - close (listen_sock); - - switch (program_stage) { - case stage_waiting: - warning ("listener caught signal %d, closing socket %d and exiting", - sig, listen_sock); - exit (0); - break; - - case stage_serving: - /* This will serve the current connection and then stop. */ - break; - - case stage_retrying: - warning ("wakeup handler caught signal %d, " - "stopping wake ups and exiting", sig); - exit (0); - - default: - msg ("Caught signal %d in program stage %s, exiting", sig, - stage_to_string (program_stage)); - exit (0); - } - } -} diff --git a/src/lib/ipfaddallow.c b/src/lib/ipfaddallow.c @@ -1,17 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int ipf_add_allow (Service *s, char const *val) { - IpFilter filter; - - if (ipf_parse (val, &filter)) - return (1); - s->allowchain = xrealloc (s->allowchain, - (s->nallowchain + 1) * sizeof(IpFilter)); - s->allowchain[s->nallowchain++] = filter; - return (0); -} diff --git a/src/lib/ipfadddeny.c b/src/lib/ipfadddeny.c @@ -1,17 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int ipf_add_deny (Service *s, char const *val) { - IpFilter filter; - - if (ipf_parse (val, &filter)) - return (1); - s->denychain = xrealloc (s->denychain, - (s->ndenychain + 1) * sizeof(IpFilter)); - s->denychain[s->ndenychain++] = filter; - return (0); -} diff --git a/src/lib/ipfallowed.c b/src/lib/ipfallowed.c @@ -1,30 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int ipf_allowed () { - int i; - - /* Always allow a connection when we ddon't have an allow list. */ - if (! activeservice->allowchain) { - msg ("No allow chain (allowing client)"); - return (1); - } - - /* Try all in the chain. */ - for (i = 0; i < activeservice->nallowchain; i++) { - msg ("Testing allow rule %d", i); - if (ipf_match (activeservice->allowchain[i])) { - msg ("Allowing client (rule %d)", i); - return (1); - } - } - - /* Allow chain doesn't match */ - msg ("Not allowing client due to non-match of allow chain"); - return (0); -} diff --git a/src/lib/ipfdenied.c b/src/lib/ipfdenied.c @@ -1,30 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int ipf_denied () { - int i; - - /* Never deny a connection when we don't have a deny list. */ - if (! activeservice->denychain) { - msg ("No deny chain (not denying client)"); - return (0); - } - - /* Try all in the chain. */ - for (i = 0; i < activeservice->ndenychain; i++) { - msg ("Testing deny rule %d", i); - if (ipf_match (activeservice->denychain[i])) { - msg ("Denying client (rule %d)", i); - return (1); - } - } - - /* Deny chain doesn't match */ - msg ("Not denying client, no deny rule matches"); - return (0); -} diff --git a/src/lib/ipfloadfile.c b/src/lib/ipfloadfile.c @@ -1,49 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int ipf_loadfile (char const *fname, IpFilter **chain, int *nchain) { - FILE *f; - char buf[80]; - int nloaded = 0, ret = 0; - IpFilter filter; - - /* No file? No update. */ - if (!fname || !*fname) - return (0); - - msg ("loading IP filter file: '%s'", fname); - - if (! (f = fopen (fname, "r")) ) { - warning ("can't read filter file '%s': %s", fname, strerror(errno)); - return (0); - } - - free (*chain); - *chain = 0; - *nchain = 0; - - while (1) { - if (fscanf (f, " %79s ", buf) < 1) - break; - if (ipf_parse (buf, &filter)) { - warning ("bad ip filter in file '%s'", fname); - ret++; - } else { - msg ("got ip filter specifier '%s'", buf); - *chain = xrealloc (*chain, (*nchain + 1) * sizeof(IpFilter)); - (*chain)[*nchain] = filter; - (*nchain)++; - nloaded++; - } - } - - msg ("loaded IP filter file '%s': %d successfully loaded", - fname, nloaded); - - fclose (f); - return (ret); -} diff --git a/src/lib/ipfmatch.c b/src/lib/ipfmatch.c @@ -1,20 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int ipf_match (IpFilter f) { - unsigned f_ip_and_mask = f.ip & f.mask; - unsigned c_ip_and_mask = client_ip_nr & f.mask; - unsigned res = f_ip_and_mask == c_ip_and_mask; - - msg ("IP match: filter IP 0x%8.8x/0x%8.8x (0x%8.8x), " - "client IP 0x%8.8x/0x%8.8x (0x%8.8x), comparison %d", - f.ip, f.mask, f_ip_and_mask, - client_ip_nr, f.mask, c_ip_and_mask, - res); - return (res); -} diff --git a/src/lib/ipfparse.c b/src/lib/ipfparse.c @@ -1,52 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int ipf_parse (char const *val, IpFilter *res) { - char *str, *cp, *last = (char *) ipf_parse; - int counter, nr, i; - - memset (res, 0, sizeof(IpFilter)); - str = xstrdup (val); - - for (counter = 0, cp = strtok_r (str, ".", &last); - cp; - counter += 8, cp = strtok_r (0, ".", &last)) { - if (counter > 24) { - warning ("Invalid IP filter specifier '%s' " - "(too many network bytes)", val); - free (str); - return (1); - } - if (sscanf (cp, "%d", &nr) < 1) { - warning ("Invalid IP filter specifier '%s' " - "('%s' not a number)", val, cp); - free (str); - return (2); - } - nr <<= counter; - res->ip |= nr; - } - free (str); - - if ( (cp = strchr (val, '/')) ) { - if (sscanf (cp + 1, "%d", &nr) < 1) { - warning ("Invalid IP filter specifier '%s' " - "(bad mask '%s')", val, cp + 1); - return (3); - } - for (i = 1; i <= nr; i++) { - res->mask <<= 1; - res->mask |= 1; - } - } else - res->mask = (unsigned) -1; - - return (0); -} - - diff --git a/src/lib/ishexdigit.c b/src/lib/ishexdigit.c @@ -1,15 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int is_hex_digit (char ch) { - if ( (ch >= '0' && ch <= '9') || - (ch >= 'a' && ch <= 'f') || - (ch >= 'A' && ch <= 'F') ) - return (1); - return (0); -} - diff --git a/src/lib/isspace.c b/src/lib/isspace.c @@ -1,15 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -/* - * I'd much rather use ctype.h's isspace macro, but it's not available - * everywhere... - */ - -int is_space (char ch) { - return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); -} diff --git a/src/lib/lexer.c b/src/lib/lexer.c @@ -1,2973 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#line 2 "lexer.c" - -#line 4 "lexer.c" - -#define YY_INT_ALIGNED short int - -/* A lexical scanner generated by flex */ - -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 33 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA -#endif - -/* First, we deal with platform-specific or compiler-specific issues. */ - -/* begin standard C headers. */ -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <stdlib.h> - -/* end standard C headers. */ - -/* flex integer type definitions */ - -#ifndef FLEXINT_H -#define FLEXINT_H - -/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ - -#if __STDC_VERSION__ >= 199901L - -/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, - * if you want the limit (max/min) macros for int types. - */ -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS 1 -#endif - -#include <inttypes.h> -typedef int8_t flex_int8_t; -typedef uint8_t flex_uint8_t; -typedef int16_t flex_int16_t; -typedef uint16_t flex_uint16_t; -typedef int32_t flex_int32_t; -typedef uint32_t flex_uint32_t; -#else -typedef signed char flex_int8_t; -typedef short int flex_int16_t; -typedef int flex_int32_t; -typedef unsigned char flex_uint8_t; -typedef unsigned short int flex_uint16_t; -typedef unsigned int flex_uint32_t; -#endif /* ! C99 */ - -/* Limits of integral types. */ -#ifndef INT8_MIN -#define INT8_MIN (-128) -#endif -#ifndef INT16_MIN -#define INT16_MIN (-32767-1) -#endif -#ifndef INT32_MIN -#define INT32_MIN (-2147483647-1) -#endif -#ifndef INT8_MAX -#define INT8_MAX (127) -#endif -#ifndef INT16_MAX -#define INT16_MAX (32767) -#endif -#ifndef INT32_MAX -#define INT32_MAX (2147483647) -#endif -#ifndef UINT8_MAX -#define UINT8_MAX (255U) -#endif -#ifndef UINT16_MAX -#define UINT16_MAX (65535U) -#endif -#ifndef UINT32_MAX -#define UINT32_MAX (4294967295U) -#endif - -#endif /* ! FLEXINT_H */ - -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -#if __STDC__ - -#define YY_USE_CONST - -#endif /* __STDC__ */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST -#define yyconst const -#else -#define yyconst -#endif - -/* Returned upon end-of-file. */ -#define YY_NULL 0 - -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. - */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) - -/* Enter a start condition. This macro really ought to take a parameter, - * but we do it the disgusting crufty way forced on us by the ()-less - * definition of BEGIN. - */ -#define BEGIN (yy_start) = 1 + 2 * - -/* Translate the current start state into a value that can be later handed - * to BEGIN to return to the state. The YYSTATE alias is for lex - * compatibility. - */ -#define YY_START (((yy_start) - 1) / 2) -#define YYSTATE YY_START - -/* Action number for EOF rule of a given start state. */ -#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - -/* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE yyrestart(yyin ) - -#define YY_END_OF_BUFFER_CHAR 0 - -/* Size of default input buffer. */ -#ifndef YY_BUF_SIZE -#define YY_BUF_SIZE 16384 -#endif - -/* The state buf must be large enough to hold one state per character in the main buffer. - */ -#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) - -#ifndef YY_TYPEDEF_YY_BUFFER_STATE -#define YY_TYPEDEF_YY_BUFFER_STATE -typedef struct yy_buffer_state *YY_BUFFER_STATE; -#endif - -extern int yyleng; - -extern FILE *yyin, *yyout; - -#define EOB_ACT_CONTINUE_SCAN 0 -#define EOB_ACT_END_OF_FILE 1 -#define EOB_ACT_LAST_MATCH 2 - - #define YY_LESS_LINENO(n) - -/* Return all but the first "n" matched characters back to the input stream. */ -#define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ - int yyless_macro_arg = (n); \ - YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ - YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up yytext again */ \ - } \ - while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -/* The following is because we cannot portably get our hands on size_t - * (without autoconf's help, which isn't available because we want - * flex-generated scanners to compile on their own). - */ - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef unsigned int yy_size_t; -#endif - -#ifndef YY_STRUCT_YY_BUFFER_STATE -#define YY_STRUCT_YY_BUFFER_STATE -struct yy_buffer_state - { - FILE *yy_input_file; - - char *yy_ch_buf; /* input buffer */ - char *yy_buf_pos; /* current position in input buffer */ - - /* Size of input buffer in bytes, not including room for EOB - * characters. - */ - yy_size_t yy_buf_size; - - /* Number of characters read into yy_ch_buf, not including EOB - * characters. - */ - int yy_n_chars; - - /* Whether we "own" the buffer - i.e., we know we created it, - * and can realloc() it to grow it, and should free() it to - * delete it. - */ - int yy_is_our_buffer; - - /* Whether this is an "interactive" input source; if so, and - * if we're using stdio for input, then we want to use getc() - * instead of fread(), to make sure we stop fetching input after - * each newline. - */ - int yy_is_interactive; - - /* Whether we're considered to be at the beginning of a line. - * If so, '^' rules will be active on the next match, otherwise - * not. - */ - int yy_at_bol; - - int yy_bs_lineno; /**< The line count. */ - int yy_bs_column; /**< The column count. */ - - /* Whether to try to fill the input buffer when we reach the - * end of it. - */ - int yy_fill_buffer; - - int yy_buffer_status; - -#define YY_BUFFER_NEW 0 -#define YY_BUFFER_NORMAL 1 - /* When an EOF's been seen but there's still some text to process - * then we mark the buffer as YY_EOF_PENDING, to indicate that we - * shouldn't try reading from the input source any more. We might - * still have a bunch of tokens to match, though, because of - * possible backing-up. - * - * When we actually see the EOF, we change the status to "new" - * (via yyrestart()), so that the user can continue scanning by - * just pointing yyin at a new input file. - */ -#define YY_BUFFER_EOF_PENDING 2 - - }; -#endif /* !YY_STRUCT_YY_BUFFER_STATE */ - -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - -/* We provide macros for accessing buffer states in case in the - * future we want to put the buffer states in a more general - * "scanner state". - * - * Returns the top of the stack, or NULL. - */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ - : NULL) - -/* Same as previous macro, but useful when we know that the buffer stack is not - * NULL or when we need an lvalue. For internal use only. - */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when yytext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int yyleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 0; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow yywrap()'s to do buffer switches - * instead of setting up a fresh yyin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void yyrestart (FILE *input_file ); -void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); -void yy_delete_buffer (YY_BUFFER_STATE b ); -void yy_flush_buffer (YY_BUFFER_STATE b ); -void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); -void yypop_buffer_state (void ); - -static void yyensure_buffer_stack (void ); -static void yy_load_buffer_state (void ); -static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); - -void *yyalloc (yy_size_t ); -void *yyrealloc (void *,yy_size_t ); -void yyfree (void * ); - -#define yy_new_buffer yy_create_buffer - -#define yy_set_interactive(is_interactive) \ - { \ - if ( ! YY_CURRENT_BUFFER ){ \ - yyensure_buffer_stack (); \ - YY_CURRENT_BUFFER_LVALUE = \ - yy_create_buffer(yyin,YY_BUF_SIZE ); \ - } \ - YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ - } - -#define yy_set_bol(at_bol) \ - { \ - if ( ! YY_CURRENT_BUFFER ){\ - yyensure_buffer_stack (); \ - YY_CURRENT_BUFFER_LVALUE = \ - yy_create_buffer(yyin,YY_BUF_SIZE ); \ - } \ - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ - } - -#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) - -typedef unsigned char YY_CHAR; - -FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; - -typedef int yy_state_type; - -extern int yylineno; - -int yylineno = 1; - -extern char *yytext; -#define yytext_ptr yytext - -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); - -/* Done after the current pattern has been matched and before the - * corresponding action - sets up yytext. - */ -#define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - yyleng = (size_t) (yy_cp - yy_bp); \ - (yy_hold_char) = *yy_cp; \ - *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; - -#define YY_NUM_RULES 98 -#define YY_END_OF_BUFFER 99 -/* This struct is not used in this scanner, - but its presence is necessary. */ -struct yy_trans_info - { - flex_int32_t yy_verify; - flex_int32_t yy_nxt; - }; -static yyconst flex_int16_t yy_accept[616] = - { 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 95, 95, 96, 96, 99, 73, 71, 72, - 73, 70, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 73, 79, 78, 78, 85, 86, 84, 85, - 85, 83, 85, 77, 76, 77, 90, 88, 89, 87, - 94, 93, 91, 92, 91, 98, 95, 96, 97, 71, - 74, 0, 70, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 68, 69, 67, 69, - - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 19, 69, 69, 69, 69, 0, - 0, 0, 0, 81, 0, 82, 80, 75, 87, 91, - 92, 91, 95, 96, 0, 3, 69, 69, 46, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 67, - 0, 0, 69, 69, 69, 69, 69, 69, 69, 69, - - 69, 69, 69, 69, 69, 69, 69, 44, 69, 69, - 69, 21, 69, 69, 69, 69, 48, 69, 69, 69, - 69, 69, 69, 69, 69, 15, 14, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 42, 69, 69, 69, 69, 0, 0, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 28, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 55, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 18, 69, 69, 69, 69, 69, 69, - - 69, 69, 0, 0, 69, 69, 69, 69, 69, 69, - 69, 13, 69, 69, 69, 69, 34, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 54, 69, 69, 32, 69, 69, 69, 29, 69, - 69, 69, 69, 16, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 27, 0, 0, 69, 69, 69, 69, - 69, 69, 69, 17, 22, 69, 69, 69, 35, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 53, 4, 23, 69, 69, 12, 69, - 69, 45, 69, 69, 69, 69, 69, 69, 69, 69, - - 69, 24, 69, 2, 0, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 66, 64, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 1, 69, 69, 65, 63, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 38, 69, 33, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 31, 69, - - 69, 69, 69, 69, 47, 69, 69, 50, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 7, 52, - 69, 69, 69, 69, 5, 8, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 20, 39, 69, 69, 69, - 69, 69, 69, 69, 30, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 56, 69, 69, 69, 69, - 69, 69, 36, 41, 69, 69, 69, 51, 69, 69, - 69, 69, 69, 69, 69, 6, 49, 69, 69, 69, - 69, 69, 69, 69, 69, 26, 69, 69, 69, 9, - 10, 57, 60, 69, 69, 69, 37, 69, 69, 69, - - 58, 61, 69, 69, 69, 43, 69, 40, 69, 69, - 25, 11, 59, 62, 0 - } ; - -static yyconst flex_int32_t yy_ec[256] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, - 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 5, 1, 6, 7, 1, 1, 1, 8, 1, - 1, 9, 1, 1, 10, 10, 11, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 10, 13, 10, - 1, 10, 1, 1, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 1, 15, 1, 1, 14, 1, 16, 17, 18, 19, - - 20, 21, 22, 23, 24, 14, 25, 26, 27, 28, - 29, 30, 14, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1 - } ; - -static yyconst flex_int32_t yy_meta[40] = - { 0, - 1, 1, 2, 1, 1, 3, 1, 1, 1, 3, - 3, 4, 1, 4, 3, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4 - } ; - -static yyconst flex_int16_t yy_base[630] = - { 0, - 0, 726, 38, 42, 45, 58, 46, 53, 73, 86, - 99, 131, 65, 113, 729, 728, 730, 734, 52, 734, - 74, 717, 0, 94, 129, 36, 127, 691, 711, 59, - 97, 710, 696, 129, 695, 142, 146, 142, 91, 703, - 702, 701, 172, 734, 734, 734, 734, 734, 734, 714, - 711, 734, 90, 734, 734, 707, 734, 734, 734, 0, - 734, 734, 120, 0, 122, 734, 67, 0, 734, 173, - 734, 714, 704, 0, 696, 688, 675, 682, 695, 692, - 681, 163, 688, 679, 165, 674, 673, 668, 676, 669, - 675, 684, 666, 682, 675, 659, 0, 674, 166, 661, - - 673, 661, 663, 95, 656, 137, 172, 660, 160, 658, - 656, 173, 656, 669, 0, 664, 652, 658, 649, 192, - 660, 651, 672, 734, 669, 734, 734, 734, 0, 185, - 0, 195, 199, 0, 673, 734, 187, 646, 0, 654, - 649, 647, 652, 177, 636, 638, 644, 649, 638, 649, - 634, 625, 632, 643, 632, 629, 638, 625, 637, 625, - 619, 192, 635, 624, 635, 617, 625, 617, 614, 627, - 614, 620, 615, 607, 191, 615, 185, 610, 606, 620, - 620, 607, 614, 614, 613, 597, 600, 613, 607, 0, - 607, 609, 600, 605, 588, 595, 596, 192, 588, 596, - - 591, 587, 598, 577, 590, 594, 575, 0, 591, 595, - 594, 0, 576, 577, 587, 586, 572, 584, 585, 586, - 572, 581, 575, 582, 568, 0, 0, 567, 571, 559, - 574, 200, 566, 571, 564, 569, 568, 557, 566, 560, - 550, 549, 561, 0, 565, 564, 550, 555, 553, 550, - 551, 543, 552, 553, 555, 542, 540, 539, 547, 538, - 549, 544, 543, 538, 543, 0, 190, 527, 541, 527, - 529, 525, 531, 524, 520, 534, 523, 0, 524, 518, - 520, 520, 526, 521, 513, 512, 524, 517, 509, 510, - 500, 506, 498, 0, 497, 513, 511, 508, 507, 512, - - 497, 495, 499, 492, 505, 489, 198, 207, 506, 503, - 499, 0, 492, 499, 485, 486, 0, 488, 482, 488, - 484, 494, 488, 494, 493, 484, 480, 475, 481, 480, - 475, 0, 469, 469, 0, 468, 471, 469, 0, 477, - 476, 460, 458, 0, 466, 462, 208, 470, 467, 471, - 462, 469, 208, 0, 466, 466, 456, 463, 456, 452, - 454, 459, 452, 0, 0, 444, 458, 451, 0, 441, - 449, 452, 444, 447, 449, 447, 441, 438, 441, 435, - 428, 436, 441, 0, 0, 0, 438, 442, 0, 430, - 437, 0, 432, 426, 425, 420, 432, 421, 424, 433, - - 419, 0, 414, 734, 426, 412, 413, 423, 415, 417, - 409, 419, 414, 404, 407, 415, 405, 0, 0, 406, - 399, 410, 407, 397, 400, 409, 402, 401, 406, 399, - 398, 388, 389, 387, 387, 388, 383, 384, 380, 384, - 395, 377, 372, 734, 386, 385, 0, 0, 387, 371, - 375, 380, 375, 371, 373, 371, 366, 374, 381, 366, - 373, 374, 360, 359, 358, 362, 361, 365, 364, 354, - 352, 359, 353, 350, 348, 358, 353, 350, 357, 356, - 347, 354, 0, 344, 0, 337, 338, 351, 343, 350, - 339, 346, 339, 332, 325, 324, 337, 327, 0, 339, - - 338, 333, 339, 331, 0, 330, 327, 0, 332, 318, - 334, 333, 315, 316, 318, 329, 320, 323, 0, 0, - 323, 323, 311, 307, 0, 0, 309, 317, 320, 319, - 305, 309, 312, 292, 301, 0, 0, 310, 309, 304, - 303, 293, 298, 296, 0, 296, 288, 298, 286, 290, - 286, 297, 296, 286, 285, 0, 292, 289, 290, 289, - 288, 287, 0, 0, 286, 285, 280, 0, 279, 270, - 266, 280, 279, 266, 278, 0, 0, 265, 264, 278, - 277, 263, 260, 261, 256, 0, 272, 256, 255, 0, - 0, 0, 0, 260, 219, 203, 0, 208, 209, 208, - - 0, 0, 213, 210, 169, 0, 129, 0, 117, 66, - 0, 0, 0, 0, 734, 239, 243, 247, 251, 255, - 259, 263, 80, 267, 271, 273, 48, 277, 281 - } ; - -static yyconst flex_int16_t yy_def[630] = - { 0, - 615, 1, 616, 616, 617, 617, 618, 618, 619, 619, - 620, 620, 621, 621, 622, 622, 615, 615, 615, 615, - 615, 615, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 615, 615, 615, 615, 615, 615, 615, 624, - 625, 615, 615, 615, 615, 615, 615, 615, 615, 626, - 615, 615, 615, 627, 627, 615, 615, 628, 615, 615, - 615, 629, 615, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 615, - 615, 615, 624, 615, 625, 615, 615, 615, 626, 615, - 627, 627, 615, 628, 629, 615, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 615, 615, 623, 623, 623, 623, 623, 623, 623, 623, - - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 615, 615, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - - 623, 623, 615, 615, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 615, 615, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - - 623, 623, 623, 615, 615, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 615, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 0, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615 - } ; - -static yyconst flex_int16_t yy_nxt[774] = - { 0, - 18, 19, 20, 18, 19, 18, 18, 18, 18, 18, - 21, 22, 18, 23, 18, 24, 25, 26, 27, 28, - 29, 23, 30, 23, 23, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 23, 42, 23, 45, - 46, 45, 45, 45, 46, 45, 45, 48, 55, 49, - 50, 131, 51, 70, 56, 55, 70, 52, 83, 53, - 48, 56, 49, 50, 84, 51, 67, 62, 133, 67, - 52, 133, 53, 57, 58, 59, 57, 58, 92, 57, - 57, 57, 71, 74, 72, 57, 57, 58, 59, 57, - 58, 93, 57, 57, 57, 127, 614, 127, 57, 61, - - 61, 62, 61, 63, 61, 61, 61, 61, 61, 61, - 61, 61, 75, 61, 67, 62, 94, 67, 114, 76, - 115, 77, 116, 78, 130, 95, 130, 171, 79, 172, - 65, 61, 61, 62, 61, 63, 61, 61, 61, 61, - 61, 61, 61, 61, 80, 61, 85, 613, 612, 98, - 86, 130, 81, 132, 87, 88, 99, 103, 100, 110, - 89, 104, 65, 101, 111, 106, 82, 174, 107, 175, - 105, 108, 112, 120, 70, 179, 120, 70, 109, 113, - 144, 145, 150, 180, 151, 164, 165, 176, 183, 130, - 121, 146, 152, 120, 147, 122, 120, 166, 177, 130, - - 133, 611, 200, 133, 193, 201, 184, 219, 233, 236, - 121, 256, 220, 320, 237, 122, 130, 257, 194, 286, - 321, 359, 234, 287, 361, 395, 132, 402, 360, 610, - 396, 403, 609, 608, 607, 606, 605, 604, 362, 44, - 44, 44, 44, 47, 47, 47, 47, 54, 54, 54, - 54, 60, 60, 60, 60, 64, 64, 64, 64, 66, - 66, 66, 66, 68, 68, 68, 68, 123, 123, 123, - 123, 125, 125, 125, 125, 129, 129, 134, 603, 134, - 134, 135, 135, 135, 135, 602, 601, 600, 599, 598, - 597, 596, 595, 594, 593, 592, 591, 590, 589, 588, - - 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, - 577, 576, 575, 574, 573, 572, 571, 570, 569, 568, - 567, 566, 565, 564, 563, 562, 561, 560, 559, 558, - 557, 556, 555, 554, 553, 552, 551, 550, 549, 548, - 547, 546, 545, 544, 543, 542, 541, 540, 539, 538, - 537, 536, 535, 534, 533, 532, 531, 530, 529, 528, - 527, 526, 525, 524, 523, 522, 521, 520, 519, 518, - 517, 516, 515, 514, 513, 512, 511, 510, 509, 508, - 507, 506, 505, 504, 503, 502, 501, 500, 499, 498, - 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, - - 487, 486, 485, 484, 483, 482, 481, 480, 479, 402, - 478, 477, 476, 475, 474, 473, 472, 471, 470, 469, - 468, 467, 466, 465, 464, 463, 462, 461, 460, 459, - 458, 457, 456, 455, 454, 453, 452, 451, 115, 450, - 449, 448, 447, 446, 445, 444, 443, 442, 441, 440, - 439, 438, 437, 436, 435, 434, 433, 432, 431, 430, - 429, 428, 427, 426, 425, 424, 423, 422, 421, 420, - 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, - 409, 408, 407, 406, 405, 404, 401, 400, 399, 398, - 397, 394, 393, 392, 391, 390, 389, 388, 387, 386, - - 385, 384, 383, 382, 381, 380, 379, 378, 377, 376, - 375, 374, 373, 372, 371, 370, 369, 368, 367, 366, - 365, 364, 363, 358, 357, 356, 355, 354, 353, 352, - 351, 350, 349, 348, 347, 346, 345, 344, 343, 342, - 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, - 331, 330, 329, 328, 327, 326, 325, 324, 323, 322, - 319, 318, 317, 316, 315, 314, 313, 312, 311, 310, - 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, - 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, - 289, 288, 285, 284, 283, 282, 281, 280, 279, 278, - - 277, 276, 275, 274, 273, 272, 97, 271, 270, 269, - 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, - 258, 255, 254, 253, 252, 251, 250, 249, 248, 247, - 246, 245, 244, 190, 243, 242, 241, 240, 239, 238, - 235, 232, 231, 230, 229, 228, 227, 226, 225, 224, - 223, 222, 221, 218, 217, 216, 215, 214, 213, 212, - 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, - 199, 198, 197, 196, 195, 136, 126, 124, 192, 191, - 190, 189, 188, 187, 186, 185, 182, 181, 178, 173, - 170, 169, 168, 167, 97, 163, 162, 161, 160, 159, - - 158, 157, 156, 155, 154, 153, 149, 148, 143, 142, - 141, 140, 139, 138, 137, 73, 136, 128, 126, 124, - 119, 118, 117, 102, 97, 96, 91, 90, 73, 615, - 69, 69, 43, 17, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615 - } ; - -static yyconst flex_int16_t yy_chk[774] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, - 3, 3, 3, 4, 4, 4, 4, 5, 7, 5, - 5, 627, 5, 19, 7, 8, 19, 5, 26, 5, - 6, 8, 6, 6, 26, 6, 13, 13, 67, 13, - 6, 67, 6, 9, 9, 9, 9, 9, 30, 9, - 9, 9, 21, 623, 21, 9, 10, 10, 10, 10, - 10, 30, 10, 10, 10, 53, 610, 53, 10, 11, - - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 24, 11, 14, 14, 31, 14, 39, 24, - 39, 24, 39, 24, 63, 31, 65, 104, 24, 104, - 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 25, 12, 27, 609, 607, 34, - 27, 63, 25, 65, 27, 27, 34, 36, 34, 38, - 27, 36, 12, 34, 38, 37, 25, 106, 37, 106, - 36, 37, 38, 43, 70, 109, 43, 70, 37, 38, - 82, 82, 85, 109, 85, 99, 99, 107, 112, 130, - 43, 82, 85, 120, 82, 43, 120, 99, 107, 132, - - 133, 605, 144, 133, 137, 144, 112, 162, 175, 177, - 120, 198, 162, 267, 177, 120, 130, 198, 137, 232, - 267, 307, 175, 232, 308, 347, 132, 353, 307, 604, - 347, 353, 603, 600, 599, 598, 596, 595, 308, 616, - 616, 616, 616, 617, 617, 617, 617, 618, 618, 618, - 618, 619, 619, 619, 619, 620, 620, 620, 620, 621, - 621, 621, 621, 622, 622, 622, 622, 624, 624, 624, - 624, 625, 625, 625, 625, 626, 626, 628, 594, 628, - 628, 629, 629, 629, 629, 589, 588, 587, 585, 584, - 583, 582, 581, 580, 579, 578, 575, 574, 573, 572, - - 571, 570, 569, 567, 566, 565, 562, 561, 560, 559, - 558, 557, 555, 554, 553, 552, 551, 550, 549, 548, - 547, 546, 544, 543, 542, 541, 540, 539, 538, 535, - 534, 533, 532, 531, 530, 529, 528, 527, 524, 523, - 522, 521, 518, 517, 516, 515, 514, 513, 512, 511, - 510, 509, 507, 506, 504, 503, 502, 501, 500, 498, - 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, - 487, 486, 484, 482, 481, 480, 479, 478, 477, 476, - 475, 474, 473, 472, 471, 470, 469, 468, 467, 466, - 465, 464, 463, 462, 461, 460, 459, 458, 457, 456, - - 455, 454, 453, 452, 451, 450, 449, 446, 445, 443, - 442, 441, 440, 439, 438, 437, 436, 435, 434, 433, - 432, 431, 430, 429, 428, 427, 426, 425, 424, 423, - 422, 421, 420, 417, 416, 415, 414, 413, 412, 411, - 410, 409, 408, 407, 406, 405, 403, 401, 400, 399, - 398, 397, 396, 395, 394, 393, 391, 390, 388, 387, - 383, 382, 381, 380, 379, 378, 377, 376, 375, 374, - 373, 372, 371, 370, 368, 367, 366, 363, 362, 361, - 360, 359, 358, 357, 356, 355, 352, 351, 350, 349, - 348, 346, 345, 343, 342, 341, 340, 338, 337, 336, - - 334, 333, 331, 330, 329, 328, 327, 326, 325, 324, - 323, 322, 321, 320, 319, 318, 316, 315, 314, 313, - 311, 310, 309, 306, 305, 304, 303, 302, 301, 300, - 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, - 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, - 277, 276, 275, 274, 273, 272, 271, 270, 269, 268, - 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, - 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, - 245, 243, 242, 241, 240, 239, 238, 237, 236, 235, - 234, 233, 231, 230, 229, 228, 225, 224, 223, 222, - - 221, 220, 219, 218, 217, 216, 215, 214, 213, 211, - 210, 209, 207, 206, 205, 204, 203, 202, 201, 200, - 199, 197, 196, 195, 194, 193, 192, 191, 189, 188, - 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, - 176, 174, 173, 172, 171, 170, 169, 168, 167, 166, - 165, 164, 163, 161, 160, 159, 158, 157, 156, 155, - 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, - 143, 142, 141, 140, 138, 135, 125, 123, 122, 121, - 119, 118, 117, 116, 114, 113, 111, 110, 108, 105, - 103, 102, 101, 100, 98, 96, 95, 94, 93, 92, - - 91, 90, 89, 88, 87, 86, 84, 83, 81, 80, - 79, 78, 77, 76, 75, 73, 72, 56, 51, 50, - 42, 41, 40, 35, 33, 32, 29, 28, 22, 17, - 16, 15, 2, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615 - } ; - -static yy_state_type yy_last_accepting_state; -static char *yy_last_accepting_cpos; - -extern int yy_flex_debug; -int yy_flex_debug = 0; - -/* The intent behind this definition is that it'll catch - * any uses of REJECT which flex missed. - */ -#define REJECT reject_used_but_not_detected -#define yymore() yymore_used_but_not_detected -#define YY_MORE_ADJ 0 -#define YY_RESTORE_YY_MORE_OFFSET -char *yytext; -#line 1 "lexer.l" -#line 2 "lexer.l" -#include "../crossroads.h" -#include "parser.h" - -/* Redefinition of YY_INPUT to respect the parserstate. */ -#define YY_INPUT(buf,res,max) (res) = parser_input( (buf), (max) ) -#define yywrap parser_close - -/* Globals */ -static int i; - -/* Lexer debugging related */ -static void lmsg (char const *fmt, ...) { - va_list args; - - if (parser_debug) { - va_start (args, fmt); - printf ("LEXER: "); - vprintf (fmt, args); - } -} - -#line 848 "lexer.c" - -#define INITIAL 0 -#define stringstate 1 -#define instr 2 -#define commentstate 3 -#define includestate 4 -#define definestate 5 -#define defineredef 6 -#define definesym 7 - -#ifndef YY_NO_UNISTD_H -/* Special case for "unistd.h", since it is non-ANSI. We include it way - * down here because we want the user's section 1 to have been scanned first. - * The user has a chance to override it with an option. - */ -#include <unistd.h> -#endif - -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif - -static int yy_init_globals (void ); - -/* Macros after this point can all be overridden by user definitions in - * section 1. - */ - -#ifndef YY_SKIP_YYWRAP -#ifdef __cplusplus -extern "C" int yywrap (void ); -#else -extern int yywrap (void ); -#endif -#endif - - static void yyunput (int c,char *buf_ptr ); - -#ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); -#endif - -#ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); -#endif - -#ifndef YY_NO_INPUT - -#ifdef __cplusplus -static int yyinput (void ); -#else -static int input (void ); -#endif - -#endif - -/* Amount of stuff to slurp up with each read. */ -#ifndef YY_READ_BUF_SIZE -#define YY_READ_BUF_SIZE 8192 -#endif - -/* Copy whatever the last rule matched to the standard output. */ -#ifndef ECHO -/* This used to be an fputs(), but since the string might contain NUL's, - * we now use fwrite(). - */ -#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) -#endif - -/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, - * is returned in "result". - */ -#ifndef YY_INPUT -#define YY_INPUT(buf,result,max_size) \ - if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ - { \ - int c = '*'; \ - size_t n; \ - for ( n = 0; n < max_size && \ - (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ - buf[n] = (char) c; \ - if ( c == '\n' ) \ - buf[n++] = (char) c; \ - if ( c == EOF && ferror( yyin ) ) \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - result = n; \ - } \ - else \ - { \ - errno=0; \ - while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ - { \ - if( errno != EINTR) \ - { \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - break; \ - } \ - errno=0; \ - clearerr(yyin); \ - } \ - }\ -\ - -#endif - -/* No semi-colon after return; correct usage is to write "yyterminate();" - - * we don't want an extra ';' after the "return" because that will cause - * some compilers to complain about unreachable statements. - */ -#ifndef yyterminate -#define yyterminate() return YY_NULL -#endif - -/* Number of entries by which start-condition stack grows. */ -#ifndef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#endif - -/* Report a fatal error. */ -#ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) -#endif - -/* end tables serialization structures and prototypes */ - -/* Default declaration of generated scanner - a define so the user can - * easily add parameters. - */ -#ifndef YY_DECL -#define YY_DECL_IS_OURS 1 - -extern int yylex (void); - -#define YY_DECL int yylex (void) -#endif /* !YY_DECL */ - -/* Code executed at the beginning of each rule, after yytext and yyleng - * have been set up. - */ -#ifndef YY_USER_ACTION -#define YY_USER_ACTION -#endif - -/* Code executed at the end of each rule. */ -#ifndef YY_BREAK -#define YY_BREAK break; -#endif - -#define YY_RULE_SETUP \ - if ( yyleng > 0 ) \ - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ - (yytext[yyleng - 1] == '\n'); \ - YY_USER_ACTION - -/** The main scanner function which does all the work. - */ -YY_DECL -{ - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 27 "lexer.l" - - -#line 1014 "lexer.c" - - if ( !(yy_init) ) - { - (yy_init) = 1; - -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif - - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ - - if ( ! yyin ) - yyin = stdin; - - if ( ! yyout ) - yyout = stdout; - - if ( ! YY_CURRENT_BUFFER ) { - yyensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - yy_create_buffer(yyin,YY_BUF_SIZE ); - } - - yy_load_buffer_state( ); - } - - while ( 1 ) /* loops until end-of-file is reached */ - { - yy_cp = (yy_c_buf_p); - - /* Support of yytext. */ - *yy_cp = (yy_hold_char); - - /* yy_bp points to the position in yy_ch_buf of the start of - * the current run. - */ - yy_bp = yy_cp; - - yy_current_state = (yy_start); - yy_current_state += YY_AT_BOL(); -yy_match: - do - { - register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 616 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - ++yy_cp; - } - while ( yy_base[yy_current_state] != 734 ); - -yy_find_action: - yy_act = yy_accept[yy_current_state]; - if ( yy_act == 0 ) - { /* have to back up */ - yy_cp = (yy_last_accepting_cpos); - yy_current_state = (yy_last_accepting_state); - yy_act = yy_accept[yy_current_state]; - } - - YY_DO_BEFORE_ACTION; - -do_action: /* This label is used only to access EOF actions. */ - - switch ( yy_act ) - { /* beginning of action switch */ - case 0: /* must back up */ - /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = (yy_hold_char); - yy_cp = (yy_last_accepting_cpos); - yy_current_state = (yy_last_accepting_state); - goto yy_find_action; - -case 1: -YY_RULE_SETUP -#line 29 "lexer.l" -{ - lmsg ("include starts\n"); - BEGIN (includestate); -} - YY_BREAK -case 2: -YY_RULE_SETUP -#line 35 "lexer.l" -{ - lmsg ("define starts\n"); - BEGIN (definestate); -} - YY_BREAK -case 3: -/* rule 3 can match eol */ -YY_RULE_SETUP -#line 40 "lexer.l" -{ - lmsg ("comment\n"); - yylineno++; -} - YY_BREAK -case 4: -YY_RULE_SETUP -#line 45 "lexer.l" -{ - lmsg ("options\n"); - return (OPTIONS); -} - YY_BREAK -case 5: -YY_RULE_SETUP -#line 50 "lexer.l" -{ - lmsg ("logactivity\n"); - return (LOGACTIVITY); -} - YY_BREAK -case 6: -YY_RULE_SETUP -#line 55 "lexer.l" -{ - lmsg ("tcpbuffersize\n"); - return (TCPBUFFERSIZE); -} - YY_BREAK -case 7: -YY_RULE_SETUP -#line 60 "lexer.l" -{ - lmsg ("dnscachettl\n"); - return (DNSCACHETTL); -} - YY_BREAK -case 8: -YY_RULE_SETUP -#line 65 "lexer.l" -{ - lmsg ("logfacility\n"); - return (LOGFACILITY); -} - YY_BREAK -case 9: -YY_RULE_SETUP -#line 70 "lexer.l" -{ - lmsg ("shmpermissions\n"); - return (SHMPERMISSIONS); -} - YY_BREAK -case 10: -YY_RULE_SETUP -#line 75 "lexer.l" -{ - lmsg ("sloppyportbind\n"); - return (SLOPPYPORTBIND); -} - YY_BREAK -case 11: -YY_RULE_SETUP -#line 80 "lexer.l" -{ - lmsg ("leaveprocesstitle\n"); - return (LEAVEPROCESSTITLE); -} - YY_BREAK -case 12: -YY_RULE_SETUP -#line 85 "lexer.l" -{ - lmsg ("service\n"); - return (SERVICE); -} - YY_BREAK -case 13: -YY_RULE_SETUP -#line 90 "lexer.l" -{ - lmsg ("bindto\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (BINDTO); -} - YY_BREAK -case 14: -YY_RULE_SETUP -#line 98 "lexer.l" -{ - lmsg ("port\n"); - return (PORT); -} - YY_BREAK -case 15: -YY_RULE_SETUP -#line 103 "lexer.l" -{ - lmsg ("over\n"); - return (OVER); -} - YY_BREAK -case 16: -YY_RULE_SETUP -#line 108 "lexer.l" -{ - lmsg ("shmkey\n"); - return (SHMKEY); -} - YY_BREAK -case 17: -YY_RULE_SETUP -#line 113 "lexer.l" -{ - lmsg ("backend\n"); - return (BACKEND); -} - YY_BREAK -case 18: -YY_RULE_SETUP -#line 118 "lexer.l" -{ - lmsg ("state\n"); - return (STATE); -} - YY_BREAK -case 19: -YY_RULE_SETUP -#line 123 "lexer.l" -{ - lmsg ("available\n"); - return (AVAILABLE); -} - YY_BREAK -case 20: -YY_RULE_SETUP -#line 128 "lexer.l" -{ - lmsg ("unavailable\n"); - return (UNAVAILABLE); -} - YY_BREAK -case 21: -YY_RULE_SETUP -#line 133 "lexer.l" -{ - lmsg ("down\n"); - return (DOWN); -} - YY_BREAK -case 22: -YY_RULE_SETUP -#line 138 "lexer.l" -{ - lmsg ("backlog\n"); - return (BACKLOG); -} - YY_BREAK -case 23: -YY_RULE_SETUP -#line 143 "lexer.l" -{ - lmsg ("retries\n"); - return (RETRIES); -} - YY_BREAK -case 24: -YY_RULE_SETUP -#line 148 "lexer.l" -{ - lmsg ("verbosity\n"); - return (VERBOSITY); -} - YY_BREAK -case 25: -YY_RULE_SETUP -#line 153 "lexer.l" -{ - lmsg ("connectiontimeout\n"); - return (CONNECTIONTIMEOUT); -} - YY_BREAK -case 26: -YY_RULE_SETUP -#line 158 "lexer.l" -{ - lmsg ("maxconnections\n"); - return (MAXCONNECTIONS); -} - YY_BREAK -case 27: -YY_RULE_SETUP -#line 163 "lexer.l" -{ - lmsg ("weight\n"); - return (WEIGHT); -} - YY_BREAK -case 28: -YY_RULE_SETUP -#line 168 "lexer.l" -{ - lmsg ("decay\n"); - return (DECAY); -} - YY_BREAK -case 29: -YY_RULE_SETUP -#line 173 "lexer.l" -{ - lmsg ("server\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (SERVER); -} - YY_BREAK -case 30: -YY_RULE_SETUP -#line 181 "lexer.l" -{ - lmsg ("dispatchmode\n"); - return (DISPATCHMODE); -} - YY_BREAK -case 31: -YY_RULE_SETUP -#line 186 "lexer.l" -{ - lmsg ("roundrobin\n"); - return (ROUNDROBIN); -} - YY_BREAK -case 32: -YY_RULE_SETUP -#line 191 "lexer.l" -{ - lmsg ("random\n"); - return (RANDOM); -} - YY_BREAK -case 33: -YY_RULE_SETUP -#line 196 "lexer.l" -{ - lmsg ("byduration\n"); - return (BYDURATION); -} - YY_BREAK -case 34: -YY_RULE_SETUP -#line 201 "lexer.l" -{ - lmsg ("bysize\n"); - return (BYSIZE); -} - YY_BREAK -case 35: -YY_RULE_SETUP -#line 206 "lexer.l" -{ - lmsg ("byorder\n"); - return (BYORDER); -} - YY_BREAK -case 36: -YY_RULE_SETUP -#line 211 "lexer.l" -{ - lmsg ("byconnections\n"); - return (BYCONNECTIONS); -} - YY_BREAK -case 37: -YY_RULE_SETUP -#line 216 "lexer.l" -{ - lmsg ("externalhandler\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (EXTERNALHANDLER); -} - YY_BREAK -case 38: -YY_RULE_SETUP -#line 224 "lexer.l" -{ - lmsg ("byclientip\n"); - return (BYCLIENTIP); -} - YY_BREAK -case 39: -YY_RULE_SETUP -#line 229 "lexer.l" -{ - lmsg ("useraccount\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (USERACCOUNT); -} - YY_BREAK -case 40: -YY_RULE_SETUP -#line 237 "lexer.l" -{ - lmsg ("revivinginterval\n"); - return (REVIVINGINTERVAL); -} - YY_BREAK -case 41: -YY_RULE_SETUP -#line 242 "lexer.l" -{ - lmsg ("checkinterval\n"); - return (CHECKINTERVAL); -} - YY_BREAK -case 42: -YY_RULE_SETUP -#line 247 "lexer.l" -{ - lmsg ("type\n"); - return (TYPE); -} - YY_BREAK -case 43: -YY_RULE_SETUP -#line 252 "lexer.l" -{ - lmsg ("headerinspection\n"); - return (HEADERINSPECTION); -} - YY_BREAK -case 44: -YY_RULE_SETUP -#line 257 "lexer.l" -{ - lmsg ("deep\n"); - return (DEEP); -} - YY_BREAK -case 45: -YY_RULE_SETUP -#line 262 "lexer.l" -{ - lmsg ("shallow\n"); - return (SHALLOW); -} - YY_BREAK -case 46: -YY_RULE_SETUP -#line 267 "lexer.l" -{ - lmsg ("any\n"); - return (ANY); -} - YY_BREAK -case 47: -YY_RULE_SETUP -#line 272 "lexer.l" -{ - lmsg ("stickyhttp\n"); - warning ("The 'stickyhttp protocol is obsolte.\n" - "You should change to 'http'."); - return (HTTP); -} - YY_BREAK -case 48: -YY_RULE_SETUP -#line 279 "lexer.l" -{ - lmsg ("http\n"); - return (HTTP); -} - YY_BREAK -case 49: -YY_RULE_SETUP -#line 284 "lexer.l" -{ - lmsg ("throughputlog\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (THROUGHPUTLOG); -} - YY_BREAK -case 50: -YY_RULE_SETUP -#line 292 "lexer.l" -{ - lmsg ("trafficlog\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (TRAFFICLOG); -} - YY_BREAK -case 51: -YY_RULE_SETUP -#line 300 "lexer.l" -{ - lmsg ("httptiminglog\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (HTTPTIMINGLOG); -} - YY_BREAK -case 52: -YY_RULE_SETUP -#line 308 "lexer.l" -{ - lmsg ("dumptraffic\n"); - warning ("The 'dumptraffic' statement is obsolete.\n" - "You should change to 'trafficlog'."); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (TRAFFICLOG); -} - YY_BREAK -case 53: -YY_RULE_SETUP -#line 318 "lexer.l" -{ - lmsg ("onstart\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ONSTART); -} - YY_BREAK -case 54: -YY_RULE_SETUP -#line 326 "lexer.l" -{ - lmsg ("onfail\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ONFAIL); -} - YY_BREAK -case 55: -YY_RULE_SETUP -#line 334 "lexer.l" -{ - lmsg ("onend\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ONEND); -} - YY_BREAK -case 56: -YY_RULE_SETUP -#line 342 "lexer.l" -{ - lmsg ("stickycookie\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (STICKYCOOKIE); -} - YY_BREAK -case 57: -YY_RULE_SETUP -#line 350 "lexer.l" -{ - lmsg ("addclientheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ADDCLIENTHEADER); -} - YY_BREAK -case 58: -YY_RULE_SETUP -#line 358 "lexer.l" -{ - lmsg ("setclientheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (SETCLIENTHEADER); -} - YY_BREAK -case 59: -YY_RULE_SETUP -#line 366 "lexer.l" -{ - lmsg ("appendclientheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (APPENDCLIENTHEADER); -} - YY_BREAK -case 60: -YY_RULE_SETUP -#line 374 "lexer.l" -{ - lmsg ("addserverheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ADDSERVERHEADER); -} - YY_BREAK -case 61: -YY_RULE_SETUP -#line 382 "lexer.l" -{ - lmsg ("setserverheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (SETSERVERHEADER); -} - YY_BREAK -case 62: -YY_RULE_SETUP -#line 390 "lexer.l" -{ - lmsg ("appendserverheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (APPENDSERVERHEADER); -} - YY_BREAK -case 63: -YY_RULE_SETUP -#line 398 "lexer.l" -{ - lmsg ("allowfrom\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ALLOWFROM); -} - YY_BREAK -case 64: -YY_RULE_SETUP -#line 406 "lexer.l" -{ - lmsg ("denyfrom\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (DENYFROM); -} - YY_BREAK -case 65: -YY_RULE_SETUP -#line 414 "lexer.l" -{ - lmsg ("allowfile\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ALLOWFILE); -} - YY_BREAK -case 66: -YY_RULE_SETUP -#line 422 "lexer.l" -{ - lmsg ("denyfile\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (DENYFILE); -} - YY_BREAK -case 67: -YY_RULE_SETUP -#line 430 "lexer.l" -{ - lmsg ("on\n"); - return (ON); -} - YY_BREAK -case 68: -YY_RULE_SETUP -#line 435 "lexer.l" -{ - lmsg ("off\n"); - return (OFF); -} - YY_BREAK -case 69: -YY_RULE_SETUP -#line 440 "lexer.l" -{ - lmsg ("identifier [%s]\n", yytext); - return (IDENTIFIER); -} - YY_BREAK -case 70: -YY_RULE_SETUP -#line 445 "lexer.l" -{ - lmsg ("number [%s]\n", yytext); - return (NUMBER); -} - YY_BREAK -case 71: -YY_RULE_SETUP -#line 450 "lexer.l" -{ - lmsg ("space(s)\n"); -} - YY_BREAK -case 72: -/* rule 72 can match eol */ -YY_RULE_SETUP -#line 454 "lexer.l" -{ - lmsg ("newline\n"); - yylineno++; -} - YY_BREAK -case 73: -YY_RULE_SETUP -#line 459 "lexer.l" -{ - lmsg ("lone char ['%c' (ASCII %d)]\n", - isprint(*yytext) ? *yytext : '.', - *yytext); - return (*yytext); -} - YY_BREAK -case 74: -YY_RULE_SETUP -#line 466 "lexer.l" -{ - lmsg ("C-comment starts\n"); - BEGIN(commentstate); -} - YY_BREAK -case 75: -YY_RULE_SETUP -#line 470 "lexer.l" -{ - lmsg ("C-comment ends\n"); - BEGIN(0); -} - YY_BREAK -case 76: -/* rule 76 can match eol */ -YY_RULE_SETUP -#line 474 "lexer.l" -{ - yylineno++; -} - YY_BREAK -case 77: -YY_RULE_SETUP -#line 477 "lexer.l" -; - YY_BREAK -case 78: -/* rule 78 can match eol */ -YY_RULE_SETUP -#line 479 "lexer.l" -{ - lmsg ("string state: skipping initial spaces [ASCII %d]\n", *yytext); - if (*yytext == '\n') - yylineno++; -} - YY_BREAK -case 79: -YY_RULE_SETUP -#line 484 "lexer.l" -{ - lmsg ("string state: entering instring on [%s (ASCII %d)]\n", - yytext, *yytext); - unput (*yytext); - BEGIN (instr); -} - YY_BREAK -case 80: -YY_RULE_SETUP -#line 491 "lexer.l" -{ - lmsg ("string part [%s]\n", yytext); - laststring = xstrcat (laststring, yytext); -} - YY_BREAK -case 81: -/* rule 81 can match eol */ -YY_RULE_SETUP -#line 496 "lexer.l" -{ - lmsg ("string part in double quotes [%s]\n", yytext); - laststring = xstrcat (laststring, yytext + 1); - laststring[strlen(laststring) - 1] = 0; -} - YY_BREAK -case 82: -/* rule 82 can match eol */ -YY_RULE_SETUP -#line 501 "lexer.l" -{ - lmsg ("string part in single quotes [%s]\n", yytext); - laststring = xstrcat (laststring, yytext + 1); - laststring[strlen(laststring) - 1] = 0; -} - YY_BREAK -case 83: -YY_RULE_SETUP -#line 506 "lexer.l" -{ - BEGIN (0); - /* Strip terminating whitespace incase the ; is preceded by them */ - if (laststring && *laststring) - for (i = strlen(laststring) - 1; i >= 0 && isspace(laststring[i]); i--) - laststring[i] = 0; - unput (';'); - lmsg ("string complete [%s]\n", laststring); - return (STRING); -} - YY_BREAK -case 84: -YY_RULE_SETUP -#line 516 "lexer.l" -{ - if (laststring) { - laststring = xstrcat (laststring, yytext); - lmsg ("string part space [%s]\n", yytext); - } -} - YY_BREAK -case 85: -YY_RULE_SETUP -#line 522 "lexer.l" -{ - lmsg ("string part char [%s]\n", yytext); - laststring = xstrcat (laststring, yytext); -} - YY_BREAK -case 86: -/* rule 86 can match eol */ -YY_RULE_SETUP -#line 526 "lexer.l" -{ - if (laststring) { - laststring = xstrcat (laststring, " "); - lmsg ("string part: newline, now space\n"); - } - yylineno++; -} - YY_BREAK -case 87: -YY_RULE_SETUP -#line 534 "lexer.l" -{ - lmsg ("includefile\n"); - /* parser_skipline(); */ - parser_open (yytext); - BEGIN (0); -} - YY_BREAK -case YY_STATE_EOF(includestate): -#line 540 "lexer.l" -{ - error ("Unterminated #include"); -} - YY_BREAK -case 88: -YY_RULE_SETUP -#line 543 "lexer.l" -; - YY_BREAK -case 89: -/* rule 89 can match eol */ -YY_RULE_SETUP -#line 544 "lexer.l" -{ - yylineno++; -} - YY_BREAK -case 90: -YY_RULE_SETUP -#line 547 "lexer.l" -{ - error ("Illegal character 0x%2.2x in #include line", *yytext); -} - YY_BREAK -case 91: -YY_RULE_SETUP -#line 551 "lexer.l" -; - YY_BREAK -case 92: -YY_RULE_SETUP -#line 552 "lexer.l" -{ - symtab_start (yytext); - BEGIN (defineredef); -} - YY_BREAK -case YY_STATE_EOF(definestate): -case YY_STATE_EOF(defineredef): -case YY_STATE_EOF(definesym): -#line 556 "lexer.l" -{ - error ("Unterminated #define"); -} - YY_BREAK -case 93: -/* rule 93 can match eol */ -YY_RULE_SETUP -#line 559 "lexer.l" -{ - error ("Unterminated #define"); -} - YY_BREAK -case 94: -YY_RULE_SETUP -#line 562 "lexer.l" -{ - error ("Unrecognized character '%c' in #define", *yytext); -} - YY_BREAK -case 95: -YY_RULE_SETUP -#line 566 "lexer.l" -{ - BEGIN (definesym); -} - YY_BREAK -case 96: -YY_RULE_SETUP -#line 569 "lexer.l" -{ - symtab_set (yytext); -} - YY_BREAK -case 97: -/* rule 97 can match eol */ -YY_RULE_SETUP -#line 572 "lexer.l" -{ - yylineno++; - symtab_end(); - BEGIN (0); -} - YY_BREAK -case 98: -YY_RULE_SETUP -#line 577 "lexer.l" -ECHO; - YY_BREAK -#line 1977 "lexer.c" -case YY_STATE_EOF(INITIAL): -case YY_STATE_EOF(stringstate): -case YY_STATE_EOF(instr): -case YY_STATE_EOF(commentstate): - yyterminate(); - - case YY_END_OF_BUFFER: - { - /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; - - /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); - YY_RESTORE_YY_MORE_OFFSET - - if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) - { - /* We're scanning a new file or input source. It's - * possible that this happened because the user - * just pointed yyin at a new source and called - * yylex(). If so, then we have to assure - * consistency between YY_CURRENT_BUFFER and our - * globals. Here is the right place to do so, because - * this is the first action (other than possibly a - * back-up) that will match for the new input source. - */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; - YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; - } - - /* Note that here we test for yy_c_buf_p "<=" to the position - * of the first EOB in the buffer, since yy_c_buf_p will - * already have been incremented past the NUL character - * (since all states make transitions on EOB to the - * end-of-buffer state). Contrast this with the test - * in input(). - */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) - { /* This was really a NUL. */ - yy_state_type yy_next_state; - - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; - - yy_current_state = yy_get_previous_state( ); - - /* Okay, we're now positioned to make the NUL - * transition. We couldn't have - * yy_get_previous_state() go ahead and do it - * for us because it doesn't know how to deal - * with the possibility of jamming (and we don't - * want to build jamming into it because then it - * will run more slowly). - */ - - yy_next_state = yy_try_NUL_trans( yy_current_state ); - - yy_bp = (yytext_ptr) + YY_MORE_ADJ; - - if ( yy_next_state ) - { - /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); - yy_current_state = yy_next_state; - goto yy_match; - } - - else - { - yy_cp = (yy_c_buf_p); - goto yy_find_action; - } - } - - else switch ( yy_get_next_buffer( ) ) - { - case EOB_ACT_END_OF_FILE: - { - (yy_did_buffer_switch_on_eof) = 0; - - if ( yywrap( ) ) - { - /* Note: because we've taken care in - * yy_get_next_buffer() to have set up - * yytext, we can now set up - * yy_c_buf_p so that if some total - * hoser (like flex itself) wants to - * call the scanner after we return the - * YY_NULL, it'll still work - another - * YY_NULL will get returned. - */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; - - yy_act = YY_STATE_EOF(YY_START); - goto do_action; - } - - else - { - if ( ! (yy_did_buffer_switch_on_eof) ) - YY_NEW_FILE; - } - break; - } - - case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; - - yy_current_state = yy_get_previous_state( ); - - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; - goto yy_match; - - case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; - - yy_current_state = yy_get_previous_state( ); - - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; - goto yy_find_action; - } - break; - } - - default: - YY_FATAL_ERROR( - "fatal flex scanner internal error--no action found" ); - } /* end of action switch */ - } /* end of scanning one token */ -} /* end of yylex */ - -/* yy_get_next_buffer - try to read in a new buffer - * - * Returns a code representing an action: - * EOB_ACT_LAST_MATCH - - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position - * EOB_ACT_END_OF_FILE - end of file - */ -static int yy_get_next_buffer (void) -{ - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; - int ret_val; - - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) - YY_FATAL_ERROR( - "fatal flex scanner internal error--end of buffer missed" ); - - if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) - { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) - { - /* We matched a single character, the EOB, so - * treat this as a final EOF. - */ - return EOB_ACT_END_OF_FILE; - } - - else - { - /* We matched some text prior to the EOB, first - * process it. - */ - return EOB_ACT_LAST_MATCH; - } - } - - /* Try to read more data. */ - - /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; - - for ( i = 0; i < number_to_move; ++i ) - *(dest++) = *(source++); - - if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) - /* don't do the read, it's not guaranteed to return an EOF, - * just force an EOF - */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; - - else - { - int num_to_read = - YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; - - while ( num_to_read <= 0 ) - { /* Not enough room in the buffer - grow it. */ - - /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; - - int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); - - if ( b->yy_is_our_buffer ) - { - int new_size = b->yy_buf_size * 2; - - if ( new_size <= 0 ) - b->yy_buf_size += b->yy_buf_size / 8; - else - b->yy_buf_size *= 2; - - b->yy_ch_buf = (char *) - /* Include room in for 2 EOB chars. */ - yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); - } - else - /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; - - if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( - "fatal error - scanner input buffer overflow" ); - - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; - - num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - - number_to_move - 1; - - } - - if ( num_to_read > YY_READ_BUF_SIZE ) - num_to_read = YY_READ_BUF_SIZE; - - /* Read in more data. */ - YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), num_to_read ); - - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } - - if ( (yy_n_chars) == 0 ) - { - if ( number_to_move == YY_MORE_ADJ ) - { - ret_val = EOB_ACT_END_OF_FILE; - yyrestart(yyin ); - } - - else - { - ret_val = EOB_ACT_LAST_MATCH; - YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = - YY_BUFFER_EOF_PENDING; - } - } - - else - ret_val = EOB_ACT_CONTINUE_SCAN; - - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; - - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; - - return ret_val; -} - -/* yy_get_previous_state - get the state just before the EOB char was reached */ - - static yy_state_type yy_get_previous_state (void) -{ - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); - yy_current_state += YY_AT_BOL(); - - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) - { - register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 616 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - } - - return yy_current_state; -} - -/* yy_try_NUL_trans - try to make a transition on the NUL character - * - * synopsis - * next_state = yy_try_NUL_trans( current_state ); - */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) -{ - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); - - register YY_CHAR yy_c = 1; - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 616 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 615); - - return yy_is_jam ? 0 : yy_current_state; -} - - static void yyunput (int c, register char * yy_bp ) -{ - register char *yy_cp; - - yy_cp = (yy_c_buf_p); - - /* undo effects of setting up yytext */ - *yy_cp = (yy_hold_char); - - if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) - { /* need to shift things up to make room */ - /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ - YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; - - while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) - *--dest = *--source; - - yy_cp += (int) (dest - source); - yy_bp += (int) (dest - source); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; - - if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) - YY_FATAL_ERROR( "flex scanner push-back overflow" ); - } - - *--yy_cp = (char) c; - - (yytext_ptr) = yy_bp; - (yy_hold_char) = *yy_cp; - (yy_c_buf_p) = yy_cp; -} - -#ifndef YY_NO_INPUT -#ifdef __cplusplus - static int yyinput (void) -#else - static int input (void) -#endif - -{ - int c; - - *(yy_c_buf_p) = (yy_hold_char); - - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) - { - /* yy_c_buf_p now points to the character we want to return. - * If this occurs *before* the EOB characters, then it's a - * valid NUL; if not, then we've hit the end of the buffer. - */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) - /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; - - else - { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); - - switch ( yy_get_next_buffer( ) ) - { - case EOB_ACT_LAST_MATCH: - /* This happens because yy_g_n_b() - * sees that we've accumulated a - * token and flags that we need to - * try matching the token before - * proceeding. But for input(), - * there's no matching to consider. - * So convert the EOB_ACT_LAST_MATCH - * to EOB_ACT_END_OF_FILE. - */ - - /* Reset buffer status. */ - yyrestart(yyin ); - - /*FALLTHROUGH*/ - - case EOB_ACT_END_OF_FILE: - { - if ( yywrap( ) ) - return 0; - - if ( ! (yy_did_buffer_switch_on_eof) ) - YY_NEW_FILE; -#ifdef __cplusplus - return yyinput(); -#else - return input(); -#endif - } - - case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; - break; - } - } - } - - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve yytext */ - (yy_hold_char) = *++(yy_c_buf_p); - - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); - - return c; -} -#endif /* ifndef YY_NO_INPUT */ - -/** Immediately switch to a different input stream. - * @param input_file A readable stream. - * - * @note This function does not reset the start condition to @c INITIAL . - */ - void yyrestart (FILE * input_file ) -{ - - if ( ! YY_CURRENT_BUFFER ){ - yyensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - yy_create_buffer(yyin,YY_BUF_SIZE ); - } - - yy_init_buffer(YY_CURRENT_BUFFER,input_file ); - yy_load_buffer_state( ); -} - -/** Switch to a different input buffer. - * @param new_buffer The new input buffer. - * - */ - void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) -{ - - /* TODO. We should be able to replace this entire function body - * with - * yypop_buffer_state(); - * yypush_buffer_state(new_buffer); - */ - yyensure_buffer_stack (); - if ( YY_CURRENT_BUFFER == new_buffer ) - return; - - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } - - YY_CURRENT_BUFFER_LVALUE = new_buffer; - yy_load_buffer_state( ); - - /* We don't actually know whether we did this switch during - * EOF (yywrap()) processing, but the only time this flag - * is looked at is after yywrap() is called, so it's safe - * to go ahead and always set it. - */ - (yy_did_buffer_switch_on_eof) = 1; -} - -static void yy_load_buffer_state (void) -{ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); -} - -/** Allocate and initialize an input buffer state. - * @param file A readable stream. - * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * - * @return the allocated buffer state. - */ - YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) -{ - YY_BUFFER_STATE b; - - b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); - - b->yy_buf_size = size; - - /* yy_ch_buf has to be 2 characters longer than the size given because - * we need to put in 2 end-of-buffer characters. - */ - b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); - if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); - - b->yy_is_our_buffer = 1; - - yy_init_buffer(b,file ); - - return b; -} - -/** Destroy the buffer. - * @param b a buffer created with yy_create_buffer() - * - */ - void yy_delete_buffer (YY_BUFFER_STATE b ) -{ - - if ( ! b ) - return; - - if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ - YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; - - if ( b->yy_is_our_buffer ) - yyfree((void *) b->yy_ch_buf ); - - yyfree((void *) b ); -} - -#ifndef __cplusplus -extern int isatty (int ); -#endif /* __cplusplus */ - -/* Initializes or reinitializes a buffer. - * This function is sometimes called more than once on the same buffer, - * such as during a yyrestart() or at EOF. - */ - static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) - -{ - int oerrno = errno; - - yy_flush_buffer(b ); - - b->yy_input_file = file; - b->yy_fill_buffer = 1; - - /* If b is the current buffer, then yy_init_buffer was _probably_ - * called from yyrestart() or through yy_get_next_buffer. - * In that case, we don't want to reset the lineno or column. - */ - if (b != YY_CURRENT_BUFFER){ - b->yy_bs_lineno = 1; - b->yy_bs_column = 0; - } - - b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; - - errno = oerrno; -} - -/** Discard all buffered characters. On the next scan, YY_INPUT will be called. - * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * - */ - void yy_flush_buffer (YY_BUFFER_STATE b ) -{ - if ( ! b ) - return; - - b->yy_n_chars = 0; - - /* We always need two end-of-buffer characters. The first causes - * a transition to the end-of-buffer state. The second causes - * a jam in that state. - */ - b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; - b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; - - b->yy_buf_pos = &b->yy_ch_buf[0]; - - b->yy_at_bol = 1; - b->yy_buffer_status = YY_BUFFER_NEW; - - if ( b == YY_CURRENT_BUFFER ) - yy_load_buffer_state( ); -} - -/** Pushes the new state onto the stack. The new state becomes - * the current state. This function will allocate the stack - * if necessary. - * @param new_buffer The new state. - * - */ -void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) -{ - if (new_buffer == NULL) - return; - - yyensure_buffer_stack(); - - /* This block is copied from yy_switch_to_buffer. */ - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } - - /* Only push if top exists. Otherwise, replace top. */ - if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; - YY_CURRENT_BUFFER_LVALUE = new_buffer; - - /* copied from yy_switch_to_buffer. */ - yy_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; -} - -/** Removes and deletes the top of the stack, if present. - * The next element becomes the new top. - * - */ -void yypop_buffer_state (void) -{ - if (!YY_CURRENT_BUFFER) - return; - - yy_delete_buffer(YY_CURRENT_BUFFER ); - YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); - - if (YY_CURRENT_BUFFER) { - yy_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; - } -} - -/* Allocates the stack if it does not exist. - * Guarantees space for at least one push. - */ -static void yyensure_buffer_stack (void) -{ - int num_to_alloc; - - if (!(yy_buffer_stack)) { - - /* First allocation is just for 2 elements, since we don't know if this - * scanner will even need a stack. We use 2 instead of 1 to avoid an - * immediate realloc on the next call. - */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc - (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; - return; - } - - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ - - /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; - - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc - ((yy_buffer_stack), - num_to_alloc * sizeof(struct yy_buffer_state*) - ); - - /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; - } -} - -/** Setup the input buffer state to scan directly from a user-specified character buffer. - * @param base the character buffer - * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. - */ -YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) -{ - YY_BUFFER_STATE b; - - if ( size < 2 || - base[size-2] != YY_END_OF_BUFFER_CHAR || - base[size-1] != YY_END_OF_BUFFER_CHAR ) - /* They forgot to leave room for the EOB's. */ - return 0; - - b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ - b->yy_buf_pos = b->yy_ch_buf = base; - b->yy_is_our_buffer = 0; - b->yy_input_file = 0; - b->yy_n_chars = b->yy_buf_size; - b->yy_is_interactive = 0; - b->yy_at_bol = 1; - b->yy_fill_buffer = 0; - b->yy_buffer_status = YY_BUFFER_NEW; - - yy_switch_to_buffer(b ); - - return b; -} - -/** Setup the input buffer state to scan a string. The next call to yylex() will - * scan from a @e copy of @a str. - * @param str a NUL-terminated string to scan - * - * @return the newly allocated buffer state object. - * @note If you want to scan bytes that may contain NUL values, then use - * yy_scan_bytes() instead. - */ -YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) -{ - - return yy_scan_bytes(yystr,strlen(yystr) ); -} - -/** Setup the input buffer state to scan the given bytes. The next call to yylex() will - * scan from a @e copy of @a bytes. - * @param bytes the byte buffer to scan - * @param len the number of bytes in the buffer pointed to by @a bytes. - * - * @return the newly allocated buffer state object. - */ -YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) -{ - YY_BUFFER_STATE b; - char *buf; - yy_size_t n; - int i; - - /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) yyalloc(n ); - if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); - - for ( i = 0; i < _yybytes_len; ++i ) - buf[i] = yybytes[i]; - - buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - - b = yy_scan_buffer(buf,n ); - if ( ! b ) - YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); - - /* It's okay to grow etc. this buffer, and we should throw it - * away when we're done. - */ - b->yy_is_our_buffer = 1; - - return b; -} - -#ifndef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#endif - -static void yy_fatal_error (yyconst char* msg ) -{ - (void) fprintf( stderr, "%s\n", msg ); - exit( YY_EXIT_FAILURE ); -} - -/* Redefine yyless() so it works in section 3 code. */ - -#undef yyless -#define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ - int yyless_macro_arg = (n); \ - YY_LESS_LINENO(yyless_macro_arg);\ - yytext[yyleng] = (yy_hold_char); \ - (yy_c_buf_p) = yytext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - yyleng = yyless_macro_arg; \ - } \ - while ( 0 ) - -/* Accessor methods (get/set functions) to struct members. */ - -/** Get the current line number. - * - */ -int yyget_lineno (void) -{ - - return yylineno; -} - -/** Get the input stream. - * - */ -FILE *yyget_in (void) -{ - return yyin; -} - -/** Get the output stream. - * - */ -FILE *yyget_out (void) -{ - return yyout; -} - -/** Get the length of the current token. - * - */ -int yyget_leng (void) -{ - return yyleng; -} - -/** Get the current token. - * - */ - -char *yyget_text (void) -{ - return yytext; -} - -/** Set the current line number. - * @param line_number - * - */ -void yyset_lineno (int line_number ) -{ - - yylineno = line_number; -} - -/** Set the input stream. This does not discard the current - * input buffer. - * @param in_str A readable stream. - * - * @see yy_switch_to_buffer - */ -void yyset_in (FILE * in_str ) -{ - yyin = in_str ; -} - -void yyset_out (FILE * out_str ) -{ - yyout = out_str ; -} - -int yyget_debug (void) -{ - return yy_flex_debug; -} - -void yyset_debug (int bdebug ) -{ - yy_flex_debug = bdebug ; -} - -static int yy_init_globals (void) -{ - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from yylex_destroy(), so don't allocate here. - */ - - (yy_buffer_stack) = 0; - (yy_buffer_stack_top) = 0; - (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; - (yy_init) = 0; - (yy_start) = 0; - -/* Defined in main.c */ -#ifdef YY_STDINIT - yyin = stdin; - yyout = stdout; -#else - yyin = (FILE *) 0; - yyout = (FILE *) 0; -#endif - - /* For future reference: Set errno on error, since we are called by - * yylex_init() - */ - return 0; -} - -/* yylex_destroy is for both reentrant and non-reentrant scanners. */ -int yylex_destroy (void) -{ - - /* Pop the buffer stack, destroying each element. */ - while(YY_CURRENT_BUFFER){ - yy_delete_buffer(YY_CURRENT_BUFFER ); - YY_CURRENT_BUFFER_LVALUE = NULL; - yypop_buffer_state(); - } - - /* Destroy the stack itself. */ - yyfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; - - /* Reset the globals. This is important in a non-reentrant scanner so the next time - * yylex() is called, initialization will occur. */ - yy_init_globals( ); - - return 0; -} - -/* - * Internal utility routines. - */ - -#ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) -{ - register int i; - for ( i = 0; i < n; ++i ) - s1[i] = s2[i]; -} -#endif - -#ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) -{ - register int n; - for ( n = 0; s[n]; ++n ) - ; - - return n; -} -#endif - -void *yyalloc (yy_size_t size ) -{ - return (void *) malloc( size ); -} - -void *yyrealloc (void * ptr, yy_size_t size ) -{ - /* The cast to (char *) in the following accommodates both - * implementations that use char* generic pointers, and those - * that use void* generic pointers. It works with the latter - * because both ANSI C and C++ allow castless assignment from - * any pointer type to void*, and deal with argument conversions - * as though doing an assignment. - */ - return (void *) realloc( (char *) ptr, size ); -} - -void yyfree (void * ptr ) -{ - free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ -} - -#define YYTABLES_NAME "yytables" - -#line 577 "lexer.l" diff --git a/src/lib/lexer.l b/src/lib/lexer.l @@ -1,576 +0,0 @@ -%{ -#include "../crossroads.h" -#include "parser.h" - -/* Redefinition of YY_INPUT to respect the parserstate. */ -#define YY_INPUT(buf,res,max) (res) = parser_input( (buf), (max) ) -#define yywrap parser_close - -/* Globals */ -static int i; - -/* Lexer debugging related */ -static void lmsg (char const *fmt, ...) { - va_list args; - - if (parser_debug) { - va_start (args, fmt); - printf ("LEXER: "); - vprintf (fmt, args); - } -} -%} - -%pointer -%x stringstate instr commentstate includestate definestate defineredef definesym - -%% - -^#[ \t]*include { - lmsg ("include starts\n"); - BEGIN (includestate); -} - - -^#[ \t]*define { - lmsg ("define starts\n"); - BEGIN (definestate); -} - -\/\/.*\n { - lmsg ("comment\n"); - yylineno++; -} - -options { - lmsg ("options\n"); - return (OPTIONS); -} - -logactivity { - lmsg ("logactivity\n"); - return (LOGACTIVITY); -} - -tcpbuffersize { - lmsg ("tcpbuffersize\n"); - return (TCPBUFFERSIZE); -} - -dnscachettl { - lmsg ("dnscachettl\n"); - return (DNSCACHETTL); -} - -logfacility { - lmsg ("logfacility\n"); - return (LOGFACILITY); -} - -shmpermissions { - lmsg ("shmpermissions\n"); - return (SHMPERMISSIONS); -} - -sloppyportbind { - lmsg ("sloppyportbind\n"); - return (SLOPPYPORTBIND); -} - -leaveprocesstitle { - lmsg ("leaveprocesstitle\n"); - return (LEAVEPROCESSTITLE); -} - -service { - lmsg ("service\n"); - return (SERVICE); -} - -bindto { - lmsg ("bindto\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (BINDTO); -} - -port { - lmsg ("port\n"); - return (PORT); -} - -over { - lmsg ("over\n"); - return (OVER); -} - -shmkey { - lmsg ("shmkey\n"); - return (SHMKEY); -} - -backend { - lmsg ("backend\n"); - return (BACKEND); -} - -state { - lmsg ("state\n"); - return (STATE); -} - -(available)|(up) { - lmsg ("available\n"); - return (AVAILABLE); -} - -unavailable { - lmsg ("unavailable\n"); - return (UNAVAILABLE); -} - -down { - lmsg ("down\n"); - return (DOWN); -} - -backlog { - lmsg ("backlog\n"); - return (BACKLOG); -} - -retries { - lmsg ("retries\n"); - return (RETRIES); -} - -(verbosity)|(verbose) { - lmsg ("verbosity\n"); - return (VERBOSITY); -} - -connectiontimeout { - lmsg ("connectiontimeout\n"); - return (CONNECTIONTIMEOUT); -} - -maxconnections { - lmsg ("maxconnections\n"); - return (MAXCONNECTIONS); -} - -weight { - lmsg ("weight\n"); - return (WEIGHT); -} - -decay { - lmsg ("decay\n"); - return (DECAY); -} - -server { - lmsg ("server\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (SERVER); -} - -dispatchmode { - lmsg ("dispatchmode\n"); - return (DISPATCHMODE); -} - -roundrobin { - lmsg ("roundrobin\n"); - return (ROUNDROBIN); -} - -random { - lmsg ("random\n"); - return (RANDOM); -} - -byduration { - lmsg ("byduration\n"); - return (BYDURATION); -} - -bysize { - lmsg ("bysize\n"); - return (BYSIZE); -} - -byorder { - lmsg ("byorder\n"); - return (BYORDER); -} - -byconnections { - lmsg ("byconnections\n"); - return (BYCONNECTIONS); -} - -externalhandler { - lmsg ("externalhandler\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (EXTERNALHANDLER); -} - -byclientip { - lmsg ("byclientip\n"); - return (BYCLIENTIP); -} - -useraccount { - lmsg ("useraccount\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (USERACCOUNT); -} - -revivinginterval { - lmsg ("revivinginterval\n"); - return (REVIVINGINTERVAL); -} - -checkinterval { - lmsg ("checkinterval\n"); - return (CHECKINTERVAL); -} - -type { - lmsg ("type\n"); - return (TYPE); -} - -headerinspection { - lmsg ("headerinspection\n"); - return (HEADERINSPECTION); -} - -deep { - lmsg ("deep\n"); - return (DEEP); -} - -shallow { - lmsg ("shallow\n"); - return (SHALLOW); -} - -any { - lmsg ("any\n"); - return (ANY); -} - -stickyhttp { - lmsg ("stickyhttp\n"); - warning ("The 'stickyhttp protocol is obsolte.\n" - "You should change to 'http'."); - return (HTTP); -} - -http { - lmsg ("http\n"); - return (HTTP); -} - -throughputlog { - lmsg ("throughputlog\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (THROUGHPUTLOG); -} - -trafficlog { - lmsg ("trafficlog\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (TRAFFICLOG); -} - -httptiminglog { - lmsg ("httptiminglog\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (HTTPTIMINGLOG); -} - -dumptraffic { - lmsg ("dumptraffic\n"); - warning ("The 'dumptraffic' statement is obsolete.\n" - "You should change to 'trafficlog'."); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (TRAFFICLOG); -} - -onstart { - lmsg ("onstart\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ONSTART); -} - -onfail { - lmsg ("onfail\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ONFAIL); -} - -onend { - lmsg ("onend\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ONEND); -} - -stickycookie { - lmsg ("stickycookie\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (STICKYCOOKIE); -} - -addclientheader { - lmsg ("addclientheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ADDCLIENTHEADER); -} - -setclientheader { - lmsg ("setclientheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (SETCLIENTHEADER); -} - -appendclientheader { - lmsg ("appendclientheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (APPENDCLIENTHEADER); -} - -addserverheader { - lmsg ("addserverheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ADDSERVERHEADER); -} - -setserverheader { - lmsg ("setserverheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (SETSERVERHEADER); -} - -appendserverheader { - lmsg ("appendserverheader\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (APPENDSERVERHEADER); -} - -allowfrom { - lmsg ("allowfrom\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ALLOWFROM); -} - -denyfrom { - lmsg ("denyfrom\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (DENYFROM); -} - -allowfile { - lmsg ("allowfile\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (ALLOWFILE); -} - -denyfile { - lmsg ("denyfile\n"); - BEGIN (stringstate); - free (laststring); - laststring = 0; - return (DENYFILE); -} - -on|true|yes { - lmsg ("on\n"); - return (ON); -} - -off|false|no { - lmsg ("off\n"); - return (OFF); -} - -[a-zA-Z_][a-zA-Z0-9_]* { - lmsg ("identifier [%s]\n", yytext); - return (IDENTIFIER); -} - -[0-9]+ { - lmsg ("number [%s]\n", yytext); - return (NUMBER); -} - -[ \t]+ { - lmsg ("space(s)\n"); -} - -\n { - lmsg ("newline\n"); - yylineno++; -} - -. { - lmsg ("lone char ['%c' (ASCII %d)]\n", - isprint(*yytext) ? *yytext : '.', - *yytext); - return (*yytext); -} - -\/\* { - lmsg ("C-comment starts\n"); - BEGIN(commentstate); -} -<commentstate>\*\/ { - lmsg ("C-comment ends\n"); - BEGIN(0); -} -<commentstate>\n { - yylineno++; -} -<commentstate>. ; - -<stringstate>[ \r\n\t] { - lmsg ("string state: skipping initial spaces [ASCII %d]\n", *yytext); - if (*yytext == '\n') - yylineno++; -} -<stringstate>. { - lmsg ("string state: entering instring on [%s (ASCII %d)]\n", - yytext, *yytext); - unput (*yytext); - BEGIN (instr); -} - -<instr>(\\\")|(\\\') { - lmsg ("string part [%s]\n", yytext); - laststring = xstrcat (laststring, yytext); -} - -<instr>\"[^\"]*\" { - lmsg ("string part in double quotes [%s]\n", yytext); - laststring = xstrcat (laststring, yytext + 1); - laststring[strlen(laststring) - 1] = 0; -} -<instr>\'[^\']*\' { - lmsg ("string part in single quotes [%s]\n", yytext); - laststring = xstrcat (laststring, yytext + 1); - laststring[strlen(laststring) - 1] = 0; -} -<instr>; { - BEGIN (0); - /* Strip terminating whitespace incase the ; is preceded by them */ - if (laststring && *laststring) - for (i = strlen(laststring) - 1; i >= 0 && isspace(laststring[i]); i--) - laststring[i] = 0; - unput (';'); - lmsg ("string complete [%s]\n", laststring); - return (STRING); -} -<instr>" " { - if (laststring) { - laststring = xstrcat (laststring, yytext); - lmsg ("string part space [%s]\n", yytext); - } -} -<instr>. { - lmsg ("string part char [%s]\n", yytext); - laststring = xstrcat (laststring, yytext); -} -<instr>\n { - if (laststring) { - laststring = xstrcat (laststring, " "); - lmsg ("string part: newline, now space\n"); - } - yylineno++; -} - -<includestate>[<>\"a-zA-Z0-9_\-\./:\\]+ { - lmsg ("includefile\n"); - /* parser_skipline(); */ - parser_open (yytext); - BEGIN (0); -} -<includestate><<EOF>> { - error ("Unterminated #include"); -} -<includestate>[ \t] ; -<includestate>\n { - yylineno++; -} -<includestate>. { - error ("Illegal character 0x%2.2x in #include line", *yytext); -} - -<definestate>[ \s]+ ; -<definestate>[A-Za-z_][A-Za-z0-9_]* { - symtab_start (yytext); - BEGIN (defineredef); -} -<definestate,defineredef,definesym><<EOF>> { - error ("Unterminated #define"); -} -<definestate,defineredef>\n { - error ("Unterminated #define"); -} -<definestate>. { - error ("Unrecognized character '%c' in #define", *yytext); -} - -<defineredef>[ \t]* { - BEGIN (definesym); -} -<definesym>.* { - symtab_set (yytext); -} -<definesym>\n { - yylineno++; - symtab_end(); - BEGIN (0); -} diff --git a/src/lib/limitsmsg.c b/src/lib/limitsmsg.c @@ -1,47 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -typedef struct { - int type; - char *desc; -} Rlimits; - -static Rlimits rlimits[] = { - { RLIMIT_CORE, "core file size" }, - { RLIMIT_CPU, "max seconds CPU usage" }, - { RLIMIT_DATA, "max bytes data segment usage" }, - { RLIMIT_FSIZE, "max bytes in created files" }, - #ifdef RLIMIT_MEMLOCK - { RLIMIT_MEMLOCK, "max bytes in locked memory" }, - #endif - { RLIMIT_NOFILE, "max nr of open files" }, - #ifdef RLIMIT_NPROC - { RLIMIT_NPROC, "max nr of processes of this id" }, - #endif - #ifdef RLIMIT_RSS - { RLIMIT_RSS, "max bytes in resident memory" }, - #endif - { RLIMIT_STACK, "max bytes in stack" }, -}; - -void limits_msg () { - struct rlimit lim; - unsigned i; - - if (!activeservice->verbosity) - return; - - msg ("*** Overview of run limits ***"); - for (i = 0; i < sizeof(rlimits) / sizeof(Rlimits); i++) { - if (getrlimit (rlimits[i].type, &lim)) - error ("failed to get limits for '%s'", rlimits[i].desc); - msg ("Limit for '%s': current %u, max %u", - rlimits[i].desc, - (unsigned) lim.rlim_cur, (unsigned)lim.rlim_max); - } -} diff --git a/src/lib/lockreporter.c b/src/lib/lockreporter.c @@ -1,32 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void lock_reporter() { - static int warning_issued = 0; - struct sembuf buf[] = { - { - 0, /* semaphore number */ - 0, /* wait for zero */ - 0, /* no special flags */ - }, - { - 0, /* semaphore number */ - 1, /* add 1 to the value */ - 0 /* no special flags */ - } - }; - - /* msg ("Locking reporter memory"); */ - if ( (!warning_issued++) && (semop (semid, buf, 2) < 0) ) { - warning ("Failed to lock reporter memory (stage %s): %s", - stage_to_string (program_stage), strerror(errno)); - if (program_stage != stage_serving) - _exit (1); - else - warning (".. but continuing serving this TCP connection"); - } -} diff --git a/src/lib/logactivityany.c b/src/lib/logactivityany.c @@ -1,19 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void log_activity_any (char const *action) { - if (!opt.log_activity || current_backend < 0) - return; - if (!logstarted++) - openlog ("crossroads", LOG_PID, opt.log_facility); - syslog (LOG_NOTICE, "%s %s %s from %s to %s:%d", - ansistamp(tm_localtime, 0), action, activeservice->name, - client_ip, - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port); -} diff --git a/src/lib/logactivitycontinuation.c b/src/lib/logactivitycontinuation.c @@ -1,10 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void log_activity_continuation () { - log_activity_any ("continuing"); -} diff --git a/src/lib/logactivityend.c b/src/lib/logactivityend.c @@ -1,10 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void log_activity_end () { - log_activity_any ("ending"); -} diff --git a/src/lib/logactivitystart.c b/src/lib/logactivitystart.c @@ -1,10 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void log_activity_start () { - log_activity_any ("starting"); -} diff --git a/src/lib/makesocket.c b/src/lib/makesocket.c @@ -1,62 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int make_socket (int port, char const *ipaddr) { - int sock, val = 1; - struct sockaddr_in name; - - /* Create the socket. */ - if ( (sock = socket (PF_INET, SOCK_STREAM, 0)) < 0) { - warning ("cannot create network socket: %s", strerror(errno)); - return (-1); - } - if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { - warning ("cannot set socket options (reuseaddr): %s", strerror(errno)); - close (sock); - return (-2); - } -#ifdef SO_NOSIGPIPE - if (setsockopt (sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) { - warning ("cannot set socket options (nosigpipe): %s", strerror(errno)); - close (sock); - return (-2); - } -#endif - - /* Give the socket a name. */ - name.sin_family = AF_INET; - name.sin_port = htons (port); - - /* Propagate 'bindto' if given. */ - if (ipaddr && *ipaddr && strcmp(ipaddr, "any")) { - msg ("creating socket for IP address '%s'", ipaddr); - if ( (name.sin_addr.s_addr = inet_addr (ipaddr)) == INADDR_NONE ) { - close (sock); - error ("cannot convert address '%s' to network bytes", ipaddr); - } - /* - if (!inet_aton (ipaddr, &name.sin_addr.s_addr)) { - close (sock); - error ("Cannnot assign bindto address '%s'", ipaddr); - } - */ - } else { - msg ("creating socket for all interfaces"); - name.sin_addr.s_addr = htonl (INADDR_ANY); - } - - /* Finally, bind the socket. */ - if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { - warning ("Cannot bind socket to port %d: %s", - port, strerror(errno)); - close (sock); - return (-3); - } - - return (sock); -} diff --git a/src/lib/markactivity.c b/src/lib/markactivity.c @@ -1,147 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void mark_activity (double nbytes, double nsec, - Backendavail newstate) { - double llval; - double dlval; - unsigned multiplier; - int i, marked_unavailable = 0; - - /* If we had a signal somewhere, don't update the values. - * If we don't have a back end yet, don't update the values. */ - if (interrupted || current_backend == -1) - return; - - /* Update values. */ - lock_reporter(); - - /* If the new activity is a failure: increase fails count. - * Upon other state, set fails count to zero. */ - if (newstate == st_unavailable && - servicereport->backendstate[current_backend].actual_avail != - st_unavailable && - program_stage != stage_retrying) { - servicereport->backendstate[current_backend].fail++; - msg ("Back end %d's fail count increased to %d", - current_backend, - servicereport->backendstate[current_backend].fail); - } else if (newstate == st_available) - servicereport->backendstate[current_backend].fail = 0; - - if (program_stage != stage_retrying) { - /* Handle the decays of all other back ends */ - for (i = 0; i < activeservice->nbackend; i++) { - if (i != current_backend && activeservice->backend[i].decay) { - if (activeservice->dispatchover) { - servicereport->backendstate[i].avg_nbytes *= - (100 - activeservice->backend[i].decay); - servicereport->backendstate[i].avg_nbytes /= 100; - servicereport->backendstate[i].avg_nsec *= - (100 - activeservice->backend[i].decay); - servicereport->backendstate[i].avg_nsec /= 100; - } else { - servicereport->backendstate[i].nbytes *= - (100 - activeservice->backend[i].decay); - servicereport->backendstate[i].nbytes /= 100; - servicereport->backendstate[i].nsec *= - (100 - activeservice->backend[i].decay); - servicereport->backendstate[i].nsec /= 100; - } - } - } - - /* Update average seconds and bytes. */ - if (activeservice->dispatchover) { - multiplier = activeservice->dispatchover - 1; - if (servicereport->backendstate[current_backend].totuses < - multiplier) - multiplier = - servicereport->backendstate[current_backend].totuses; - - dlval = servicereport->backendstate[current_backend].avg_nsec; - dlval *= multiplier; - dlval += nsec; - dlval /= (multiplier + 1); - servicereport->backendstate[current_backend].avg_nsec = dlval; - - llval = servicereport->backendstate[current_backend].avg_nbytes; - llval *= multiplier; - llval += nbytes; - llval /= (multiplier + 1); - servicereport->backendstate[current_backend].avg_nbytes = llval; - } - - /* Update total secs / bytes. */ - servicereport->backendstate[current_backend].nsec += nsec; - servicereport->backendstate[current_backend].nbytes += nbytes; - if (newstate == st_unavailable) - servicereport->backendstate[current_backend].failures++; - } - - /* Set the state, unless it's already marked for wakeup or brought down. - * Do this only for 'final' states, not for intermediate. */ - if (servicereport->backendstate[current_backend].actual_avail - != st_waking && - servicereport->backendstate[current_backend].actual_avail - != st_down) { - /* When trying to mark as unavailable, only proceed when we - * actually exceed the retries setting. */ - if (newstate == st_unavailable) { - if (servicereport->backendstate[current_backend].fail >= - (unsigned) activeservice->backend[current_backend].retries) { - msg ("backend %d: %d fails, %d retries, going offline", - current_backend, - servicereport->backendstate[current_backend].fail, - activeservice->backend[current_backend].retries); - servicereport->backendstate[current_backend].fail = 0; - servicereport->backendstate[current_backend].actual_avail = - st_unavailable; - marked_unavailable++; - } - servicereport->backendstate[current_backend].totuses++; - } else if (newstate != st_intermediate) { - /* When applying any other state than intermediate, it's OK */ - servicereport->backendstate[current_backend].fail = 0; - servicereport->backendstate[current_backend].actual_avail = - newstate; - servicereport->backendstate[current_backend].totuses++; - } - } - - /* Don't update servicereport beyond this point! */ - unlock_reporter(); - - /* Give some feedback (though not during intermediate updates) */ - if (newstate != st_intermediate && program_stage != stage_retrying) - msg ("updated stats for backend %d (%s): " - "hits=%lu, " - "totfails=%lu, secs=%g, avgsecs=%g, " - "bytes=%g, avgbytes=%lu, state=%s", - current_backend, activeservice->backend[current_backend].name, - servicereport->backendstate[current_backend].totuses, - servicereport->backendstate[current_backend].failures, - servicereport->backendstate[current_backend].nsec, - servicereport->backendstate[current_backend].avg_nsec, - servicereport->backendstate[current_backend].nbytes, - servicereport->backendstate[current_backend].avg_nbytes, - state_to_string - (servicereport->backendstate[current_backend].actual_avail)); - - /* Run the onfailure hook if one is specified. */ - if (marked_unavailable && program_stage != stage_retrying) { - msg ("extcmd-onfail: " - " current_backend = %d, clients = %d, totclients = %d, cmd = %s", - current_backend, - servicereport->backendstate[current_backend].nclients, - servicereport->nclients, - activeservice->backend[current_backend].onfail); - - sysrun (activeservice->backend[current_backend].onfail); - } -} diff --git a/src/lib/msg.c b/src/lib/msg.c @@ -1,32 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void msg (char const *fmt, ...) { - va_list args; - char *str; - - if (!flag_verbose || !fmt || !*fmt) - return; - va_start (args, fmt); - str = str_vprintf (fmt, args); - if (!daemonized) { - if (activeservice) - fprintf (stderr, "INFO: Service %s: %s\n", activeservice->name, - str); - else - fprintf (stderr, "INFO: Crossroads: %s\n", str); - } else { - if (activeservice) - writelog (LOG_NOTICE, "INFO: Service %s: %s", - activeservice->name, str); - else - writelog (LOG_NOTICE, "INFO: Crossroads: %s", str); - } - free (str); - va_end (args); -} diff --git a/src/lib/msgdumpbuf.c b/src/lib/msgdumpbuf.c @@ -1,48 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void msgdumpbuf (unsigned char const *buf, int buflen) { - char - hexbuf[80] = { 0 }, - disp[17] = { 0 }, - tmp[5]; - int i, j, n = 0; - - if (! flag_verbose) - return; - - for (i = 0; i < buflen; i++) { - if (! n) - snprintf (hexbuf, 80, "%8.8x ", i); - - snprintf (tmp, 5, " %2.2x", buf[i]); - strlcat (hexbuf, tmp, 80); - - disp[n] = isprint (buf[i]) ? buf[i] : '.'; - disp[n + 1] = 0; - - if (++n == 16) { - strlcat (hexbuf, " ", 80); - strlcat (hexbuf, disp, 80); - msg ("Message hexdump: %s", hexbuf); - n = 0; - disp[0] = 0; - hexbuf[0] = 0; - } - } - - if (n < 16 - 1) { - for (j = n; j < 16; j++) - strlcat (hexbuf, " ", 80); - strlcat (hexbuf, " ", 80); - strlcat (hexbuf, disp, 80); - msg ("Message hex end: %s", hexbuf); - } - else - msg ("Message hex ends"); -} - diff --git a/src/lib/netallocbufs.c b/src/lib/netallocbufs.c @@ -1,18 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void net_allocbufs () { - if (!clbuf) { - clbuf = xmalloc (opt.tcp_bufsz + 1); - clbuf[opt.tcp_bufsz] = 0; - srbuf = xmalloc (opt.tcp_bufsz + 1); - srbuf[opt.tcp_bufsz] = 0; - clbufmax = srbufmax = clbufpos = srbufpos = 0; - msg ("Allocated client and server buffers"); - } -} diff --git a/src/lib/netbuffer.c b/src/lib/netbuffer.c @@ -1,31 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -unsigned char *net_buffer (CopyDirection dir, unsigned *sz) { - if (dir == dir_server_to_client) { - if (srbufmax) { - *sz = srbufmax - srbufpos; - msg ("%d bytes still in server buffer", *sz); - srbufmax = 0; - return (srbuf + srbufpos); - } else { - msg ("server buffer is empty"); - return (0); - } - } else { - if (clbufmax) { - *sz = clbufmax - clbufpos; - msg ("%d bytes still in client buffer", *sz); - clbufmax = 0; - return (clbuf + clbufpos); - } else { - msg ("client buffer is empty"); - return (0); - } - } -} diff --git a/src/lib/netbufread.c b/src/lib/netbufread.c @@ -1,54 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -unsigned char *net_bufread (int sock, int max, int *nread, - int is_client) { - unsigned char *buf, *ret; - int *bufpos, *bufmax, rest; - - /* Make sure we have the buffers. */ - net_allocbufs(); - - /* Set our own ptrs */ - if (is_client) { - buf = clbuf; - bufpos = &clbufpos; - bufmax = &clbufmax; - } else { - buf = srbuf; - bufpos = &srbufpos; - bufmax = &srbufmax; - } - - /* If buffer is empty, do an actual read */ - if (! *bufmax) { - *bufmax = net_read (sock, opt.tcp_bufsz, buf, is_client); - msg ("Got %d bytes from %s", *bufmax, is_client ? "client" : "server"); - *bufpos = 0; - } - - /* Got buffer contents. How much do we return? */ - rest = *bufmax - *bufpos; - - if (rest > max) { - /* Caller wants less than what we have in cache. */ - if (nread) - *nread = max; - ret = buf + *bufpos; - *bufpos += max; - } else { - /* Caller wants more than what we have in cache. - * We'll return what we got. */ - if (nread) - *nread = rest; - ret = buf + *bufpos; - *bufmax = *bufpos = 0; - } - - return (ret); -} diff --git a/src/lib/netcopy.c b/src/lib/netcopy.c @@ -1,111 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -int net_copy (int cl, int sr, int max, unsigned char *buf) { - fd_set readset, exset; - struct timeval tv, *tvp, tv1, tv2; - int nfd; - int nread, nwritten, totwritten; - CopyDirection dir; - double microsec; - - /* Prepare select() sets */ - FD_ZERO (&readset); - FD_ZERO (&exset); - - FD_SET (cl, &readset); - FD_SET (cl, &exset); - FD_SET (sr, &readset); - FD_SET (sr, &exset); - - /* Prepare timout state */ - if (activeservice->connectiontimeout) { - tv.tv_sec = activeservice->connectiontimeout; - tv.tv_usec = 0; - tvp = &tv; - } else - tvp = 0; - - /* Wait for a socket to become readable */ - gettimeofday (&tv1, 0); - nfd = select (FD_SETSIZE, &readset, 0, &exset, tvp); - if (nfd < 1) { - decr_client_count(); - log_activity_end(); - mark_activity (0, 0, st_available); - error ("network copy stopped after timeout"); - } - - /* Check for exceptions. */ - if (FD_ISSET (cl, &exset)) { - decr_client_count(); - log_activity_end(); - error ("client exception"); - } - if (FD_ISSET (sr, &exset)) { - decr_client_count(); - log_activity_end(); - mark_activity (0, 0, st_unavailable); - error ("server exception"); - } - - /* Do the read. */ - if (FD_ISSET (sr, &readset)) - dir = dir_server_to_client; - else - dir = dir_client_to_server; - - nread = read (dir == dir_client_to_server ? cl : sr, buf, max); - if (nread < 1) { - decr_client_count(); - log_activity_end(); - if (nread < 0) { - /* An exception on the network socket. If this is the server - * that's sending, then we issue an error message. - * If this is the client, then we'll just issue a verbose - * message; clients are notorious for just dropping dead. */ - if (dir == dir_server_to_client) - error ("Read error from server"); - else { - msg ("Read error from client"); - exit (0); - } - } else { - msg ("%s signals end of data", - dir == dir_client_to_server ? "client" : "server"); - exit (0); - } - } - - /* Update thruput / traffic logs. */ - trafficlog (buf, nread, dir); - thruputlog (buf, nread, dir); - - /* Write to dest. */ - totwritten = 0; - while (totwritten < nread) { - nwritten = write (dir == dir_client_to_server ? sr : cl, - buf + totwritten, - nread - totwritten); - if (nwritten < 1) { - decr_client_count(); - log_activity_end(); - warning ("write error when sending data to %s", - dir == dir_client_to_server ? "server" : "client"); - exit (0); - } - totwritten += nwritten; - } - - gettimeofday (&tv2, 0); - microsec = ( (tv2.tv_sec * 1000000 + tv2.tv_usec) - - (tv1.tv_sec * 1000000 + tv1.tv_usec) ); - - mark_activity (totwritten, microsec / 1000000, st_intermediate); - - return (nread); -} diff --git a/src/lib/netread.c b/src/lib/netread.c @@ -1,101 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* For special-extra verbosity: add "verbose yes" to crossroads.conf - * and undefine the following line. */ -/* #define DEBUG */ - -#ifdef DEBUG -#define LOGGIT msg ("back end %d", current_backend) -#else -#define LOGGIT -#endif - -int net_read (int sock, int max, unsigned char *buf, int is_client) { - fd_set readset, exset; - struct timeval tv, *tvp, tv1, tv2; - int nfd; - int nread; - CopyDirection dir; - double microsec; - char *desc; - - desc = is_client ? "client" : "server"; - dir = is_client ? dir_client_to_server : dir_server_to_client; - - msg ("fetching atmost %u bytes from %s", max, desc); - - /* Prepare select() sets */ - FD_ZERO (&readset); - FD_ZERO (&exset); - FD_SET (sock, &readset); - FD_SET (sock, &exset); - - /* Prepare timout state */ - if (activeservice->connectiontimeout) { - tv.tv_sec = activeservice->connectiontimeout; - tv.tv_usec = 0; - tvp = &tv; - } else - tvp = 0; - - /* Wait for the socket to become readable */ - gettimeofday (&tv1, 0); - nfd = select (FD_SETSIZE, &readset, 0, &exset, tvp); - if (nfd < 1) { - decr_client_count(); - log_activity_end(); - mark_activity (0, 0, is_client ? st_available : st_unavailable); - if (is_client) - msg ("read from %s timed out", desc); - else - error ("read from %s timed out", desc); - } - - /* Check for exceptions. */ - if (FD_ISSET (sock, &exset)) { - decr_client_count(); - log_activity_end(); - mark_activity (0, 0, is_client ? st_available : st_unavailable); - error ("%s exception", desc); - } - - /* Do the read. */ - nread = read (sock, buf, max); - LOGGIT; - if (nread < 1) { - decr_client_count(); - log_activity_end(); - if (nread < 0) { - /* An exception on the network socket. If this is the server - * that's sending, then we issue an error message. - * If this is the client, then we'll just issue a verbose - * message; clients are notorious for just dropping dead. */ - if (dir == dir_server_to_client) - error ("Read error from server"); - else { - msg ("Read error from client"); - exit (0); - } - } else { - msg ("%s signals end of data", desc); - exit (0); - } - } - - /* Update thruput / traffic logs. */ - trafficlog (buf, nread, dir); - thruputlog (buf, nread, dir); - gettimeofday (&tv2, 0); - microsec = ( (tv2.tv_sec * 1000000 + tv2.tv_usec) - - (tv1.tv_sec * 1000000 + tv1.tv_usec) ); - - mark_activity (nread, microsec / 1000000, st_intermediate); - - return (nread); -} diff --git a/src/lib/netwrite.c b/src/lib/netwrite.c @@ -1,109 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* Define DUMPFILE if you want Crossroads to dump the contents of what's - * written to the client or to the server to - * /tmp/crossroads-sentto{client,server}.dump. This is of course for deep - * mode debugging only. - */ - -/* #define DUMPFILE */ - -int net_write (int sock, unsigned char const *buf, int buflen, - int is_client) { - int ret, nfd; - struct timeval tv, tv1, tv2, *tvp; - double microsec; - fd_set fdset, exset; - - #ifdef DUMPFILE - FILE *f; - char *fname; - - if (is_client) - fname = "/tmp/crossroads-senttobackend.dump"; - else - fname = "/tmp/crossroads-senttoclient.dump"; - if (! (f = fopen (fname, "a"))) - f = fopen (fname, "w"); - if (f) { - fprintf (f, "[0x%x %d bytes] ", buflen, buflen); - fwrite (buf, 1, buflen, f); - fclose (f); - } - #endif - - /* Start timer */ - gettimeofday (&tv1, 0); - - /* Prepare the write. */ - FD_ZERO (&fdset); - FD_ZERO (&exset); - FD_SET (sock, &fdset); - FD_SET (sock, &exset); - if (activeservice->connectiontimeout) { - tv.tv_sec = activeservice->connectiontimeout; - tv.tv_usec = 0; - tvp = &tv; - } else - tvp = 0; - if ( (nfd = select (FD_SETSIZE, 0, &fdset, &exset, tvp)) < 1) { - if (!nfd) { - /* Issue an appropriate warning for TIMEOUTS */ - if (is_client) - warning ("Timeout while writing client on socket %d", sock); - else - warning ("Timeout while writing backend %s on socket %d", - activeservice->backend[current_backend].name, - sock); - } else { - /* Issue an appropriate warning for NETWORK ERRORS */ - if (is_client) - warning ("Select failed while writing client on socket %d", - sock); - else - warning ("Select failed while writing backend %s on socket %d", - activeservice->backend[current_backend].name, - sock); - } - - decr_client_count(); - log_activity_end(); - exit (0); - } - - if (FD_ISSET (sock, &exset)) { - decr_client_count(); - log_activity_end(); - error ("exception on %s network", is_client ? "client" : "server"); - } - - /* We can write! */ - ret = write (sock, buf, buflen); - - /* Check for finished / errors */ - if (ret < 1) { - msg ("failed to send to %s: %s (%d)", - is_client ? "client" : "server", - strerror(errno), ret); - decr_client_count(); - log_activity_end (); - exit (0); - } - - /* Update activity. */ - gettimeofday (&tv2, 0); - microsec = ( (tv2.tv_sec * 1000000 + tv2.tv_usec) - - (tv1.tv_sec * 1000000 + tv1.tv_usec) ); - mark_activity (ret, microsec / 1000000, st_intermediate); - - /* Signal caller how many bytes were written. */ - return (ret); -} - - diff --git a/src/lib/optionsread.c b/src/lib/optionsread.c @@ -1,27 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void options_read (int fd) { - int res; - - if ( (res = read (fd, &opt, sizeof(Options))) != sizeof(Options) ) - error ("Received only %d bytes (out of %d) " - "while reading options on fd %d", - res, (int)sizeof(Options), fd); - -# ifdef DEBUG - printf ("Option log_activity: %d\n", opt.log_activity); - printf ("Option tcp_bufsz: %u\n", opt.tcp_bufsz); - printf ("Option dns_cachettl: %d\n", opt.dns_cachettl); - printf ("Option log_facility: %d\n", opt.log_facility); - printf ("Option shmperm: %d\n", opt.shmperm); - printf ("Option sloppyportbind: %d\n", opt.sloppyportbind); - printf ("Option leave_proctitle: %d\n", opt.leave_proctitle); - printf ("Option log_http_activity: %d\n", opt.log_http_activity); -# endif -} diff --git a/src/lib/optionswrite.c b/src/lib/optionswrite.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void options_write (int fd) { - int res; - - msg ("Writing binary program options to fd %d", fd); - if ( (res = write (fd, &opt, sizeof(Options))) != sizeof(Options) ) - error ("Failed to transmit options, sent %d bytes of %d", - res, (int)sizeof(Options)); -} diff --git a/src/lib/parser.c b/src/lib/parser.c @@ -1,3538 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -/* A Bison parser, made by GNU Bison 2.3. */ - -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Bison version. */ -#define YYBISON_VERSION "2.3" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Using locations. */ -#define YYLSP_NEEDED 0 - - - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - IDENTIFIER = 258, - NUMBER = 259, - STRING = 260, - SERVICE = 261, - PORT = 262, - VERBOSITY = 263, - MAXCONNECTIONS = 264, - TYPE = 265, - ANY = 266, - HTTP = 267, - BACKEND = 268, - SERVER = 269, - BINDTO = 270, - CONNECTIONTIMEOUT = 271, - ON = 272, - OFF = 273, - DISPATCHMODE = 274, - ROUNDROBIN = 275, - RANDOM = 276, - BYDURATION = 277, - BYSIZE = 278, - BYCONNECTIONS = 279, - BYORDER = 280, - BYCLIENTIP = 281, - OVER = 282, - DECAY = 283, - REVIVINGINTERVAL = 284, - CHECKINTERVAL = 285, - RETRIES = 286, - SHMKEY = 287, - WEIGHT = 288, - ONSTART = 289, - ONFAIL = 290, - BACKLOG = 291, - THROUGHPUTLOG = 292, - TRAFFICLOG = 293, - HTTPTIMINGLOG = 294, - STICKYCOOKIE = 295, - ADDCLIENTHEADER = 296, - SETCLIENTHEADER = 297, - APPENDCLIENTHEADER = 298, - ADDSERVERHEADER = 299, - SETSERVERHEADER = 300, - APPENDSERVERHEADER = 301, - ALLOWFROM = 302, - DENYFROM = 303, - ALLOWFILE = 304, - DENYFILE = 305, - EXTERNALHANDLER = 306, - USERACCOUNT = 307, - ONEND = 308, - HEADERINSPECTION = 309, - DEEP = 310, - SHALLOW = 311, - STATE = 312, - AVAILABLE = 313, - UNAVAILABLE = 314, - DOWN = 315, - OPTIONS = 316, - LOGACTIVITY = 317, - TCPBUFFERSIZE = 318, - DNSCACHETTL = 319, - LOGFACILITY = 320, - SHMPERMISSIONS = 321, - SLOPPYPORTBIND = 322, - LEAVEPROCESSTITLE = 323 - }; -#endif -/* Tokens. */ -#define IDENTIFIER 258 -#define NUMBER 259 -#define STRING 260 -#define SERVICE 261 -#define PORT 262 -#define VERBOSITY 263 -#define MAXCONNECTIONS 264 -#define TYPE 265 -#define ANY 266 -#define HTTP 267 -#define BACKEND 268 -#define SERVER 269 -#define BINDTO 270 -#define CONNECTIONTIMEOUT 271 -#define ON 272 -#define OFF 273 -#define DISPATCHMODE 274 -#define ROUNDROBIN 275 -#define RANDOM 276 -#define BYDURATION 277 -#define BYSIZE 278 -#define BYCONNECTIONS 279 -#define BYORDER 280 -#define BYCLIENTIP 281 -#define OVER 282 -#define DECAY 283 -#define REVIVINGINTERVAL 284 -#define CHECKINTERVAL 285 -#define RETRIES 286 -#define SHMKEY 287 -#define WEIGHT 288 -#define ONSTART 289 -#define ONFAIL 290 -#define BACKLOG 291 -#define THROUGHPUTLOG 292 -#define TRAFFICLOG 293 -#define HTTPTIMINGLOG 294 -#define STICKYCOOKIE 295 -#define ADDCLIENTHEADER 296 -#define SETCLIENTHEADER 297 -#define APPENDCLIENTHEADER 298 -#define ADDSERVERHEADER 299 -#define SETSERVERHEADER 300 -#define APPENDSERVERHEADER 301 -#define ALLOWFROM 302 -#define DENYFROM 303 -#define ALLOWFILE 304 -#define DENYFILE 305 -#define EXTERNALHANDLER 306 -#define USERACCOUNT 307 -#define ONEND 308 -#define HEADERINSPECTION 309 -#define DEEP 310 -#define SHALLOW 311 -#define STATE 312 -#define AVAILABLE 313 -#define UNAVAILABLE 314 -#define DOWN 315 -#define OPTIONS 316 -#define LOGACTIVITY 317 -#define TCPBUFFERSIZE 318 -#define DNSCACHETTL 319 -#define LOGFACILITY 320 -#define SHMPERMISSIONS 321 -#define SLOPPYPORTBIND 322 -#define LEAVEPROCESSTITLE 323 - - - - -/* Copy the first part of user declarations. */ -#line 4 "parser.y" - -/* Prologue */ -#include "../crossroads.h" - -#define YYSTYPE Confsets - -/* Static parser vars */ -static int i; /* Loop counter */ -static Backend cur_backend; /* Storage for a handled backend */ -static Service cur_service; /* Storage for a handled service */ - -/* Parser debugging related */ -static void pmsg (char const *fmt, ...) { - va_list args; - - if (parser_debug) { - printf ("PARSER: "); - va_start (args, fmt); - vprintf (fmt, args); - } -} - -/* Conversion of yytext for symbol table */ -#define SYMBOL (symtab_lookup((yytext))) - -/* Error handler for yyparse() */ -static int yyerror (char *msg) { - char *sym; - - if (! yytext || ! *yytext) - sym = "<end of file>"; - else if (isspace (*yytext)) - sym = "<white space>"; - else if (isprint (*yytext)) - sym = yytext; - else - sym = "<nonprintable>"; - - error ("Parse error at %s, line %d, '%s': %s", - parser_filename(), yylineno + 1, - sym, yyerrmsg); -} - -/* Store an encountered string */ -static char *laststr; -static void setlaststr (char const *what) { - free (laststr); - laststr = xstrdup (what); -} - -/* Store an encountered number */ -static int lastnr; -static void setlastnr (char const *what) { - if (what && *what && *what == '0') - lastnr = strtol (what, 0, 8); - else - lastnr = atoi (what); -} - -/* Store an encountered 'over' number */ -static int lastovernr; -static void setlastovernr (char const *what) { - if (what && *what && *what == '0') - lastovernr = strtol (what, 0, 8); - else - lastovernr = atoi(what); -} - -/* Store an encountered 'externalhandler' */ -static char *lastext; -static void setlastext (char const *what) { - free (lastext); - lastext = xstrdup (what); -} - -/* Get the server part from HOSTNAME:PORT in allocated memory */ -static char *serverpart (char const *what) { - char *ret = xstrdup (what); - char *cp; - - if ( (cp = strchr (ret, ':')) ) - *cp = 0; - return (ret); -} - -/* Get the port part from HOSTNAME:PORT */ -static int portpart (char const *what) { - char *cp; - - if ( (cp = strchr (what, ':')) ) - return (atoi (cp + 1)); - return (0); -} - -/* Add a list of IP filters to the allowlist or the denylist */ -static void add_any (char *what, int chain) { - char *item, *last = (char*) add_any; - int result; - - for (item = strtok_r (what, " ", &last); - item; - item = strtok_r (0, " ", &last)) { - if (chain) - result = ipf_add_allow (&cur_service, item); - else - result = ipf_add_deny (&cur_service, item); - if (result) - error ("Bad IP filter specifier '%s' at line %d", - item, yylineno + 1); - } -} -static void add_allowfrom (char *what) { - add_any (what, 1); -} -static void add_denyfrom (char *what) { - add_any (what, 0); -} - -/* Set uid/gid for external commands. */ -static void setuseraccount (char *username) { - struct passwd *pw; - - if (! (pw = getpwnam (username)) ) - error ("Invalid username '%s' at line %d", username, yylineno + 1); - cur_service.uid = pw->pw_uid; - cur_service.gid = pw->pw_gid; -} - - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef int YYSTYPE; -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 -#endif - - - -/* Copy the second part of user declarations. */ - - -/* Line 216 of yacc.c. */ -#line 372 "parser.c" - -#ifdef short -# undef short -#endif - -#ifdef YYTYPE_UINT8 -typedef YYTYPE_UINT8 yytype_uint8; -#else -typedef unsigned char yytype_uint8; -#endif - -#ifdef YYTYPE_INT8 -typedef YYTYPE_INT8 yytype_int8; -#elif (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -typedef signed char yytype_int8; -#else -typedef short int yytype_int8; -#endif - -#ifdef YYTYPE_UINT16 -typedef YYTYPE_UINT16 yytype_uint16; -#else -typedef unsigned short int yytype_uint16; -#endif - -#ifdef YYTYPE_INT16 -typedef YYTYPE_INT16 yytype_int16; -#else -typedef short int yytype_int16; -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned int -# endif -#endif - -#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) - -#ifndef YY_ -# if YYENABLE_NLS -# if ENABLE_NLS -# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) -# endif -# endif -# ifndef YY_ -# define YY_(msgid) msgid -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) -#else -# define YYUSE(e) /* empty */ -#endif - -/* Identity function, used to suppress warnings about constant conditions. */ -#ifndef lint -# define YYID(n) (n) -#else -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static int -YYID (int i) -#else -static int -YYID (i) - int i; -#endif -{ - return i; -} -#endif - -#if ! defined yyoverflow || YYERROR_VERBOSE - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined _STDLIB_H \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ - - -#if (! defined yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - yytype_int16 yyss; - YYSTYPE yyvs; - }; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 10 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 254 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 72 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 110 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 173 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 277 - -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 323 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -static const yytype_uint8 yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 69, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 70, 2, 71, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68 -}; - -#if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const yytype_uint16 yyprhs[] = -{ - 0, 0, 3, 6, 8, 10, 12, 17, 20, 22, - 25, 27, 29, 31, 33, 35, 37, 39, 44, 48, - 52, 56, 60, 65, 70, 76, 79, 82, 85, 87, - 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, - 110, 112, 114, 116, 118, 120, 122, 124, 126, 130, - 134, 137, 140, 143, 148, 150, 152, 157, 159, 161, - 162, 165, 168, 170, 173, 175, 177, 179, 181, 183, - 185, 187, 189, 193, 196, 201, 206, 208, 209, 212, - 216, 220, 224, 228, 232, 235, 237, 239, 243, 246, - 248, 250, 254, 258, 262, 266, 269, 275, 278, 281, - 283, 286, 288, 290, 292, 294, 296, 298, 300, 302, - 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, - 324, 326, 328, 333, 337, 341, 343, 347, 351, 354, - 356, 358, 360, 364, 368, 372, 376, 380, 384, 387, - 390, 394, 397, 401, 405, 409, 413, 417, 421, 424, - 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, - 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, - 447, 450, 451, 452 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yytype_int16 yyrhs[] = -{ - 73, 0, -1, 73, 74, -1, 74, -1, 86, -1, - 75, -1, 61, 177, 76, 178, -1, 76, 77, -1, - 77, -1, 181, 78, -1, 79, -1, 80, -1, 81, - -1, 82, -1, 83, -1, 84, -1, 85, -1, 62, - 165, 98, 96, -1, 63, 95, 96, -1, 64, 95, - 96, -1, 65, 95, 96, -1, 66, 95, 96, -1, - 67, 165, 98, 96, -1, 68, 165, 98, 96, -1, - 87, 88, 177, 89, 178, -1, 161, 6, -1, 169, - 3, -1, 89, 90, -1, 90, -1, 163, 91, -1, - 92, -1, 93, -1, 97, -1, 99, -1, 108, -1, - 109, -1, 112, -1, 113, -1, 114, -1, 115, -1, - 116, -1, 119, -1, 122, -1, 124, -1, 123, -1, - 125, -1, 106, -1, 127, -1, 7, 95, 96, -1, - 15, 94, 96, -1, 171, 5, -1, 159, 4, -1, - 164, 69, -1, 8, 165, 98, 96, -1, 17, -1, - 18, -1, 19, 104, 100, 96, -1, 101, -1, 103, - -1, -1, 27, 102, -1, 159, 4, -1, 146, -1, - 166, 105, -1, 20, -1, 21, -1, 22, -1, 23, - -1, 25, -1, 24, -1, 51, -1, 26, -1, 52, - 107, 96, -1, 175, 5, -1, 29, 95, 110, 96, - -1, 30, 95, 110, 96, -1, 111, -1, -1, 51, - 146, -1, 36, 95, 96, -1, 32, 95, 96, -1, - 16, 95, 96, -1, 9, 95, 96, -1, 10, 117, - 96, -1, 172, 118, -1, 11, -1, 12, -1, 54, - 120, 96, -1, 173, 121, -1, 55, -1, 56, -1, - 47, 126, 96, -1, 48, 126, 96, -1, 49, 147, - 96, -1, 50, 147, 96, -1, 174, 5, -1, 13, - 128, 177, 129, 178, -1, 170, 3, -1, 129, 130, - -1, 130, -1, 162, 131, -1, 132, -1, 92, -1, - 97, -1, 140, -1, 142, -1, 141, -1, 143, -1, - 144, -1, 145, -1, 133, -1, 134, -1, 115, -1, - 148, -1, 150, -1, 151, -1, 152, -1, 153, -1, - 154, -1, 155, -1, 136, -1, 137, -1, 14, 160, - 135, 96, -1, 33, 95, 96, -1, 28, 95, 96, - -1, 5, -1, 31, 95, 96, -1, 57, 138, 96, - -1, 176, 139, -1, 58, -1, 59, -1, 60, -1, - 34, 146, 96, -1, 35, 146, 96, -1, 53, 146, - 96, -1, 38, 147, 96, -1, 37, 147, 96, -1, - 39, 147, 96, -1, 167, 5, -1, 168, 5, -1, - 40, 149, 96, -1, 158, 5, -1, 41, 156, 96, - -1, 42, 156, 96, -1, 43, 156, 96, -1, 44, - 156, 96, -1, 45, 156, 96, -1, 46, 156, 96, - -1, 157, 5, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 179, 70, -1, 180, 71, -1, - -1, -1, -1 -}; - -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const yytype_uint16 yyrline[] = -{ - 0, 161, 161, 164, 168, 170, 174, 181, 184, 188, - 193, 195, 197, 199, 201, 203, 205, 209, 218, 226, - 234, 242, 250, 259, 268, 292, 297, 308, 311, 315, - 320, 326, 332, 338, 348, 356, 363, 369, 375, 381, - 387, 393, 399, 405, 411, 417, 423, 430, 604, 616, - 628, 637, 644, 649, 662, 666, 672, 681, 683, 685, - 691, 708, 715, 725, 734, 738, 742, 746, 750, 754, - 758, 762, 768, 780, 789, 807, 825, 827, 833, 842, - 854, 866, 878, 890, 902, 907, 911, 917, 929, 934, - 938, 944, 956, 968, 980, 992, 1001, 1011, 1023, 1031, - 1037, 1044, 1049, 1054, 1059, 1064, 1069, 1074, 1079, 1084, - 1089, 1094, 1099, 1104, 1109, 1114, 1119, 1124, 1129, 1134, - 1139, 1144, 1151, 1164, 1176, 1188, 1194, 1206, 1218, 1223, - 1227, 1231, 1237, 1249, 1261, 1273, 1285, 1297, 1309, 1318, - 1327, 1339, 1348, 1360, 1372, 1384, 1396, 1408, 1420, 1428, - 1433, 1438, 1443, 1448, 1453, 1458, 1463, 1468, 1473, 1478, - 1483, 1488, 1493, 1498, 1503, 1508, 1513, 1518, 1523, 1529, - 1534, 1538, 1542, 1546 -}; -#endif - -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "$end", "error", "$undefined", "IDENTIFIER", "NUMBER", "STRING", - "SERVICE", "PORT", "VERBOSITY", "MAXCONNECTIONS", "TYPE", "ANY", "HTTP", - "BACKEND", "SERVER", "BINDTO", "CONNECTIONTIMEOUT", "ON", "OFF", - "DISPATCHMODE", "ROUNDROBIN", "RANDOM", "BYDURATION", "BYSIZE", - "BYCONNECTIONS", "BYORDER", "BYCLIENTIP", "OVER", "DECAY", - "REVIVINGINTERVAL", "CHECKINTERVAL", "RETRIES", "SHMKEY", "WEIGHT", - "ONSTART", "ONFAIL", "BACKLOG", "THROUGHPUTLOG", "TRAFFICLOG", - "HTTPTIMINGLOG", "STICKYCOOKIE", "ADDCLIENTHEADER", "SETCLIENTHEADER", - "APPENDCLIENTHEADER", "ADDSERVERHEADER", "SETSERVERHEADER", - "APPENDSERVERHEADER", "ALLOWFROM", "DENYFROM", "ALLOWFILE", "DENYFILE", - "EXTERNALHANDLER", "USERACCOUNT", "ONEND", "HEADERINSPECTION", "DEEP", - "SHALLOW", "STATE", "AVAILABLE", "UNAVAILABLE", "DOWN", "OPTIONS", - "LOGACTIVITY", "TCPBUFFERSIZE", "DNSCACHETTL", "LOGFACILITY", - "SHMPERMISSIONS", "SLOPPYPORTBIND", "LEAVEPROCESSTITLE", "';'", "'{'", - "'}'", "$accept", "input", "element", "option_element", - "option_statements", "option_statement", "optstat", - "logactivity_statement", "tcpbuffersize_statement", - "dnscachettl_statement", "logfacility_statement", - "shmpermissions_statement", "sloppyportbind_statement", - "leaveprocesstitle_statement", "service_element", "service", - "servicename", "servicestatements", "servicestatement", "servicebody", - "portstatement", "bindstatement", "ipaddress", "number", "semicol", - "verbositystatement", "onoff", "dispatchmodestatement", "dispatchtail", - "dispatchover", "overnumber", "dispatchext", "dispatchmethod", - "dispatchmethodspec", "useraccountstatement", "useraccount", - "revivingintervalstatement", "checkintervalstatement", - "opt_externalhandler", "externalhandler", "backlogstatement", - "shmkeystatement", "connectiontimeoutstatement", - "maxconnectionsstatement", "typestatement", "typespec", "typespecifier", - "inspectionstatement", "shallowdeepspec", "shallowdeepspecifier", - "allowfromstatement", "denyfromstatement", "allowfilestatement", - "denyfilestatement", "ipfilters", "backendblock", "backendname", - "backenddefinitions", "backenddefinition", "backendstatement", - "serverstatement", "weightstatement", "decaystatement", "serveraddress", - "retriesstatement", "initialstatestatement", "statedef", "somestate", - "onstartstatement", "onfailstatement", "onendstatement", - "dumptrafficstatement", "throughputstatement", "httptiminglogstatement", - "commandline", "filename", "stickycookiestatement", "cookiespecifier", - "addclientheaderstatement", "setclientheaderstatement", - "appendclientheaderstatement", "addserverheaderstatement", - "setserverheaderstatement", "appendserverheaderstatement", - "headerstring", "headerstring_expected", "cookie_expected", - "number_expected", "serveraddress_expected", "service_expected", - "backendstatement_expected", "servicebody_expected", "semicol_expected", - "onoff_expected", "dispatchmethod_expected", "commandline_expected", - "filename_expected", "servicename_expected", "backendname_expected", - "ipaddress_expected", "type_expected", "shallowdeep_expected", - "ipfilters_expected", "useraccount_expected", "statedef_expected", - "openbrace", "closebrace", "openbrace_expected", "closebrace_expected", - "option_statement_expected", 0 -}; -#endif - -# ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ -static const yytype_uint16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, - 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, - 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 59, - 123, 125 -}; -# endif - -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const yytype_uint8 yyr1[] = -{ - 0, 72, 73, 73, 74, 74, 75, 76, 76, 77, - 78, 78, 78, 78, 78, 78, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, 89, 90, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 92, 93, - 94, 95, 96, 97, 98, 98, 99, 100, 100, 100, - 101, 102, 103, 104, 105, 105, 105, 105, 105, 105, - 105, 105, 106, 107, 108, 109, 110, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 118, 119, 120, 121, - 121, 122, 123, 124, 125, 126, 127, 128, 129, 129, - 130, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 132, 133, 134, 135, 136, 137, 138, 139, - 139, 139, 140, 141, 142, 143, 144, 145, 146, 147, - 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, - 178, 179, 180, 181 -}; - -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const yytype_uint8 yyr2[] = -{ - 0, 2, 2, 1, 1, 1, 4, 2, 1, 2, - 1, 1, 1, 1, 1, 1, 1, 4, 3, 3, - 3, 3, 4, 4, 5, 2, 2, 2, 1, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, - 2, 2, 2, 4, 1, 1, 4, 1, 1, 0, - 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 2, 4, 4, 1, 0, 2, 3, - 3, 3, 3, 3, 2, 1, 1, 3, 2, 1, - 1, 3, 3, 3, 3, 2, 5, 2, 2, 1, - 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 4, 3, 3, 1, 3, 3, 2, 1, - 1, 1, 3, 3, 3, 3, 3, 3, 2, 2, - 3, 2, 3, 3, 3, 3, 3, 3, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 2, 0, 0, 0 -}; - -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ -static const yytype_uint8 yydefact[] = -{ - 153, 171, 153, 3, 5, 4, 161, 0, 173, 0, - 1, 2, 171, 0, 25, 173, 8, 0, 169, 155, - 26, 7, 6, 0, 157, 151, 151, 151, 151, 157, - 157, 9, 10, 11, 12, 13, 14, 15, 16, 155, - 28, 0, 170, 0, 156, 0, 156, 156, 156, 0, - 0, 27, 24, 151, 157, 151, 164, 162, 163, 151, - 158, 151, 151, 151, 151, 166, 166, 160, 160, 167, - 165, 29, 30, 31, 32, 33, 46, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 44, 43, 45, 47, - 54, 55, 156, 18, 0, 51, 19, 20, 21, 156, - 156, 156, 0, 156, 156, 0, 171, 0, 156, 0, - 156, 59, 0, 77, 77, 156, 156, 156, 0, 156, - 156, 0, 156, 156, 0, 156, 0, 17, 52, 22, - 23, 48, 156, 82, 83, 85, 86, 84, 154, 97, - 49, 50, 81, 151, 156, 57, 58, 62, 0, 64, - 65, 66, 67, 69, 68, 71, 70, 63, 159, 156, - 76, 156, 80, 79, 91, 95, 92, 93, 139, 94, - 72, 73, 87, 89, 90, 88, 53, 154, 99, 0, - 60, 0, 56, 138, 78, 74, 75, 98, 96, 152, - 151, 151, 151, 159, 159, 160, 160, 160, 150, 149, - 149, 149, 149, 149, 149, 159, 168, 102, 103, 112, - 100, 101, 110, 111, 120, 121, 104, 106, 105, 107, - 108, 109, 113, 114, 115, 116, 117, 118, 119, 61, - 0, 156, 156, 156, 156, 156, 156, 156, 156, 156, - 0, 156, 0, 156, 156, 156, 156, 156, 156, 156, - 0, 125, 156, 124, 126, 123, 132, 133, 136, 135, - 137, 140, 141, 142, 148, 143, 144, 145, 146, 147, - 134, 127, 129, 130, 131, 128, 122 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int16 yydefgoto[] = -{ - -1, 2, 3, 4, 15, 16, 31, 32, 33, 34, - 35, 36, 37, 38, 5, 6, 12, 39, 40, 71, - 72, 73, 108, 44, 93, 74, 92, 75, 144, 145, - 180, 146, 111, 157, 76, 123, 77, 78, 159, 160, - 79, 80, 81, 82, 83, 104, 137, 84, 125, 175, - 85, 86, 87, 88, 117, 89, 106, 177, 178, 210, - 211, 212, 213, 252, 214, 215, 249, 275, 216, 217, - 218, 219, 220, 221, 147, 120, 222, 239, 223, 224, - 225, 226, 227, 228, 241, 242, 240, 45, 230, 7, - 179, 41, 94, 43, 112, 148, 121, 13, 107, 109, - 105, 126, 118, 124, 250, 8, 22, 9, 23, 17 -}; - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -150 -static const yytype_int16 yypact[] = -{ - -51, -150, 11, -150, -150, -150, -150, 8, -150, -54, - -150, -150, -150, 26, -150, -40, -150, 41, -150, -150, - -150, -150, -150, -36, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -40, - -150, 200, -150, 16, -150, 43, -150, -150, -150, 16, - 16, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -19, -150, -150, -150, -150, -150, - -150, -150, 16, -150, -150, 31, -150, 48, -150, 47, - -150, 10, -3, 12, 12, -150, -150, -150, 56, -150, - -150, 61, -150, -150, 62, -150, 4, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, 63, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -40, -150, 86, - -150, 71, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - 73, -150, -150, -150, -150, -150, -150, -150, -150, -150, - 80, -150, 82, -150, -150, -150, -150, -150, -150, -150, - -32, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int16 yypgoto[] = -{ - -150, -150, 87, -150, -150, 75, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, 49, -150, - -88, -150, -150, -23, -46, -87, -37, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -18, -150, - -150, -150, -150, -82, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, 33, -150, -150, -150, -76, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -149, -61, -150, -150, -150, -150, - -150, -150, -150, -150, -120, -150, -150, -31, -150, -150, - -150, -150, -150, -5, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -4, -33, -150, -150, -150 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -173 -static const yytype_int16 yytable[] = -{ - 96, 97, 98, 46, 47, 48, 52, 122, 19, 184, - 1, 10, 99, 100, 14, -159, 18, 149, 150, 151, - 152, 153, 154, 155, 49, 50, 272, 273, 274, 20, - 101, -172, 103, 90, 91, 42, 110, 143, 113, 114, - 115, 116, 135, 136, 234, 235, 127, 95, 156, 102, - 128, 139, 141, 129, 130, 131, 248, 133, 134, 173, - 174, 165, 140, 158, 142, 132, 168, 171, 183, 162, - 163, 164, 1, 166, 167, 229, 169, 170, 251, 172, - 243, 244, 245, 246, 247, 262, 176, 264, 51, 11, - 21, 207, 208, 53, 54, 55, 161, 209, 182, 119, - 189, 187, 138, 24, 25, 26, 27, 28, 29, 30, - 0, 0, 181, 185, 190, 186, 0, 191, 0, 192, - 193, 194, 0, 195, 196, 197, 198, 199, 200, 201, - 202, 203, 204, 0, 236, 237, 238, 0, 0, 205, - 0, 0, 0, 206, 188, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 231, 232, 233, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 253, 254, 255, 256, 257, - 258, 259, 260, 261, 0, 263, 0, 265, 266, 267, - 268, 269, 270, 271, 0, 0, 276, 53, 54, 55, - 56, 0, 0, 57, 0, 58, 59, 0, 0, 60, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, - 62, 0, 63, 0, 0, 0, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 65, 66, 67, - 68, 0, 69, 0, 70 -}; - -static const yytype_int16 yycheck[] = -{ - 46, 47, 48, 26, 27, 28, 39, 68, 12, 158, - 61, 0, 49, 50, 6, 5, 70, 20, 21, 22, - 23, 24, 25, 26, 29, 30, 58, 59, 60, 3, - 53, 71, 55, 17, 18, 71, 59, 27, 61, 62, - 63, 64, 11, 12, 193, 194, 92, 4, 51, 54, - 69, 3, 5, 99, 100, 101, 205, 103, 104, 55, - 56, 5, 108, 51, 110, 102, 5, 5, 5, 115, - 116, 117, 61, 119, 120, 4, 122, 123, 5, 125, - 200, 201, 202, 203, 204, 5, 132, 5, 39, 2, - 15, 179, 179, 7, 8, 9, 114, 179, 144, 66, - 14, 177, 106, 62, 63, 64, 65, 66, 67, 68, - -1, -1, 143, 159, 28, 161, -1, 31, -1, 33, - 34, 35, -1, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, -1, 195, 196, 197, -1, -1, 53, - -1, -1, -1, 57, 177, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 190, 191, 192, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 231, 232, 233, 234, 235, - 236, 237, 238, 239, -1, 241, -1, 243, 244, 245, - 246, 247, 248, 249, -1, -1, 252, 7, 8, 9, - 10, -1, -1, 13, -1, 15, 16, -1, -1, 19, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 29, - 30, -1, 32, -1, -1, -1, 36, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 47, 48, 49, - 50, -1, 52, -1, 54 -}; - -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const yytype_uint8 yystos[] = -{ - 0, 61, 73, 74, 75, 86, 87, 161, 177, 179, - 0, 74, 88, 169, 6, 76, 77, 181, 70, 177, - 3, 77, 178, 180, 62, 63, 64, 65, 66, 67, - 68, 78, 79, 80, 81, 82, 83, 84, 85, 89, - 90, 163, 71, 165, 95, 159, 95, 95, 95, 165, - 165, 90, 178, 7, 8, 9, 10, 13, 15, 16, - 19, 29, 30, 32, 36, 47, 48, 49, 50, 52, - 54, 91, 92, 93, 97, 99, 106, 108, 109, 112, - 113, 114, 115, 116, 119, 122, 123, 124, 125, 127, - 17, 18, 98, 96, 164, 4, 96, 96, 96, 98, - 98, 95, 165, 95, 117, 172, 128, 170, 94, 171, - 95, 104, 166, 95, 95, 95, 95, 126, 174, 126, - 147, 168, 147, 107, 175, 120, 173, 96, 69, 96, - 96, 96, 98, 96, 96, 11, 12, 118, 177, 3, - 96, 5, 96, 27, 100, 101, 103, 146, 167, 20, - 21, 22, 23, 24, 25, 26, 51, 105, 51, 110, - 111, 110, 96, 96, 96, 5, 96, 96, 5, 96, - 96, 5, 96, 55, 56, 121, 96, 129, 130, 162, - 102, 159, 96, 5, 146, 96, 96, 130, 178, 14, - 28, 31, 33, 34, 35, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 53, 57, 92, 97, 115, - 131, 132, 133, 134, 136, 137, 140, 141, 142, 143, - 144, 145, 148, 150, 151, 152, 153, 154, 155, 4, - 160, 95, 95, 95, 146, 146, 147, 147, 147, 149, - 158, 156, 157, 156, 156, 156, 156, 156, 146, 138, - 176, 5, 135, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 5, 96, 5, 96, 96, 96, 96, 96, - 96, 96, 58, 59, 60, 139, 96 -}; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab - - -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ - -#define YYFAIL goto yyerrlab - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (YYID (0)) - - -#define YYTERROR 1 -#define YYERRCODE 256 - - -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - -#ifndef YY_LOCATION_PRINT -# if YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif -#endif - - -/* YYLEX -- calling `yylex' with the right arguments. */ - -#ifdef YYLEX_PARAM -# define YYLEX yylex (YYLEX_PARAM) -#else -# define YYLEX yylex () -#endif - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (YYID (0)) - -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Type, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (YYID (0)) - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_value_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif -{ - if (!yyvaluep) - return; -# ifdef YYPRINT - if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# else - YYUSE (yyoutput); -# endif - switch (yytype) - { - default: - break; - } -} - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif -{ - if (yytype < YYNTOKENS) - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); - - yy_symbol_value_print (yyoutput, yytype, yyvaluep); - YYFPRINTF (yyoutput, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) -#else -static void -yy_stack_print (bottom, top) - yytype_int16 *bottom; - yytype_int16 *top; -#endif -{ - YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (YYID (0)) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_reduce_print (YYSTYPE *yyvsp, int yyrule) -#else -static void -yy_reduce_print (yyvsp, yyrule) - YYSTYPE *yyvsp; - int yyrule; -#endif -{ - int yynrhs = yyr2[yyrule]; - int yyi; - unsigned long int yylno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - fprintf (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], - &(yyvsp[(yyi + 1) - (yynrhs)]) - ); - fprintf (stderr, "\n"); - } -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyvsp, Rule); \ -} while (YYID (0)) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - - -#if YYERROR_VERBOSE - -# ifndef yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static YYSIZE_T -yystrlen (const char *yystr) -#else -static YYSIZE_T -yystrlen (yystr) - const char *yystr; -#endif -{ - YYSIZE_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) - continue; - return yylen; -} -# endif -# endif - -# ifndef yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static char * -yystpcpy (char *yydest, const char *yysrc) -#else -static char * -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -#endif -{ - char *yyd = yydest; - const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -# endif - -# ifndef yytnamerr -/* Copy to YYRES the contents of YYSTR after stripping away unnecessary - quotes and backslashes, so that it's suitable for yyerror. The - heuristic is that double-quoting is unnecessary unless the string - contains an apostrophe, a comma, or backslash (other than - backslash-backslash). YYSTR is taken from yytname. If YYRES is - null, do not copy; instead, return the length of what the result - would have been. */ -static YYSIZE_T -yytnamerr (char *yyres, const char *yystr) -{ - if (*yystr == '"') - { - YYSIZE_T yyn = 0; - char const *yyp = yystr; - - for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; - - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; - - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } - do_not_strip_quotes: ; - } - - if (! yyres) - return yystrlen (yystr); - - return yystpcpy (yyres, yystr) - yyres; -} -# endif - -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; - - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } - - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - - if (yysize_overflow) - return YYSIZE_MAXIMUM; - - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; - } -} -#endif /* YYERROR_VERBOSE */ - - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) -#else -static void -yydestruct (yymsg, yytype, yyvaluep) - const char *yymsg; - int yytype; - YYSTYPE *yyvaluep; -#endif -{ - YYUSE (yyvaluep); - - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - - switch (yytype) - { - - default: - break; - } -} - - -/* Prevent warnings from -Wmissing-prototypes. */ - -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ - - - -/* The look-ahead symbol. */ -int yychar; - -/* The semantic value of the look-ahead symbol. */ -YYSTYPE yylval; - -/* Number of syntax errors so far. */ -int yynerrs; - - - -/*----------. -| yyparse. | -`----------*/ - -#ifdef YYPARSE_PARAM -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -yyparse (void *YYPARSE_PARAM) -#else -int -yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -#endif -#else /* ! YYPARSE_PARAM */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -yyparse (void) -#else -int -yyparse () - -#endif -#endif -{ - - int yystate; - int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ - int yytoken = 0; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif - - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss = yyssa; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp; - - - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - YYSIZE_T yystacksize = YYINITDEPTH; - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - - yyssp = yyss; - yyvsp = yyvs; - - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; - - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyexhaustedlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yytype_int16 *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - - /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to look-ahead token. */ - yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) - goto yydefault; - - /* Not known => get a look-ahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; - } - - if (yychar <= YYEOF) - { - yychar = yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yytoken = YYTRANSLATE (yychar); - YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - if (yyn == YYFINAL) - YYACCEPT; - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the look-ahead token. */ - YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - - /* Discard the shifted token unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; - - yystate = yyn; - *++yyvsp = yylval; - - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 17: -#line 212 "parser.y" - { - opt.log_activity = lastnr; - ;} - break; - - case 18: -#line 220 "parser.y" - { - opt.tcp_bufsz = lastnr; - ;} - break; - - case 19: -#line 228 "parser.y" - { - opt.dns_cachettl = lastnr; - ;} - break; - - case 20: -#line 236 "parser.y" - { - opt.log_facility = lastnr; - ;} - break; - - case 21: -#line 244 "parser.y" - { - opt.shmperm = lastnr; - ;} - break; - - case 22: -#line 253 "parser.y" - { - opt.sloppyportbind = lastnr; - ;} - break; - - case 23: -#line 262 "parser.y" - { - opt.leave_proctitle = lastnr; - ;} - break; - - case 24: -#line 272 "parser.y" - { - /* Check integrity, supply defaults */ - check_service (cur_service); - - if (!cur_service.shmkey) - cur_service.shmkey = (unsigned) hashpjw (cur_service.name); - msg ("Service %s: shared memory key set to 0x%8.8x", - cur_service.name, cur_service.shmkey); - - if (!cur_service.check_cmd) - cur_service.check_cmd = xstrdup (""); - - /* Add to the list, reset cur_service for next run */ - service = xrealloc (service, ++nservice * sizeof(Service)); - service[nservice - 1] = cur_service; - memset (&cur_service, 0, sizeof(Service)); - ;} - break; - - case 26: -#line 298 "parser.y" - { - pmsg ("service [%s]\n", SYMBOL); - for (i = 0; i < nservice; i++) - if (!strcmp (service[i].name, SYMBOL)) - error ("Duplicate definition of service '%s'", SYMBOL); - cur_service.name = xstrdup(SYMBOL); - ;} - break; - - case 30: -#line 320 "parser.y" - { - pmsg ("sevice port [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.port = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 31: -#line 326 "parser.y" - { - pmsg ("service binding [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - cur_service.bind = (yyvsp[(1) - (1)]).set[0].v.sval; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 32: -#line 332 "parser.y" - { - pmsg ("service verbosity [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.verbosity = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 33: -#line 338 "parser.y" - { - pmsg ("service dispatch mode [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - pmsg ("service dispatch over [%d]\n", lastovernr); - pmsg ("service dispatch exthandler [%s]\n", lastext); - cur_service.dispatchtype = (yyvsp[(1) - (1)]).set[0].v.ival; - cur_service.dispatchover = lastovernr; - cur_service.dispatchext = xstrdup(lastext); - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 34: -#line 348 "parser.y" - { - pmsg ("service revival interval [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - pmsg ("service revival cmd [%s]\n", (yyvsp[(1) - (1)]).set[1].v.sval); - cur_service.rev_interval = (yyvsp[(1) - (1)]).set[0].v.ival; - cur_service.check_cmd = (yyvsp[(1) - (1)]).set[1].v.sval; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 35: -#line 356 "parser.y" - { - pmsg ("service check interval [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - pmsg ("service check cmd [%s]\n", (yyvsp[(1) - (1)]).set[1].v.sval); - cur_service.check_interval = (yyvsp[(1) - (1)]).set[0].v.ival; - cur_service.check_cmd = (yyvsp[(1) - (1)]).set[1].v.sval; - ;} - break; - - case 36: -#line 363 "parser.y" - { - pmsg ("service backlog [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.backlog = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 37: -#line 369 "parser.y" - { - pmsg ("service shmkey [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.shmkey = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 38: -#line 375 "parser.y" - { - pmsg ("connection timout [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.connectiontimeout = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 39: -#line 381 "parser.y" - { - pmsg ("max clients in service [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.maxconnections = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 40: -#line 387 "parser.y" - { - pmsg ("service type [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.type = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 41: -#line 393 "parser.y" - { - pmsg ("service header inspection [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - cur_service.inspection = (yyvsp[(1) - (1)]).set[0].v.ival; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 42: -#line 399 "parser.y" - { - pmsg ("allow from [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - add_allowfrom ((yyvsp[(1) - (1)]).set[0].v.sval); - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 43: -#line 405 "parser.y" - { - pmsg ("allow file [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - cur_service.allowfile = (yyvsp[(1) - (1)]).set[0].v.sval; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 44: -#line 411 "parser.y" - { - pmsg ("deny from [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - add_denyfrom ((yyvsp[(1) - (1)]).set[0].v.sval); - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 45: -#line 417 "parser.y" - { - pmsg ("deny file [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - cur_service.denyfile = (yyvsp[(1) - (1)]).set[0].v.sval; - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 46: -#line 423 "parser.y" - { - pmsg ("user account [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - setuseraccount ((yyvsp[(1) - (1)]).set[0].v.sval); - free ((yyvsp[(1) - (1)]).set[0].v.sval); - free ((yyvsp[(1) - (1)]).set); - ;} - break; - - case 47: -#line 430 "parser.y" - { - pmsg ("converting backend statements, count is [%d]\n", (yyvsp[(1) - (1)]).n); - for (i = 0; i < (yyvsp[(1) - (1)]).n; i++) - switch ((yyvsp[(1) - (1)]).set[i].cf) { - case cf_portspec: - pmsg ("backend port [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.initial_port = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - case cf_serverspec: - pmsg ("backend server [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.initial_server = serverpart ((yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.initial_port = portpart ((yyvsp[(1) - (1)]).set[i].v.sval); - free ((yyvsp[(1) - (1)]).set[i].v.sval); - break; - case cf_verbosityspec: - pmsg ("backend verbosity [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.verbosity = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - case cf_onstartspec: - pmsg ("backend onstart [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.onstart = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_onfailspec: - pmsg ("backend onfail [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.onfail = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_onendspec: - pmsg ("backend onend [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.onend = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_dumpspec: - pmsg ("backend trafficlog [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.dumpfile = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_thruspec: - pmsg ("backend throughputlog [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.thruputfile = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_httptimingspec: - if (cur_service.type != type_http) - error ("'httptiminglog' only allowed in 'type http' " - "service definitions"); - pmsg ("backend http timing log [%s]\n", (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.timinglog = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_weightspec: - pmsg ("backend weight [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.weight = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - case cf_decayspec: - pmsg ("backend decay [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - if ((yyvsp[(1) - (1)]).set[i].v.ival >= 100) - error ("Decay specifier %d must be a percentage, " - "never more than 99", - (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.decay = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - case cf_maxconnectionsspec: - pmsg ("backend max clients [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.maxconnections = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - case cf_stickycookiespec: - pmsg ("backend sticky cookie [%s]\n", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.stickycookie = (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_addclientheaderspec: - pmsg ("client header to add [%s]\n", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.addclientheader = - xrealloc (cur_backend.addclientheader, - (cur_backend.naddclientheader + 1) * - sizeof(char*)); - cur_backend.addclientheader - [cur_backend.naddclientheader++] = - (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_setclientheaderspec: - pmsg ("client header to set [%s]\n", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.setclientheader = - xrealloc (cur_backend.setclientheader, - (cur_backend.nsetclientheader + 1) * - sizeof(char*)); - cur_backend.setclientheader - [cur_backend.nsetclientheader++] = - (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_appendclientheaderspec: - pmsg ("client header to append [%s]\n", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.appendclientheader = - xrealloc (cur_backend.appendclientheader, - (cur_backend.nappendclientheader + 1) * - sizeof(char*)); - cur_backend.appendclientheader - [cur_backend.nappendclientheader++] = - (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_addserverheaderspec: - pmsg ("server header to add [%s]\n", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.addserverheader = - xrealloc (cur_backend.addserverheader, - (cur_backend.naddserverheader + 1) * - sizeof(char*)); - cur_backend.addserverheader - [cur_backend.naddserverheader++] = - (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_setserverheaderspec: - pmsg ("server header to set [%s]\n", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.setserverheader = - xrealloc (cur_backend.setserverheader, - (cur_backend.nsetserverheader + 1) * - sizeof(char*)); - cur_backend.setserverheader - [cur_backend.nsetserverheader++] = - (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_appendserverheaderspec: - pmsg ("server header to append:", - (yyvsp[(1) - (1)]).set[i].v.sval); - cur_backend.appendserverheader = - xrealloc (cur_backend.appendserverheader, - (cur_backend.nappendserverheader + 1) * - sizeof(char*)); - cur_backend.appendserverheader - [cur_backend.nappendserverheader++] = - (yyvsp[(1) - (1)]).set[i].v.sval; - break; - case cf_retriesspec: - pmsg ("backend retries [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.retries = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - case cf_statespec: - pmsg ("backend initial state [%d]\n", (yyvsp[(1) - (1)]).set[i].v.ival); - cur_backend.initial_avail = (yyvsp[(1) - (1)]).set[i].v.ival; - break; - default: - error ("Internal jam, unhandled type %d " - "in backend specification", - (yyvsp[(1) - (1)]).set[i].cf); - } - free ((yyvsp[(1) - (1)]).set); - - /* Verify the backend block, supply defaults, - * And so on. - */ - if (!cur_backend.initial_port) - error ("Service %s: back end %s lacks port", - cur_service.name, cur_backend.name); - if (cur_service.dispatchext && cur_backend.maxconnections) - error ("Service %s: back end %s defines maxconnections, " - "but an external dispatcher is in effect", - cur_service.name, cur_backend.name); - if (cur_backend.weight < 1) - cur_backend.weight = 1; - if (cur_backend.retries < 1) - cur_backend.retries = 1; - - /* Add to the list. */ - cur_service.backend = xrealloc (cur_service.backend, - ++cur_service.nbackend * - sizeof(Backend)); - cur_service.backend[cur_service.nbackend - 1] = - cur_backend; - pmsg ("this was backend definition [%d]\n", cur_service.nbackend); - memset (&cur_backend, 0, sizeof(cur_backend)); - ;} - break; - - case 48: -#line 606 "parser.y" - { - pmsg ("port statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_portspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 49: -#line 618 "parser.y" - { - pmsg ("bindto statement [%d]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_bindspec; - (yyval).set[0].v.sval = xstrdup(laststr); - ;} - break; - - case 50: -#line 629 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 51: -#line 638 "parser.y" - { - setlastnr (SYMBOL); - ;} - break; - - case 53: -#line 652 "parser.y" - { - pmsg ("verbosity statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_verbosityspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 54: -#line 662 "parser.y" - { - lastnr = 1; - ;} - break; - - case 55: -#line 666 "parser.y" - { - lastnr = 0; - ;} - break; - - case 56: -#line 675 "parser.y" - { - (yyval) = (yyvsp[(2) - (4)]); - ;} - break; - - case 59: -#line 685 "parser.y" - { - (yyval).n = 0; - ;} - break; - - case 60: -#line 692 "parser.y" - { - pmsg ("dispatch mode statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_dispatchspec; - (yyval).set[0].v.ival = lastnr; - - if (lastovernr && - (lastnr != ds_bysize && lastnr != ds_byduration)) - error ("Service '%s': this dispatch mode " - "doesn't support 'over <connections>'", - cur_service.name); - ;} - break; - - case 61: -#line 709 "parser.y" - { - setlastovernr (SYMBOL); - ;} - break; - - case 62: -#line 715 "parser.y" - { - pmsg ("external handler [%s]\n", laststr); - if (lastnr != ds_externalhandler) - error ("Service %s: this dispatch mode doesn't support " - "an external handler", cur_service.name); - setlastext (laststr); - ;} - break; - - case 63: -#line 726 "parser.y" - { - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 64: -#line 734 "parser.y" - { - lastnr = ds_roundrobin; - ;} - break; - - case 65: -#line 738 "parser.y" - { - lastnr = ds_random; - ;} - break; - - case 66: -#line 742 "parser.y" - { - lastnr = ds_byduration; - ;} - break; - - case 67: -#line 746 "parser.y" - { - lastnr = ds_bysize; - ;} - break; - - case 68: -#line 750 "parser.y" - { - lastnr = ds_byorder; - ;} - break; - - case 69: -#line 754 "parser.y" - { - lastnr = ds_byconnections; - ;} - break; - - case 70: -#line 758 "parser.y" - { - lastnr = ds_externalhandler; - ;} - break; - - case 71: -#line 762 "parser.y" - { - lastnr = ds_byclientip; - ;} - break; - - case 72: -#line 770 "parser.y" - { - pmsg ("user account statement [%d]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_useraccountspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 73: -#line 781 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 74: -#line 792 "parser.y" - { - pmsg ("reviving interval statement [%d]\n", lastnr); - (yyval).n = 2; - (yyval).set = xmalloc (2 * sizeof(Confset)); - (yyval).set[0].cf = cf_revivespec; - (yyval).set[0].v.ival = lastnr; - if ((yyvsp[(3) - (4)]).n) { - pmsg ("reviving externalhandler [%s]\n", (yyvsp[(3) - (4)]).set[0].v.sval); - (yyval).set[1].v.sval = (yyvsp[(3) - (4)]).set[0].v.sval; - } else - (yyval).set[1].v.sval = 0; - ;} - break; - - case 75: -#line 810 "parser.y" - { - pmsg ("check interval [%d]\n", lastnr); - (yyval).n = 2; - (yyval).set = xmalloc (2 * sizeof(Confset)); - (yyval).set[0].cf = cf_checkspec; - (yyval).set[0].v.ival = lastnr; - if ((yyvsp[(3) - (4)]).n) { - pmsg ("check externalhandler [%s]\n", (yyvsp[(3) - (4)]).set[0].v.sval); - (yyval).set[1].v.sval = (yyvsp[(3) - (4)]).set[0].v.sval; - } else - (yyval).set[1].v.sval = 0; - ;} - break; - - case 77: -#line 827 "parser.y" - { - (yyval).n = 0; - ;} - break; - - case 78: -#line 834 "parser.y" - { - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 79: -#line 844 "parser.y" - { - pmsg ("backlog statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_backlogspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 80: -#line 856 "parser.y" - { - pmsg ("shmkey statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_shmkeyspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 81: -#line 868 "parser.y" - { - pmsg ("connection timeout statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_connectiontimeoutspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 82: -#line 880 "parser.y" - { - pmsg ("max clients statement (service) [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_maxconnectionsspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 83: -#line 892 "parser.y" - { - pmsg ("service type [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_typespec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 85: -#line 907 "parser.y" - { - lastnr = type_any; - ;} - break; - - case 86: -#line 911 "parser.y" - { - lastnr = type_http; - ;} - break; - - case 87: -#line 919 "parser.y" - { - pmsg ("service header inspection [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_inspectionspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 89: -#line 934 "parser.y" - { - lastnr = ins_deep; - ;} - break; - - case 90: -#line 938 "parser.y" - { - lastnr = ins_shallow; - ;} - break; - - case 91: -#line 946 "parser.y" - { - pmsg ("allow from [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_allowfromspec; - (yyval).set[0].v.sval = xstrdup(laststr); - ;} - break; - - case 92: -#line 958 "parser.y" - { - pmsg ("deny from [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_denyfromspec; - (yyval).set[0].v.sval = xstrdup(laststr); - ;} - break; - - case 93: -#line 970 "parser.y" - { - pmsg ("allow file [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_allowfilespec; - (yyval).set[0].v.sval = xstrdup(laststr); - ;} - break; - - case 94: -#line 982 "parser.y" - { - pmsg ("deny file [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_allowfilespec; - (yyval).set[0].v.sval = xstrdup(laststr); - ;} - break; - - case 95: -#line 993 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 96: -#line 1005 "parser.y" - { - (yyval) = (yyvsp[(4) - (5)]); - ;} - break; - - case 97: -#line 1012 "parser.y" - { - pmsg ("backend name [%s]\n", SYMBOL); - for (i = 0; i < cur_service.nbackend; i++) - if (!strcmp (cur_service.backend[i].name, SYMBOL)) - error ("Service %s: back end '%s' multiply defined", - cur_service.name, SYMBOL); - cur_backend.name = xstrdup (SYMBOL); - ;} - break; - - case 98: -#line 1024 "parser.y" - { - (yyvsp[(1) - (2)]).n++; - (yyvsp[(1) - (2)]).set = xrealloc ((yyvsp[(1) - (2)]).set, (yyvsp[(1) - (2)]).n * sizeof(Confset)); - (yyvsp[(1) - (2)]).set[(yyvsp[(1) - (2)]).n - 1] = (yyvsp[(2) - (2)]).set[0]; - (yyval) = (yyvsp[(1) - (2)]); - ;} - break; - - case 99: -#line 1031 "parser.y" - { - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 100: -#line 1038 "parser.y" - { - (yyval) = (yyvsp[(2) - (2)]); - ;} - break; - - case 101: -#line 1044 "parser.y" - { - pmsg ("backend server [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 102: -#line 1049 "parser.y" - { - pmsg ("backend port [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 103: -#line 1054 "parser.y" - { - pmsg ("backend verbosity [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 104: -#line 1059 "parser.y" - { - pmsg ("backend onstart [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 105: -#line 1064 "parser.y" - { - pmsg ("backend onend [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 106: -#line 1069 "parser.y" - { - pmsg ("backend onfail [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 107: -#line 1074 "parser.y" - { - pmsg ("backend trafficlog [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 108: -#line 1079 "parser.y" - { - pmsg ("backend trafficlog [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 109: -#line 1084 "parser.y" - { - pmsg ("backend HTTP timing log [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 110: -#line 1089 "parser.y" - { - pmsg ("backend weight [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 111: -#line 1094 "parser.y" - { - pmsg ("backend decay [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 112: -#line 1099 "parser.y" - { - pmsg ("backend maxconnections [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 113: -#line 1104 "parser.y" - { - pmsg ("backend sticky cookie [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 114: -#line 1109 "parser.y" - { - pmsg ("addclientheader [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 115: -#line 1114 "parser.y" - { - pmsg ("setclientheader [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 116: -#line 1119 "parser.y" - { - pmsg ("appendclientheader [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 117: -#line 1124 "parser.y" - { - pmsg ("addserverheader [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 118: -#line 1129 "parser.y" - { - pmsg ("setserverheader [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 119: -#line 1134 "parser.y" - { - pmsg ("appendserverheader [%s]\n", (yyvsp[(1) - (1)]).set[0].v.sval); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 120: -#line 1139 "parser.y" - { - pmsg ("backend retries [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 121: -#line 1144 "parser.y" - { - pmsg ("backend state [%d]\n", (yyvsp[(1) - (1)]).set[0].v.ival); - (yyval) = (yyvsp[(1) - (1)]); - ;} - break; - - case 122: -#line 1154 "parser.y" - { - pmsg ("server statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_serverspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 123: -#line 1166 "parser.y" - { - pmsg ("weight statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_weightspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 124: -#line 1178 "parser.y" - { - pmsg ("decay statement [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_decayspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 125: -#line 1188 "parser.y" - { - setlaststr (laststring); - ;} - break; - - case 126: -#line 1196 "parser.y" - { - pmsg ("retries [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_retriesspec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 127: -#line 1208 "parser.y" - { - pmsg ("state [%d]\n", lastnr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_statespec; - (yyval).set[0].v.ival = lastnr; - ;} - break; - - case 129: -#line 1223 "parser.y" - { - lastnr = st_available; - ;} - break; - - case 130: -#line 1227 "parser.y" - { - lastnr = st_unavailable; - ;} - break; - - case 131: -#line 1231 "parser.y" - { - lastnr = st_down; - ;} - break; - - case 132: -#line 1239 "parser.y" - { - pmsg ("onstart statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_onstartspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 133: -#line 1251 "parser.y" - { - pmsg ("onfail statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_onfailspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 134: -#line 1263 "parser.y" - { - pmsg ("onend statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_onendspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 135: -#line 1275 "parser.y" - { - pmsg ("trafficlog statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_dumpspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 136: -#line 1287 "parser.y" - { - pmsg ("throughputlog statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_thruspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 137: -#line 1299 "parser.y" - { - pmsg ("httptiminglog statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_httptimingspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 138: -#line 1310 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 139: -#line 1319 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 140: -#line 1329 "parser.y" - { - pmsg ("insertcookie statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof (Confset)); - (yyval).set[0].cf = cf_stickycookiespec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 141: -#line 1340 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 142: -#line 1350 "parser.y" - { - pmsg ("addclientheader statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_addclientheaderspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 143: -#line 1362 "parser.y" - { - pmsg ("setclientheader statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_setclientheaderspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 144: -#line 1374 "parser.y" - { - pmsg ("appendclientheader statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_appendclientheaderspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 145: -#line 1386 "parser.y" - { - pmsg ("addserverheader statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_addserverheaderspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 146: -#line 1398 "parser.y" - { - pmsg ("setserverheader statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_setserverheaderspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 147: -#line 1410 "parser.y" - { - pmsg ("appendserverheader statement [%s]\n", laststr); - (yyval).n = 1; - (yyval).set = xmalloc (sizeof(Confset)); - (yyval).set[0].cf = cf_appendserverheaderspec; - (yyval).set[0].v.sval = xstrdup (laststr); - ;} - break; - - case 148: -#line 1421 "parser.y" - { - setlaststr (laststring); - free (laststring); - laststring = 0; - ;} - break; - - case 149: -#line 1428 "parser.y" - { - yyerrmsg = "HTTP header specifier expected"; -;} - break; - - case 150: -#line 1433 "parser.y" - { - yyerrmsg = "cookie specifier expected"; -;} - break; - - case 151: -#line 1438 "parser.y" - { - yyerrmsg = "number expected"; -;} - break; - - case 152: -#line 1443 "parser.y" - { - yyerrmsg = "hostname or IP address expected"; -;} - break; - - case 153: -#line 1448 "parser.y" - { - yyerrmsg = "'service' expected"; -;} - break; - - case 154: -#line 1453 "parser.y" - { - yyerrmsg = "backend definition statement expected"; -;} - break; - - case 155: -#line 1458 "parser.y" - { - yyerrmsg = "service body statement expected"; -;} - break; - - case 156: -#line 1463 "parser.y" - { - yyerrmsg = "semicolon (;) expected"; -;} - break; - - case 157: -#line 1468 "parser.y" - { - yyerrmsg = "'on' or 'off' expetcted"; -;} - break; - - case 158: -#line 1473 "parser.y" - { - yyerrmsg = "dispatch method expected"; -;} - break; - - case 159: -#line 1478 "parser.y" - { - yyerrmsg = "command line expected"; -;} - break; - - case 160: -#line 1483 "parser.y" - { - yyerrmsg = "file name expected"; -;} - break; - - case 161: -#line 1488 "parser.y" - { - yyerrmsg = "service name (identifier) expected"; -;} - break; - - case 162: -#line 1493 "parser.y" - { - yyerrmsg = "backend name (identifier) expected"; -;} - break; - - case 163: -#line 1498 "parser.y" - { - yyerrmsg = "IP address such as 1.2.3.4 or 'any' expected"; -;} - break; - - case 164: -#line 1503 "parser.y" - { - yyerrmsg = "Service type expected ('any', 'stickyhttp', ...)"; -;} - break; - - case 165: -#line 1508 "parser.y" - { - yyerrmsg = "Header inspection mode mode expected ('shallow' or 'deep')"; -;} - break; - - case 166: -#line 1513 "parser.y" - { - yyerrmsg = "IP filter(s) expected"; -;} - break; - - case 167: -#line 1518 "parser.y" - { - yyerrmsg = "username expected"; -;} - break; - - case 168: -#line 1523 "parser.y" - { - yyerrmsg = "state definition expected"; -;} - break; - - case 171: -#line 1538 "parser.y" - { - yyerrmsg = "'{' expected"; -;} - break; - - case 172: -#line 1542 "parser.y" - { - yyerrmsg = "'}' expected"; -;} - break; - - case 173: -#line 1546 "parser.y" - { - yyerrmsg = "option statement (logactivity, tcpbuffersize, ...) expected"; -;} - break; - - -/* Line 1267 of yacc.c. */ -#line 3320 "parser.c" - default: break; - } - YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); - - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - - *++yyvsp = yyval; - - - /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; - - goto yynewstate; - - -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ -yyerrlab: - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; -#if ! YYERROR_VERBOSE - yyerror (YY_("syntax error")); -#else - { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } - } -#endif - } - - - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse look-ahead token after an - error, discard it. */ - - if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } - else - { - yydestruct ("Error: discarding", - yytoken, &yylval); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse look-ahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - - /* Pacify compilers like GCC when the user code never invokes - YYERROR and the label yyerrorlab therefore never appears in user - code. */ - if (/*CONSTCOND*/ 0) - goto yyerrorlab; - - /* Do not reclaim the symbols of the rule which action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - - yydestruct ("Error: popping", - yystos[yystate], yyvsp); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - - if (yyn == YYFINAL) - YYACCEPT; - - *++yyvsp = yylval; - - - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; - -#ifndef yyoverflow -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ -yyexhaustedlab: - yyerror (YY_("memory exhausted")); - yyresult = 2; - /* Fall through. */ -#endif - -yyreturn: - if (yychar != YYEOF && yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - /* Do not reclaim the symbols of the rule which action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", - yystos[*yyssp], yyvsp); - YYPOPSTACK (1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif -#if YYERROR_VERBOSE - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); -#endif - /* Make sure YYID is used. */ - return YYID (yyresult); -} - - - diff --git a/src/lib/parser.h b/src/lib/parser.h @@ -1,189 +0,0 @@ -/* A Bison parser, made by GNU Bison 2.3. */ - -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - IDENTIFIER = 258, - NUMBER = 259, - STRING = 260, - SERVICE = 261, - PORT = 262, - VERBOSITY = 263, - MAXCONNECTIONS = 264, - TYPE = 265, - ANY = 266, - HTTP = 267, - BACKEND = 268, - SERVER = 269, - BINDTO = 270, - CONNECTIONTIMEOUT = 271, - ON = 272, - OFF = 273, - DISPATCHMODE = 274, - ROUNDROBIN = 275, - RANDOM = 276, - BYDURATION = 277, - BYSIZE = 278, - BYCONNECTIONS = 279, - BYORDER = 280, - BYCLIENTIP = 281, - OVER = 282, - DECAY = 283, - REVIVINGINTERVAL = 284, - CHECKINTERVAL = 285, - RETRIES = 286, - SHMKEY = 287, - WEIGHT = 288, - ONSTART = 289, - ONFAIL = 290, - BACKLOG = 291, - THROUGHPUTLOG = 292, - TRAFFICLOG = 293, - HTTPTIMINGLOG = 294, - STICKYCOOKIE = 295, - ADDCLIENTHEADER = 296, - SETCLIENTHEADER = 297, - APPENDCLIENTHEADER = 298, - ADDSERVERHEADER = 299, - SETSERVERHEADER = 300, - APPENDSERVERHEADER = 301, - ALLOWFROM = 302, - DENYFROM = 303, - ALLOWFILE = 304, - DENYFILE = 305, - EXTERNALHANDLER = 306, - USERACCOUNT = 307, - ONEND = 308, - HEADERINSPECTION = 309, - DEEP = 310, - SHALLOW = 311, - STATE = 312, - AVAILABLE = 313, - UNAVAILABLE = 314, - DOWN = 315, - OPTIONS = 316, - LOGACTIVITY = 317, - TCPBUFFERSIZE = 318, - DNSCACHETTL = 319, - LOGFACILITY = 320, - SHMPERMISSIONS = 321, - SLOPPYPORTBIND = 322, - LEAVEPROCESSTITLE = 323 - }; -#endif -/* Tokens. */ -#define IDENTIFIER 258 -#define NUMBER 259 -#define STRING 260 -#define SERVICE 261 -#define PORT 262 -#define VERBOSITY 263 -#define MAXCONNECTIONS 264 -#define TYPE 265 -#define ANY 266 -#define HTTP 267 -#define BACKEND 268 -#define SERVER 269 -#define BINDTO 270 -#define CONNECTIONTIMEOUT 271 -#define ON 272 -#define OFF 273 -#define DISPATCHMODE 274 -#define ROUNDROBIN 275 -#define RANDOM 276 -#define BYDURATION 277 -#define BYSIZE 278 -#define BYCONNECTIONS 279 -#define BYORDER 280 -#define BYCLIENTIP 281 -#define OVER 282 -#define DECAY 283 -#define REVIVINGINTERVAL 284 -#define CHECKINTERVAL 285 -#define RETRIES 286 -#define SHMKEY 287 -#define WEIGHT 288 -#define ONSTART 289 -#define ONFAIL 290 -#define BACKLOG 291 -#define THROUGHPUTLOG 292 -#define TRAFFICLOG 293 -#define HTTPTIMINGLOG 294 -#define STICKYCOOKIE 295 -#define ADDCLIENTHEADER 296 -#define SETCLIENTHEADER 297 -#define APPENDCLIENTHEADER 298 -#define ADDSERVERHEADER 299 -#define SETSERVERHEADER 300 -#define APPENDSERVERHEADER 301 -#define ALLOWFROM 302 -#define DENYFROM 303 -#define ALLOWFILE 304 -#define DENYFILE 305 -#define EXTERNALHANDLER 306 -#define USERACCOUNT 307 -#define ONEND 308 -#define HEADERINSPECTION 309 -#define DEEP 310 -#define SHALLOW 311 -#define STATE 312 -#define AVAILABLE 313 -#define UNAVAILABLE 314 -#define DOWN 315 -#define OPTIONS 316 -#define LOGACTIVITY 317 -#define TCPBUFFERSIZE 318 -#define DNSCACHETTL 319 -#define LOGFACILITY 320 -#define SHMPERMISSIONS 321 -#define SLOPPYPORTBIND 322 -#define LEAVEPROCESSTITLE 323 - - - - -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef int YYSTYPE; -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 -#endif - -extern YYSTYPE yylval; - diff --git a/src/lib/parser.y b/src/lib/parser.y @@ -1,1548 +0,0 @@ - -/* Parser of crossroads configuration files */ - -%{ -/* Prologue */ -#include "../crossroads.h" - -#define YYSTYPE Confsets - -/* Static parser vars */ -static int i; /* Loop counter */ -static Backend cur_backend; /* Storage for a handled backend */ -static Service cur_service; /* Storage for a handled service */ - -/* Parser debugging related */ -static void pmsg (char const *fmt, ...) { - va_list args; - - if (parser_debug) { - printf ("PARSER: "); - va_start (args, fmt); - vprintf (fmt, args); - } -} - -/* Conversion of yytext for symbol table */ -#define SYMBOL (symtab_lookup((yytext))) - -/* Error handler for yyparse() */ -static int yyerror (char *msg) { - char *sym; - - if (! yytext || ! *yytext) - sym = "<end of file>"; - else if (isspace (*yytext)) - sym = "<white space>"; - else if (isprint (*yytext)) - sym = yytext; - else - sym = "<nonprintable>"; - - error ("Parse error at %s, line %d, '%s': %s", - parser_filename(), yylineno + 1, - sym, yyerrmsg); -} - -/* Store an encountered string */ -static char *laststr; -static void setlaststr (char const *what) { - free (laststr); - laststr = xstrdup (what); -} - -/* Store an encountered number */ -static int lastnr; -static void setlastnr (char const *what) { - if (what && *what && *what == '0') - lastnr = strtol (what, 0, 8); - else - lastnr = atoi (what); -} - -/* Store an encountered 'over' number */ -static int lastovernr; -static void setlastovernr (char const *what) { - if (what && *what && *what == '0') - lastovernr = strtol (what, 0, 8); - else - lastovernr = atoi(what); -} - -/* Store an encountered 'externalhandler' */ -static char *lastext; -static void setlastext (char const *what) { - free (lastext); - lastext = xstrdup (what); -} - -/* Get the server part from HOSTNAME:PORT in allocated memory */ -static char *serverpart (char const *what) { - char *ret = xstrdup (what); - char *cp; - - if ( (cp = strchr (ret, ':')) ) - *cp = 0; - return (ret); -} - -/* Get the port part from HOSTNAME:PORT */ -static int portpart (char const *what) { - char *cp; - - if ( (cp = strchr (what, ':')) ) - return (atoi (cp + 1)); - return (0); -} - -/* Add a list of IP filters to the allowlist or the denylist */ -static void add_any (char *what, int chain) { - char *item, *last = (char*) add_any; - int result; - - for (item = strtok_r (what, " ", &last); - item; - item = strtok_r (0, " ", &last)) { - if (chain) - result = ipf_add_allow (&cur_service, item); - else - result = ipf_add_deny (&cur_service, item); - if (result) - error ("Bad IP filter specifier '%s' at line %d", - item, yylineno + 1); - } -} -static void add_allowfrom (char *what) { - add_any (what, 1); -} -static void add_denyfrom (char *what) { - add_any (what, 0); -} - -/* Set uid/gid for external commands. */ -static void setuseraccount (char *username) { - struct passwd *pw; - - if (! (pw = getpwnam (username)) ) - error ("Invalid username '%s' at line %d", username, yylineno + 1); - cur_service.uid = pw->pw_uid; - cur_service.gid = pw->pw_gid; -} - -%} - -/* Tokens: the meta tokens */ -%token IDENTIFIER NUMBER STRING - - /* Tokens: the literal ones (these go automatically into the docs) */ -%token SERVICE PORT VERBOSITY MAXCONNECTIONS - TYPE ANY HTTP - BACKEND SERVER BINDTO CONNECTIONTIMEOUT - ON OFF - DISPATCHMODE ROUNDROBIN RANDOM BYDURATION BYSIZE BYCONNECTIONS - BYORDER BYCLIENTIP - OVER DECAY - REVIVINGINTERVAL CHECKINTERVAL RETRIES SHMKEY WEIGHT - ONSTART ONFAIL BACKLOG - THROUGHPUTLOG TRAFFICLOG HTTPTIMINGLOG - STICKYCOOKIE ADDCLIENTHEADER SETCLIENTHEADER APPENDCLIENTHEADER - ADDSERVERHEADER SETSERVERHEADER APPENDSERVERHEADER - ALLOWFROM DENYFROM ALLOWFILE DENYFILE - EXTERNALHANDLER USERACCOUNT ONEND - HEADERINSPECTION DEEP SHALLOW - STATE AVAILABLE UNAVAILABLE DOWN - OPTIONS LOGACTIVITY TCPBUFFERSIZE DNSCACHETTL LOGFACILITY - SHMPERMISSIONS SLOPPYPORTBIND LEAVEPROCESSTITLE - -%% -/* Config file grammar rules */ - -input: - input - element -| - element -; - -element: - service_element -| - option_element -; - -option_element: - OPTIONS - openbrace - option_statements - closebrace -; - -option_statements: - option_statements - option_statement -| - option_statement -; - -option_statement: - option_statement_expected - optstat -; - -optstat: - logactivity_statement -| - tcpbuffersize_statement -| - dnscachettl_statement -| - logfacility_statement -| - shmpermissions_statement -| - sloppyportbind_statement -| - leaveprocesstitle_statement -; - -logactivity_statement: - LOGACTIVITY - onoff_expected - onoff - semicol { - opt.log_activity = lastnr; - } -; - -tcpbuffersize_statement: - TCPBUFFERSIZE - number - semicol { - opt.tcp_bufsz = lastnr; - } -; - -dnscachettl_statement: - DNSCACHETTL - number - semicol { - opt.dns_cachettl = lastnr; - } -; - -logfacility_statement: - LOGFACILITY - number - semicol { - opt.log_facility = lastnr; - } -; - -shmpermissions_statement: - SHMPERMISSIONS - number - semicol { - opt.shmperm = lastnr; - } -; - -sloppyportbind_statement: - SLOPPYPORTBIND - onoff_expected - onoff - semicol { - opt.sloppyportbind = lastnr; - } -; - -leaveprocesstitle_statement: - LEAVEPROCESSTITLE - onoff_expected - onoff - semicol { - opt.leave_proctitle = lastnr; - } -; - -service_element: - service - servicename - openbrace - servicestatements - closebrace { - /* Check integrity, supply defaults */ - check_service (cur_service); - - if (!cur_service.shmkey) - cur_service.shmkey = (unsigned) hashpjw (cur_service.name); - msg ("Service %s: shared memory key set to 0x%8.8x", - cur_service.name, cur_service.shmkey); - - if (!cur_service.check_cmd) - cur_service.check_cmd = xstrdup (""); - - /* Add to the list, reset cur_service for next run */ - service = xrealloc (service, ++nservice * sizeof(Service)); - service[nservice - 1] = cur_service; - memset (&cur_service, 0, sizeof(Service)); - } -; - -service: - service_expected - SERVICE -; - -servicename: - servicename_expected - IDENTIFIER { - pmsg ("service [%s]\n", SYMBOL); - for (i = 0; i < nservice; i++) - if (!strcmp (service[i].name, SYMBOL)) - error ("Duplicate definition of service '%s'", SYMBOL); - cur_service.name = xstrdup(SYMBOL); - } -; - -servicestatements: - servicestatements - servicestatement -| - servicestatement -; - -servicestatement: - servicebody_expected - servicebody -; - -servicebody: - portstatement { - pmsg ("sevice port [%d]\n", $1.set[0].v.ival); - cur_service.port = $1.set[0].v.ival; - free ($1.set); - } -| - bindstatement { - pmsg ("service binding [%s]\n", $1.set[0].v.sval); - cur_service.bind = $1.set[0].v.sval; - free ($1.set); - } -| - verbositystatement { - pmsg ("service verbosity [%d]\n", $1.set[0].v.ival); - cur_service.verbosity = $1.set[0].v.ival; - free ($1.set); - } -| - dispatchmodestatement { - pmsg ("service dispatch mode [%d]\n", $1.set[0].v.ival); - pmsg ("service dispatch over [%d]\n", lastovernr); - pmsg ("service dispatch exthandler [%s]\n", lastext); - cur_service.dispatchtype = $1.set[0].v.ival; - cur_service.dispatchover = lastovernr; - cur_service.dispatchext = xstrdup(lastext); - free ($1.set); - } -| - revivingintervalstatement { - pmsg ("service revival interval [%d]\n", $1.set[0].v.ival); - pmsg ("service revival cmd [%s]\n", $1.set[1].v.sval); - cur_service.rev_interval = $1.set[0].v.ival; - cur_service.check_cmd = $1.set[1].v.sval; - free ($1.set); - } -| - checkintervalstatement { - pmsg ("service check interval [%d]\n", $1.set[0].v.ival); - pmsg ("service check cmd [%s]\n", $1.set[1].v.sval); - cur_service.check_interval = $1.set[0].v.ival; - cur_service.check_cmd = $1.set[1].v.sval; - } -| - backlogstatement { - pmsg ("service backlog [%d]\n", $1.set[0].v.ival); - cur_service.backlog = $1.set[0].v.ival; - free ($1.set); - } -| - shmkeystatement { - pmsg ("service shmkey [%d]\n", $1.set[0].v.ival); - cur_service.shmkey = $1.set[0].v.ival; - free ($1.set); - } -| - connectiontimeoutstatement { - pmsg ("connection timout [%d]\n", $1.set[0].v.ival); - cur_service.connectiontimeout = $1.set[0].v.ival; - free ($1.set); - } -| - maxconnectionsstatement { - pmsg ("max clients in service [%d]\n", $1.set[0].v.ival); - cur_service.maxconnections = $1.set[0].v.ival; - free ($1.set); - } -| - typestatement { - pmsg ("service type [%d]\n", $1.set[0].v.ival); - cur_service.type = $1.set[0].v.ival; - free ($1.set); - } -| - inspectionstatement { - pmsg ("service header inspection [%d]\n", $1.set[0].v.ival); - cur_service.inspection = $1.set[0].v.ival; - free ($1.set); - } -| - allowfromstatement { - pmsg ("allow from [%s]\n", $1.set[0].v.sval); - add_allowfrom ($1.set[0].v.sval); - free ($1.set); - } -| - allowfilestatement { - pmsg ("allow file [%s]\n", $1.set[0].v.sval); - cur_service.allowfile = $1.set[0].v.sval; - free ($1.set); - } -| - denyfromstatement { - pmsg ("deny from [%s]\n", $1.set[0].v.sval); - add_denyfrom ($1.set[0].v.sval); - free ($1.set); - } -| - denyfilestatement { - pmsg ("deny file [%s]\n", $1.set[0].v.sval); - cur_service.denyfile = $1.set[0].v.sval; - free ($1.set); - } -| - useraccountstatement { - pmsg ("user account [%s]\n", $1.set[0].v.sval); - setuseraccount ($1.set[0].v.sval); - free ($1.set[0].v.sval); - free ($1.set); - } -| - backendblock { - pmsg ("converting backend statements, count is [%d]\n", $1.n); - for (i = 0; i < $1.n; i++) - switch ($1.set[i].cf) { - case cf_portspec: - pmsg ("backend port [%d]\n", $1.set[i].v.ival); - cur_backend.initial_port = $1.set[i].v.ival; - break; - case cf_serverspec: - pmsg ("backend server [%s]\n", $1.set[i].v.sval); - cur_backend.initial_server = serverpart ($1.set[i].v.sval); - cur_backend.initial_port = portpart ($1.set[i].v.sval); - free ($1.set[i].v.sval); - break; - case cf_verbosityspec: - pmsg ("backend verbosity [%d]\n", $1.set[i].v.ival); - cur_backend.verbosity = $1.set[i].v.ival; - break; - case cf_onstartspec: - pmsg ("backend onstart [%s]\n", $1.set[i].v.sval); - cur_backend.onstart = $1.set[i].v.sval; - break; - case cf_onfailspec: - pmsg ("backend onfail [%s]\n", $1.set[i].v.sval); - cur_backend.onfail = $1.set[i].v.sval; - break; - case cf_onendspec: - pmsg ("backend onend [%s]\n", $1.set[i].v.sval); - cur_backend.onend = $1.set[i].v.sval; - break; - case cf_dumpspec: - pmsg ("backend trafficlog [%s]\n", $1.set[i].v.sval); - cur_backend.dumpfile = $1.set[i].v.sval; - break; - case cf_thruspec: - pmsg ("backend throughputlog [%s]\n", $1.set[i].v.sval); - cur_backend.thruputfile = $1.set[i].v.sval; - break; - case cf_httptimingspec: - if (cur_service.type != type_http) - error ("'httptiminglog' only allowed in 'type http' " - "service definitions"); - pmsg ("backend http timing log [%s]\n", $1.set[i].v.sval); - cur_backend.timinglog = $1.set[i].v.sval; - break; - case cf_weightspec: - pmsg ("backend weight [%d]\n", $1.set[i].v.ival); - cur_backend.weight = $1.set[i].v.ival; - break; - case cf_decayspec: - pmsg ("backend decay [%d]\n", $1.set[i].v.ival); - if ($1.set[i].v.ival >= 100) - error ("Decay specifier %d must be a percentage, " - "never more than 99", - $1.set[i].v.ival); - cur_backend.decay = $1.set[i].v.ival; - break; - case cf_maxconnectionsspec: - pmsg ("backend max clients [%d]\n", $1.set[i].v.ival); - cur_backend.maxconnections = $1.set[i].v.ival; - break; - case cf_stickycookiespec: - pmsg ("backend sticky cookie [%s]\n", - $1.set[i].v.sval); - cur_backend.stickycookie = $1.set[i].v.sval; - break; - case cf_addclientheaderspec: - pmsg ("client header to add [%s]\n", - $1.set[i].v.sval); - cur_backend.addclientheader = - xrealloc (cur_backend.addclientheader, - (cur_backend.naddclientheader + 1) * - sizeof(char*)); - cur_backend.addclientheader - [cur_backend.naddclientheader++] = - $1.set[i].v.sval; - break; - case cf_setclientheaderspec: - pmsg ("client header to set [%s]\n", - $1.set[i].v.sval); - cur_backend.setclientheader = - xrealloc (cur_backend.setclientheader, - (cur_backend.nsetclientheader + 1) * - sizeof(char*)); - cur_backend.setclientheader - [cur_backend.nsetclientheader++] = - $1.set[i].v.sval; - break; - case cf_appendclientheaderspec: - pmsg ("client header to append [%s]\n", - $1.set[i].v.sval); - cur_backend.appendclientheader = - xrealloc (cur_backend.appendclientheader, - (cur_backend.nappendclientheader + 1) * - sizeof(char*)); - cur_backend.appendclientheader - [cur_backend.nappendclientheader++] = - $1.set[i].v.sval; - break; - case cf_addserverheaderspec: - pmsg ("server header to add [%s]\n", - $1.set[i].v.sval); - cur_backend.addserverheader = - xrealloc (cur_backend.addserverheader, - (cur_backend.naddserverheader + 1) * - sizeof(char*)); - cur_backend.addserverheader - [cur_backend.naddserverheader++] = - $1.set[i].v.sval; - break; - case cf_setserverheaderspec: - pmsg ("server header to set [%s]\n", - $1.set[i].v.sval); - cur_backend.setserverheader = - xrealloc (cur_backend.setserverheader, - (cur_backend.nsetserverheader + 1) * - sizeof(char*)); - cur_backend.setserverheader - [cur_backend.nsetserverheader++] = - $1.set[i].v.sval; - break; - case cf_appendserverheaderspec: - pmsg ("server header to append:", - $1.set[i].v.sval); - cur_backend.appendserverheader = - xrealloc (cur_backend.appendserverheader, - (cur_backend.nappendserverheader + 1) * - sizeof(char*)); - cur_backend.appendserverheader - [cur_backend.nappendserverheader++] = - $1.set[i].v.sval; - break; - case cf_retriesspec: - pmsg ("backend retries [%d]\n", $1.set[i].v.ival); - cur_backend.retries = $1.set[i].v.ival; - break; - case cf_statespec: - pmsg ("backend initial state [%d]\n", $1.set[i].v.ival); - cur_backend.initial_avail = $1.set[i].v.ival; - break; - default: - error ("Internal jam, unhandled type %d " - "in backend specification", - $1.set[i].cf); - } - free ($1.set); - - /* Verify the backend block, supply defaults, - * And so on. - */ - if (!cur_backend.initial_port) - error ("Service %s: back end %s lacks port", - cur_service.name, cur_backend.name); - if (cur_service.dispatchext && cur_backend.maxconnections) - error ("Service %s: back end %s defines maxconnections, " - "but an external dispatcher is in effect", - cur_service.name, cur_backend.name); - if (cur_backend.weight < 1) - cur_backend.weight = 1; - if (cur_backend.retries < 1) - cur_backend.retries = 1; - - /* Add to the list. */ - cur_service.backend = xrealloc (cur_service.backend, - ++cur_service.nbackend * - sizeof(Backend)); - cur_service.backend[cur_service.nbackend - 1] = - cur_backend; - pmsg ("this was backend definition [%d]\n", cur_service.nbackend); - memset (&cur_backend, 0, sizeof(cur_backend)); - } -; - -portstatement: - PORT - number - semicol { - pmsg ("port statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_portspec; - $$.set[0].v.ival = lastnr; - } -; - -bindstatement: - BINDTO - ipaddress - semicol { - pmsg ("bindto statement [%d]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_bindspec; - $$.set[0].v.sval = xstrdup(laststr); - } -; - -ipaddress: - ipaddress_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -number: - number_expected - NUMBER { - setlastnr (SYMBOL); - } -; - -semicol: - semicol_expected - ';' -; - -verbositystatement: - VERBOSITY - onoff_expected - onoff - semicol { - pmsg ("verbosity statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_verbosityspec; - $$.set[0].v.ival = lastnr; - } -; - -onoff: - ON { - lastnr = 1; - } -| - OFF { - lastnr = 0; - } -; - -dispatchmodestatement: - DISPATCHMODE - dispatchmethod - dispatchtail - semicol { - $$ = $2; - } -; - -dispatchtail: - dispatchover - | - dispatchext - | - /* empty */ { - $$.n = 0; - } -; - -dispatchover: - OVER - overnumber { - pmsg ("dispatch mode statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_dispatchspec; - $$.set[0].v.ival = lastnr; - - if (lastovernr && - (lastnr != ds_bysize && lastnr != ds_byduration)) - error ("Service '%s': this dispatch mode " - "doesn't support 'over <connections>'", - cur_service.name); - } -; - -overnumber: - number_expected - NUMBER { - setlastovernr (SYMBOL); - } -; - -dispatchext: - commandline { - pmsg ("external handler [%s]\n", laststr); - if (lastnr != ds_externalhandler) - error ("Service %s: this dispatch mode doesn't support " - "an external handler", cur_service.name); - setlastext (laststr); - } -; - -dispatchmethod: - dispatchmethod_expected - dispatchmethodspec { - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].v.ival = lastnr; - } -; - -dispatchmethodspec: - ROUNDROBIN { - lastnr = ds_roundrobin; - } -| - RANDOM { - lastnr = ds_random; - } -| - BYDURATION { - lastnr = ds_byduration; - } -| - BYSIZE { - lastnr = ds_bysize; - } -| - BYORDER { - lastnr = ds_byorder; - } -| - BYCONNECTIONS { - lastnr = ds_byconnections; - } -| - EXTERNALHANDLER { - lastnr = ds_externalhandler; - } -| - BYCLIENTIP { - lastnr = ds_byclientip; - } -; - -useraccountstatement: - USERACCOUNT - useraccount - semicol { - pmsg ("user account statement [%d]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_useraccountspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -useraccount: - useraccount_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -revivingintervalstatement: - REVIVINGINTERVAL - number - opt_externalhandler - semicol { - pmsg ("reviving interval statement [%d]\n", lastnr); - $$.n = 2; - $$.set = xmalloc (2 * sizeof(Confset)); - $$.set[0].cf = cf_revivespec; - $$.set[0].v.ival = lastnr; - if ($3.n) { - pmsg ("reviving externalhandler [%s]\n", $3.set[0].v.sval); - $$.set[1].v.sval = $3.set[0].v.sval; - } else - $$.set[1].v.sval = 0; - } -; - -checkintervalstatement: - CHECKINTERVAL - number - opt_externalhandler - semicol { - pmsg ("check interval [%d]\n", lastnr); - $$.n = 2; - $$.set = xmalloc (2 * sizeof(Confset)); - $$.set[0].cf = cf_checkspec; - $$.set[0].v.ival = lastnr; - if ($3.n) { - pmsg ("check externalhandler [%s]\n", $3.set[0].v.sval); - $$.set[1].v.sval = $3.set[0].v.sval; - } else - $$.set[1].v.sval = 0; - } -; - -opt_externalhandler: - externalhandler - | - /* empty */ { - $$.n = 0; - } -; - -externalhandler: - EXTERNALHANDLER - commandline { - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].v.sval = xstrdup (laststr); - } -; - -backlogstatement: - BACKLOG - number - semicol { - pmsg ("backlog statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_backlogspec; - $$.set[0].v.ival = lastnr; - } -; - -shmkeystatement: - SHMKEY - number - semicol { - pmsg ("shmkey statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_shmkeyspec; - $$.set[0].v.ival = lastnr; - } -; - -connectiontimeoutstatement: - CONNECTIONTIMEOUT - number - semicol { - pmsg ("connection timeout statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_connectiontimeoutspec; - $$.set[0].v.ival = lastnr; - } -; - -maxconnectionsstatement: - MAXCONNECTIONS - number - semicol { - pmsg ("max clients statement (service) [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_maxconnectionsspec; - $$.set[0].v.ival = lastnr; - } -; - -typestatement: - TYPE - typespec - semicol { - pmsg ("service type [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_typespec; - $$.set[0].v.ival = lastnr; - } -; - -typespec: - type_expected - typespecifier - ; - -typespecifier: - ANY { - lastnr = type_any; - } -| - HTTP { - lastnr = type_http; - } -; - -inspectionstatement: - HEADERINSPECTION - shallowdeepspec - semicol { - pmsg ("service header inspection [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_inspectionspec; - $$.set[0].v.ival = lastnr; - } -; - -shallowdeepspec: - shallowdeep_expected - shallowdeepspecifier - ; - -shallowdeepspecifier: - DEEP { - lastnr = ins_deep; - } -| - SHALLOW { - lastnr = ins_shallow; - } -; - -allowfromstatement: - ALLOWFROM - ipfilters - semicol { - pmsg ("allow from [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_allowfromspec; - $$.set[0].v.sval = xstrdup(laststr); - } -; - -denyfromstatement: - DENYFROM - ipfilters - semicol { - pmsg ("deny from [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_denyfromspec; - $$.set[0].v.sval = xstrdup(laststr); - } -; - -allowfilestatement: - ALLOWFILE - filename - semicol { - pmsg ("allow file [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_allowfilespec; - $$.set[0].v.sval = xstrdup(laststr); - } -; - -denyfilestatement: - DENYFILE - filename - semicol { - pmsg ("deny file [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_allowfilespec; - $$.set[0].v.sval = xstrdup(laststr); - } -; - -ipfilters: - ipfilters_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -backendblock: - BACKEND - backendname - openbrace - backenddefinitions - closebrace { - $$ = $4; - } -; - -backendname: - backendname_expected - IDENTIFIER { - pmsg ("backend name [%s]\n", SYMBOL); - for (i = 0; i < cur_service.nbackend; i++) - if (!strcmp (cur_service.backend[i].name, SYMBOL)) - error ("Service %s: back end '%s' multiply defined", - cur_service.name, SYMBOL); - cur_backend.name = xstrdup (SYMBOL); - } -; - -backenddefinitions: - backenddefinitions - backenddefinition { - $1.n++; - $1.set = xrealloc ($1.set, $1.n * sizeof(Confset)); - $1.set[$1.n - 1] = $2.set[0]; - $$ = $1; - } -| - backenddefinition { - $$ = $1; - } -; - -backenddefinition: - backendstatement_expected - backendstatement { - $$ = $2; - } -; - -backendstatement: - serverstatement { - pmsg ("backend server [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - portstatement { - pmsg ("backend port [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -| - verbositystatement { - pmsg ("backend verbosity [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -| - onstartstatement { - pmsg ("backend onstart [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - onendstatement { - pmsg ("backend onend [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - onfailstatement { - pmsg ("backend onfail [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - dumptrafficstatement { - pmsg ("backend trafficlog [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - throughputstatement { - pmsg ("backend trafficlog [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - httptiminglogstatement { - pmsg ("backend HTTP timing log [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - weightstatement { - pmsg ("backend weight [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -| - decaystatement { - pmsg ("backend decay [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -| - maxconnectionsstatement { - pmsg ("backend maxconnections [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -| - stickycookiestatement { - pmsg ("backend sticky cookie [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - addclientheaderstatement { - pmsg ("addclientheader [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - setclientheaderstatement { - pmsg ("setclientheader [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - appendclientheaderstatement { - pmsg ("appendclientheader [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - addserverheaderstatement { - pmsg ("addserverheader [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - setserverheaderstatement { - pmsg ("setserverheader [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - appendserverheaderstatement { - pmsg ("appendserverheader [%s]\n", $1.set[0].v.sval); - $$ = $1; - } -| - retriesstatement { - pmsg ("backend retries [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -| - initialstatestatement { - pmsg ("backend state [%d]\n", $1.set[0].v.ival); - $$ = $1; - } -; - -serverstatement: - SERVER - serveraddress_expected - serveraddress - semicol { - pmsg ("server statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_serverspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -weightstatement: - WEIGHT - number - semicol { - pmsg ("weight statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_weightspec; - $$.set[0].v.ival = lastnr; - } -; - -decaystatement: - DECAY - number - semicol { - pmsg ("decay statement [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_decayspec; - $$.set[0].v.ival = lastnr; - } -; - -serveraddress: - STRING { - setlaststr (laststring); - } -; - -retriesstatement: - RETRIES - number - semicol { - pmsg ("retries [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_retriesspec; - $$.set[0].v.ival = lastnr; - } -; - -initialstatestatement: - STATE - statedef - semicol { - pmsg ("state [%d]\n", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_statespec; - $$.set[0].v.ival = lastnr; - } -; - -statedef: - statedef_expected - somestate -; - -somestate: - AVAILABLE { - lastnr = st_available; - } -| - UNAVAILABLE { - lastnr = st_unavailable; - } -| - DOWN { - lastnr = st_down; - } -; - -onstartstatement: - ONSTART - commandline - semicol { - pmsg ("onstart statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_onstartspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -onfailstatement: - ONFAIL - commandline - semicol { - pmsg ("onfail statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_onfailspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -onendstatement: - ONEND - commandline - semicol { - pmsg ("onend statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_onendspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -dumptrafficstatement: - TRAFFICLOG - filename - semicol { - pmsg ("trafficlog statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_dumpspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -throughputstatement: - THROUGHPUTLOG - filename - semicol { - pmsg ("throughputlog statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_thruspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -httptiminglogstatement: - HTTPTIMINGLOG - filename - semicol { - pmsg ("httptiminglog statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_httptimingspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -commandline: - commandline_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -filename: - filename_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -stickycookiestatement: - STICKYCOOKIE - cookiespecifier - semicol { - pmsg ("insertcookie statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_stickycookiespec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -cookiespecifier: - cookie_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -addclientheaderstatement: - ADDCLIENTHEADER - headerstring - semicol { - pmsg ("addclientheader statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_addclientheaderspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -setclientheaderstatement: - SETCLIENTHEADER - headerstring - semicol { - pmsg ("setclientheader statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_setclientheaderspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -appendclientheaderstatement: - APPENDCLIENTHEADER - headerstring - semicol { - pmsg ("appendclientheader statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_appendclientheaderspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -addserverheaderstatement: - ADDSERVERHEADER - headerstring - semicol { - pmsg ("addserverheader statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_addserverheaderspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -setserverheaderstatement: - SETSERVERHEADER - headerstring - semicol { - pmsg ("setserverheader statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_setserverheaderspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -appendserverheaderstatement: - APPENDSERVERHEADER - headerstring - semicol { - pmsg ("appendserverheader statement [%s]\n", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_appendserverheaderspec; - $$.set[0].v.sval = xstrdup (laststr); - } -; - -headerstring: - headerstring_expected - STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; - } -; - -headerstring_expected: { - yyerrmsg = "HTTP header specifier expected"; -} -; - -cookie_expected: { - yyerrmsg = "cookie specifier expected"; -} -; - -number_expected: { - yyerrmsg = "number expected"; -} -; - -serveraddress_expected: { - yyerrmsg = "hostname or IP address expected"; -} -; - -service_expected: { - yyerrmsg = "'service' expected"; -} -; - -backendstatement_expected: { - yyerrmsg = "backend definition statement expected"; -} -; - -servicebody_expected: { - yyerrmsg = "service body statement expected"; -} -; - -semicol_expected: { - yyerrmsg = "semicolon (;) expected"; -} -; - -onoff_expected: { - yyerrmsg = "'on' or 'off' expetcted"; -} -; - -dispatchmethod_expected: { - yyerrmsg = "dispatch method expected"; -} -; - -commandline_expected: { - yyerrmsg = "command line expected"; -} -; - -filename_expected: { - yyerrmsg = "file name expected"; -} -; - -servicename_expected: { - yyerrmsg = "service name (identifier) expected"; -} -; - -backendname_expected: { - yyerrmsg = "backend name (identifier) expected"; -} -; - -ipaddress_expected: { - yyerrmsg = "IP address such as 1.2.3.4 or 'any' expected"; -} -; - -type_expected: { - yyerrmsg = "Service type expected ('any', 'stickyhttp', ...)"; -} -; - -shallowdeep_expected: { - yyerrmsg = "Header inspection mode mode expected ('shallow' or 'deep')"; -} -; - -ipfilters_expected: { - yyerrmsg = "IP filter(s) expected"; -} -; - -useraccount_expected: { - yyerrmsg = "username expected"; -} -; - -statedef_expected: { - yyerrmsg = "state definition expected"; -} -; - -openbrace: - openbrace_expected - '{' -; - -closebrace: - closebrace_expected - '}' -; - -openbrace_expected: { - yyerrmsg = "'{' expected"; -} - -closebrace_expected: { - yyerrmsg = "'}' expected"; -} - -option_statement_expected: { - yyerrmsg = "option statement (logactivity, tcpbuffersize, ...) expected"; -} diff --git a/src/lib/parserclose.c b/src/lib/parserclose.c @@ -1,29 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -int parser_close () { - int remaining = 0; - - nparserstate--; - if (nparserstate > 0) { - /* We still have files in the parserstate stack. Set up yyin and - * yylineno, and return 0 to signal that yyin has been set up - * again. */ - yyin = parserstate[nparserstate - 1].file; - yylineno = parserstate[nparserstate - 1].lineno; - if (parserstate[nparserstate - 1].buf && - *parserstate[nparserstate - 1].buf) - remaining = strlen(parserstate[nparserstate - 1].buf); - msg ("Returning to configuration '%s' (line %d), %d bytes remain", - parserstate[nparserstate - 1].name, yylineno, remaining); - return (0); - } - - /* The parser stack is empty. Return 1 to signal that we're really done. */ - return (1); -} diff --git a/src/lib/parserfilename.c b/src/lib/parserfilename.c @@ -1,13 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -char const *parser_filename() { - if (nparserstate > 0) - return (parserstate[nparserstate - 1].name); - return ("<unknown file>"); -} diff --git a/src/lib/parserinput.c b/src/lib/parserinput.c @@ -1,40 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* #define DEBUG */ - -int parser_input (char *buf, int max) { - int i, ndef = 0; - char *cp, *newbuf; - - if (! nparserstate || - ! parserstate[nparserstate - 1].buf || - ! *parserstate[nparserstate - 1].buf) - return (0); - - for (i = 0; i < nsymtab; i++) { - if (!strncmp (parserstate[nparserstate - 1].buf, symtab[i].symbol, - strlen(symtab[i].symbol))) { - if (ndef++ >= DEF_MAX) - error ("Too many #defines (%d) at %s (circular definition?", - ndef, symtab[i].symbol); - cp = parserstate[nparserstate - 1].buf + strlen(symtab[i].symbol); - newbuf = xstrdup (symtab[i].redef); - newbuf = xstrcat (newbuf, cp); - free (parserstate[nparserstate - 1].buf); - parserstate[nparserstate - 1].buf = newbuf; - } - } - - *buf = *parserstate[nparserstate - 1].buf; - #ifdef DEBUG - fprintf (stderr, "LEXER input char: %c (%d)\n", *buf, *buf); - #endif - parser_skipchar(); - return (1); -} diff --git a/src/lib/parseropen.c b/src/lib/parseropen.c @@ -1,56 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void parser_open (char const *name) { - FILE *f; - struct stat statbuf; - int nread; - char *actual, *cp; - - /* "name" or <name> is converted to name */ - actual = xstrdup (name); - cp = actual + strlen(actual) - 1; - if ( (*actual == '"' && *cp == '"') || - (*actual == '<' && *cp == '>') ) { - *cp = 0; - strcpy (actual, actual + 1); - } - - if (! (f = fopen (actual, "r")) ) - error ("Cannot read configuration %s: %s", actual, strerror(errno)); - if (fstat(fileno(f), &statbuf)) - error ("Cannot stat configuration %s: %s", actual, strerror(errno)); - - /* Anticipate the terminating \n, process it and store. */ - yylineno++; - if (nparserstate > 0) { - parserstate[nparserstate - 1].lineno = yylineno; - msg ("%d bytes remain in configuration %s beyond line %d", - (int) strlen(parserstate[nparserstate - 1].buf), - parserstate[nparserstate - 1].name, yylineno); - } - - parserstate = xrealloc (parserstate, - (nparserstate + 1) * sizeof(ParserState)); - parserstate[nparserstate].name = actual; - parserstate[nparserstate].file = f; - parserstate[nparserstate].lineno = 0; - parserstate[nparserstate].buf = xmalloc (statbuf.st_size + 1); - nread = fread (parserstate[nparserstate].buf, 1, statbuf.st_size, f); - /* msg ("Got %d bytes from configuration %s", nread, actual); */ - if (nread < statbuf.st_size) - error ("Read only %d bytes from configuration %s (out of %d)", - nread, actual, (int) statbuf.st_size); - parserstate[nparserstate].buf[statbuf.st_size] = 0; - nparserstate++; - - yylineno = 0; - yyin = f; -} - - diff --git a/src/lib/parserrun.c b/src/lib/parserrun.c @@ -1,11 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void parser_run () { - yyparse(); -} diff --git a/src/lib/parserskipchar.c b/src/lib/parserskipchar.c @@ -1,26 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* #define DEBUG */ - -void parser_skipchar () { - if (! parserstate[nparserstate - 1].buf || - ! *parserstate[nparserstate - 1].buf) - return; - - #ifdef DEBUG - fprintf (stderr, "LEXER skipping %c (%d)\n", - *parserstate[nparserstate - 1].buf, - *parserstate[nparserstate - 1].buf); - #endif - - memmove (parserstate[nparserstate - 1].buf, - parserstate[nparserstate - 1].buf + 1, - strlen(parserstate[nparserstate - 1].buf)); -} - diff --git a/src/lib/parserskipline.c b/src/lib/parserskipline.c @@ -1,21 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void parser_skipline () { - char ch; - - while (parserstate[nparserstate - 1].buf && - *parserstate[nparserstate - 1].buf) { - ch = *parserstate[nparserstate - 1].buf; - parser_skipchar(); - if (ch == '\n') { - yylineno++; - break; - } - } -} diff --git a/src/lib/proxyerror.txt b/src/lib/proxyerror.txt @@ -1,10 +0,0 @@ -<html> - <head> - <title>Internal Server Error</title> - </head> - <body> - <h1>Internal Server Error</h1> - Your request could not be fulfilled at this time.<br> - Please try again later. - </body> -</html> diff --git a/src/lib/runservice.c b/src/lib/runservice.c @@ -1,130 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void runservice () { - int pid; - int first_serving = 1; - - /* Allocate the shared mem segment. */ - alloc_reporter(activeservice, 1); - - /* Load any allow- or deny filter files if appropriate. */ - if (ipf_loadfile (activeservice->allowfile, &(activeservice->allowchain), - &(activeservice->nallowchain))) - error ("Error in allow file"); - if (ipf_loadfile (activeservice->denyfile, &(activeservice->denychain), - &(activeservice->ndenychain))) - error ("Error in deny file"); - - /* Go into the background. */ - if ( (pid = fork()) < 0 ) { - /* Fork failed */ - error ("fork failure: %s", strerror(errno)); - } else if (pid) { - /* Parent branch */ - msg ("detached as PID %d", pid); - return; - } - - /*** Child branch ***/ - - /* We've forked for the first time */ - daemonized++; - program_stage = stage_waiting; - - /* Promote verbosity. */ - flag_verbose = activeservice->verbosity; - - /* ALF support: open the status file fd */ - #ifdef ALF - { - char file[80]; - snprintf (file, 80, "/tmp/xr-%d.alf", getpid()); - close (alf_fd); - alf_fd = open (file, O_WRONLY | O_CREAT); - msg ("Service listener: alf logging to '%s', fd %d", file, alf_fd); - } - #endif - - /* Provide configuration/limits feedback */ - if (flag_verbose) { - config_msg(); - limits_msg(); - } - - msg ("STARTING on port %d", activeservice->port); - - set_program_title ("crossroads - Service %s: listening", - activeservice->name); - close (0); - close (1); - close (2); - if ( (open ("/dev/null", O_RDONLY) < 0) || - (open ("/dev/null", O_WRONLY) < 0) || - (open ("/dev/null", O_WRONLY) < 0) ) - error ("failed to reopen stdin/out/err on /dev/null"); - if (setsid() < 0) - error ("failed to become session leader"); - - /* In 'forever' mode, we create a server-side socket and ask tcpserve() - * to service it. tcpserve() will return to us if there are no back - * ends to service the request. In that case, we close the listening - * socket (so that new clients get denied), take a nap, and redo. - * Of course we only start listening if we have back ends at all... - */ - while (1) { - /* We need at least one working back end to operate. */ - if (!backend_count()) { - msg ("taking a nap..."); - sleep (SLEEP_TIME); - continue; - } - - /* We have (at least) one back end. Let's run the service. - * Create the socket, bind to port. */ - if ( (listen_sock = make_socket (activeservice->port, - activeservice->bind)) < 0 ) { - if (!opt.sloppyportbind) - error ("failed to listen to port %d: %s", - activeservice->port, - strerror(errno)); - warning ("listening socket creation failed.. will retry"); - - /* We wait here for some time, to let the system regain - * equilibrium. Something's really afoot, so let's not retry - * right away. */ - sleep (SLEEP_TIME); - - continue; - } - - msg ("server-side network socket: %d", listen_sock); - - /* Zero out the stats for first usage. */ - if (first_serving) { - first_serving = 0; - memset (servicereport, 0, sizeof(Servicereport)); - /* Store our pid, so that 'crossroads stop' may kill us. */ - lock_reporter(); - servicereport->pid = getpid(); - unlock_reporter(); - } - - /* Start serving the port! */ - tcpserve (listen_sock); - - /* tcpserve() returned -- must be because this service is out - * of back ends */ - if (shutdown (listen_sock, SHUT_RDWR)) - warning ("failed to shut down server-side socket %d: %s", - listen_sock, strerror(errno)); - if (close (listen_sock)) - warning ("failed to close server-side socket %d: %s", - listen_sock, strerror(errno)); - } -} diff --git a/src/lib/setproctitle.c b/src/lib/setproctitle.c @@ -1,64 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* This defines setproctitle() incase your system lacks it. */ - -#if HAVE_SETPROCTITLE == 0 - -extern char **environ; - -void setproctitle (char const *fmt, ...) { -#if SET_PROC_TITLE_BY_ARGV == 1 - int i; - char buf[SPT_BUFSIZE], **envp; - va_list ap; - static int argv_lth = -1; - - if (argv_lth == -1) { - /* First time around. Save the environment, initialize. */ - envp = environ; - for (i = 0; envp[i]; i++) - ; - environ = xmalloc (sizeof(char*) * (i + 1)); - for (i = 0; envp[i]; i++) - environ[i] = xstrdup(envp[i]); - environ[i] = 0; - - if (i) - argv_lth = envp[i - 1] + strlen(envp[i - 1]) - org_argv[0]; - else - argv_lth = org_argv[org_argc - 1] + - strlen(org_argv[org_argc - 1]) - org_argv[0]; - } - - /* Prepare the new commandline */ - va_start (ap, fmt); - vsnprintf (buf, SPT_BUFSIZE, fmt, ap); - va_end (ap); - - /* Determine max length */ - i = strlen (buf); - if (i > argv_lth - 2) { - i = argv_lth - 2; - buf[i] = 0; - } - - /* Set into original argv[0] */ -#if HAVE_PROGNAME_FULL == 1 - strcpy (__progname_full, buf); -#else - memset (org_argv[0], 0, argv_lth); - strcpy (org_argv[0], buf); -#endif /* HAVE_PROGNAME_FULL */ - - org_argv[1] = 0; -#endif /* SET_PROC_TITLE_BY_ARGV */ -} - -#endif /* HAVE_SETPROCTITLE */ - diff --git a/src/lib/setprogramtitle.c b/src/lib/setprogramtitle.c @@ -1,20 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void set_program_title (char const *fmt, ...) { - char *buf; - va_list args; - - if (opt.leave_proctitle) - return; - va_start (args, fmt); - buf = str_vprintf (fmt, args); - va_end (args); - setproctitle (buf); - free (buf); -} diff --git a/src/lib/showconfig.c b/src/lib/showconfig.c @@ -1,54 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -#define SHOWDEF(a,b,c) \ - printf ("%-22s %-31s: %s \n", b, c, a ? "yes" : "no"); - -void show_config () { - printf ("VER version: %s\n" - "REVVER revision version: %s\n" - "YEARS it's been around since: %s\n" - "AUTHORNAME author: %s\n" - "MAINTAINERNAME maintainer: %s\n" - "MAINTAINEREMAIL e-mail of maintainer: %s\n" - "FQDN_LENGTH max length of a hostname: %d\n" - "SLEEP_TIME service inactivy pause: %d\n" - "DEF_MAX max nr of defines in a file %d\n" - "DNS_CACHESIZE size of the DNS cache: %d\n" - "DNS_CACHETTL default ttl of cached DNS: %d\n" - "CONNECT_TIMEOUT backend connect timeout: %d\n" - "RETRY_WAIT backend retry pause: %d\n" - "DEFAULT_TCP_BUFSZ default network buffer size: %d\n" - "PREFIX installation tree prefix: %s\n" - "BINDIR directory for binaries: %s\n" - "DEFAULT_SPT_BUFSIZE default cmdline length: %d\n" - "SPT_BUFSIZE used cmdline length: %d\n" - "SET_PROC_TITLE_BY_ARGV argv assumed linear array: %d\n" - "EXTRALIBS extra library dependencies: %s\n" - "LIBS standard library dependencies: %s\n" - , - VER, REVVER, YEARS, AUTHORNAME, MAINTAINERNAME, - MAINTAINEREMAIL, - FQDN_LENGTH, SLEEP_TIME, DEF_MAX, - DNS_CACHESIZE, DNS_CACHETTL, - CONNECT_TIMEOUT, RETRY_WAIT, DEFAULT_TCP_BUFSZ, PREFIX, BINDIR, - DEFAULT_SPT_BUFSIZE, SPT_BUFSIZE, - SET_PROC_TITLE_BY_ARGV, EXTRALIBS, LIBS); - SHOWDEF (HAVE_MALLOC_H, "HAVE_MALLOC_H", "<malloc.h> present"); - SHOWDEF (HAVE_STDINT_H, "HAVE_STDINT_H", "<stdint.h> present"); - SHOWDEF (HAVE_FLOCK, "HAVE_FLOCK", "flock() present"); - SHOWDEF (HAVE_LOCKF, "HAVE_LOCKF", "lockf() present"); - SHOWDEF (HAVE_STRLCAT, "HAVE_STRLCAT", "strlcat() present"); - SHOWDEF (HAVE_SRANDDEV, "HAVE_SRANDDEV", "sranddev() present"); - SHOWDEF (HAVE_VSYSLOG, "HAVE_VSYSLOG", "vsyslog() present"); - SHOWDEF (HAVE_STRUPR, "HAVE_STRUPR", "strupr() present"); - SHOWDEF (HAVE_STRCASESTR, "HAVE_STRCASESTR", "strcasestr() present"); - SHOWDEF (HAVE_SETPROCTITLE, "HAVE_SETPROCTITLE", "setproctitle() present"); - SHOWDEF (HAVE_STRNSTR, "HAVE_STRNSTR", "strnstr() present"); - SHOWDEF (HAVE_PROGNAME_FULL,"HAVE_PROGNAME_FULL","__progname_full present"); -} diff --git a/src/lib/stagetostring.c b/src/lib/stagetostring.c @@ -1,21 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -char *stage_to_string (Programstage stage) { - switch (stage) { - case stage_main: - return ("main"); - case stage_waiting: - return ("waiting"); - case stage_serving: - return ("serving"); - case stage_retrying: - return ("retrying"); - default: - return ("unknown"); - } -} diff --git a/src/lib/statetostring.c b/src/lib/statetostring.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -char *state_to_string (Backendavail avail) { - int i; - - for (i = 0; statestringmap[i].nm; i++) - if (avail == statestringmap[i].av) - return (statestringmap[i].nm); - return ("UNKNOWN"); -} diff --git a/src/lib/strcasestr.c b/src/lib/strcasestr.c @@ -1,25 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -#if HAVE_STRCASESTR == 0 -char *strcasestr (char const *big, char const *little) { - char *bcopy, *lcopy, *res; - - if (!little || !*little) - return ( (char*) big); - if (!big || !*big) - return (0); - - bcopy = strupr (xstrdup (big)); - lcopy = strupr (xstrdup (little)); - if ( (res = strstr (bcopy, lcopy)) ) - res = (char*) big + (res - bcopy); - free (bcopy); - free (lcopy); - return (res); -} -#endif diff --git a/src/lib/strexpandformat.c b/src/lib/strexpandformat.c @@ -1,191 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -char *fmt_expand (char const *s, int *skip, int fmt_number) { - static char buf[80]; - int nextnumber; - - /* Expansion table: - * %a - availability of current back end (if any), 0 not available, - * %1a = first back end etc. - * %b - name of current back end (if any), %1b = first back end etc. - * %e - seconds since epoch, number is an addition count - * %g - GMT format timestamp. The time in between is the minutes - * offset into the future, so %30g is now + 1/2 hour - * %h - actual serveraddress of current back end, %h1 = first etc. - * %l - last connect time of current back end, %l1 = first etc. - * %n - nr of clients of current back end, %1n = first back end etc. - * %p - actual port of current back end, %1p = first etc. - * %P - process ID of the Crossroads handler (debugging) - * %r - client IP, number is not interpreted - * %s - name of service, number is not interpreted - * %t, %T - ANSI timestamp of local or GMT time, number is offset in min - * into the future - * %v - Crossroads version, number not interpreted - * %w - weight of current back end, %1w = first back end etc. - */ - - (*skip)++; - /* - msg ("Fmt expand [%s], skip=%d, nr=%d", - s, *skip, fmt_number); - */ - - switch (*s) { - - case 'a': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number < 0 || fmt_number >= activeservice->nbackend || - !backend_available (fmt_number)) - return ("0"); - return ("1"); - - case 'b': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number >= 0 && fmt_number < activeservice->nbackend) - return (activeservice->backend[fmt_number].name); - return ("noname"); - - case 'e': - snprintf (buf, sizeof(buf) - 1, "%u", (unsigned) time(0) + fmt_number); - return (buf); - - case 'g': - return (gmtstamp (fmt_number)); - - case 'h': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number < 0 || fmt_number >= activeservice->nbackend) - return ("noname"); - return (servicereport->backendstate[fmt_number].actual_server); - - case 'l': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number >= 0 && fmt_number < activeservice->nbackend) { - snprintf (buf, sizeof(buf) - 1, "%f", - servicereport->backendstate[fmt_number].last_con_sec); - return (buf); - } - return ("0"); - - case 'n': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number >= 0 && fmt_number < activeservice->nbackend) { - snprintf (buf, sizeof(buf) - 1, "%u", - (unsigned) - servicereport->backendstate[fmt_number].nclients); - return (buf); - } - return ("0"); - - case 'p': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number < 0 || fmt_number >= activeservice->nbackend) - return ("0"); - snprintf (buf, sizeof(buf), "%d", - servicereport->backendstate[fmt_number].actual_port); - return (buf); - - case 'P': - snprintf (buf, sizeof(buf), "%u", getpid()); - return (buf); - - case 'r': - if (client_ip && *client_ip) - return (client_ip); - return ("0.0.0.0"); - - case 's': - return (activeservice->name); - - case 't': - return (ansistamp (tm_localtime, fmt_number)); - - case 'T': - return (ansistamp (tm_gmtime, fmt_number)); - - case 'v': - return (VER); - - case 'w': - if (!fmt_number) - fmt_number = current_backend; - else - fmt_number--; - if (fmt_number >= 0 && fmt_number < activeservice->nbackend) { - snprintf (buf, sizeof(buf) - 1, "%u", - (unsigned) - activeservice->backend[fmt_number].weight); - return (buf); - } - return ("0"); - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - nextnumber = atoi(s); - while (isdigit (*s)) { - (*skip)++; - s++; - } - (*skip)--; - return (fmt_expand (s, skip, nextnumber)); - - default: - *buf = *(s + 1); - *(buf + 1) = 0; - return (buf); - } -} - -char *str_expand_format (char const *h) { - char *ret = 0; - char const *cp; - int skip; - - for (cp = h; cp && *cp; cp++) { - if (*cp == '%') { - skip = 0; - ret = xstrcat (ret, fmt_expand (cp + 1, &skip, 0)); - cp += skip; - } else { - ret = xstrcatch (ret, *cp); - } - } - - /* - msg ("String [%s] expanded to [%s]", - h, ret); - */ - return (ret); -} diff --git a/src/lib/stringtostate.c b/src/lib/stringtostate.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -Backendavail string_to_state (char const *str) { - int i; - - for (i = 0; statestringmap[i].nm; i++) - if (!strcasecmp (str, statestringmap[i].nm)) - return (statestringmap[i].av); - return (st_unknown); -} diff --git a/src/lib/strlcat.c b/src/lib/strlcat.c @@ -1,30 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#if HAVE_STRLCAT == 0 - -#include "../crossroads.h" - -size_t strlcat (char *dst, char const *src, size_t size) { - size_t bytes = 0; - char *q = dst; - char const *p = src; - char ch; - - while ( bytes < size && *q ) { - q++; - bytes++; - } - while ( (ch = *p++) ) { - if ( bytes < size ) - *q++ = ch; - bytes++; - } - *q = '\0'; - return bytes; -} - -#endif diff --git a/src/lib/strnstr.c b/src/lib/strnstr.c @@ -1,35 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -#if HAVE_STRNSTR == 0 -char *strnstr (char const *big, char const *little, size_t len) { - int i, max, llen; - - /* Check that we have strings */ - if (!big) - return (0); - if (!little) - return ( (char*) big); - - /* Get lengths */ - llen = strlen(little); - max = strlen(big) - llen; - - /* Check that big is bigger */ - if (max < 1) - return (0); - - /* Do the search */ - for (i = 0; i <= max; i++) - if (!strncmp (big + i, little, llen)) - return ((char*)(big + i)); - - /* Nothing found... */ - return (0); -} -#endif diff --git a/src/lib/strprintf.c b/src/lib/strprintf.c @@ -1,13 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -char *str_printf (char const *fmt, ...) { - va_list args; - - va_start (args, fmt); - return (str_vprintf (fmt, args)); -} diff --git a/src/lib/strupr.c b/src/lib/strupr.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -#if HAVE_STRUPR == 0 -char *strupr (char *buf) { - char *cp; - - for (cp = buf; cp && *cp; cp++) - *cp = toupper(*cp); - return (buf); -} -#endif diff --git a/src/lib/strvprintf.c b/src/lib/strvprintf.c @@ -1,32 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -#define STR_BLOCK 512 - -char *str_vprintf (char const *fmt, va_list arguments) { - int size = STR_BLOCK; /* initial size guess */ - char *buffer = xmalloc (size); /* initial buffer */ - int nchars; /* return value of vsnprintf */ - - while (1) { /* try to make string */ - nchars = vsnprintf (buffer, size, fmt, arguments); - - /* if this worked, return string */ - if (nchars > -1 && nchars < size) - return (buffer); - - /* try again with more space */ - if (nchars > -1) - size = nchars + 1; - else - size += STR_BLOCK; - - buffer = xrealloc (buffer, size); - } - - return (0); /* to satisfy prototype */ -} diff --git a/src/lib/symtabend.c b/src/lib/symtabend.c @@ -1,13 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void symtab_end () { - if (!symtab[nsymtab].redef) - error ("#define %s lacks a redefinition", symtab[nsymtab].symbol); - nsymtab++; -} diff --git a/src/lib/symtablookup.c b/src/lib/symtablookup.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -char const *symtab_lookup (char const *txt) { - int i; - - for (i = 0; i < nsymtab; i++) - if (!strcmp (symtab[i].symbol, txt)) - return (symtab[i].redef); - return (txt); -} diff --git a/src/lib/symtabset.c b/src/lib/symtabset.c @@ -1,11 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void symtab_set (char const *redef) { - symtab[nsymtab].redef = xstrdup (redef); -} diff --git a/src/lib/symtabstart.c b/src/lib/symtabstart.c @@ -1,13 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void symtab_start (char const *sym) { - symtab = xrealloc (symtab, (nsymtab + 1) * sizeof(Symtab)); - symtab[nsymtab].symbol = xstrdup (sym); - symtab[nsymtab].redef = 0; -} diff --git a/src/lib/sysrun.c b/src/lib/sysrun.c @@ -1,50 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* To see the command that runs, even in the wakeup handler, undefine: */ -/* #define DEBUG */ - -int sysrun (char const *cmd) { - char *expanded; - int ret; - - if (!cmd || ! *cmd) - return (0); - - expanded = str_expand_format (cmd); - -# ifdef DEBUG - msg ("running command '%s'", expanded); -# else - if (program_stage != stage_retrying) - msg ("running command: '%s'", expanded); -# endif - - /* Child signals are handled in system now. Also assume the right UID. */ - signal (SIGCHLD, SIG_DFL); - uid_assume(); - ret = system (expanded); - - /* Get our own UID back and ignore child sigs. */ - uid_restore(); - signal (SIGCHLD, SIG_IGN); - - if (ret > 0) - warning ("command '%s' returned %d", expanded, ret); - else if (ret < 0) - warning ("failed to run command '%s': %s", expanded, strerror(errno)); - -# ifdef DEBUG - if (!ret) - msg ("command '%s' returned %d", expanded, ret); -# endif - - free (expanded); - - return (ret); -} diff --git a/src/lib/tcpserve.c b/src/lib/tcpserve.c @@ -1,235 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void tcpserve (int server_sock) { - fd_set set; - int backend_sock, new, size, i, pid, counter; - unsigned octet; - char *tmp, *cp, *last = (char *) tcpserve; - struct sockaddr_in clientname; - static int wakeup_started = 0; - - /* Set up our signal handlers. */ - if (! wakeup_started++) { - /* Interruption signals */ - for (i = 0; relevant_sigs[i]; i++) - signal (relevant_sigs[i], interrupt); - /* Child termination is ignored. */ - signal (SIGCHLD, SIG_IGN); - /* Set wakeup handler for the wakeup calls. */ - if (activeservice->rev_interval || activeservice->check_interval) { - msg ("Starting wakeup handler"); - if ( (pid = fork()) < 0 ) - error ("Fork failed: %s", strerror(errno)); - else if (!pid) { - set_program_title ("crossroads - Service %s: wakeup", - activeservice->name); - wakeup_handler(); - } else { - msg ("started wakeup handler at pid %d", pid); - servicereport->rev_pid = pid; - } - } else { - msg ("Wakeup hander not started (reviving interval %d, check %d)", - activeservice->rev_interval, - activeservice->check_interval); - } - } - - /* Promote all config-side "initial" settings to "actual" ones. */ - for (i = 0; i < activeservice->nbackend; i++) { - msg ("Initial availability of back end %s: %s, " - "initial server:port: %s:%d", - activeservice->backend[i].name, - state_to_string(activeservice->backend[i].initial_avail), - activeservice->backend[i].initial_server, - activeservice->backend[i].initial_port); - servicereport->backendstate[i].actual_avail = - activeservice->backend[i].initial_avail; - servicereport->backendstate[i].actual_port = - activeservice->backend[i].initial_port; - strncpy (servicereport->backendstate[i].actual_server, - activeservice->backend[i].initial_server, - FQDN_LENGTH - 1); - } - - /* Start the listener. */ - msg ("awaiting activity on port %d (socket fd %d)", - activeservice->port, server_sock); - if (listen (server_sock, activeservice->backlog + 1) < 0) - error ("failed to listen to server_socket: %s", strerror(errno)); - - /* We're a service now. Never return, never exit - * (unless something is REALLY wrong). - */ - while (1) { - /* Reload allow/deny files if requested so due to SIGHUP. */ - if (reload_allow_deny) { - reload_allow_deny = 0; - if (ipf_loadfile (activeservice->allowfile, - &(activeservice->allowchain), - &(activeservice->nallowchain))) - warning ("Bad syntax in allow file"); - if (ipf_loadfile (activeservice->denyfile, - &(activeservice->denychain), - &(activeservice->ndenychain))) - warning ("Bad syntax in deny file"); - msg ("allow chain has %d entries, " - "deny chain has %d entries", - activeservice->nallowchain, activeservice->ndenychain); - } - - /* Block until we get a connection. */ - FD_ZERO (&set); - FD_SET (server_sock, &set); - if (select (FD_SETSIZE, &set, 0, 0, 0) < 0) { - msg ("interrupt while waiting for activity: %d (%s)", - errno, strerror(errno)); - continue; - } - - /* Accept the client-side connection. */ - size = sizeof(clientname); - if ( (new = accept (server_sock, (struct sockaddr *) &clientname, - (socklen_t *) &size)) < 0 ) { - warning ("failure while accepting on server socket: %s", - strerror(errno)); - continue; - } - - /* Store client IP, it's used for lots of logging. */ - client_ip = inet_ntoa (clientname.sin_addr); - msg ("connection from %s, socket %d", client_ip, new); - - /* Leave it alone if there are no back ends or if we exceed - * the max allowed clients. IN THAT CASE WE RETURN so that - * runservice() may close the listener socket, sleep, and - * create a new one. Exception is when the service has configured an - * external dispatcher: in that case our backend count is not - * reliable, and the external dispatcher may decide otherwise.. - */ - if (activeservice->dispatchtype != ds_externalhandler && - backend_count() == 0) { - warning ("no back ends available"); - close (new); - return; - } - - /* Build up the actual client IP as an int. - * We need to do this in reverse order; e.g. 1.2.3.4 needs to become - * 4<<24 | 3<<16 | 2<<8 | 1. Reason is that when mask /16 applies, - * we can OR this value with 16, and we'll keep 2<<8 | 1, the only - * significant digits. */ - tmp = xstrdup (client_ip); - client_ip_nr = 0; - counter = 0; - for (counter = 0, cp = strtok_r (tmp, ".", &last); - cp; - counter += 8, cp = strtok_r (0, ".", &last)) { - if (sscanf (cp, "%u", &octet) > 0) { - client_ip_nr |= (octet << counter); - /* - msg ("Client IP part 0x%2.2x (%d), " - "hex client so far: 0x%8.8x", - octet, octet, client_ip_nr); - */ - } - } - free (tmp); - - /* Check allow/deny lists. */ - if (ipf_denied ()) { - warning ("%s matches deny list, terminating connection", client_ip); - close (new); - continue; - } - if (!ipf_allowed ()) { - warning ("%s failes to match allow list, terminating connection", - client_ip); - close (new); - continue; - } - - /* All dispatching below is done in a separate fork. - * Dispatching delays (eg. external handlers) won't therefore - * hinder the listener. - */ - if ( (pid = fork()) < 0) - error ("Fork failed: %s", strerror(errno)); - else if (pid) { - msg ("Dispatching phase started as pid %d", pid); - close (new); - continue; - } - - /* Here's the child process as dispatcher. The backend has yet - * to be defined. - * NOTE NOTE NOTE No continue's below this point - EXIT! */ - - /* ALF support */ - #ifdef ALF - { - char file[80]; - - snprintf (file, 80, "/tmp/xd-%d.alf", getpid()); - close (alf_fd); - alf_fd = open (file, O_WRONLY); - msg ("Service handler: alf logging to '%s', fd %d", file, alf_fd); - } - #endif - - current_backend = -1; - - /* Incase of a http type service: handle separately. */ - if (activeservice->type == type_http) { - http_serve (new); - exit (0); - } - - /* Retry back ends until we succeed. */ - while (1) { - msg ("About to choose a back end"); - choose_backend(); - if (current_backend < 0) { - error ("Could not select a back end"); - close (new); - } - - /* Connect to the backend. If this fails then we'll sleep - * and re-enter the backend selection loop instead of returning. - * In the loop we may need to decide to quit after all. */ - msg ("trying back end %s, port %d", - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port); - if ( (backend_sock = backend_connect()) < 0 ) { - warning ("failed to connect to server %s:%d (ret %d)", - servicereport->backendstate[current_backend].actual_server, - servicereport->backendstate[current_backend].actual_port, - backend_sock); - continue; - } - - /* Got a live one! */ - program_stage = stage_serving; - for (i = 0; relevant_sigs[i]; i++) - signal (relevant_sigs[i], interrupt); - set_program_title ("crossroads - Service %s: serving %s to %s", - activeservice->name, client_ip, - activeservice->backend[current_backend].name); - - /* Child branch: piggyback to and fro. - * This one never returns. */ - incr_client_count(); - log_activity_start (); - flag_verbose = activeservice->backend[current_backend].verbosity; - copysockets (new, backend_sock); - - exit (0); /* superfluous, but what the heck */ - } - } -} diff --git a/src/lib/thruputlog.c b/src/lib/thruputlog.c @@ -1,58 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void thruputlog (unsigned char const *buf, int len, CopyDirection dir) { - static double d_start = 0; - double d_now; - int i; - FILE *f; - - if (current_backend < 0 || - ! activeservice->backend[current_backend].thruputfile || - ! *activeservice->backend[current_backend].thruputfile) - return; - - /* Initialize timer if necessary. Get current time. */ - if (!d_start) - d_start = timeofday(); - d_now = timeofday(); - - /* Get a handle on the reporting log. */ - if ( (! (f = fopen (activeservice->backend[current_backend].thruputfile, - "a"))) && - (! (f = fopen (activeservice->backend[current_backend].thruputfile, - "w"))) ) { - warning ("Cannot write %s: %s", - activeservice->backend[current_backend].thruputfile, - strerror(errno)); - return; - } - -#if HAVE_FLOCK == 1 - flock (fileno(f), LOCK_EX); -#elif HAVE_LOCKF == 1 - lockf (fileno(f), F_LOCK, 0); -#endif - - /* Report the activity. */ - fprintf (f, "%7.7d %15f %c %8d ", - getpid(), - d_now - d_start, - dir == dir_client_to_server ? 'C' : 'B', - len); - for (i = 0; i < 100 && i < len; i++) - fputc (isprint (buf[i]) ? buf[i] : '.', f); - fputc ('\n', f); - -#if defined HAVE_FLOCK - flock (fileno(f), LOCK_UN); -#elif defined HAVE_LOCKF - lockf (fileno(f), F_ULOCK, 0); -#endif - - fclose (f); -} diff --git a/src/lib/timeofday.c b/src/lib/timeofday.c @@ -1,17 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -double timeofday () { - struct timeval tv; - double ret; - - if (gettimeofday (&tv, 0)) - error ("Failed to get the time of day: %s", strerror(errno)); - ret = (double)tv.tv_sec + (double)tv.tv_usec / 1000000; - return (ret); -} diff --git a/src/lib/trafficlog.c b/src/lib/trafficlog.c @@ -1,66 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void trafficlog (unsigned char const *buf, int len, CopyDirection dir) { - int i, j, n = 0; - char disp[17]; - FILE *f; - - if (current_backend < 0 || - ! activeservice->backend[current_backend].dumpfile || - ! *activeservice->backend[current_backend].dumpfile) - return; - - if ( (! (f = fopen (activeservice->backend[current_backend].dumpfile, - "a"))) && - (! (f = fopen (activeservice->backend[current_backend].dumpfile, - "w"))) ) { - warning ("Cannot write %s: %s", - activeservice->backend[current_backend].dumpfile, - strerror(errno)); - return; - } - -#if HAVE_FLOCK == 1 - flock (fileno(f), LOCK_EX); -#elif HAVE_LOCKF == 1 - lockf (fileno(f), F_LOCK, 0); -#endif - - for (i = 0; i < len; i++) { - if (! n) - fprintf (f, "%4.4d %c %4.4x ", getpid(), - dir == dir_client_to_server ? 'C' : 'B', i); - fprintf (f, " %2.2x", buf[i]); - disp[n] = isprint(buf[i]) ? buf[i] : '.'; - - n++; - - if (n == 16) { - fputc (' ', f); - for (j = 0; j < n; j++) - fputc (disp[j], f); - fputc ('\n', f); - n = 0; - } - } - - for (j = n; j < 16; j++) - fprintf (f, " "); - fputc (' ', f); - for (j = 0; j < n; j++) - fputc (disp[j], f); - fputc ('\n', f); - -#if defined HAVE_FLOCK - flock (fileno(f), LOCK_UN); -#elif defined HAVE_LOCKF - lockf (fileno(f), F_ULOCK, 0); -#endif - - fclose (f); -} diff --git a/src/lib/uidassume.c b/src/lib/uidassume.c @@ -1,28 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void uid_assume() { - uid_set = 0; - gid_set = 0; - - if (activeservice->uid) { - if (activeservice->gid) { - gid_org = getgid(); - if (setegid (activeservice->gid)) - error ("cannot set effective gid to %d: %s", - activeservice->gid, - strerror(errno)); - gid_set++; - } - uid_org = getuid(); - if (seteuid (activeservice->uid)) - error ("cannot set effective uid to %d: %s", - activeservice->uid, strerror(errno)); - uid_set++; - } -} diff --git a/src/lib/uidrestore.c b/src/lib/uidrestore.c @@ -1,13 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void uid_restore () { - if (uid_set) - seteuid (uid_org); - if (gid_set) - setegid (gid_org); -} diff --git a/src/lib/unlockreporter.c b/src/lib/unlockreporter.c @@ -1,26 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void unlock_reporter() { - static int warning_issued = 0; - - struct sembuf buf = { - 0, /* semaphore number */ - -1, /* subtract 1 */ - 0, /* no special flags */ - }; - - /* msg ("UnLocking reporter memory"); */ - if ( (! warning_issued++) && (semop (semid, &buf, 1) < 0) ) { - warning ("Failed to unlock reporter memory (stage %s): %s", - stage_to_string (program_stage), strerror(errno)); - if (program_stage != stage_serving) - _exit (1); - else - warning (".. but continuing serving this TCP connection"); - } -} diff --git a/src/lib/usage.c b/src/lib/usage.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - - -#include "../crossroads.h" -#include "usage.h" - -void usage () { - fprintf (stderr, "\n" USAGETEXT "\n", VER, AUTHORNAME, YEARS, - MAINTAINERNAME, MAINTAINEREMAIL, - DEFAULT_TCP_BUFSZ, DNS_CACHETTL); - exit (1); -} diff --git a/src/lib/usage.txt b/src/lib/usage.txt @@ -1,49 +0,0 @@ -This is Crossroads %s, a load balancer and fail-over utility for TCP. -Copyright (c) %s / e-tunity %s ff. All rights reserved. -Maintained by %s / %s. -For distributions and updates, visit http://crossroads.e-tunity.com. - -Usage: - ** Controlling the daemon: ** - crossroads [flags] start: start all services - crossroads [flags] status [SERVICE [BACKEND]]: show the services - and their status, optionally of stated service and/or back end - crossroads [flags] stop: stop all services - crossroads [flags] restart: stop and then start - crossroads [flags] tell SERVICE BACKEND ACTION ARGS - Backend BACKEND of service SERVICE is controlled. The ACTION - and ARGS can be: - 'state' NEWSTATE: Sets the state of the back end. The NEWSTATE - must be 'available'/'up', 'unavailable', or 'down'. - 'server' ADDRESS: Sets the back end address, which must be a - hostname, colon, and a port. - ** Configuration related: ** - crossroads [flags] configtest: verify that the config is OK - crossroads [flags] services: show the configured services - -Supported flags: - -a: Logs starting and finishing activity to syslog - -b: Writes binary options and configuration to stdout (debugging only) - -B BUFSZ Defines the network buffer size, default %d bytes - -C: Shows compile-time configuration and stops - -c CONFIG: Uses the named configuration - -d TTL: Specifies the time to live of cached DNS entries, - default %d (seconds, 0 is no caching) - -h: Logs timings of HTTP processing to syslog - -l FAC: Specifies the openlog(3) facility to use when logging. Default - is LOG_DAEMON. Allowed values are 0..7 for LOG_LOCAL0 to - LOG_LOCAL7. - -m PERM: Sets permissions for shm access to PERM, which is an - octal number. Defaults to 0644. - -P: Parser and lexer actions will be verbose (debugging only) - -p: Process title will not be altered; 'ps' will show - 'crossroads-daemon' instead of custom title - -s: Sloppy binding to the listen port of each service (if the port - is busy, Crossroads will wait for it to become free). - -t: 'status' output is shown as a tabular availability view - -x: 'status' output is shown as an XML document - -X XSLT: Embed reference to XSLT in 'crossroads -x status' - -v: Enables verbosity upon startup. Other verbosity (services - and back ends) is controlled in the configuration. - -V: Shows the version ID and stops. - -?: Shows this message. diff --git a/src/lib/vsyslog.c b/src/lib/vsyslog.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -#if HAVE_VSYSLOG == 0 -void vsyslog (int pri, char const *fmt, va_list args) { - char *msg; - - msg = str_vprintf (fmt, args); - syslog (pri, "%s", msg); - free (msg); -} -#endif diff --git a/src/lib/wakeuphandler.c b/src/lib/wakeuphandler.c @@ -1,93 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -/* For more verbosity of the wake up handler, undefine: */ -/* #define DEBUG */ - -void wakeup_handler () { - int sock, i; - Backendavail av, prev; - - - /* Set the stage and any signals that we want. */ - program_stage = stage_retrying; - for (i = 0; relevant_sigs[i]; i++) - signal (relevant_sigs[i], interrupt); - - /* Forever, until a signal kills us, or Sol explodes, causing - * massive electromagnetic pulses that fry my CPU. - */ - while (1) { - /* Wait for the alarm to go off. */ - if (activeservice->rev_interval) - sleep (activeservice->rev_interval); - else - sleep (activeservice->check_interval); - - /* Now do our stuff. */ - for (current_backend = 0; - current_backend < activeservice->nbackend; - current_backend++) { - - /* If we're in the REVIVING mode and the back end isn't - * unavailable, then we don't need to check it. Also, - * back ends marked DOWN on purpose are skipped. */ - prev = servicereport->backendstate[current_backend].actual_avail; - if ( (prev == st_down) || - (activeservice->rev_interval && prev != st_unavailable) ) - continue; - -# ifdef DEBUG - msg ("verifying back end %s, old state %s", - activeservice->backend[current_backend].name, - state_to_string(prev)); -# endif - - /* Mark state as WAKING */ - lock_reporter(); - servicereport->backendstate[current_backend].actual_avail = - st_waking; - unlock_reporter(); - - if (! activeservice->check_cmd) { - /* In TCP-only mode: do the connect */ - if ( (sock = backend_connect()) >= 0 ) { - close (sock); - av = st_available; - } else - av = st_unavailable; - } else { - /* In checkscript mode: do the checking */ - if (!sysrun (activeservice->check_cmd)) - av = st_available; - else - av = st_unavailable; - } - - /* Set state accordingly */ - if (prev != av) { - if (av == st_available) - warning ("Backend %s of service %s has woken up", - activeservice->backend[current_backend].name, - activeservice->name); - else - warning ("Backend %s of service %s has died", - activeservice->backend[current_backend].name, - activeservice->name); - } - - /* Store the new state. If the new state is AVAILABLE - * then reset the error count (used by retries config value). */ - lock_reporter(); - servicereport->backendstate[current_backend].actual_avail = av; - if (av == st_available) - servicereport->backendstate[current_backend].fail = 0; - unlock_reporter(); - } - } -} diff --git a/src/lib/warning.c b/src/lib/warning.c @@ -1,32 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void warning (char const *fmt, ...) { - va_list args; - char *str; - - if (!fmt || !*fmt) - return; - - va_start (args, fmt); - str = str_vprintf (fmt, args); - if (!daemonized) { - if (activeservice) - fprintf (stderr, "WARNING, Service %s: %s", - activeservice->name, str); - else - fprintf (stderr, "WARNING: %s\n", str); - } else { - if (activeservice) - writelog (LOG_ERR, "WARNING, Service %s: %s", - activeservice->name, str); - else - writelog (LOG_ERR, "WARNING: %s", str); - } - free (str); -} diff --git a/src/lib/writelog.c b/src/lib/writelog.c @@ -1,15 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void writelog (int prio, char const *fmt, ...) { - va_list args; - - va_start (args, fmt); - if (!logstarted++) - openlog ("crossroads", LOG_PID, opt.log_facility); - vsyslog (prio, fmt, args); -} diff --git a/src/lib/xcalloc.c b/src/lib/xcalloc.c @@ -1,18 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ - -#include "../crossroads.h" - -void *xcalloc (unsigned sz) { - void *ret; - - if (!sz) - return (0); - - ret = xmalloc (sz); - memset (ret, 0, sz); - return (ret); -} diff --git a/src/lib/xmalloc.c b/src/lib/xmalloc.c @@ -1,10 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void *xmalloc (unsigned sz) { - return (xrealloc (0, sz)); -} diff --git a/src/lib/xrealloc.c b/src/lib/xrealloc.c @@ -1,27 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -void *xrealloc (void *block, unsigned sz) { - void *newblock; - - /* msg ("Realloc: block=%x, sz=%u", block, sz); */ - if (! sz) { - free (block); - newblock = 0; - /* msg ("Realloc: freed %x", block); */ - } else if (!block) { - if (! (newblock = malloc (sz)) ) - error ("Out of memory (while allocating %u bytes)", sz); - /* msg ("Realloc: malloc'd %x (%u bytes)", newblock, sz); */ - } else { - if (! (newblock = realloc (block, sz)) ) - error ("Out of memory (while increasing block to %u bytes)", sz); - /* msg ("Realloc'd %x to %x (%u bytes)", block, newblock, sz); */ - } - return (newblock); -} - diff --git a/src/lib/xstrcat.c b/src/lib/xstrcat.c @@ -1,19 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -char *xstrcat (char *what, char const *rest) { - if (!what || !*what) - return (xstrdup (rest)); - if (!rest || !*rest) - return (what); - - if (! (what = realloc (what, strlen(what) + strlen(rest) + 1)) ) - error ("Out of memory (while adding '%s' to '%s'", - rest, what); - strlcat (what, rest, strlen(what) + strlen(rest) + 1); - return (what); -} diff --git a/src/lib/xstrcatch.c b/src/lib/xstrcatch.c @@ -1,14 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -char *xstrcatch (char *what, char ch) { - char buf[2]; - - buf[0] = ch; - buf[1] = 0; - return (xstrcat (what, buf)); -} diff --git a/src/lib/xstrdup.c b/src/lib/xstrdup.c @@ -1,16 +0,0 @@ -/************************************************************************* - * This file is part of Crosroads 1.81, a load balancer and fail over - * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. - * Visit http://crossroads.e-tunity.com for information. - *************************************************************************/ -#include "../crossroads.h" - -char *xstrdup (char const *what) { - char *ret; - - if (!what) - return (0); - if (! (ret = strdup (what)) ) - error ("Out of memory (while copying '%s')", what); - return (ret); -} diff --git a/test/Makefile b/test/Makefile @@ -1,30 +0,0 @@ -include $(BASE)/etc/Makefile.def -include $(BASE)/etc/Makefile.conf - -foo: hashtest - @echo 'Running simplistic tests...' - @echo - @for f in t*.conf ; do \ - echo "Testing $$f: should succeed" ; \ - echo "TEST: ../src/crossroads/crossroads -vc $$f services" ; \ - ../src/crossroads/crossroads -vc $$f services || exit 1 ; \ - echo "$$f succeeded, good." ; \ - echo ; \ - done - @for f in bad*.conf ; do \ - echo "Testing $$f: should fail" ; \ - echo "TEST: ../src/crossroads/crossroads -vc $$f services" ; \ - ../src/crossroads/crossroads -vc $$f services && exit 1 ; \ - echo "$$f failed, good." ; \ - echo ; \ - done - @echo - @echo 'All tests have completed successfully.' - @echo 'Run test/hashtest to test the hashing algorithm.' - @echo - -hashtest: hashtest.c - $(CC) $(DEFS) -o hashtest hashtest.c -L../src/lib -lcrossroads - -clean: - rm -f *.o hashtest diff --git a/test/allowfrom.conf b/test/allowfrom.conf @@ -1,15 +0,0 @@ -/* Test of 'allow from' on a client level. */ - -#define DEBUG on - -service testservice { - port 2222; - verbosity DEBUG; - revivinginterval 3; - allowfrom 127/8; - - backend sshd { - server localhost:22; - verbosity DEBUG; - } -} diff --git a/test/bad01.conf b/test/bad01.conf @@ -1,16 +0,0 @@ -/* Bad configuration - * multiple service 'bla' definitions */ -service bla { - port 10000; - backend one { - server x:80; - } -} - -service bla { - port 10001; - backend one { - server x:80; - } -} - diff --git a/test/bad02.conf b/test/bad02.conf @@ -1,11 +0,0 @@ -/* Bad configuration - * multiple backend 'one' definitions */ -service bla { - port 10000; - backend one { - server x:80; - } - backend one { - server x:80; - } -} diff --git a/test/bad03.conf b/test/bad03.conf @@ -1,3 +0,0 @@ -/* Bad configuration - * no services */ - diff --git a/test/bad04.conf b/test/bad04.conf @@ -1,5 +0,0 @@ -/* Bad configuration - * no back ends */ -service bla { - port 10000; -} diff --git a/test/bad05.conf b/test/bad05.conf @@ -1,9 +0,0 @@ -/* Bad configuration - * no back end port */ -service bla { - port 10000; - backend one { - server x; - } -} - diff --git a/test/bad06.conf b/test/bad06.conf @@ -1,8 +0,0 @@ -/* Bad configuration - * no service port */ -service bla { - backend one { - server x:10000; - } -} - diff --git a/test/bad07.conf b/test/bad07.conf @@ -1,11 +0,0 @@ -/* Bad configuration - * bad define 1 */ - -#define -service bla { - port 80; - backend one { - server x:10000; - } -} - diff --git a/test/bad08.conf b/test/bad08.conf @@ -1,11 +0,0 @@ -/* Bad configuration - * bad define 2 */ - -#define bla -service bla { - port 80; - backend one { - server x:10000; - } -} - diff --git a/test/bad09.conf b/test/bad09.conf @@ -1,5 +0,0 @@ -/* Bad configuration - * bad define 3 */ - -#define - diff --git a/test/bad10.conf b/test/bad10.conf @@ -1,6 +0,0 @@ -/* Bad configuration - * bad include */ - -#include - - diff --git a/test/bad11.conf b/test/bad11.conf @@ -1,12 +0,0 @@ -/* Bad configuration - * bad statement in bad11a.inc */ - -#include "bad11a.inc" - -service bla { - port 80; - backend one { - server localhost:91; - } -} - diff --git a/test/bad11a.inc b/test/bad11a.inc @@ -1,6 +0,0 @@ -service tester { - port xyzzy; - backend two { - server localhost:80; - } -} diff --git a/test/bad12.conf b/test/bad12.conf @@ -1,12 +0,0 @@ -/* Bad configuration - * bad statement in bad12.inc */ - -#include "bad12a.inc" - -service bla { - port xyzzy; - backend one { - server localhost:91; - } -} - diff --git a/test/bad12a.inc b/test/bad12a.inc @@ -1,6 +0,0 @@ -service tester { - port 10000; - backend two { - server localhost:80; - } -} diff --git a/test/bad13.conf b/test/bad13.conf @@ -1,40 +0,0 @@ -/* Bad configuration - simple parse error */ - -service test { - port 10001; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server localhost; - port 10000; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} - -error is here diff --git a/test/bad14.conf b/test/bad14.conf @@ -1,40 +0,0 @@ -/* Bad configuration - both revivinginterval and checkinterval present */ - -service test { - port 10001; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - revivinginterval 12; - checkinterval 13; - - backend a { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server localhost; - port 10000; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/bad15.conf b/test/bad15.conf @@ -1,39 +0,0 @@ -/* Bad configuration - syntax error in checkinterval */ - -service test { - port 10001; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - checkinterval 13 xyzzy; - - backend a { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server localhost; - port 10000; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/bad16.conf b/test/bad16.conf @@ -1,44 +0,0 @@ -/* bad state def */ - -#define SERVICENAME test -#define SERVICEPORT 10001 -#define BACKENDSERVER localhost -#define BACKPORT 80 - -service test { - port SERVICEPORT; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - checkinterval 5 externalhandler "myscript"; - - backend a { - server BACKENDSERVER; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server BACKENDSERVER:BACKPORT; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server BACKENDSERVER; - port BACKPORT; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - state blabla; - } -} diff --git a/test/bad17.conf b/test/bad17.conf @@ -1,14 +0,0 @@ -/* - * Two service statements with same ports and same bindto's. - */ -service one { - port 80; - bindto 1.2.3.4; - backend one { server 10.1.1.1:80; } -} - -service two { - port 80; - bindto 1.2.3.4; - backend two { server 10.1.1.1:80; } -} diff --git a/test/bad18.conf b/test/bad18.conf @@ -1,13 +0,0 @@ -/* - * Two service statements with same ports and only one has a bindto. - */ -service one { - port 80; - bindto 1.2.3.4; - backend one { server 10.1.1.1:80; } -} - -service two { - port 80; - backend two { server 10.1.1.1:80; } -} diff --git a/test/bad19.conf b/test/bad19.conf @@ -1,12 +0,0 @@ -/* - * Two service statements with same ports and no bindto's. - */ -service one { - port 80; - backend one { server 10.1.1.1:80; } -} - -service two { - port 80; - backend two { server 10.1.1.1:80; } -} diff --git a/test/bad20.conf b/test/bad20.conf @@ -1,13 +0,0 @@ -/* - * Two service statements with same ports and only one has a bindto. - */ -service one { - port 80; - backend one { server 10.1.1.1:80; } -} - -service two { - port 80; - bindto 1.2.3.4; - backend two { server 10.1.1.1:80; } -} diff --git a/test/bad21.conf b/test/bad21.conf @@ -1,9 +0,0 @@ -/* HTTP commands with 'type any' */ -service one { - port 80; - backend one { - server 10.1.1.1:80; - addclientheader "X-bla: blerk"; - } -} - diff --git a/test/bad22.conf b/test/bad22.conf @@ -1,8 +0,0 @@ -/* A back end with a httptiminglog directive, but the service isn't http */ -service test { - port 2000; - backend one { - server localhost:2001; - httptiminglog /tmp/logfile; - } -} diff --git a/test/bad23.conf b/test/bad23.conf @@ -1,9 +0,0 @@ -/* A back end with maxconnctions, but there is an external handler */ -service test { - port 2000; - dispatchmode externalhandler 'bla'; - backend one { - server localhost:2001; - maxconnections 20; - } -} diff --git a/test/breakingclient b/test/breakingclient @@ -1,58 +0,0 @@ -#!/usr/bin/perl - -use strict; -$|++; - -sub sigcatcher { - # warn ("$$: Stopping wgets\n"); - system ("killall -9 wget"); - exit (0); -} - -$SIG{HUP} = \&sigcatcher; - -system ("mkdir -p /tmp/$$") and die ("Cannot mkdir $$"); -chdir ("/tmp/$$") or die ("Cannot chdir to /tmp/$$: $!\n"); -my $run = 0; -while (1) { - print ("$$ Run: ", ++$run); - system ("rm -rf /tmp/$$/localhost* >/dev/null 2>&1"); - my $pid = fork(); - die ("Fork failed: $!\n") unless (defined ($pid)); - if ($pid) { - sleep (3); - system ("killall -9 wget"); - kill ('HUP', $pid); - wait(); - my @fls = glob ("/tmp/$$/localhost:10000/~karel/imagepage/img*png"); - print (" ", $#fls + 1, " images retrieved\n"); - sleep (1); - } else { - open (my $if, - "wget -r -S --http-user=user --http-password=password " . - "--no-cache " . - "http://localhost:10000/~karel/imagepage/index.php 2>&1 |") - or die ("Cannot start wget\n"); - my $via = ''; - while (my $line = <$if>) { - # print ($line); - chomp ($line); - if ($line =~ /Set-Cookie/ and $line =~ /XRID/) { - $line =~ s{.*XRID=}{}; - $line =~ s{;.*}{}; - if ($via eq '') { - $via = $line; - } elsif ($via ne $line) { - warn ("Warning: via changed from $via to $line\n"); - } - } elsif ($line =~ /HTTP\//) { - $line =~ s{.*HTTP/[01. ]*}{}; - $line =~ s{ .*}{}; - warn ("Warning: got status $line\n") - if ($line != 200 and $line != 404); - } - } - close ($if); - exit(0); - } -} diff --git a/test/chunkedserver b/test/chunkedserver @@ -1,110 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Socket; -use POSIX; - -# Verbose messaging -sub msg { - print STDERR ("chunkedserver: ", @_); -} - -# Child process catcher -sub reaper { - while ((my $waitedpid = waitpid(-1,WNOHANG)) > 0) { - msg ("Child $waitedpid done", - ($? ? " with exit $?" : ''), - "\n"); - } - $SIG{CHLD} = \&reaper; -} - -# Connection handler -sub handleconnection () { - my $pid; - - # Daemonize - if (!defined ($pid = fork())) { - msg ("Can't fork: $!\n"); - return; - } elsif ($pid) { - # Parent branch - msg ("Connection handler started as pid $pid\n"); - return; - } - - # Child branch - while (defined (my $line = <Client>)) { - chomp ($line); - $line =~ s/\r//; - last if ($line eq ''); - msg ("Client says: [$line]\n"); - } - - msg ("Sending response\n"); - print Client ("HTTP/1.1 200 OK\r\n", - "Connection: close\r\n", - "Transfer-Encoding: chunked\r\n", - "Content-Type: text/plain\r\n", - "\r\n", - "5\r\n", - "Hello\r\n", - "1\r\n", - " \r\n", - "5\r\n", - "World\r\n", - "2\r\n", - "!\n\r\n", - "0\r\n", - "\r\n"); - exit (0); -} - -# Daemon server -sub serve ($) { - my $port = shift; - - # Create the tcp service. - socket (Server, PF_INET, SOCK_STREAM, getprotobyname ('tcp')) - or die ("Can't create chunkedserver socket: $!\n"); - setsockopt (Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) - or die ("Can't set socket options: $!\n"); - bind (Server, sockaddr_in ($port, INADDR_ANY)) - or die ("Can't bind to port: $!\n"); - listen (Server, SOMAXCONN) - or die ("Can't listen to socket: $!\n"); - msg ("Chunkedserver started on port $port.\n"); - - $SIG{CHLD} = \&reaper; - $SIG{INT} = sub { - msg ("Interrupt caught, terminating..\n"); - exit(0); - }; - - while (1) { - for (my $waitedpid = 0; - (my $paddr = accept (Client, Server)) || $waitedpid; - $waitedpid = 0, close (Client)) { - next if ($waitedpid and not $paddr); - my ($port, $iaddr) = sockaddr_in ($paddr); - my $name = gethostbyaddr ($iaddr, AF_INET); - - msg ("Connection from $name/", inet_ntoa ($iaddr), ":$port\n"); - - handleconnection (); - } - } -} - -# Main starts here -if ($#ARGV != 1) { - die <<"ENDUSAGE"; - -Usage: chunkedserver nsec portnr - -ENDUSAGE -} - -alarm (shift (@ARGV)); -serve (shift (@ARGV)); - diff --git a/test/client b/test/client @@ -1,55 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Socket; - -if ($#ARGV != 2) { - die <<"ENDUSAGE"; - -Usage: client ntimes host port - -ENDUSAGE -} - -my ($ntimes, $host, $port) = @ARGV; - -print ("Starting client loop to query $host:$port..\n"); -select (undef, undef, undef, 0.2); - -my %hits = (a => 0, b => 0, c => 0, unknown => 0, errors => 0); - -for my $n (1..$ntimes) { - open (my $if, "wget -vSO- http://$host:$port 2>&1 |") - or die ("Can't start wget: $!\n"); - my ($a, $b, $c, $unk, $err); - $err = 1; - while (my $msg = <$if>) { - chomp ($msg); - # print ("Server says: [$msg]\n"); - if ($msg =~ /HTTP.*200 OK/ or $msg =~ /Hello World/) { - $err = 0; - } elsif ($msg =~ /Backend_A/) { - $a = 1; - } elsif ($msg =~ /Backend_B/) { - $b = 1; - } elsif ($msg =~ /Backend_C/) { - $c = 1; - } - } - close ($if); - - $unk = 1 if ($a == 0 and $b == 0 and $c == 0); - - $hits{a} += $a; - $hits{b} += $b; - $hits{c} += $c; - $hits{unknown} += $unk; - $hits{errors} += $err; - - for my $k (sort (keys (%hits))) { - print ("$k=$hits{$k} "); - } - print ("\n"); - - # select (undef, undef, undef, 0.2); -}; diff --git a/test/extcheck.conf b/test/extcheck.conf @@ -1,14 +0,0 @@ -/* This verifies an externalhandler for backend availability checks. - * After hitting XR on port 30000, the test-backend will be marked - * available. Within 5 secs, XR will decide that it's up after all, - * cuz the externalhandler says so. */ - -service test { - verbosity on; - port 30000; - checkinterval 5 externalhandler "echo up | grep up"; - backend test { - verbosity on; - server localhost:30001; - } -} diff --git a/test/extdispatcher b/test/extdispatcher @@ -1,14 +0,0 @@ -#!/usr/bin/perl - -use strict; - -sub msg { - open (my $of, ">>/tmp/extdispatcher.log") or return; - print $of (@_); -} - -# Main -msg ("Run started, arguments: @ARGV\n"); - -# Reply that backend "one" is the right one to choose. -print ("one\n"); diff --git a/test/extdispatcher.conf b/test/extdispatcher.conf @@ -1,19 +0,0 @@ -/* Test of an external dispatcher */ - -#define DEBUG on - -service test { - port 20000; - verbosity DEBUG; - dispatchmode externalhandler - "/usr/local/src/crossroads/test/extdispatcher %r %s"; - - backend one { - server localhost:20001; - verbosity DEBUG; - } - backend two { - server localhost:20002; - verbosity DEBUG; - } -} diff --git a/test/hashtest.c b/test/hashtest.c @@ -1,45 +0,0 @@ -#include "../src/crossroads.h" - -int hits[100]; - -int main (int argc, char **argv) { - int max, b1, b2, b3, b4, index, n = 0; - char buf[255]; - - if (argc == 2) { - if (! (max = atoi (argv[1])) ) { - fprintf (stderr, "%s is not a number (1..100 required)\n", argv[1]); - exit (1); - } - for (b1 = 1; b1 <= 255; b1++) - for (b2 = 1; b2 <= 255; b2++) - for (b3 = 1; b3 <= 255; b3++) - for (b4 = 1; b4 <= 255; b4++) { - sprintf (buf, "%d.%d.%d.%d", b1, b2, b3, b4); - index = hashpjw (buf) % max; - hits[index]++; - if (! (n % 100000)) { - printf ("\r%-30s %d", buf, index); - fflush (stdout); - n = 0; - } - n++; - } - - for (index = 0; index < max; index++) - printf ("\n\ndone\n" - "index %d: %d hits\n", index, hits[index]); - } else if (argc == 3) { - if (! (max = atoi (argv[1])) ) { - fprintf (stderr, "%s is not a number (1..100 required)\n", argv[1]); - exit (1); - } - printf ("%d\n", hashpjw (argv[2]) % max); - } else { - fprintf (stderr, "Usage: hashtest nbackends [ip-address]\n"); - exit (1); - } - - return (0); -} - diff --git a/test/maxcon.conf b/test/maxcon.conf @@ -1,27 +0,0 @@ -/* Test of 'maxconnections' on a client level. - * Each new connection must be handled by a different client, until - * the connections are exhausted. */ - -#define DEBUG on - -service threeallowed { - port 2222; - verbosity DEBUG; - revivinginterval 3; - - backend v01 { - server localhost:22; - maxconnections 1; - verbosity DEBUG; - } - backend v02 { - server localhost:22; - maxconnections 1; - verbosity DEBUG; - } - backend v03 { - server localhost:22; - maxconnections 1; - verbosity DEBUG; - } -} diff --git a/test/runtest b/test/runtest @@ -1,65 +0,0 @@ -#!/usr/bin/perl - - -# Small test runner. For debugging only. - -use strict; - -my $tests = <<'ENDTEST'; - ../src/crossroads/crossroads -c t01.conf start - ./server 12 10000 & - ./client 200 localhost 10001 - sleep 5 - ../src/crossroads/crossroads -c t01.conf status - ../src/crossroads/crossroads -c t01.conf stop - - ../src/crossroads/crossroads -c t02.conf start - ./server 12 10000 & - ./client 200 localhost 10001 - sleep 5 - ../src/crossroads/crossroads -c t02.conf status - ../src/crossroads/crossroads -c t02.conf stop - - ../src/crossroads/crossroads -c t03.conf start - time ssh -p23 root@localhost; true - ../src/crossroads/crossroads -c t03.conf status - ../src/crossroads/crossroads -c t03.conf stop - - ../src/crossroads/crossroads -c t04.conf start - ./server 12 10000 & - ./client 200 localhost 10001 - sleep 5 - ../src/crossroads/crossroads -c t04.conf status - ../src/crossroads/crossroads -c t04.conf stop - - ../src/crossroads/crossroads -c t06.conf start - ./server 12 10000 & - ./client 200 localhost 10001 - sleep 5 - ../src/crossroads/crossroads -c t06.conf status - ../src/crossroads/crossroads -c t06.conf stop - -ENDTEST - -sub runtest { - for my $cmd (@_) { - $cmd =~ s/^ *//; - print STDERR ("runtest: [$cmd]\n"); - system ($cmd) and die ("runtest: [$cmd] has failed\n"); - } - print ("Press ENTER for next test\n"); - <STDIN>; -} - -# Main -my @cmds = (); -for my $s (split (/\n/, $tests)) { - if ($s ne '') { - push (@cmds, $s); - } else { - if ($#cmds > -1) { - runtest (@cmds); - @cmds = (); - } - } -} diff --git a/test/server b/test/server @@ -1,100 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Socket; -use POSIX; - -# Verbose messaging -sub msg { - # print STDERR ("server: ", @_); -} - -# Child process catcher -sub reaper { - while ((my $waitedpid = waitpid(-1,WNOHANG)) > 0) { - msg ("Child $waitedpid done", - ($? ? " with exit $?" : ''), - "\n"); - } - $SIG{CHLD} = \&reaper; -} - -# Connection handler -sub handleconnection () { - my $pid; - - # Daemonize - if (!defined ($pid = fork())) { - msg ("Can't fork: $!\n"); - return; - } elsif ($pid) { - # Parent branch - msg ("Connection handler started as pid $pid\n"); - return; - } - - # Child branch - while (defined (my $line = <Client>)) { - chomp ($line); - $line =~ s/\r//; - last if ($line eq ''); - msg ("Client says: [$line]\n"); - } - - msg ("Sending response\n"); - my $msg = "Hello World!\n"; - print Client ("HTTP/1.0 200 OK\r\n", - "Content-length: ", length($msg), "\r\n", - "\r\n", - $msg); - exit (0); -} - -# Daemon server -sub serve ($) { - my $port = shift; - - # Create the tcp service. - socket (Server, PF_INET, SOCK_STREAM, getprotobyname ('tcp')) - or die ("Can't create server socket: $!\n"); - setsockopt (Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) - or die ("Can't set socket options: $!\n"); - bind (Server, sockaddr_in ($port, INADDR_ANY)) - or die ("Can't bind to port: $!\n"); - listen (Server, SOMAXCONN) - or die ("Can't listen to socket: $!\n"); - msg ("Server started on port $port.\n"); - - $SIG{CHLD} = \&reaper; - $SIG{INT} = sub { - msg ("Interrupt caught, terminating..\n"); - exit(0); - }; - - while (1) { - for (my $waitedpid = 0; - (my $paddr = accept (Client, Server)) || $waitedpid; - $waitedpid = 0, close (Client)) { - next if ($waitedpid and not $paddr); - my ($port, $iaddr) = sockaddr_in ($paddr); - my $name = gethostbyaddr ($iaddr, AF_INET); - - msg ("Connection from $name/", inet_ntoa ($iaddr), ":$port\n"); - - handleconnection (); - } - } -} - -# Main starts here -if ($#ARGV != 1) { - die <<"ENDUSAGE"; - -Usage: server nsec portnr - -ENDUSAGE -} - -alarm (shift (@ARGV)); -serve (shift (@ARGV)); - diff --git a/test/t01.conf b/test/t01.conf @@ -1,35 +0,0 @@ -service test { - port 10001; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server localhost; - port 10000; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t02.conf b/test/t02.conf @@ -1,36 +0,0 @@ -service test { - port 10001; - type http; - bindto any; - revivinginterval 5; - dispatchmode random; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost; - verbosity on; - port 10000; - weight 2; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server localhost; - verbosity on; - port 10000; - weight 3; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server localhost; - verbosity on; - port 10000; - weight 3; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t03.conf b/test/t03.conf @@ -1,13 +0,0 @@ -/* Another testing scenario. - * This one forwards port 23 to localhost:22 (sshd), and logs out - * after 10 secs of inactivity. */ - -service ssh { - port 23; - connectiontimeout 10; - verbosity true; - backend a { - verbosity true; - server localhost:22; - } -} diff --git a/test/t04.conf b/test/t04.conf @@ -1,35 +0,0 @@ -service test { - port 10001; - type http; - revivinginterval 5; - dispatchmode roundrobin; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost; - port 10000; - verbosity on; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - addserverheader "X-Real-IP: %r"; - } - - backend b { - server localhost; - port 10000; - verbosity on; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - addserverheader "X-Real-IP: %r"; - } - - backend c { - server localhost; - port 10000; - verbosity on; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - addserverheader "X-Real-IP: %r"; - } -} diff --git a/test/t05.conf b/test/t05.conf @@ -1,31 +0,0 @@ -service test { - port 10001; - type http; - bindto any; - revivinginterval 5; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost; - port 10000; - weight 2; - decay 5; - } - - backend b { - server localhost; - port 10000; - weight 2; - decay 5; - } - - backend c { - server localhost; - port 10000; - weight 3; - decay 5; - - } -} diff --git a/test/t06.conf b/test/t06.conf @@ -1,44 +0,0 @@ -service test { - port 10001; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - setclientheader "Host: undisclosed-webserver"; - setclientheader "X-Timestamp: %t"; - setserverheader "X-Real-IP: %r"; - } - - backend b { - server localhost; - port 10000; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - setclientheader "Host: undisclosed-webserver"; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - setclientheader "X-Timestamp: %t"; - setserverheader "X-Real-IP: %r"; - } - - backend c { - server localhost; - port 10000; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - setclientheader "Host: undisclosed-webserver"; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - setclientheader "X-Timestamp: %t"; - setserverheader "X-Real-IP: %r"; - } -} diff --git a/test/t07.conf b/test/t07.conf @@ -1,13 +0,0 @@ -service t7 { - port 10000; - type http; - verbosity on; - backend t7a { - server localhost:3128; - verbosity on; - } - backend t7b { - server localhost:3128; - verbosity on; - } -} diff --git a/test/t08.conf b/test/t08.conf @@ -1,32 +0,0 @@ -service test { - port 10000; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - - backend a { - server localhost:80; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server localhost:80; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server localhost:80; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t09.conf b/test/t09.conf @@ -1,13 +0,0 @@ -#include t09a.inc - -service second { - port 10001; - backend one { - server localhost:80; - } - backend two { - server localhost:80; - } -} - -#include t09b.inc diff --git a/test/t09a.inc b/test/t09a.inc @@ -1,10 +0,0 @@ -service first { - port 10000; - backend one { - server localhost:80; - } - backend two { - server localhost:80; - } -} - diff --git a/test/t09b.inc b/test/t09b.inc @@ -1,10 +0,0 @@ -service third { - port 10002; - backend one { - server localhost:80; - } - backend two { - server localhost:80; - } -} - -\ No newline at end of file diff --git a/test/t10.conf b/test/t10.conf @@ -1,40 +0,0 @@ -#define SERVICENAME test -#define SERVICEPORT 10001 -#define BACKENDSERVER localhost -#define BACKPORT 80 - -service test { - port SERVICEPORT; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - - backend a { - server BACKENDSERVER; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server BACKENDSERVER:BACKPORT; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server BACKENDSERVER; - port BACKPORT; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t11.conf b/test/t11.conf @@ -1,41 +0,0 @@ -#define SERVICENAME test -#define SERVICEPORT 10001 -#define BACKENDSERVER localhost -#define BACKPORT 80 - -service test { - port SERVICEPORT; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - checkinterval 5; - - backend a { - server BACKENDSERVER; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server BACKENDSERVER:BACKPORT; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server BACKENDSERVER; - port BACKPORT; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t12.conf b/test/t12.conf @@ -1,41 +0,0 @@ -#define SERVICENAME test -#define SERVICEPORT 10001 -#define BACKENDSERVER localhost -#define BACKPORT 80 - -service test { - port SERVICEPORT; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - checkinterval 5 externalhandler "myscript"; - - backend a { - server BACKENDSERVER; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - } - - backend b { - server BACKENDSERVER:BACKPORT; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - } - - backend c { - server BACKENDSERVER; - port BACKPORT; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t13.conf b/test/t13.conf @@ -1,43 +0,0 @@ -#define SERVICENAME test -#define SERVICEPORT 10001 -#define BACKENDSERVER localhost -#define BACKPORT 80 - -service test { - port SERVICEPORT; - type http; - bindto any; - dispatchmode bysize; - connectiontimeout 600; - verbosity on; - checkinterval 5 externalhandler "myscript"; - - backend a { - server BACKENDSERVER; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_A; - addclientheader "Set-Cookie: BalancerID=Backend_A; Path=/"; - state down; - } - - backend b { - server BACKENDSERVER:BACKPORT; - port BACKPORT; - weight 2; - decay 5; - stickycookie BalancerID=Backend_B; - addclientheader "Set-Cookie: BalancerID=Backend_B; Path=/"; - state unavailable; - } - - backend c { - server BACKENDSERVER; - port BACKPORT; - weight 3; - decay 5; - stickycookie BalancerID=Backend_C; - addclientheader "Set-Cookie: BalancerID=Backend_C; Path=/"; - } -} diff --git a/test/t14.conf b/test/t14.conf @@ -1,42 +0,0 @@ -/* Example of a virtual server pool - see also virtpoolmgr */ - -service vpool { - port 80; - dispatchmode byconnections; - revivinginterval 3; - - /* Backend on localhost, always present */ - backend real { - server localhost:8000; - } - - /* The virtual pool: 30 on standby. The initial server setting - * is but a dymmy, it will get set using virtpoolmgr */ - backend v00 { server localhost:8000; state down; } - backend v02 { server localhost:8000; state down; } - backend v03 { server localhost:8000; state down; } - backend v04 { server localhost:8000; state down; } - backend v05 { server localhost:8000; state down; } - backend v06 { server localhost:8000; state down; } - backend v07 { server localhost:8000; state down; } - backend v08 { server localhost:8000; state down; } - backend v09 { server localhost:8000; state down; } - backend v10 { server localhost:8000; state down; } - backend v12 { server localhost:8000; state down; } - backend v13 { server localhost:8000; state down; } - backend v14 { server localhost:8000; state down; } - backend v15 { server localhost:8000; state down; } - backend v16 { server localhost:8000; state down; } - backend v17 { server localhost:8000; state down; } - backend v18 { server localhost:8000; state down; } - backend v19 { server localhost:8000; state down; } - backend v20 { server localhost:8000; state down; } - backend v22 { server localhost:8000; state down; } - backend v23 { server localhost:8000; state down; } - backend v24 { server localhost:8000; state down; } - backend v25 { server localhost:8000; state down; } - backend v26 { server localhost:8000; state down; } - backend v27 { server localhost:8000; state down; } - backend v28 { server localhost:8000; state down; } - backend v29 { server localhost:8000; state down; } -} diff --git a/test/t15.conf b/test/t15.conf @@ -1,14 +0,0 @@ -/* - * Two service statements with same ports but different bindto's. - */ -service one { - port 80; - bindto 1.2.3.4; - backend one { server 10.1.1.1:80; } -} - -service two { - port 80; - bindto 5.6.7.8; - backend two { server 10.1.1.1:80; } -} diff --git a/test/t16.conf b/test/t16.conf @@ -1,8 +0,0 @@ -/* - * 'any' binding - */ -service one { - port 25000; - bindto any; - backend one { server 10.1.1.1:80; } -} diff --git a/test/t20.conf b/test/t20.conf @@ -1,14 +0,0 @@ -/* Allow from test. Not used in the regression script, except that it must - * pass parsing. */ - -service ssh { - port 2222; - allowfrom 192.168.2.203 127/8; - // denyfrom 127.0.0.1; - verbose yes; - - backend a { - server localhost:22; - verbose yes; - } -} diff --git a/test/virtpoolmgr b/test/virtpoolmgr @@ -1,139 +0,0 @@ -#!/usr/bin/perl - -use strict; - -# Crossroads control package -# -------------------------- -package Crossroads; - -# Instantiate object. Argument can be a Crossroads calling command, defaults -# to plain 'crossroads'. -sub new { - my ($proto, $xrbin) = @_; - my $self = {}; - - # Get a snapshot of the state. - $self->{xrbin} = $xrbin || "crossroads"; - my $cmd = $self->{xrbin} . ' -x status |'; - open (my $if, $cmd) or die ("Cannot run '$cmd'"); - my ($curservice, $curbackend, - %service, %servicedata, %backend, %backenddata, @services, @backends); - while (my $line = <$if>) { - chomp ($line); - my $tag = _xtag($line); - if ($tag eq 'service') { - $curservice = _xatt ($line, "name"); - push (@services, $curservice); - @backends = (); - %backenddata = (); - } elsif ($tag eq 'connections') { - if ($curbackend eq '') { - $service{connections} = _xdata($line); - } else { - $backend{connections} = _xdata($line); - } - } elsif ($tag =~ /^backends_/) { - $service{$tag} = _xdata($line); - } elsif ($tag eq 'backend') { - $curbackend = _xatt ($line, 'name'); - push (@backends, $curbackend); - %backend = (); - } elsif ($tag eq 'availability') { - $backend{availability} = _xdata($line); - $backend{availability_id} = _xatt($line, 'id'); - } elsif ($tag eq '/backend') { - $backenddata{$curbackend} = \%backend; - } elsif ($tag eq '/service') { - $servicedata{$curservice} = [ \@backends, \%backenddata ]; - } - } - - $self->{services} = \@services; - $self->{servicedata} = \%servicedata; - - bless ($self, $proto); - return ($self); -} - -# Return array of available services. -sub services { - return ( @{ $_[0]->{services} } ); -} - -# Return back ends of a given service. -sub backends { - my %servicedata = %{ $_[0]->{servicedata} }; - my $data = $servicedata{$_[1]}; - return (undef) unless ($data); - my ($backendsref, $backenddataref) = @{ $data }; - return (@{ $backendsref }); -} - -# Wrappers for back end details of a given service. -sub availability { _backenddata(@_, 'availability'); } - -# Access stored data from XML document -sub _backenddata { - my ($self, $service, $backend, $detail) = @_; - my %servicedata = %{ $self->{servicedata} } or return (undef); - my $data = $servicedata{$service} or return (undef); - my ($backendsref, $backenddataref) = @{ $data }; - my %backenddata = %{ $backenddataref } or return (undef); - my %backenddetail = %{ $backenddata{$backend} } or return (undef); - return ($backenddetail{$detail}); -} - -# Simple XML support functions. All classless! -sub _xtag ($) { - my $line = shift; - for my $p (split (/[ <>]/, $line)) { - if ($p ne '') { - # print ("tag of $line: $p\n"); - return ($p); - } - } - return (undef); -} -sub _xatt ($$) { - my ($line, $att) = @_; - my $ret = $line; - $ret =~ s/.*$att=\"//; - $ret =~ s/\".*//; - # print ("att $att of $line: $ret\n"); - return ($ret); -} -sub _xdata ($) { - my $line = shift; - my $ret = $line; - $ret =~ s/<\/.*//; - $ret =~ s/.*>//; - # print ("data of $line: $ret\n"); - return ($ret); -} - - -# Main programs is below here -# --------------------------- - -package main; - -# Configuration: -# -------------- - -# What to call when requesting a pool worker. This program is expected -# to output an IP address, a : and a port. -my $getip = "/usr/local/src/crossroads/test/virtpoolhelper get"; - -# What to call when returning (deactivating) a pool worker. This -# program gets an IP address, a : and a port as its only argument. -my $releaseip = "/usr/local/src/crossroads/test/virtpoolhelper release"; - -print ("Start of run at ", scalar(localtime()), "\n"); -my $xr = new Crossroads ('crossroads -c /sw/src/crossroads/test/t14.conf'); - -for my $s ($xr->services()) { - print ("Service: $s\n"); - for my $b ($xr->backends ($s)) { - print (" Back end: $b, ", $xr->availability ($s, $b), "\n"); - } -} diff --git a/tools/getkeywords b/tools/getkeywords @@ -1,23 +0,0 @@ -#!/usr/bin/perl - -use strict; - -# Filter that reads parser.y and dumps the keywords on stdout. -# Each keyword appears in tt(..) for Yodl. - -my $active = 0; -while (my $line = <STDIN>) { - chomp ($line); - if ($active == 0 and $line =~ /Tokens/ and $line =~ /literal/) { - $active++; - } elsif ($line =~ /^\s*$/) { - $active = 0; - } elsif ($active) { - for my $k (split /\s+/, $line) { - next if ($k eq '' or $k =~ /%/); - print ("tt(", lc($k), ")\n"); - } - } -} - - diff --git a/tools/gettools b/tools/gettools @@ -1,24 +0,0 @@ -#!/usr/bin/perl - -use strict; - -my $base = $0; -$base =~ s{gettools$}{}; -$base = './' if ($base eq ''); - -foreach my $f (@ARGV) { - my $srcfound = 0; - my ($src, $dst); - foreach my $srcdir ("$ENV{EBASE}/bin", "$ENV{EBASE}/etc") { - $src = "$srcdir/$f"; - next unless (-f $src); - $dst = "$base/../tools/$f"; - - if (! -f $dst or - (stat($src))[9] > (stat($dst))[9]) { - print ("gettools: $src -> $dst\n"); - system ("cp $src $dst") == 0 - or die ("gettools: copying of '$src' to '$dst' failed\n"); - } - } -} diff --git a/tools/installdoc b/tools/installdoc @@ -1,34 +0,0 @@ -#!/usr/bin/perl - -use strict; - -sub basename ($) { - my $f = shift; - $f =~ s{^.*/}{}; - return ($f); -} - -die ("Usage: installdoc PREFIX MANFILES\n") if ($#ARGV < 1); -my $prefix = shift (@ARGV); -die ("Prefix directory '$prefix' not accessible\n") unless (-d $prefix); -for my $f (@ARGV) { - die ("Cannot see man page '$f' to install\n") unless (-f $f); - my $targetdir = ''; - my $sect = $f; - $sect =~ s{.*(\d)}{$1}; - for my $d ("$prefix/share/man", "$prefix/man") { - if (-d "$d/man$sect") { - $targetdir = "$d/man$sect"; - last; - } - } - if ($targetdir eq '') { - print STDERR ("No suitable man/ subdir found under $prefix,\n", - "manual pages not installed\n"); - exit (0); - } - my $dest = "$targetdir/" . basename($f); - next if (stat($dest) and (stat($dest))[9] >= (stat($f))[9]); - print ("$f -> $dest\n"); - system ("cp $f $dest") and die ("Failed to copy $f to $dest\n"); -} diff --git a/tools/makedist b/tools/makedist @@ -1,44 +0,0 @@ -#!/usr/bin/perl - -use strict; - -sub run ($) { - my $cmd = shift; - print ("RUN> $cmd\n"); - system ($cmd) == 0 - or die ("Command [$cmd] failed\n"); -} - -my $ver = $ARGV[0] - or die ("Version argument required.\n", - "You can only run this from 'make dist'!\n"); - -print ("Making crossroads distribution.. version is $ver\n"); - -my $distdir = "/tmp/crossroads-$ver"; -if (-d $distdir) { - print ("Removing old dist dir $distdir\n"); - run ("rm -rf $distdir"); -} -print ("Preparing for distro (make documentation, make clean)\n"); -run ("make documentation"); -run ("make clean"); -mkdir ($distdir) - or die ("Cannot makedir $distdir: $!\n"); - -print ("Copying all...\n"); -run ("cp -r * $distdir"); -print ("Removing CVS entries in dist dir\n"); -run ("find $distdir -type d -name CVS -exec rm -rf {} \\; || true"); -print ("Removing SVN entries in dist dir\n"); -run ("find $distdir -type d -name .svn -exec rm -rf {} \\; || true"); - -print ("Making archive...\n"); -chdir ("/tmp") - or die ("Hm.. /tmp is gone\n"); -run ("tar czf crossroads-$ver.tar.gz crossroads-$ver"); - -print ("----------------------------------------------------------\n", - " Distro archive is now in /tmp/crossroads-$ver.tar.gz\n", - "----------------------------------------------------------\n"); - diff --git a/tools/patch-header b/tools/patch-header @@ -1,87 +0,0 @@ -#!/usr/bin/perl - -use strict; - -sub diff ($$) { - my ($a, $b) = @_; - - open (my $fa, $a) or die ("Cannot read $a: $!\n"); - open (my $fb, $b) or die ("Cannot read $b: $!\n"); - - while (1) { - my $la = <$fa>; - my $lb = <$fb>; - - if (defined ($la)) { - return (1) - if ( (defined ($lb) and $la ne $lb) or - (! defined ($lb)) ); - } else { - return (1) - if (defined ($lb)); - return (0); - } - } -} - -die ("Usage: patch-header header-template version file(s)\n") - if ($#ARGV < 2); -my $hdrfile = shift (@ARGV); -my $ver = shift (@ARGV); -my ($header, $firstline, $lastline); -open (my $if, $hdrfile) - or die ("Can't read header file $hdrfile: $!\n"); -while (my $line = <$if>) { - chomp ($line); - - $line =~ s/__VER__/$ver/g; - $header .= "$line\n"; - - $firstline = $line if ($firstline eq ''); - $lastline = $line if ($line ne ''); -} -close ($if); - -for my $src (@ARGV) { - my $dst = "$src.new"; - open (my $of, ">$dst") - or die ("Can't write $dst: $!\n"); - print $of ($header); - - open (my $if, $src) - or die ("Can't read $src: $!\n"); - my $state = 0; - while (my $line = <$if>) { - chomp ($line); - # print ("Line: [$line], first: [$firstline], state: [$state]\n"); - if ($state == 2) { - print $of ("$line\n"); - next; - } - if ($state == 0 and $line eq $firstline) { - $state = 1; - next; - } - if ($state == 1 and $line eq $lastline) { - $state = 2; - next; - } - if ($state == 0) { - print $of ("$line\n"); - next; - } - } - close ($of); - close ($if); - - if (diff ($src, $dst)) { - unlink ($src) - or die ("Cannot unlink $src: $!\n"); - rename ($dst, $src) - or die ("Cannot rename $dst to $src: $!\n"); - print ("patch-header: $ver $src\n"); - } else { - unlink ($dst) - or die ("Cannot unlink $dst: $!\n"); - } -} diff --git a/tools/patch-mgr b/tools/patch-mgr @@ -1,30 +0,0 @@ -#!/usr/bin/perl - -use strict; - -die ("Usage: $0 <version> <xsltfile> <usageblock> <input >output\n") - if ($#ARGV != 2); - -while (my $line = <STDIN>) { - if ($line =~ /__XSLT__/) { - open (my $if, $ARGV[1]) or die ("Cannot read $ARGV[1]: $!\n"); - while (my $x = <$if>) { - $x =~ s/__VER__/$ARGV[0]/; - print ($x); - } - } elsif ($line =~ /__USAGE__/) { - open (my $if, $ARGV[2]) or die ("Cannot read $ARGV[2]: $!\n"); - while (my $x = <$if>) { - $x =~ s/__VER__/$ARGV[0]/; - print ($x); - } - } elsif ($line =~ /__WARNING__/) { - print ("# Do not make changes in this file.\n", - "# They will be overwritten in the next installation.\n", - "# For changes, see crossroads-mgr.in in the Crossroads ", - "source tree.\n"); - } else { - $line =~ s/__VER__/$ARGV[0]/; - print ($line); - } -} diff --git a/tools/untab b/tools/untab @@ -1,32 +0,0 @@ -#!/usr/bin/perl - -use strict; - -die ("Usage: untab file [file...]\n") if ($#ARGV == -1); - -for my $in (@ARGV) { - my $out = "$in.new"; - open (my $fin, $in) or die ("Can't read $in: $!\n"); - open (my $fout, ">$out") or die ("Can't write $out: $!\n"); - - my $changed = 0; - while (defined (my $line = <$fin>)) { - while ($line =~ /^\t/) { - $line =~ s/^\t/ /; - $changed++; - } - print $fout ($line); - } - - close ($fin); - close ($fout); - - if (!$changed) { - unlink ($out) or die ("Can't unlink $out: $!\n"); - } else { - print ("Untabbed $in ($changed times)\n"); - unlink ($in) or die ("Can't unlink $in: $!\n"); - rename ($out, $in) or die ("Can't rename $out to $in: $!\n"); - } -} - diff --git a/xr/Makefile b/xr/Makefile @@ -0,0 +1,42 @@ + +# Configuration +DIRS = $(shell find . -type d -mindepth 1 -maxdepth 1) +BUILDDIR = build +BIN = $(BUILDDIR)/xr +LIB = $(BUILDDIR)/libxr.a +CC = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache c++-compiler) +TMPXR = /tmp/xr-$(shell whoami) + +foo: + $(MAKE) subdirs + $(MAKE) $(BIN) + +install: $(BINDIR)/xr +$(BINDIR)/xr: $(BUILDDIR)/xr + cp $(BUILDDIR)/xr $(TMPXR) + strip $(TMPXR) + install $(TMPXR) $(BINDIR)/xr + rm -f $(TMPXR) + +subdirs: $(BUILDDIR)/usage.h + @for f in $(DIRS) ; \ + do \ + echo "Making: $$f"; \ + CC=$(CC) BUILDDIR=$(BUILDDIR) VER='$(VER)' \ + AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \ + $(MAKE) -C $$f -f ../etc/Makefile.class \ + || exit 1; \ + done + ar rs $(LIB) */*.o + ranlib $(LIB) +$(BUILDDIR)/usage.h: etc/usage.txt + etc/e-txt2c USAGE <etc/usage.txt >$(BUILDDIR)/usage.h + +$(BIN): $(BUILDDIR)/libxr.a + $(CC) -g -o $(BIN) -L$(BUILDDIR) -lxr \ + $(shell etc/c-conf -vc $(BUILDIR)/config.cache lib \ + ucb nsl pthread socket m alf) + +clean: + rm -f $(BIN) $(LIB) core obj/*.o etc/usage.h $(BUILDDIR)/config.cache + find . -name \*.bak -exec rm {} \; diff --git a/xr/algorithm/algorithm b/xr/algorithm/algorithm @@ -0,0 +1,12 @@ +#ifndef _ALGORITHM_ +#define _ALGORITHM_ + +#include "../thread/thread" + +class Algorithm: public Thread { +public: + virtual ~Algorithm(); + virtual int target() = 0; +}; + +#endif diff --git a/xr/algorithm/algorithm.cc b/xr/algorithm/algorithm.cc @@ -0,0 +1,4 @@ +#include "algorithm" + +Algorithm::~Algorithm() { +} diff --git a/xr/backend/available.cc b/xr/backend/available.cc @@ -0,0 +1,7 @@ +#include "backend" + +bool Backend::available() const { + if (!maxconn()) + return (islive); + return (islive && connections() < maxconn()); +} diff --git a/xr/backend/availablestr.cc b/xr/backend/availablestr.cc @@ -0,0 +1,5 @@ +#include "backend" + +string Backend::availablestr() const { + return (available() ? "available" : "unavailable"); +} diff --git a/xr/backend/backend b/xr/backend/backend @@ -0,0 +1,49 @@ +#ifndef _BACKEND_ +#define _BACKEND_ + +#include "../sys/sys" +#include "../backenddef/backenddef" +#include "../fdset/fdset" +#include "../error/error" + +using namespace std; + +class Backend { +public: + Backend (); + Backend (BackendDef const &b); + virtual ~Backend(); + bool connect(); + 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); } + string server() const { return (bdef.server()); } + int port() const { return (bdef.port()); } + unsigned maxconn() const { return (bdef.maxconn()); } + unsigned connections() const { return (nconn); } + void startconnection() { nconn++; totconn++; } + void endconnection() { nconn--; } + void addbytes (unsigned n) { bytes_served += n; } + double bytesserved() const { return (bytes_served); } + unsigned clientsserved() const { return (totconn); } + + BackendDef const &backenddef() const { + return (bdef); + } + +private: + BackendDef bdef; + bool islive; + int clsocket; + unsigned nconn; + unsigned totconn; + double bytes_served; +}; + +#endif diff --git a/xr/backend/backend1.cc b/xr/backend/backend1.cc @@ -0,0 +1,5 @@ +#include "backend" + +Backend::Backend () : + islive(true), clsocket(-1), nconn(0), totconn(0), bytes_served(0) { +} diff --git a/xr/backend/backend2.cc b/xr/backend/backend2.cc @@ -0,0 +1,5 @@ +#include "backend" + +Backend::Backend (BackendDef const &b) : + bdef(b), islive(true), clsocket(-1), nconn(0), totconn(0), bytes_served(0) { +} diff --git a/xr/backend/backend3.cc b/xr/backend/backend3.cc @@ -0,0 +1,4 @@ +#include "backend" + +Backend::~Backend() { +} diff --git a/xr/backend/check.cc b/xr/backend/check.cc @@ -0,0 +1,6 @@ +#include "backend" + +void Backend::check() { + connect(); + close (clsocket); +} diff --git a/xr/backend/connect.cc b/xr/backend/connect.cc @@ -0,0 +1,57 @@ +#include "backend" + +bool Backend::connect() { + // Assume the backend is dead + islive = false; + + // Create client socket + if ( (clsocket = socket (PF_INET, SOCK_STREAM, 0)) < 0 ) + throw ((Error)"Failed to create client socket: " + strerror(errno)); + + // Resolve hostname, prepare binding + struct hostent *hostaddr; + if (! (hostaddr = gethostbyname(bdef.server().c_str())) ) + throw ((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); + + // Client socket goes into nonblocking mode, so we can connect + // and enforce a timeout later. + int flags; + if ( (flags = fcntl (clsocket, F_GETFL, 0)) == -1 ) + throw ((Error) "Failed to get fd flags: " + strerror(errno)); + if (fcntl (clsocket, F_SETFL, flags | O_NONBLOCK) == -1) + throw ((Error) "Failed to fd in nonblocking mode: " + strerror(errno)); + + // Do the connect + int conres = ::connect (clsocket, (struct sockaddr *)&backendaddr, + sizeof(backendaddr)); + int conerrno = errno; + + // Put socket again in blocking mode. + if (fcntl (clsocket, F_SETFL, flags) == -1) + throw ((Error) "Failed to put fd in blocking mode: " + + strerror(errno)); + + // Check on the outcome of the connect + if (!conres || conerrno == EINPROGRESS) { + // Wait for socket to go writable. + Fdset fdset (config.backend_timeout()); + fdset.add (clsocket); + if (fdset.readwriteable() == -1 && fdset.writeable() == clsocket) + islive = true; + } + + /* + msg << "Back end " << bdef.server() << ":" << bdef.port(); + if (islive) + msg << " is live on socket " << clsocket << "\n"; + else + msg << " is NOT live\n"; + */ + + return (islive); +} diff --git a/xr/backend/description.cc b/xr/backend/description.cc @@ -0,0 +1,7 @@ +#include "backend" + +string Backend::description() const { + ostringstream o; + o << server() << ":" << port(); + return (o.str()); +} diff --git a/xr/backend/live.cc b/xr/backend/live.cc @@ -0,0 +1,8 @@ +#include "backend" + +void Backend::live (bool state) { + bool oldstate = islive; + islive = state; + if (oldstate != state) + msg ("Marking back end " + description() + " as " + livestr() + "\n"); +} diff --git a/xr/backend/livestr.cc b/xr/backend/livestr.cc @@ -0,0 +1,5 @@ +#include "backend" + +string Backend::livestr() const { + return (live() ? "alive" : "dead"); +} diff --git a/xr/backenddef/backenddef b/xr/backenddef/backenddef @@ -0,0 +1,26 @@ +#ifndef _BACKENDDEF_ +#define _BACKENDDEF_ + +#include "../sys/sys" +#include "../error/error" + +using namespace std; + +class BackendDef { +public: + BackendDef(); + BackendDef (string s, string p, string m = ""); + + void server(string s) { srv = s; } + string server() const { return (srv); } + void port (int p) { prt = p; } + int port() const { return (prt); } + unsigned maxconn() const { return (max); } + +private: + string srv; + int prt; + unsigned max; +}; + +#endif diff --git a/xr/backenddef/backenddef1.cc b/xr/backenddef/backenddef1.cc @@ -0,0 +1,13 @@ +#include "backenddef" + +BackendDef::BackendDef (string server, string port, string maxclients) : + srv(server), prt(0), max(0) { + + if (sscanf (port.c_str(), "%d", &prt) < 1) + throw ((Error) "Bad backend port specifier: '" + port + + "' is not a number"); + if (maxclients.size() && + sscanf (maxclients.c_str(), "%u", &max) < 1) + throw ((Error) "Bad maximum connections specifier: '" + + maxclients + "' is not a number"); +} diff --git a/xr/backenddef/backenddef2.cc b/xr/backenddef/backenddef2.cc @@ -0,0 +1,4 @@ +#include "backenddef" + +BackendDef::BackendDef () : srv(""), prt(-1) { +} diff --git a/xr/balancer/addbackend.cc b/xr/balancer/addbackend.cc @@ -0,0 +1,7 @@ +#include "balancer" + +void Balancer::addbackend (BackendDef const &b) { + Backend newb (b); + backends.push_back (newb); + backends[backends.size() - 1].check(); +} diff --git a/xr/balancer/balancer b/xr/balancer/balancer @@ -0,0 +1,44 @@ +#ifndef _BALANCER_ +#define _BALANCER_ + +#include "../sys/sys" + +#include "../backend/backend" +#include "../backenddef/backenddef" +#include "../config/config" +#include "../wakeupthread/wakeupthread" +#include "../checkupthread/checkupthread" +#include "../fdset/fdset" +#include "../error/error" + +using namespace std; + +class Balancer { +public: + Balancer (); + void init(); + void addbackend (BackendDef const &b); + void serve(); + + unsigned nbackends() { return (backends.size()); } + Backend &backend (unsigned i) { return (backends[i]); } + bool terminate() const { return (term); } + void terminate (bool t) { term = t; } + bool report() const { return (rep); } + void report (bool r) { rep = r; } + + unsigned connections(); + +private: + void init_tcp (); + void init_fd (); + + int server_fd; + vector<Backend> backends; + bool term; + bool rep; +}; + +extern Balancer balancer; + +#endif diff --git a/xr/balancer/balancer1.cc b/xr/balancer/balancer1.cc @@ -0,0 +1,4 @@ +#include "balancer" + +Balancer::Balancer () : server_fd(-1), backends(), term(false), rep(false) { +} diff --git a/xr/balancer/connections.cc b/xr/balancer/connections.cc @@ -0,0 +1,9 @@ +#include "balancer" + +unsigned Balancer::connections() { + unsigned ret = 0; + + for (unsigned i = 0; i < nbackends(); i++) + ret += backend(i).connections(); + return (ret); +} diff --git a/xr/balancer/init.cc b/xr/balancer/init.cc @@ -0,0 +1,18 @@ +#include "balancer" + +void Balancer::init() { + // Set the listening socket. + if (config.sport()) + server_fd = serversocket (config.sipaddr(), config.sport(), + "balancer"); + else + server_fd = 0; + + // Add workable back ends, display initial states. + for (int i = 0; i < config.backends(); i++) + addbackend (config.backend(i)); + + for (unsigned i = 0; i < nbackends(); i++) + msg ("Initial backend state: " + backend(i).description() + " is " + + backend(i).availablestr() + "\n"); +} diff --git a/xr/balancer/serve.cc b/xr/balancer/serve.cc @@ -0,0 +1,207 @@ +#include "balancer" +#include "../tcpdispatcher/tcpdispatcher" +#include "../httpdispatcher/httpdispatcher" + +void Balancer::serve() { + int clsock; + + // Start up wakeup/checkup handlers. + if (config.wakeupsec() && !config.foregroundmode() && config.sport()) { + msg ("Starting wakeup thread.\n"); + Wakeupthread *wt = new Wakeupthread(); + wt->start(); + } + if (config.checkupsec() && !config.foregroundmode() && config.sport()) { + msg ("Starting checkup thread.\n"); + Checkupthread *ct = new Checkupthread(); + ct->start(); + } + + // Write the PID file. + if (config.pidfile() != "") { + FILE *f; + if (! (f = fopen (config.pidfile().c_str(), "w")) ) + throw ((Error) "Cannot write pid file " + config.pidfile() + + ": " + strerror(errno)); + fprintf (f, "%u\n", getpid()); + fclose (f); + } + + // Wait for activity, serve it. + ostringstream o; + o << server_fd; + msg ("Awaiting activity on fd " + o.str() +"\n"); + while (1) { + Fdset fdset(0); + fdset.add (server_fd); + if (fdset.readable() < 0) { + // We caught a signal. Either a request to report status, + // or to terminate. + msg ("Interrupt seen\n"); + if (terminate()) { + msg ("Termination requested, XR will stop.\n"); + close (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"); + } + report (false); + reportmsg ("*** XR STATUS REPORT ENDS ***\n"); + continue; + } else { + msg ("Non-meaningful interrupt, resuming\n"); + continue; + } + } + + if (server_fd) { + // If tcp-serving: server_fd > 0; serve and loop again + int size; + struct sockaddr_in clname; + + size = sizeof(clname); + if ( (clsock = accept (server_fd, (struct sockaddr *) &clname, + (socklen_t*) &size)) < 0 ) + throw ((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"); + bool allowed = false; + for (unsigned n = 0; n < config.nallow(); n++) { + if (ipmatch (clname.sin_addr, config.allow(n))) { + allowed = true; + break; + } + } + if (!allowed) { + msg ("Not serving client " + clientip + + ": no match in allow list\n"); + close (clsock); + continue; + } + } + // If the client is in the deny list, deny it. + debugmsg ("Matching " + clientip + " against deny list\n"); + bool denied = false; + for (unsigned n = 0; n < config.ndeny(); n++) + if (ipmatch (clname.sin_addr, config.deny(n))) { + denied = true; + break; + } + if (denied) { + msg ("Not serving client " + clientip + + ": match in deny list\n"); + close (clsock); + continue; + } + + // Show whom we've accepted + if (config.verbose()) { + ostringstream o; + o << clsock; + msg ("Accepted connection from " + clientip + + " as client fd " + o.str() +"\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"); + ostringstream n; + n << connections(); + msg ("Balancer is serving " + n.str() + " clients\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"); + close (clsock); + continue; + } + + TcpDispatcher *d; + switch (config.stype()) { + case Servertype::t_tcp: + d = new TcpDispatcher (clsock, clientip); + break; + case Servertype::t_http: + d = new HttpDispatcher (clsock, clientip); + break; + default: + throw ((Error) "Internal error, can't choose dispatcher"); + break; + } + + // 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"); + } + + d->start(); + } else { + // If fd-serving, serve and close. Don't thread it up. + TcpDispatcher *d; + switch (config.stype()) { + case Servertype::t_tcp: + d = new TcpDispatcher (server_fd, "0.0.0.0"); + break; + case Servertype::t_http: + d = new HttpDispatcher (server_fd, "0.0.0.0"); + break; + default: + throw ((Error) "Internal error, can't choose dispatcher"); + break; + } + d->execute(); + break; + } + } + + // If a PID stamp was created, remove it now. + if (config.pidfile() != "") + unlink (config.pidfile().c_str()); +} diff --git a/xr/buffer/add.cc b/xr/buffer/add.cc @@ -0,0 +1,12 @@ +#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 ((Error) "Memory fault in Buffer::add"); + memcpy (buf_data + buf_len, b, len); + buf_len += len; + } +} diff --git a/xr/buffer/buffer b/xr/buffer/buffer @@ -0,0 +1,34 @@ +#ifndef _BUFFER_ +#define _BUFFER_ + +#include "../error/error" + +class Buffer { +public: + Buffer(); + Buffer(Buffer const &other); + ~Buffer(); + + Buffer &operator= (Buffer const &other); + Buffer &operator= (char const *b); + + 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; + 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); + +private: + void copy (Buffer const &other); + void destroy(); + + char *buf_data; + unsigned buf_len; +}; + +#endif diff --git a/xr/buffer/buffer1.cc b/xr/buffer/buffer1.cc @@ -0,0 +1,4 @@ +#include "buffer" + +Buffer::Buffer(): buf_data(0), buf_len(0) { +} diff --git a/xr/buffer/buffer2.cc b/xr/buffer/buffer2.cc @@ -0,0 +1,5 @@ +#include "buffer" + +Buffer::Buffer (Buffer const &other): buf_data(0), buf_len(0){ + copy(other); +} diff --git a/xr/buffer/buffer3.cc b/xr/buffer/buffer3.cc @@ -0,0 +1,5 @@ +#include "buffer" + +Buffer::~Buffer() { + destroy(); +} diff --git a/xr/buffer/charat.cc b/xr/buffer/charat.cc @@ -0,0 +1,7 @@ +#include "buffer" + +char Buffer::charat (unsigned index) const { + if (index >= buf_len) + return (0); + return (buf_data[index]); +} diff --git a/xr/buffer/copy.cc b/xr/buffer/copy.cc @@ -0,0 +1,13 @@ +#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 ((Error) "Memory fault in Buffer::copy"); + memcpy (buf_data, other.buf_data, other.buf_len); + buf_len = other.buf_len; + } +} diff --git a/xr/buffer/data.cc b/xr/buffer/data.cc @@ -0,0 +1,5 @@ +#include "buffer" + +char const *Buffer::data() const { + return (buf_data); +} diff --git a/xr/buffer/destroy.cc b/xr/buffer/destroy.cc @@ -0,0 +1,7 @@ +#include "buffer" + +void Buffer::destroy() { + free (buf_data); + buf_data = 0; + buf_len = 0; +} diff --git a/xr/buffer/insertat1.cc b/xr/buffer/insertat1.cc @@ -0,0 +1,5 @@ +#include "buffer" + +void Buffer::insertat (unsigned index, string s) { + insertat (index, s.c_str(), s.size()); +} diff --git a/xr/buffer/insertat2.cc b/xr/buffer/insertat2.cc @@ -0,0 +1,13 @@ +#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 ((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; +} diff --git a/xr/buffer/oparray.cc b/xr/buffer/oparray.cc @@ -0,0 +1,9 @@ +#include "buffer" + +char &Buffer::operator[] (unsigned index) { + static char dummy = 0; + + if (index >= buf_len) + return (dummy); + return (buf_data[index]); +} diff --git a/xr/buffer/opequal.cc b/xr/buffer/opequal.cc @@ -0,0 +1,9 @@ +#include "buffer" + +Buffer &Buffer::operator= (Buffer const &other) { + if (this != &other) { + destroy(); + copy (other); + } + return (*this); +} diff --git a/xr/buffer/removeat.cc b/xr/buffer/removeat.cc @@ -0,0 +1,14 @@ +#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; +} + diff --git a/xr/buffer/set.cc b/xr/buffer/set.cc @@ -0,0 +1,9 @@ +#include "buffer" + +void Buffer::set (char const *b, unsigned len) { + destroy(); + if (! (buf_data = (char*)malloc (len)) ) + throw ((Error) "Memory fault in Buffer::set"); + memcpy (buf_data, b, len); + buf_len = len; +} diff --git a/xr/buffer/size.cc b/xr/buffer/size.cc @@ -0,0 +1,5 @@ +#include "buffer" + +unsigned Buffer::size() const { + return (buf_len); +} diff --git a/xr/buffer/stringat.cc b/xr/buffer/stringat.cc @@ -0,0 +1,14 @@ +#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); +} diff --git a/xr/checkupthread/checkupthread b/xr/checkupthread/checkupthread @@ -0,0 +1,14 @@ +#ifndef _CHECKUPTHREAD_ +#define _CHECKUPTHREAD_ + +#include "../sys/sys" +#include "../thread/thread" +#include "../balancer/balancer" +#include "../error/error" + +class Checkupthread: public Thread { +public: + void execute(); +}; + +#endif diff --git a/xr/checkupthread/execute.cc b/xr/checkupthread/execute.cc @@ -0,0 +1,23 @@ +#include "checkupthread" + +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"); + } + } + sleep (config.checkupsec()); + } +} + diff --git a/xr/config/Makefile.lib b/xr/config/Makefile.lib @@ -0,0 +1,2 @@ +DIR = config +include ../etc/Makefile.lib diff --git a/xr/config/addallow.cc b/xr/config/addallow.cc @@ -0,0 +1,8 @@ +#include "config" + +void Config::addallow (string a) { + struct in_addr in; + if (!inet_aton (a.c_str(), &in)) + throw ((Error) "Bad allow-from specfier '" + a + "'"); + allowlist.push_back (in); +} diff --git a/xr/config/adddeny.cc b/xr/config/adddeny.cc @@ -0,0 +1,8 @@ +#include "config" + +void Config::adddeny (string d) { + struct in_addr in; + if (!inet_aton (d.c_str(), &in)) + throw ((Error) "Bad deny-from specfier '" + d + "'"); + denylist.push_back (in); +} diff --git a/xr/config/config b/xr/config/config @@ -0,0 +1,97 @@ +#ifndef _CMDLINE_ +#define _CMDLINE_ + +#include "../sys/sys" +#include "../backenddef/backenddef" +#include "../servertype/servertype" +#include "../dispatchmode/dispatchmode" +#include "../error/error" + +using namespace std; + +class Config { +public: + Config (); + + // Init by cmdline + void parsecmdline (int ac, char **av); + + // Accessors + bool verbose() const { return (verbose_flag); } + Servertype::Type stype() const { return (styp.type()); } + string sipaddr() const { return (sip); } + int sport() const { return (lport); } + int backends() const { return (blist.size()); } + int client_timeout() const { return (c_timeout); } + int backend_timeout() const { return (b_timeout); } + int wakeupsec() const { return (wakeup); } + int checkupsec() const { return (checkup); } + unsigned buffersize() const { return (bufsize); } + void buffersize (unsigned b) { bufsize = b; } + bool foregroundmode() const { return (foreground_mode); } + bool addxrversion() const { return (add_xr_version); } + bool debug() const { return (debug_flag); } + bool addxforwardedfor() const { return (add_x_forwarded_for); } + bool stickyhttp() const { return (sticky_http); } + unsigned maxconn() const { return (max_conn); } + string externalalgorithm() const { return (external_algorithm); } + string pidfile() const { return (pid_file); } + void pidfile (string p) { pid_file = p; } + bool prefixtimestamp() const { return (prefix_timestamp); } + void prefixtimestamp (bool p) { prefix_timestamp = p; } + void addserverheader (string s) { serverheaders.push_back(s); } + unsigned nserverheaders() const { return (serverheaders.size()); } + string serverheader (unsigned n) { return (serverheaders[n]); } + unsigned nallow() const { return (allowlist.size()); } + unsigned ndeny() const { return (denylist.size()); } + + 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]); + } + Dispatchmode::Mode dispatchmode() const { + return (dmode.mode()); + } + +private: + void setbackend (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; + static string sip; + static vector<BackendDef> blist; + static Dispatchmode dmode; + static int c_timeout; + static int b_timeout; + static int wakeup; + static int checkup; + static unsigned bufsize; + static bool foreground_mode; + static bool add_xr_version; + static bool debug_flag; + static bool add_x_forwarded_for; + static bool sticky_http; + static unsigned max_conn; + static string external_algorithm; + static string pid_file; + static bool prefix_timestamp; + static vector<string> serverheaders; + static vector<struct in_addr> allowlist; + static vector<struct in_addr> denylist; +}; + +extern Config config; + +#endif diff --git a/xr/config/config1.cc b/xr/config/config1.cc @@ -0,0 +1,28 @@ +#include "config" + +bool Config::verbose_flag = false; +int Config::lport = 10000; +Servertype Config::styp; +string Config::sip = "0"; +vector<BackendDef> Config::blist; +Dispatchmode Config::dmode; +int Config::c_timeout = 30; +int Config::b_timeout = 30; +int Config::wakeup = 5; +int Config::checkup = 0; +unsigned Config::bufsize = 2048; +bool Config::foreground_mode = false; +bool Config::add_xr_version = false; +bool Config::debug_flag = false; +bool Config::add_x_forwarded_for = false; +bool Config::sticky_http = false; +unsigned Config::max_conn = 0; +string Config::external_algorithm = ""; +string Config::pid_file = ""; +bool Config::prefix_timestamp = false; +vector<string> Config::serverheaders; +vector<struct in_addr> Config::allowlist; +vector<struct in_addr> Config::denylist; + +Config::Config () { +} diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc @@ -0,0 +1,141 @@ +#include "config" +#include "../build/usage.h" + +using namespace std; + +void Config::parsecmdline (int ac, char **av) { + // Prepare invoking command line. + string cmdline; + for (int i = 0; i < ac; i++) { + if (i) + cmdline += ' '; + cmdline += av[i]; + } + // Not a single argument? Usage. + if (ac == 1) + throw ((Error) "Bad command line '" + cmdline + "'\n" + USAGE); + + static struct option longopts[] = { + { "allow-from", required_argument, 0, 'a' }, + { "deny-from", required_argument, 0, 'A' }, + { "backend", required_argument, 0, 'b' }, + { "buffer-size", required_argument, 0, 'B' }, + { "checkup-interval", required_argument, 0, 'c' }, + { "debug", no_argument, 0, 'D' }, + { "dispatch-mode", required_argument, 0, 'd' }, + { "foreground", no_argument, 0, 'f' }, + { "help", no_argument, 0, 'h' }, + { "add-server-header", required_argument, 0, 'H' }, + { "max-connections", required_argument, 0, 'm' }, + { "prefix-timestamp", no_argument, 0, 'P' }, + { "pidfile", required_argument, 0, 'p' }, + { "server", required_argument, 0, 's' }, + { "sticky-http", no_argument, 0, 'S' }, + { "backend-timeout", required_argument, 0, 't' }, + { "client-timeout", required_argument, 0, 'T' }, + { "verbose", no_argument, 0, 'v' }, + { "version", no_argument, 0, 'V' }, + { "wakeup-interval", required_argument, 0, 'w' }, + { "add-xr-version", no_argument, 0, 'X' }, + { "add-x-forwarded-for", no_argument, 0, 'x' }, + { 0, 0, 0, 0 } + }; + + int opt; + bool backend_set = false; + while ( (opt = getopt_long (ac, av, "?a:A:B:b:c:Dd:fhH:m:p:Ss:t:T:vVw:xX", + longopts, 0)) > 0) { + switch (opt) { + case 'a': + addallow (optarg); + break; + case 'A': + adddeny (optarg); + break; + case 'b': + setbackend (optarg); + backend_set = true; + break; + case 'B': + bufsize = (unsigned)setinteger (optarg); + break; + case 'c': + checkup = setinteger (optarg); + break; + case 'D': + verbose_flag = true; + debug_flag = true; + break; + case 'd': + setdispatchmode (optarg); + break; + case 'f': + foreground_mode = true; + break; + case 'h': + throw ((Error) USAGE); + break; + case 'H': + addserverheader (optarg); + break; + case 'm': + max_conn = (unsigned)setinteger (optarg); + break; + case 'P': + prefix_timestamp = true; + break; + case 'p': + pid_file = optarg; + break; + case 's': + setserver (optarg); + break; + case 'S': + sticky_http = true; + break; + case 't': + b_timeout = setinteger (optarg); + break; + case 'T': + c_timeout = setinteger (optarg); + break; + case 'v': + verbose_flag = true; + break; + case 'V': + cout << "This is XR V" << VER << "\n" + << "Written by: " << AUTHOR << "\n" + << "Maintained by: " << MAINTAINER << "\n" + << "Please report bugs to the maintainer.\n"; + exit (0); + case 'w': + wakeup = setinteger (optarg); + break; + case 'X': + add_xr_version = true; + break; + case 'x': + add_x_forwarded_for = true; + break; + default: + throw ((Error) "Unknown flag, try 'xr -h' for usage"); + break; + } + } + + if (ac != optind) + throw ((Error) "Bad command line '" + cmdline + "'\n" + USAGE); + + if (!backend_set) + throw ((Error) + "No backend defined, use '-b...' at least once, " + "or try 'xr -h' for usage"); + + msg ("+--------------------------------------------+\n"); + msg ("| Welcome to xr V" VER " |\n"); + msg ("| Copyright (c) Karel Kubat <karel@kubat.nl> |\n"); + msg ("| Distributed under GPLV3. |\n"); + msg ("+--------------------------------------------+\n"); + + msg ("Invoking command line: '" + cmdline + "'\n"); +} diff --git a/xr/config/setbackend.cc b/xr/config/setbackend.cc @@ -0,0 +1,23 @@ +#include "config" + +void Config::setbackend (string str) { + vector<string> parts; + int pos; + while ( (pos = str.find_first_of(":")) > 0) { + if (pos > 0) + parts.push_back (str.substr(0, pos)); + str = str.substr(pos + 1); + } + if (str.length() > 0) + parts.push_back (str); + if (parts.size() < 2 || parts.size() > 3) + throw((Error) "Bad back end specifier in '-b" + str + + "', expected: SERVER:PORT or SERVER:PORT:MAXCONNECTIONS"); + if (parts.size() == 2) { + BackendDef bd (parts[0], parts[1]); + blist.push_back (bd); + } else { + BackendDef bd (parts[0], parts[1], parts[2]); + blist.push_back (bd); + } +} diff --git a/xr/config/setdispatcmode.cc b/xr/config/setdispatcmode.cc @@ -0,0 +1,23 @@ +#include "config" + +void Config::setdispatchmode (string s) { + if (s == "r" || s == "round-robin") + dmode.mode (Dispatchmode::m_roundrobin); + else if (s == "l" || s == "least-connections") + dmode.mode (Dispatchmode::m_leastconn); + else if (s == "f" || s == "first-available") + dmode.mode (Dispatchmode::m_firstactive); + else if (s.substr(0, 2) == "e:") { + dmode.mode (Dispatchmode::m_external); + external_algorithm = s.substr (2); + } else if (s.substr (0, 9) == "external:") { + dmode.mode (Dispatchmode::m_external); + external_algorithm = s.substr(9); + } + else + throw ((Error) "Bad dispatch mode -d" + s); + + if (dmode.mode() == Dispatchmode::m_external && + external_algorithm.size() < 1) + throw ((Error) "External algorithm handler missing"); +} diff --git a/xr/config/setinteger.cc b/xr/config/setinteger.cc @@ -0,0 +1,9 @@ +#include "config" + +int Config::setinteger (string s) const { + int ret; + if (sscanf (s.c_str(), "%d", &ret) < 1) + throw ((Error) "Bad numeric specifier in '" + s + + ": not a number"); + return (ret); +} diff --git a/xr/config/setserver.cc b/xr/config/setserver.cc @@ -0,0 +1,21 @@ +#include "config" + +void Config::setserver (string str) { + // Split into 3 parts + vector<string> parts; + int pos; + while ( (pos = str.find_first_of(":")) > 0) { + if (pos > 0) + parts.push_back (str.substr(0, pos)); + str = str.substr(pos + 1); + } + if (str.length() > 0) + parts.push_back (str); + if (parts.size() != 3) + throw((Error) "Bad server specifier, expected: TYPE:IPADDRESS:PORT"); + + // Store type, IP and port + styp.type (parts[0]); + sip = parts[1]; + lport = setinteger (parts[2]); +} diff --git a/xr/dispatchmode/dispatchmode b/xr/dispatchmode/dispatchmode @@ -0,0 +1,28 @@ +#ifndef _DISPATCHMODE_ +#define _DISPATCHMODE_ + +#include "../sys/sys" + +using namespace std; + +class Dispatchmode { +public: + + enum Mode { + m_leastconn, + m_roundrobin, + m_firstactive, + m_external, + }; + + Dispatchmode() : mymode(m_leastconn) { + } + + void mode (Mode m) { mymode = m; } + Mode mode() const { return (mymode); } + +private: + Mode mymode; +}; + +#endif diff --git a/xr/error/error b/xr/error/error @@ -0,0 +1,21 @@ +#ifndef _ERROR_ +#define _ERROR_ + +#include "../sys/sys" + +using namespace std; + +class Error { +public: + Error (string s); + Error (int i); + Error &operator+ (Error const &other); + Error &operator+ (string const s); + Error &operator+ (int i); + string txt() const; + +private: + string desc; +}; + +#endif diff --git a/xr/error/error1.cc b/xr/error/error1.cc @@ -0,0 +1,4 @@ +#include "error" + +Error::Error (string s): desc(s) { +} diff --git a/xr/error/error2.cc b/xr/error/error2.cc @@ -0,0 +1,7 @@ +#include "error" + +Error::Error (int i) { + ostringstream o; + o << i; + desc = o.str(); +} diff --git a/xr/error/opplus1.cc b/xr/error/opplus1.cc @@ -0,0 +1,6 @@ +#include "error" + +Error &Error::operator+ (Error const &other) { + desc += other.desc; + return (*this); +} diff --git a/xr/error/opplus2.cc b/xr/error/opplus2.cc @@ -0,0 +1,6 @@ +#include "error" + +Error &Error::operator+ (string s) { + desc += s; + return (*this); +} diff --git a/xr/error/opplus3.cc b/xr/error/opplus3.cc @@ -0,0 +1,10 @@ +#include "error" + +Error &Error::operator+ (int i) { + ostringstream o; + + o << i; + desc += o.str(); + + return (*this); +} diff --git a/xr/error/txt.cc b/xr/error/txt.cc @@ -0,0 +1,10 @@ +#include "error" +#include "../config/config" + +string Error::txt() const { + ostringstream o; + if (config.prefixtimestamp()) + o << timestamp() << ' '; + o << pthread_self() << " ERROR: " << desc; + return (o.str()); +} diff --git a/xr/etc/Makefile.class b/xr/etc/Makefile.class @@ -0,0 +1,9 @@ +SRC = $(wildcard *.cc) +OBJ = $(patsubst %.cc, ../$(BUILDDIR)/$(DIR)_%.o, $(SRC)) +DIR = $(shell pwd | sed 's:.*/::') + +class-compile: $(OBJ) + +../$(BUILDDIR)/$(DIR)_%.o: %.cc + $(CC) -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \ + -DMAINTAINER='"$(MAINTAINER)"' -c -g -Wall -o $@ $< diff --git a/tools/c-conf b/xr/etc/c-conf diff --git a/tools/e-txt2c b/xr/etc/e-txt2c diff --git a/tools/e-ver b/xr/etc/e-ver diff --git a/xr/etc/firstav.pl b/xr/etc/firstav.pl @@ -0,0 +1,21 @@ +# firstav.pl - sample "first available" dispatching algorithm, +# implemented as an external Perl program + +#!/usr/bin/perl + +use strict; + +print STDERR ("firstav.pl: Invocation: @ARGV\n"); +my $n = shift (@ARGV); +for my $i (0..$n - 1) { + my $addr = shift (@ARGV); + my $av = shift (@ARGV); + my $conn = shift (@ARGV); + print STDERR ("firstav.pl: $i: backend at $addr, $av, $conn connections\n"); + if ($av eq 'available') { + print ("$i\n"); + exit (0); + } +} +print STDERR ("firstav.pl: Nothing available!\n"); +exit (1); diff --git a/xr/etc/gettools b/xr/etc/gettools @@ -0,0 +1,26 @@ +#!/usr/bin/perl + +use strict; + +die ("Usage: gettools SRCDIR DSTDIR TOOL [TOOL...]\n") + if ($#ARGV < 2); + +my $srcdir = shift (@ARGV); +die ("gettools: source dir $srcdir not found\n") unless (-d $srcdir); +my $dstdir = shift (@ARGV); +die ("gettools: dest dir $dstdir not found\n") unless (-d $dstdir); +for my $t (@ARGV) { + gettool ($srcdir, $dstdir, $t); +} + +sub gettool ($$$) { + my ($sd, $dd, $t) = @_; + my $src = "$sd/$t"; + return unless (-f $src); + my $dst = "$dd/$t"; + + if (! -f $dst or + (stat($src))[9] > (stat($dst))[9]) { + system ("cp '$src' '$dst'") and die ("gettools: cp failed\n"); + } +} diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt @@ -0,0 +1,76 @@ + +This is XR, a load balancer and failover utility for TCP/HTTP services. +Usage: xr [flags], where the flags may be: + -a MASK, --allow-from MASK + Allow only clients that match MASK. MASK is e.g. 192.168.255.255, which + would allow the class B network 192.168.*.* + -A MASK, --deny-from MASK + Deny clients that match MASK. + -b ADDRESS:PORT[:MAX], --backend ADDRESS:PORT[:MAX] + Specifies a back end, use multiple -b... to specify several back ends. + At least one -b... must be given. Specifier MAX is optional: when given, + defines the maximum connections for the back end. + -b SIZE, --buffer-size SIZE + Sets the network buffer size, default is 2048 (in bytes) + -c SEC, --checkup-interval SEC + Defines the back end checking period. Each SEC seconds, every back end + is checked whether it is alive. Default is 0 (off). + -D, --debug + Sets debugging on, more verbosity on top of -v + -d METHOD, --dispatch-mode METHOD + Defines how to dispatch over back ends, the method may be: + r, round-robin - back ends take turns + l, least-connections - back end with least TCP connections is taken + f, first-active - first live back end gets all traffic + e:EXT, external:EXT - external program EXT is queried + Default method is l (least-connections). When external mode is selected, + program EXT is started with arguments <nbackends> <b0> <b0-availability> + <b0-connections> (b0 repeated for all back ends). Here <b0> is the back + end definition, eg. "10.1.1.1:80"; <b0-availablility> is "available" or + "unavailable", <b0-connections> is the nr. of connections. The program + must reply with a back end number (0..max) on stdout. + -f, --foreground + Suppresses forking/threading, useful for debugging. Also suppresses + wakeup (-w) and checkup (-c) threads. + -h, -?, --help + This text. + -H HDR, --add-server-header HDR + Inserts HDR into back end bound HTTP messages. + -m MAX, --max-connections MAX + Sets the maximum number of connections to the balancer. Default is 0, + no maximum. + -P, --prefix-timestamp + 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 + -S, --sticky-http + Enables sticky HTTP sessions by injecting XRTarget cookies into HTTP + streams. Only effective with -s http:.... + -s TYPE:IPADDRESS:PORT, --server TYPE:IPADDRESS:PORT + Specifies the server. TYPE is tcp or http. IPADDRESS is the IP address + to listen to. PORT defines the TCP port to listen (for type tcp or + http); when port is 0, XR will listen to stdin (inetd-mode). + Default: tcp:0:10000 (TCP balancing, on all interfaces, via port 10000). + -t SEC, --backend-timeout SEC + Defines network timeouts for back ends, default 3 sec. Use 0 to + prevent timing out. + -T SEC, --client-timeout SEC + Defines network timeouts for clients, default 30 sec. Use 0 to + prevent timing out. + -v, --verbose + Increases verbosity, default is silent operation. + -V, --version + Shows the version info, and author/maintainer contacts (for reporting + bugs). + -w SEC, --wakeup-interval SEC + Defines wakeup period (rechecking) in seconds, of unavailable back + ends. Default is 5. Use -w0 to suppress. + -X, --add-xr-version + Adds an XR version header to client and back end streams in HTTP + messages. + -x, --add-x-forwarded-for + Adds X-Forwarded-For with external IP address to back end streams in + HTTP messages. +XR's messages are sent to stderr. Invoke XR daemons using something like +"xr -b ... [other flags] 2>&1 | logger &", or use xrctl. + diff --git a/xr/etc/xr-test b/xr/etc/xr-test @@ -0,0 +1,36 @@ +#!/usr/bin/perl + +use strict; +use LWP::UserAgent; +use Time::HiRes qw(sleep gettimeofday tv_interval); + +die ("Usage: $0 URL TRIALS\n") if ($#ARGV != 1); +$|++; + +my $n = 0; +my $totaltime = 0; + +my $ua = LWP::UserAgent->new(); +$ua->timeout(5); +while ($n < $ARGV[1]) { + my $success = 0; + for my $i (1..10) { + my $t0 = [ gettimeofday() ]; + my $resp = $ua->get($ARGV[0]); + if ($resp->is_success()) { + $success++; + $n++; + $totaltime += tv_interval ($t0); + last; + } + if ($i == 3) { + die ($resp->status_line(), "\n"); + } + sleep (1); + } + sleep (0.1); + printf ("\rN: %5d Tot:%8.3g Avg:%8.3g", $n, $totaltime, $totaltime / $n) + if (! ($n % 50)); +} + +print ("\n"); diff --git a/xr/external/external b/xr/external/external @@ -0,0 +1,14 @@ +#ifndef _EXTERNAL_ +#define _EXTERNAL_ + +#include "../sys/sys" +#include "../error/error" +#include "../algorithm/algorithm" +#include "../balancer/balancer" +#include "../config/config" + +class External: public Algorithm { + int target(); +}; + +#endif diff --git a/xr/external/target.cc b/xr/external/target.cc @@ -0,0 +1,33 @@ +#include "external" + +int External::target() { + // Prepare command to run + ostringstream o; + o << config.externalalgorithm() << ' ' << balancer.nbackends(); + for (unsigned i = 0; i < balancer.nbackends(); i++) + o << ' ' << balancer.backend(i).description() << ' ' + << balancer.backend(i).availablestr() + << ' ' << balancer.backend(i).connections(); + msg ("External algorithm: invoking '" + o.str() + "'\n"); + + FILE *f; + if (! (f = popen (o.str().c_str(), "r")) ) + throw ((Error) "Cannot start '" + o.str() + "': " + strerror(errno)); + int i; + if (fscanf (f, "%d", &i) < 1) + throw ((Error) "External algorithm '" + o.str() + "' did not reply " + "with a number"); + + ostringstream n; + n << i; + msg ("External algorithm says: " + n.str() + "\n"); + + if (i < 0 || i >= (int)balancer.nbackends()) + throw ((Error) "External algorithm '" + o.str() + "': answer " + + n.str() + " out of bounds"); + if (pclose (f)) + throw ((Error) "External algorithm '" + o.str() + "' terminated " + "with error"); + + return (i); +} diff --git a/xr/fdset/fdset b/xr/fdset/fdset @@ -0,0 +1,40 @@ +#ifndef _FDSET_ +#define _FDSET_ + +#include "../sys/sys" +#include "../error/error" +#include "../config/config" + +using namespace std; + +class Fdset { +public: + Fdset(int t); + + int timeout() const { + return (tsec); + } + void timeout (int t) { + tsec = t; + } + void add (int fd) { + set.push_back(fd); + } + unsigned size() const { + return (set.size()); + } + int fd (unsigned index) const { + return (set[index]); + } + + int readable() const; + int writeable() const; + int readwriteable() const; + + +private: + int tsec; + vector<int> set; +}; + +#endif diff --git a/xr/fdset/fdset1.cc b/xr/fdset/fdset1.cc @@ -0,0 +1,4 @@ +#include "fdset" + +Fdset::Fdset (int t) : tsec(t), set() { +} diff --git a/xr/fdset/readable.cc b/xr/fdset/readable.cc @@ -0,0 +1,57 @@ +#include "fdset" + +int Fdset::readable() const { + fd_set readset, exceptset; + struct timeval tv, *tvp; + + // No fd's? Nothing is readable. + if (set.size() < 1) + return (-1); + + // Prepare select sets. + FD_ZERO (&readset); + FD_ZERO (&exceptset); + for (unsigned i = 0; i < set.size(); i++) { + FD_SET (set[i], &readset); + FD_SET (set[i], &exceptset); + } + + // Prepare timout specifier. + if (tsec) { + tv.tv_sec = tsec; + tv.tv_usec = 0; + tvp = &tv; + } else + tvp = 0; + + // Run the select. Signal interrupts are returned as -1, so that + // the caller can handle them gracefully. + if (select (FD_SETSIZE, &readset, 0, &exceptset, tvp) < 0) { + if (errno != EINTR) + throw ((Error) "Select failure: " + "failed to wait for readable state: " + strerror(errno)); + else + return (-1); + } + + // Check for exceptions. + for (unsigned i = 0; i < set.size(); i++) + if (FD_ISSET (set[i], &exceptset)) + throw ((Error) "Exception on fd/socket " + set[i]); + + // Check what's readable. + for (unsigned i = 0; i < set.size(); i++) + if (FD_ISSET (set[i], &readset)) { + if (config.debug()) { + ostringstream o; + o << set[i]; + debugmsg ("Fd " + o.str() + " has become readable\n"); + } + return (set[i]); + } + + // Nothing.. + debugmsg ("No readable fd's at this time\n"); + return (-1); +} + diff --git a/xr/fdset/readwriteable.cc b/xr/fdset/readwriteable.cc @@ -0,0 +1,46 @@ +#include "fdset" + +int Fdset::readwriteable() const { + fd_set readset, writeset, exceptset; + struct timeval tv, *tvp; + + // No fd's? Nothing is readable. + if (set.size() < 1) + return (-1); + + // Prepare select sets. + FD_ZERO (&readset); + FD_ZERO (&writeset); + FD_ZERO (&exceptset); + for (unsigned i = 0; i < set.size(); i++) { + FD_SET (set[i], &readset); + FD_SET (set[i], &writeset); + FD_SET (set[i], &exceptset); + } + + // Prepare timout specifier. + if (tsec) { + tv.tv_sec = tsec; + tv.tv_usec = 0; + tvp = &tv; + } else + tvp = 0; + + // Run the select. + if (select (FD_SETSIZE, &readset, &writeset, &exceptset, tvp) < 0) + throw ((Error) "Select failure: failed to wait for rw state"); + + // Check for exceptions. + for (unsigned i = 0; i < set.size(); i++) + if (FD_ISSET (set[i], &exceptset)) + throw ((Error) "Exception on fd/socket " + set[i]); + + // Check what's active. + for (unsigned i = 0; i < set.size(); i++) + if (FD_ISSET (set[i], &readset) && FD_ISSET (set[i], &writeset)) + return (set[i]); + + // Nothing.. + return (-1); +} + diff --git a/xr/fdset/writeable.cc b/xr/fdset/writeable.cc @@ -0,0 +1,51 @@ +#include "fdset" + +int Fdset::writeable() const { + fd_set writeset, exceptset; + struct timeval tv, *tvp; + + // No fd's? Nothing is writeable. + if (set.size() < 1) + return (-1); + + // Prepare select sets. + FD_ZERO (&writeset); + FD_ZERO (&exceptset); + for (unsigned i = 0; i < set.size(); i++) { + FD_SET (set[i], &writeset); + FD_SET (set[i], &exceptset); + } + + // Prepare timout specifier. + if (tsec) { + tv.tv_sec = tsec; + tv.tv_usec = 0; + tvp = &tv; + } else + tvp = 0; + + // Run the select. + if (select (FD_SETSIZE, 0, &writeset, &exceptset, tvp) < 0) + throw ((Error) "Select failure: failed to wait for writable state"); + + // Check for exceptions. + for (unsigned i = 0; i < set.size(); i++) + if (FD_ISSET (set[i], &exceptset)) + throw ((Error) "Exception on fd/socket " + set[i]); + + // Check what's writeable. + for (unsigned i = 0; i < set.size(); i++) + if (FD_ISSET (set[i], &writeset)) { + if (config.debug()) { + ostringstream o; + o << set[i]; + debugmsg ("Fd " + o.str() + " has become writeable\n"); + } + return (set[i]); + } + + // Nothing.. + debugmsg ("No writeable fd's at this time\n"); + return (-1); +} + diff --git a/xr/firstactive/firstactive b/xr/firstactive/firstactive @@ -0,0 +1,13 @@ +#ifndef _FIRSTACTIVE_ +#define _FIRSTACTIVE_ + +#include "../sys/sys" +#include "../error/error" +#include "../algorithm/algorithm" +#include "../balancer/balancer" + +class Firstactive: public Algorithm { + int target(); +}; + +#endif diff --git a/xr/firstactive/target.cc b/xr/firstactive/target.cc @@ -0,0 +1,9 @@ +#include "firstactive" + +int Firstactive::target() { + for (unsigned i = 0; i < balancer.nbackends(); i++) + if (balancer.backend(i).available()) + return ( (int) i); + throw ((Error) "First-active algorithm: no available back ends"); +} + diff --git a/xr/httpbuffer/addheader.cc b/xr/httpbuffer/addheader.cc @@ -0,0 +1,10 @@ +#include "httpbuffer" + +void Httpbuffer::addheader (string var, string val) { + string old = headerval(var); + if (old.size()) { + old += ", " + val; + setheader(var, old); + } else + setheader (var, val); +} diff --git a/xr/httpbuffer/addheader1.cc b/xr/httpbuffer/addheader1.cc @@ -0,0 +1,14 @@ +#include "httpbuffer" + +void Httpbuffer::addheader (string h) { + unsigned i; + for (i = 0; i < h.size(); i++) + if (h[i] == ':') { + string var = h.substr(0, i); + i++; + while (isspace(h[i])) + i++; + string val = h.substr(i); + addheader (var, val); + } +} diff --git a/xr/httpbuffer/bodyreceived.cc b/xr/httpbuffer/bodyreceived.cc @@ -0,0 +1,46 @@ +#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); +} + diff --git a/xr/httpbuffer/cookievalue.cc b/xr/httpbuffer/cookievalue.cc @@ -0,0 +1,17 @@ +#include "httpbuffer" + +string Httpbuffer::cookievalue (string c) { + string cval = headerval ("Cookie"); + size_t pos; + string ret = ""; + if ( (pos = cval.find (c)) != string::npos) { + pos++; + pos += c.size(); + for (char ch = cval[pos]; + pos < cval.size() && ch != ';' && ch != ',' && ch; + ch = cval[++pos]) { + ret += ch; + } + } + return (ret); +} diff --git a/xr/httpbuffer/findheader.cc b/xr/httpbuffer/findheader.cc @@ -0,0 +1,25 @@ +#include "httpbuffer" + +unsigned Httpbuffer::findheader(string h) { + if (!headersdone()) + return (0); + + for (unsigned int i = 0; i < bodystart - h.size(); ) { + // At header? + if (stringat(i, h.size()) == h) + return (i); + // No, advance beyond next \n + while (1) { + char ch = charat(i); + if (!ch) + return (0); + if (ch == '\n') { + i++; + break; + } + i++; + } + } + + return (0); +} diff --git a/xr/httpbuffer/firstline.cc b/xr/httpbuffer/firstline.cc @@ -0,0 +1,12 @@ +#include "httpbuffer" + +string Httpbuffer::firstline() const { + string ret; + for (unsigned i = 0; i < size(); i++) { + char ch = charat(i); + if (ch == '\n' || ch == '\r') + break; + ret += ch; + } + return (ret); +} diff --git a/xr/httpbuffer/headersdone.cc b/xr/httpbuffer/headersdone.cc @@ -0,0 +1,22 @@ +#include "httpbuffer" + +bool Httpbuffer::headersdone() { + if (bodystart) + return (true); + + bool prevnl = false; + for (unsigned i = 0; i < size(); i++) { + if (charat(i) == '\n') { + if (prevnl) { + bodystart = i + 1; + return (true); + } + prevnl = true; + } else { + if (charat(i) != '\r') + prevnl = false; + } + } + + return (false); +} diff --git a/xr/httpbuffer/headerval.cc b/xr/httpbuffer/headerval.cc @@ -0,0 +1,26 @@ +#include "httpbuffer" + +string Httpbuffer::headerval (string var) { + string ret; + + if (!headersdone()) + return (""); + + if (var[var.size() - 1] != ':') + var += ":"; + + unsigned int start; + if (! (start = findheader(var)) ) + ret = ""; + else { + start += var.size(); + for (char ch = charat(start); ch && isspace(ch); ch = charat(++start)) + ; + for (char ch = charat(start); ch && ch != '\r' && ch != '\n'; + ch = charat(++start)) + ret += ch; + } + + debugmsg ("Header " + var + " '" + ret + "'\n"); + return (ret); +} diff --git a/xr/httpbuffer/httpbuffer b/xr/httpbuffer/httpbuffer @@ -0,0 +1,32 @@ +#ifndef _HTTPBUFFER_ +#define _HTTPBUFFER_ + +#include "../sys/sys" +#include "../buffer/buffer" +#include "../config/config" + +class Httpbuffer: public Buffer { +public: + enum Bodystatus { + b_is_received, + b_is_not_received, + b_unknown, + }; + + Httpbuffer(); + bool headersdone(); + string headerval (string var); + Bodystatus bodyreceived(); + string firstline() const; + bool setversion(string v); + void setheader (string var, string val); + void addheader (string var, string val); + void addheader (string h); + string cookievalue (string var); + +private: + unsigned findheader (string h); + unsigned bodystart; +}; + +#endif diff --git a/xr/httpbuffer/httpbuffer1.cc b/xr/httpbuffer/httpbuffer1.cc @@ -0,0 +1,4 @@ +#include "httpbuffer" + +Httpbuffer::Httpbuffer(): bodystart(0) { +} diff --git a/xr/httpbuffer/setheader.cc b/xr/httpbuffer/setheader.cc @@ -0,0 +1,26 @@ +#include "httpbuffer" + +void Httpbuffer::setheader (string var, string val) { + if (!bodystart) + return; + if (var[var.size() - 1] != ':') + var += ':'; + + unsigned i; + if (! (i = findheader (var)) ) { + while (charat(i) && charat(i) != '\n') + i++; + i++; + insertat (i, var + " " + val + "\r\n"); + } else { + i += var.size(); + while (isspace(charat(i))) + i++; + unsigned len = i; + while (charat(len) && charat(len) != '\r' && charat(len) != '\n') + len++; + len -= i; + removeat (i, len); + insertat (i, val); + } +} diff --git a/xr/httpbuffer/setversion.cc b/xr/httpbuffer/setversion.cc @@ -0,0 +1,20 @@ +#include "httpbuffer" + +bool Httpbuffer::setversion (string v) { + if (!headersdone()) + return (false); + + // 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); +} diff --git a/xr/httpdispatcher/dispatch.cc b/xr/httpdispatcher/dispatch.cc @@ -0,0 +1,38 @@ +#include "httpdispatcher" + +void HttpDispatcher::dispatch() { + unsigned stickytarget; + + // Get the client's request. May need for cookie inspection. + if (!getclientrequest()) + throw ((Error) "Didn't receive a valid client request.\n"); + + // 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(), + "%d", &stickytarget) < 1 || + stickytarget >= balancer.nbackends()) { + issticky(false); + TcpDispatcher::dispatch(); + } else { + // Got a sticky target. Try to connect. If that fails, fallback + // to non-sticky dispatching. + targetbackend(stickytarget); + Backend tb = balancer.backend(stickytarget); + msg ("Sticky HTTP request for " + tb.description() + "\n"); + if (! tb.connect()) { + lock(); + balancer.backend(stickytarget).live(false); + unlock(); + msg ("Failed to connect to back end " + tb.description() + + ", trying to dispatch to other\n"); + issticky(false); + TcpDispatcher::dispatch(); + } else { + backendfd(tb.sock()); + issticky(true); + } + } +} + diff --git a/xr/httpdispatcher/getclientrequest.cc b/xr/httpdispatcher/getclientrequest.cc @@ -0,0 +1,16 @@ +#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); +} + diff --git a/xr/httpdispatcher/getserverresponse.cc b/xr/httpdispatcher/getserverresponse.cc @@ -0,0 +1,17 @@ +#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); +} diff --git a/xr/httpdispatcher/handle.cc b/xr/httpdispatcher/handle.cc @@ -0,0 +1,45 @@ +#include "httpdispatcher" + +void 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"); + + // Apply other server-side directed info + if (config.addxrversion()) + clientrequest.setheader ("XR", VER); + if (config.addxforwardedfor()) + clientrequest.addheader ("X-Forwarded-For", clientip()); + for (unsigned n = 0; n < config.nserverheaders(); n++) + clientrequest.addheader (config.serverheader(n)); + + // Send to server + if (!sendclientrequest() ) { + msg ("Failed to send client request to back end, not processing\n"); + return; + } + + // Get server's response + if (!getserverresponse()) { + msg ("Failed to receive a valid back end response, not processing\n"); + 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 + writechunk (clientfd(), serverresponse.data(), serverresponse.size()); + + // Then switch to the TCP copy/thru protocol. + TcpDispatcher::handle(); +} diff --git a/xr/httpdispatcher/httpdispatcher b/xr/httpdispatcher/httpdispatcher @@ -0,0 +1,28 @@ +#ifndef _HTTPDISPATCHER_ +#define _HTTPDISPATCHER_ + +#include "../sys/sys" +#include "../tcpdispatcher/tcpdispatcher" +#include "../httpbuffer/httpbuffer" + +class HttpDispatcher: public TcpDispatcher { +public: + HttpDispatcher (int fd, string ip); + + void dispatch(); + void handle(); + bool issticky() const { return (is_sticky); } + void issticky (bool s) { is_sticky = s; } + +private: +private: + bool getclientrequest(); + bool sendclientrequest(); + bool getserverresponse(); + bool sendserverresponse(); + + Httpbuffer clientrequest, serverresponse; + bool is_sticky; +}; + +#endif diff --git a/xr/httpdispatcher/httpdispatcher1.cc b/xr/httpdispatcher/httpdispatcher1.cc @@ -0,0 +1,5 @@ +#include "httpdispatcher" + +HttpDispatcher::HttpDispatcher (int fd, string ip) : + TcpDispatcher (fd, ip), clientrequest(), serverresponse() { +} diff --git a/xr/httpdispatcher/sendclientrequest.cc b/xr/httpdispatcher/sendclientrequest.cc @@ -0,0 +1,10 @@ +#include "httpdispatcher" + +bool HttpDispatcher::sendclientrequest() { + Fdset set (config.backend_timeout()); + set.add (backendfd()); + return ( (set.writeable() == backendfd()) && + (writechunk (backendfd(), + clientrequest.data(), clientrequest.size()) == + clientrequest.size()) ); +} diff --git a/xr/httpdispatcher/sendserverresponse.cc b/xr/httpdispatcher/sendserverresponse.cc @@ -0,0 +1,12 @@ +#include "httpdispatcher" + +bool HttpDispatcher::sendserverresponse() { + msg ("Sending server response '" + serverresponse.firstline() + + "' to client.\n"); + Fdset set (config.client_timeout()); + set.add (clientfd()); + return ( (set.writeable() == clientfd()) && + (writechunk (clientfd(), + serverresponse.data(), serverresponse.size()) == + serverresponse.size()) ); +} diff --git a/xr/leastconn/leastconn b/xr/leastconn/leastconn @@ -0,0 +1,13 @@ +#ifndef _LEASTCONN_ +#define _LEASTCONN_ + +#include "../sys/sys" +#include "../error/error" +#include "../algorithm/algorithm" +#include "../balancer/balancer" + +class Leastconn: public Algorithm { + int target(); +}; + +#endif diff --git a/xr/leastconn/target.cc b/xr/leastconn/target.cc @@ -0,0 +1,23 @@ +#include "leastconn" + +int Leastconn::target() { + bool found = false; + unsigned nconn; + int t; + + for (unsigned i = 0; i < balancer.nbackends(); i++) { + if (!balancer.backend(i).available()) + continue; + if (!found || + balancer.backend(i).connections() < nconn) { + nconn = balancer.backend(i).connections(); + t = i; + found = true; + } + } + + if (!found) + throw ((Error) "Least-connections algorithm: " + "no available back ends"); + return (t); +} diff --git a/xr/roundrobin/roundrobin b/xr/roundrobin/roundrobin @@ -0,0 +1,13 @@ +#ifndef _ROUNDROBIN_ +#define _ROUNDROBIN_ + +#include "../sys/sys" +#include "../error/error" +#include "../algorithm/algorithm" +#include "../balancer/balancer" + +class Roundrobin: public Algorithm { + int target(); +}; + +#endif diff --git a/xr/roundrobin/target.cc b/xr/roundrobin/target.cc @@ -0,0 +1,26 @@ +#include "roundrobin" + +int Roundrobin::target() { + static int last = -1; + int t = last + 1; + + if (last == -1) { + lock(); + last = 0; + unlock(); + } + + while (1) { + if (balancer.backend(t).available()) { + lock(); + last = t; + unlock(); + return (t); + } + t++; + t %= balancer.nbackends(); + if (t == last) + throw ((Error) "Round-robin algorithm: no available back ends"); + } +} + diff --git a/xr/servertype/servertype b/xr/servertype/servertype @@ -0,0 +1,31 @@ +#ifndef _SERVERTYPE_ +#define _SERVERTYPE_ + +#include "../sys/sys" +#include "../error/error" + +using namespace std; + +class Servertype { +public: + enum Type { + t_tcp, + t_http, + }; + + Servertype() : t(t_tcp) { + } + Servertype (string id) { + type (id); + } + + void type (string id); + Type type() const { + return (t); + } + +private: + Type t; +}; + +#endif diff --git a/xr/servertype/type1.cc b/xr/servertype/type1.cc @@ -0,0 +1,11 @@ +#include "servertype" + +void Servertype::type (string id) { + if (id == "tcp") + t = t_tcp; + else if (id == "http") + t = t_http; + else + throw ((Error) "Bad server type '" + id + + "', supported are 'tcp' or 'http'"); +} diff --git a/xr/stopthread/execute.cc b/xr/stopthread/execute.cc @@ -0,0 +1,30 @@ +#include "stopthread" + +void Stopthread::execute() { + static int stopping; + static int last_connections; + int loc_stopping; + + if ( (loc_stopping = stopping) ) { + msg ("XR is waiting to shut down.\n"); + return; + } + + lock(); + stopping++; + unlock(); + msg ("XR will shut down.\n"); + + while (1) { + if (balancer.connections()) { + ostringstream o; + o << balancer.connections(); + msg ("There are still " + o.str() + " connections, " + "XR will stop when they do.\n"); + sleep (1); + continue; + } + msg ("No active connections, terminating XR.\n"); + exit (0); + } +} diff --git a/xr/stopthread/stopthread b/xr/stopthread/stopthread @@ -0,0 +1,13 @@ +#ifndef _STOPTHREAD_ +#define _STOPTHREAD_ + +#include "../sys/sys" +#include "../thread/thread" +#include "../balancer/balancer" + +class Stopthread: public Thread { +public: + void execute(); +}; + +#endif diff --git a/xr/sys/debugmsg.cc b/xr/sys/debugmsg.cc @@ -0,0 +1,15 @@ +#include "sys" +#include "../config/config" +#include "../thread/thread" + +void debugmsg (string const &s) { + if (config.debug()) { + Thread th; + th.lock(); + if (config.prefixtimestamp()) + cerr << timestamp() << ' '; + cerr << pthread_self() << " DEBUG: " << s; + cerr.flush(); + th.unlock(); + } +} diff --git a/xr/sys/ipmatch.cc b/xr/sys/ipmatch.cc @@ -0,0 +1,14 @@ +#include "sys" +#include "../config/config" + +bool ipmatch (struct in_addr adr, struct in_addr mask) { + long laddr = * ((long*)&adr); + long lmask = * ((long*)&mask); + 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"); + return (match); +} diff --git a/xr/sys/main.cc b/xr/sys/main.cc @@ -0,0 +1,53 @@ + +#include "../sys/sys" +#include "../config/config" +#include "../balancer/balancer" +#include "../error/error" +#include "../stopthread/stopthread" + +using namespace std; + +Config config; +Balancer balancer; + +static void sigcatcher (int sig) { + if (sig == SIGHUP) + balancer.report(true); + else if (sig != SIGPIPE) + balancer.terminate(true); +} + +static void out_of_memory() { + cerr << "ERROR: Out of memory\n"; + exit (1); +} + +int main (int argc, char **argv) { + static int relevant_sig[] = { + SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP, SIGPIPE + }; + + try { + // Load configuration from the commandline, promote verbosity + config.parsecmdline (argc, argv); + + ostringstream o; + o << getpid(); + msg ("XR running as PID " + o.str() + "\n"); + + // Load the signal handler. + for (unsigned i = 0; i < sizeof(relevant_sig); i++) + signal (relevant_sig[i], sigcatcher); + + // Configure the balancer and start serving. + balancer.init(); + balancer.serve(); + } catch (Error e) { + cerr << e.txt() << endl; + return (1); + } catch (...) { + cerr << "ERROR: Unidentified exception caught" << endl; + } + + return (0); +} diff --git a/xr/sys/msg.cc b/xr/sys/msg.cc @@ -0,0 +1,15 @@ +#include "sys" +#include "../config/config" +#include "../thread/thread" + +void msg (string const &s) { + if (config.verbose()) { + Thread th; + th.lock(); + if (config.prefixtimestamp()) + cerr << timestamp() << ' '; + cerr << pthread_self() << " INFO: " << s; + cerr.flush(); + th.unlock(); + } +} diff --git a/xr/sys/reportmsg.cc b/xr/sys/reportmsg.cc @@ -0,0 +1,13 @@ +#include "sys" +#include "../config/config" +#include "../thread/thread" + +void reportmsg (string const &s) { + Thread th; + th.lock(); + if (config.prefixtimestamp()) + cerr << timestamp() << ' '; + cerr << pthread_self() << " REPORT: " << s; + cerr.flush(); + th.unlock(); +} diff --git a/xr/sys/serversocket.cc b/xr/sys/serversocket.cc @@ -0,0 +1,48 @@ +#include "sys" +#include "../error/error" + +int serversocket (string addr, int port, string desc) { + int sock; + + // Create the server socket, set options + if ( (sock = socket (PF_INET, SOCK_STREAM, 0)) < 0 ) + throw ((Error) "Failed to create " + desc + " socket: " + + strerror(errno)); + int val = 1; + if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) + throw ((Error) "Failed to set socket options for " + desc + ": " + + strerror(errno)); + + // Prepare binding + struct sockaddr_in saddr; + memset (&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + + // Assign interface to listen to + 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 ((Error) "Cannot convert " + desc + " IP '" + addr + + "' to network bytes"); + display_ip << addr; + } else { + 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 ((Error) "Failed to bind " + desc + " to " + display_ip.str() + + ": " + strerror(errno)); + if (listen (sock, 5) < 0) + throw ((Error) "Failed to listen to " + desc + " IP " + + display_ip.str() + ": " + strerror(errno)); + + msg ("TCP server for " + desc + " listening to " + display_ip.str() + "\n"); + + return (sock); +} + diff --git a/xr/sys/sys b/xr/sys/sys @@ -0,0 +1,44 @@ +#ifndef _SYS_ +#define _SYS_ + +/* System-wide includes, 'cuz I'm too lazy to repeat them throughout + * the entire source tree. + */ + +// C +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <netdb.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> + +// C++ +#include <iostream> +#include <map> +#include <sstream> +#include <string> +#include <vector> + +/* Generic functions */ + +using namespace std; + +void msg (string const &s); +void debugmsg (string const &s); +void reportmsg (string const &s); +int serversocket (string addr, int port, string description); +string timestamp(); +bool ipmatch (struct in_addr addr, struct in_addr mask); + +#endif diff --git a/xr/sys/timestamp.cc b/xr/sys/timestamp.cc @@ -0,0 +1,15 @@ +#include "sys" + +string timestamp() { + struct timeval tv; + gettimeofday (&tv, 0); + + struct tm *tmp = localtime(&tv.tv_sec); + + char buf[80]; + sprintf (buf, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d,%3.3d", + tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec, + tv.tv_usec / 1000); + return (buf); +} diff --git a/xr/tcpdispatcher/dispatch.cc b/xr/tcpdispatcher/dispatch.cc @@ -0,0 +1,28 @@ +#include "tcpdispatcher" + +void TcpDispatcher::dispatch() { + Backend tb; + bool connected = false; + + while (!connected) { + target_backend = algorithm->target(); + tb = balancer.backend(target_backend); + if (!tb.connect()) { + lock(); + balancer.backend(target_backend).live(false); + unlock(); + msg ("Failed to connect to back end " + tb.description() + + ", trying to dispatch to other\n"); + } else { + connected = true; + backendfd(tb.sock()); + + ostringstream o; + o << target_backend; + msg ("Dispatching client to back end " + o.str() + " " + + tb.description() + "\n"); + + break; + } + } +} diff --git a/xr/tcpdispatcher/execute.cc b/xr/tcpdispatcher/execute.cc @@ -0,0 +1,43 @@ +#include "tcpdispatcher" + +void TcpDispatcher::execute() { + ostringstream o; + o << clientfd(); + msg ("Dispatch request for client fd " + o.str() + "\n"); + + try { + dispatch(); + } catch (Error e) { + cerr << e.txt() << "\n"; + close (clientfd()); + return; + } + + ostringstream co; + co << clientfd(); + ostringstream bo; + bo << balancer.backend(target_backend).sock(); + msg ("Dispatching client fd " + co.str() + " to " + + balancer.backend(target_backend).description() + + ", fd " + bo.str() + "\n"); + + lock(); + balancer.backend(target_backend).startconnection(); + unlock(); + + try { + handle(); + } catch (Error e) { + cerr << e.txt() << "\n"; + } + + lock(); + balancer.backend(target_backend).endconnection(); + unlock(); + + close (clientfd()); + close (backendfd()); + msg ("Done dispatching client fd " + co.str() + " at " + + balancer.backend(target_backend).description() + "\n"); + +} diff --git a/xr/tcpdispatcher/handle.cc b/xr/tcpdispatcher/handle.cc @@ -0,0 +1,15 @@ +#include "tcpdispatcher" + +void TcpDispatcher::handle() { + while (1) { + Fdset readset (config.client_timeout()); + readset.add (clientfd()); + readset.add (backendfd()); + + int s; + if ((s = readset.readable()) < 0 || !readchunk(s)) + break; + writechunk (s == clientfd() ? backendfd() : clientfd(), + databuf(), databufsize()); + } +} diff --git a/xr/tcpdispatcher/printable.cc b/xr/tcpdispatcher/printable.cc @@ -0,0 +1,24 @@ +#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 ("."); +} diff --git a/xr/tcpdispatcher/readchunk.cc b/xr/tcpdispatcher/readchunk.cc @@ -0,0 +1,20 @@ +#include "tcpdispatcher" + +unsigned TcpDispatcher::readchunk (int src) { + ssize_t nread = read (src, data_buf, config.buffersize()); + if (nread < 0) + throw ((Error) "Read failed on fd " + src); + + balancer.backend(targetbackend()).addbytes (databufsize()); + + 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); +} diff --git a/xr/tcpdispatcher/tcpdispatcher b/xr/tcpdispatcher/tcpdispatcher @@ -0,0 +1,47 @@ +#ifndef _TCPDISPATCHER_ +#define _TCPDISPATCHER_ + +#include "../sys/sys" +#include "../balancer/balancer" +#include "../config/config" +#include "../thread/thread" +#include "../algorithm/algorithm" +#include "../roundrobin/roundrobin" +#include "../firstactive/firstactive" +#include "../leastconn/leastconn" +#include "../external/external" + +class TcpDispatcher: public Thread { +public: + + TcpDispatcher (int fd, string ip); + virtual ~TcpDispatcher(); + + virtual void execute(); + + virtual void dispatch(); + virtual void handle(); + + int targetbackend() const { return target_backend; } + void targetbackend(int t) { target_backend = t; } + string clientip() const { return client_ip; } + int clientfd() const { return client_fd; } + void clientfd(int c) { client_fd = c; } + int backendfd() const { return backend_fd; } + void backendfd(int b) { backend_fd = b; } + char const *databuf() const { return data_buf; } + unsigned databufsize() const { return data_bufsz; } + + unsigned readchunk (int src); + unsigned writechunk (int dst, char const *b, unsigned blen); + +private: + string printable (char ch) const; + string client_ip; + int target_backend, client_fd, backend_fd; + char *data_buf; + unsigned data_bufsz; + Algorithm *algorithm; +}; + +#endif diff --git a/xr/tcpdispatcher/tcpdispatcher1.cc b/xr/tcpdispatcher/tcpdispatcher1.cc @@ -0,0 +1,25 @@ +#include "tcpdispatcher" + +TcpDispatcher::TcpDispatcher(int cfd, string cip): + Thread(), client_ip(cip), target_backend(-1), client_fd(cfd), + backend_fd(-1), data_bufsz(0) { + + // Set up a data buffer for network transfers + data_buf = new char[config.buffersize()]; + + // Instantiate dispatchmode algorithm + switch (config.dispatchmode()) { + case Dispatchmode::m_roundrobin: + algorithm = new Roundrobin; + break; + case Dispatchmode::m_leastconn: + algorithm = new Leastconn; + break; + case Dispatchmode::m_firstactive: + algorithm = new Firstactive; + break; + case Dispatchmode::m_external: + algorithm = new External; + break; + } +} diff --git a/xr/tcpdispatcher/tcpdispatcher2.cc b/xr/tcpdispatcher/tcpdispatcher2.cc @@ -0,0 +1,7 @@ +#include "tcpdispatcher" + +TcpDispatcher::~TcpDispatcher() { + delete data_buf; + delete algorithm; + debugmsg ("TCP dispatcher finished\n"); +} diff --git a/xr/tcpdispatcher/writechunk.cc b/xr/tcpdispatcher/writechunk.cc @@ -0,0 +1,27 @@ +#include "tcpdispatcher" + +unsigned TcpDispatcher::writechunk (int dst, char const *b, unsigned blen) { + // Client fd 0 (stdin) gets written to stdout. + if (!dst) + dst = 1; + + unsigned totwritten = 0; + while (totwritten < blen) { + ssize_t nwritten = write (dst, (void*) (b + totwritten), + blen - totwritten); + if (nwritten < 1) + throw ((Error) "Write failed"); + + if (config.debug()) { + ostringstream o; + o << "Sent " << nwritten << " bytes to fd " << dst << ": "; + for (unsigned i = 0; i < (unsigned) nwritten; i++) + o << printable(b[totwritten + i]); + o << "\n"; + debugmsg (o.str()); + } + + totwritten += nwritten; + } + return (totwritten); +} diff --git a/xr/thread/execute.cc b/xr/thread/execute.cc @@ -0,0 +1,5 @@ +#include "thread" + +// Dummy incase the derived class doesn't define one +void Thread::execute() { +} diff --git a/xr/thread/lock.cc b/xr/thread/lock.cc @@ -0,0 +1,7 @@ +#include "thread" + +void Thread::lock() { + int res; + if ( (res = pthread_mutex_lock (&thread_mutex)) ) + throw ((Error) "Failed to obtain mutex lock: " + strerror(res)); +} diff --git a/xr/thread/start.cc b/xr/thread/start.cc @@ -0,0 +1,51 @@ +#include "thread" + +static void *_run (void *data) { + Thread *t = (Thread*) data; + + try { + t->execute(); + } catch (Error e) { + cerr << e.txt() << "\n"; + } + + // Cleanups + delete (t); + + // To satisfy the prototype + return (0); +} + +void Thread::start() { + + if (config.foregroundmode()) + _run ((void*)this); + else { + pthread_t th; + pthread_attr_t attr; + int res; + + if (pthread_attr_init (&attr)) + throw ((Error) "Cannot initialize thread attributes"); + if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED)) + throw ((Error) "Cannot set thread state as detached"); + for (int i = 0; i < 3; i++) { + res = pthread_create (&th, &attr, _run, (void*) this); + if (!res) { + pthread_attr_destroy (&attr); + return; + } else if (res == EAGAIN) { + msg ("Failed to start thread: " + (string)strerror(res) + + ", retrying\n"); + sleep (1); + continue; + } else { + pthread_attr_destroy (&attr); + throw ((Error) "Failed to start thread: " + strerror(res)); + } + } + + throw ((Error) "Failed to start thread: " + "Resources unavailable after 3 tries, giving up"); + } +} diff --git a/xr/thread/thread b/xr/thread/thread @@ -0,0 +1,22 @@ +#ifndef _THREAD_ +#define _THREAD_ + +#include "../sys/sys" +#include "../error/error" +#include "../config/config" + +using namespace std; + +class Thread { +public: + Thread(); + virtual ~Thread(); + void start(); + void lock(); + void unlock(); + virtual void execute(); +private: + pthread_mutex_t thread_mutex; +}; + +#endif diff --git a/xr/thread/thread1.cc b/xr/thread/thread1.cc @@ -0,0 +1,8 @@ +#include "thread" + +Thread::Thread() { + int res; + + if ( (res = pthread_mutex_init (&thread_mutex, 0)) ) + throw ((Error) "Failed to initialize mutex: " + strerror(res)); +} diff --git a/xr/thread/thread2.cc b/xr/thread/thread2.cc @@ -0,0 +1,4 @@ +#include "thread" + +Thread::~Thread() { +} diff --git a/xr/thread/unlock.cc b/xr/thread/unlock.cc @@ -0,0 +1,7 @@ +#include "thread" + +void Thread::unlock() { + int res; + if ( (res = pthread_mutex_unlock (&thread_mutex)) ) + throw ((Error) "Failed to release mutex lock: " + strerror(res)); +} diff --git a/xr/wakeupthread/execute.cc b/xr/wakeupthread/execute.cc @@ -0,0 +1,19 @@ +#include "wakeupthread" + +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); + } + } + } + sleep (config.wakeupsec()); + } +} + diff --git a/xr/wakeupthread/wakeupthread b/xr/wakeupthread/wakeupthread @@ -0,0 +1,14 @@ +#ifndef _WAKEUPTHREAD_ +#define _WAKEUPTHREAD_ + +#include "../sys/sys" +#include "../thread/thread" +#include "../balancer/balancer" +#include "../error/error" + +class Wakeupthread: public Thread { +public: + void execute(); +}; + +#endif diff --git a/xrctl/xrctl b/xrctl/xrctl @@ -0,0 +1,465 @@ +#!/usr/bin/perl +use strict; + +# Configuration section. Enter your favorite values here. +# ------------------------------------------------------- + +# 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 +my $pscmd = '/bin/ps ax -o pid,command'; + +# 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. +# See the examples below to help you model your favorite dispatcher. +my %services = + ( + + # Basically just a port forwarder to localhost:80. I use it for + # benchmarking. + 'fwd1' => + { '--server' => [ qw(tcp:0:10000) ], + '--backend' => [ qw(localhost:80) ], + # '--verbose' => undef, + # '--debug' => undef, + }, + + # Same thing, but a HTTP forwarder that adds X-Forwarded-For headers. + # I use it for benchmarking HTTP handling. + 'fwd2' => + { '--server' => [ qw(http:0:10001) ], + '--backend' => [ qw(localhost:80) ], + # '--verbose' => undef, + }, + + + # 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. + '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) ], + }, + + + # 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-active, 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-active) ], + '--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}); + } +} + +# 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); +} else { + usage(); +} + +# 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 +} + +# List services and command lines +# ------------------------------- +sub list { + print ("Configured services: ", + join (', ', sort (keys (%services))), + "\n"); + 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"); + } +} + +# Show the status of the commands +# ------------------------------- +sub status { + for my $s (@_) { + die ("xrctl: No such service '$s'\n") unless ($services{$s}); + print ("Service $s: ", getstatus($s), "\n"); + } +} + +# Start service(s) +# ---------------- +sub start { + 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"); + } + } +} + +# Stop service(s) +# --------------- +sub stop { + for my $s (@_) { + print ("Service $s: "); + my $status = getstatus($s); + if ($status =~ /^not/) { + print ($status, "\n"); + } else { + my $pid = servicebypidfile($s); + $pid = servicebypslist($s) unless ($pid); + die ("Failed to get PID\n") unless ($pid); + kill (15, $pid); + print ("stopping\n"); + } + } +} + +# Restart service(s) +# ------------------ +sub restart { + 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"); + } + } +} + +# Force service(s) up +# ------------------- +sub force { + for my $s (@_) { + print ("Service $s: "); + my $status = getstatus($s); + if ($status =~ /^not/) { + rundaemon ($s, xrcommand($s)); + print ("started\n"); + } else { + print ($status, "\n"); + } + } +} + +# Rotate logs +# ----------- +sub rotate { + if ($use_logger and findbin('logger')) { + print ("Rotating disabled, logging via logger/syslog\n"); + return; + } + for my $s (@_) { + print ("Service $s: "); + my $f = logfile($s); + if (! -f $f) { + print ("no logfile $f\n"); + } elsif ((stat($f))[7] < $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); + } + } +} + +# 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)"); + } +} + +# Return a command to start XR +# ---------------------------- +sub xrcommand ($) { + 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 (! $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); + } + } + } + } + return (@ret); +} + +# Return the PID file of a given service +# -------------------------------------- +sub pidfile ($) { + my $s = shift; + return ("$piddir/xr-$s.pid"); +} + +# 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); + } else { + return (undef); + } +} + +# 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/); + } + 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"); +} + +# Find a binary along the path +# ----------------------------- +sub findbin ($) { + my $b = shift; + for my $d (@bindirs) { + return ("$d/$b") if (-x "$d/$b"); + } + return (undef); +} + +# Run a command as a daemon +# ------------------------- +sub rundaemon { + my $s = shift; + my @args = @_; + + my $logger = findbin('logger'); + my $outfile = logfile($s); + + my $pid = fork(); + return if ($pid > 0); + + # 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"); + } 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 $truecmd = shift (@args); + exec ({ $truecmd } @args); + exit (1); +}