]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Author: Jens-S. V�ckler <voeckler@rvs.uni-hannover.de>
authorAmos Jeffries <squid3@treenet.co.nz>
Thu, 8 Jul 2010 12:28:08 +0000 (00:28 +1200)
committerAmos Jeffries <squid3@treenet.co.nz>
Thu, 8 Jul 2010 12:28:08 +0000 (00:28 +1200)
Import squid cache 'purge' tool

Just the original code import for crediting the original author.

TODO:
 - patch to fix build problems in modern systems
 - upgrade to cope with current cache_dir formats
 - bundle and distribute updated tool

16 files changed:
tools/purge/Makefile [new file with mode: 0644]
tools/purge/README [new file with mode: 0644]
tools/purge/conffile.cc [new file with mode: 0644]
tools/purge/conffile.hh [new file with mode: 0644]
tools/purge/convert.cc [new file with mode: 0644]
tools/purge/convert.hh [new file with mode: 0644]
tools/purge/copyout.cc [new file with mode: 0644]
tools/purge/copyout.hh [new file with mode: 0644]
tools/purge/hexd.c [new file with mode: 0644]
tools/purge/purge.cc [new file with mode: 0644]
tools/purge/signal.cc [new file with mode: 0644]
tools/purge/signal.hh [new file with mode: 0644]
tools/purge/socket.cc [new file with mode: 0644]
tools/purge/socket.hh [new file with mode: 0644]
tools/purge/squid-tlv.cc [new file with mode: 0644]
tools/purge/squid-tlv.hh [new file with mode: 0644]

diff --git a/tools/purge/Makefile b/tools/purge/Makefile
new file mode 100644 (file)
index 0000000..946f394
--- /dev/null
@@ -0,0 +1,181 @@
+#
+# Makefile
+#
+# The Makefile is divided into three sections, the "generic section", the
+# "host section" and the "rules section". The generics section defines
+# defaults which you should not change. Changes should solely be made to
+# the rules section.
+#
+# You will need to select several parameters befitting your compiler/system:
+#
+# -DHAS_BOOL   - set, if your C++ compiler knows about the 'bool' type.
+# -DHAS_PSIGNAL - set, if your libc supports psignal(int,const char*).
+# -fno-exceptions - may not be recognized by all variants of g++
+# -ffor-scope  - the new ANSI C++ scoping of for() variables is used...
+#
+# === [1] ==================================================== generics section
+#
+CXX    = g++ -ffor-scope -DHAS_BOOL -DHAS_PSIGNAL
+CC     = gcc
+LD     = $(CC)         # yes, I do mean gcc and not g++
+CXXFLAGS = # -pg -g # -fprofile-arcs -ftest-coverage
+SYSTEM  = $(shell uname -s | tr '[a-z]' '[A-Z]' | tr -d '_ -/')
+CPU    = $(shell uname -p)
+VERSION = $(shell uname -r)
+HOST   = $(shell uname -n)
+MAJOR   = $(firstword $(subst ., ,$(VERSION)))
+MINOR   = $(strip $(word 2,$(subst ., ,$(VERSION))))
+LOADLIBES =
+SOCKLEN        = int # default except for glibc2?
+
+# optimization levels - Do *not* use levels above -O1 with g++,
+# if -fprofile-arcs or -ftest-coverage is selected! Set to different
+# values in the host specific section below.
+#
+# - OPT_NORM for normal level optimization, O2 is a good choice.
+#
+OPT_NORM = -O2
+
+# electric fence library, for test purposes only (helps w/ memory leaks)
+# (developers only)
+EFENCE = -L/usr/local/lib -lefence
+
+#
+# === [2] ======================================================= hosts section
+#
+
+ifeq (SUNOS,${SYSTEM})
+ifeq (5,${MAJOR})
+# use these for the SUN CC compiler (for STL, see below or above)
+# You must define this for Solaris 2.x: CXXFLAGS = -DSOLARIS 
+CC     = cc
+#CXX   = CC -DHAS_BOOL -DHAS_PSIGNAL -DHAS_MUTABLE
+#CXXFLAGS = -DSOLARIS  '-library=%none,Cstd,Crun' 
+#CXXFLAGS += -dalign -ftrap=%none -fsimple -xlibmil
+#OPT_NORM = -xtarget=ultra2 -xO4
+#EXTRALIB += -lnsl -lsocket
+#LD    = CC
+#
+## g++ settings for Solaris on Ultra Sparcs (comment out all of above):
+CXXFLAGS += -DSOLARIS # -ggdb
+OPT_NORM = -O2 # -mcpu=supersparc
+LD     = $(CC)
+##
+#EXTRALIB += -lnsl -lsocket -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic
+else
+# old SunOS 4.1.x, not supported!
+CXXFLAGS += -DSUN
+endif
+endif
+
+ifeq (IRIX64,${SYSTEM})
+# The regular 64bit Irix stuff is just too slow, use n32!
+SYSTEM        := IRIX
+endif
+
+ifeq (FREEBSD,${SYSTEM})
+SOCKLEN        = socklen_t
+endif
+
+ifeq (IRIX,${SYSTEM})
+CXX     = CC -n32 -mips3 -r4000 -DEFAULT:abi=n32:isa=mips3:proc=r4k
+CXX    += -LANG:ansi-for-init-scope=on -LANG:bool=on
+CXX    += -LANG:exceptions=off -LANG:explicit=off -LANG:wchar_t=off
+CXX    += -LANG:mutable=on -LANG:namespaces=on -LANG:std
+CC     = cc -n32 -mips3 -r4000
+CXXFLAGS = -woff 1174 -LANG:exceptions=off -DHAS_BOOL -DHAS_PSIGNAL
+LD     = $(CXX)
+OPT_NORM = -O3 -IPA -LNO:opt=1
+# for g++
+#CXXFLAGS += -mips3 -mcpu=r4000 
+endif
+
+ifeq (AIX,${SYSTEM})
+ifeq (,${MINOR})
+MINOR  := ${MAJOR}
+MAJOR  = 4
+endif
+CXX    = xlC -UHAS_BOOL -UHAS_PSIGNAL
+CC     = xlc
+CXXFLAGS = -qtune=pwr # -qdbxextra -g
+#CXX   = g++ -ffor-scope -DHAS_BOOL -UHAS_PSIGNAL
+SOCKLEN        = size_t
+LD     = $(CXX)
+endif
+
+ifeq (LINUX,${SYSTEM})
+# determine highest version of all installed libc's.
+LIBCVER = $(shell /bin/ls /lib/libc.so.? | \
+       awk -F'.' '{ if (m<$$3) m=$$3;} END { print m} ')
+ifeq (6,${LIBCVER})
+SOCKLEN        = size_t
+endif
+CXXFLAGS += -DHAS_PSIGNAL -DLIBCVERSION=$(LIBCVER) -pipe # -Wall -pedantic
+OPT_NORM = -march=pentium -O2
+# if your g++ balks (e.g. SuSE still uses 2.7.2.3)
+#CXXFLAGS += -DHAS_PSIGNAL -DLIBCVERSION=$(LIBCVER) -m486
+LD     = $(CC)
+EXTRALIB = -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic
+endif
+
+#
+# === [3] ======================================================= rules section
+# There is no need to change things below this line.
+CXXFLAGS += -D${SYSTEM} -DMAJOR=${MAJOR} -DMINOR=${MINOR} -DSOCKLEN=${SOCKLEN}
+CFLAGS = $(CXXFLAGS)
+LDFLAGS += $(OPT_NORM)
+
+%.o:%.cc
+       $(CXX) $(CXXFLAGS) $(OPT_NORM) -c $< -o $@
+
+OBJS   = convert.o socket.o signal.o squid-tlv.o copyout.o conffile.o
+SRCS   = $(OBJS:.o=.cc)
+HDRS   = $(OBJS:.o=.hh)
+FILES  = $(SRCS) $(HDRS) Makefile purge.cc hexd.c
+DIST   = $(addprefix purge/,$(FILES) README)
+
+all: purge
+
+purge: $(OBJS) purge.o 
+       $(LD) $(OPT_NORM) $(LDFLAGS) $^ -o $@ $(LOADLIBES) $(EXTRALIB)
+hexd: hexd.o
+       $(CC) $(OPT_NORM) $(LDFLAGS) $^ -o $@ $(LOADLIBES)
+#
+# object file rules, generated with "g++ -MM -E *.cc"
+#
+purge.o: purge.cc $(HDRS)
+       $(CXX) $(CXXFLAGS) $(OPT_NORM) -c $< -o $@
+convert.o: convert.cc convert.hh
+conffile.o: conffile.cc conffile.hh
+signal.o: signal.cc signal.hh
+socket.o: socket.cc socket.hh convert.hh
+squid-tlv.o: squid-tlv.cc squid-tlv.hh
+copyout.o: copyout.cc copyout.hh
+hexd.o: hexd.c
+
+clean:
+       $(RM) *.o
+       if [ "${SYSTEM}" = "IRIX"  ]; then rm -rf ii_files; fi
+       if [ "${SYSTEM}" = "SUNOS" ]; then rm -rf Templates.DB; fi
+       if [ "${SYSTEM}" = "SUNOS" ]; then rm -rf SunWS_cache; fi
+
+distclean: clean
+       $(RM) purge hexd
+
+realclean: distclean
+clobber: distclean
+
+co-all:        $(FILES)
+       echo all checked out
+co-all-lock:
+       co -l $(FILES)
+ci-all:
+       for i in $(FILES); do \
+               test -w $$i && ci $$i; \
+               rm -f $$i; \
+       done
+
+dist: distclean co-all
+       ( cd .. ; gtar cvzf purge-`date +"%Y%m%d"`-src.tar.gz $(DIST) )
+tar: distclean ci-all
+       ( cd .. ; gtar cvzf purge-`date +"%Y%m%d"`-all.tar.gz purge )
diff --git a/tools/purge/README b/tools/purge/README
new file mode 100644 (file)
index 0000000..d41905a
--- /dev/null
@@ -0,0 +1,330 @@
+purge
+=====
+
+The purge tool is a kind of magnifying glass into your squid-2 cache. You
+can use purge to have a look at what URLs are stored in which file within
+your cache. The purge tool can also be used to release objects which URLs
+match user specified regular expressions. A more troublesome feature is the
+ability to remove files squid does not seem to know about any longer.
+
+    USE AT YOUR OWN RISK! NO GUARANTEES, WHATSOEVER! DON'T BLAME US!
+                        YOU HAVE BEEN WARNED!
+
+
+
+compilation
+===========
+
+Purge has been successfully compiled under the following OSes:
+
+       SYSTEM          g++     native
+       ------          ---     ------
+       Solaris 2.7     yes     CC
+       IRIX 6.5        yes     CC -n32
+       Linux 2.0.36    yes     (g++ IS native)
+       FreeBSD 4.x     yes     gmake port must be installed
+                               (g++ IS supported)
+
+The recent move of the Linux community towards glibc2 may cause some
+troubles, though. The compilation requires GNU make, no other make will work
+correctly. The source distribution contains all files checked into the
+revision control repository. Therefore, you will need to install GNU RCS
+first (which in turn needs the GNU diffutils). 
+
+The repository also contains the prototypical Perl implementation. The user
+interface in the C++ implementation changed a little when compared to the
+Perl one. You will have to state at least one regular expression for purge
+to start working. Also, printing the complete cache URLs, you will need to
+specify the "-e ." regular expression.
+
+In order to compile the purge tool, untar the source distribution and
+change into the purge directory. With RCS and GNU make installed, just say
+"make". GNU make will automagically retrieve all necessary files from the
+repository and create the binary. 
+
+Systems not stated above will need to retrieve the makefile (use "co -l
+Makefile" for this) and add their own platform specific definitions to
+section [2] in the makefile.
+
+
+
+squid preparation
+=================
+
+In order to use purge for real PURGEs, you will have to enable this feature
+in squid. By default, PURGE is disabled. You should watch closely for whom
+you enable the PURGE ability, otherwise total stranger just might wipe your
+cache content. The following lines will need to be added to your squid.conf
+(you may want to add further networks to the src_local ACL):
+
+       acl purge method PURGE
+       acl src_local src 127.0.0.0/8
+       http_access allow purge src_local
+       http_access deny  purge
+
+Reconfigure or restart (preferred) your squid after changing the
+configuration file.
+
+
+
+modes of operation
+==================
+
+$Id: purge.cc,v 1.15 2000/09/21 09:05:56 cached Exp $
+Usage:  purge   [-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] [-p h[:p]]
+                [-P #] [-s] [-v] [-C dir [-H]] [-n]
+
+ -a     display a little rotating thingy to indicate that I am alive (tty only).
+ -c c   squid.conf location, default "/usr/local/squid/etc/squid.conf".
+ -C dir base directory for content extraction (copy-out mode).
+ -d l   debug level, an OR of different debug options.
+ -e re  single regular expression per -e instance (use quotes!).
+ -E re  single case sensitive regular expression like -e.
+ -f fn  name of textfile containing one regular expression per line.
+ -F fn  name of textfile like -f containing case sensitive REs.
+ -H     prepend HTTP reply header to destination files in copy-out mode.
+ -n     do not fork() when using more than one cache_dir.
+ -p h:p cache runs on host h and optional port p, default is localhost:3128.
+ -P #   if 0, just print matches; otherwise OR the following purge modes:
+           0x01 really send PURGE to the cache.
+           0x02 remove all caches files reported as 404 (not found).
+           0x04 remove all weird (inaccessible or too small) cache files.
+        0 and 1 are recommended - slow rebuild your cache with other modes.
+ -s     show all options after option parsing, but before really starting.
+ -v     show more information about the file, e.g. MD5, timestamps and flags.
+
+--- &< snip, snip ---
+
+-a     is a kind of "i am alive" flag. It can only be activated, if
+       your stdout is a tty. If active, it will display a little 
+       rotating line to indicate that there is actually something
+       happening. You should not use this switch, if you capture 
+       your stdout in a file, or if your expression list produces
+       many matches. The -a flag is also incompatible with the
+       (default) multi cache_dir mode.
+
+       default: off
+       See also: -n
+
+-c cd  CHANGED!
+       this option lets you specify the location of the squid.conf file.
+       Purge now understands about more than one cache_dir, and does so
+       by parsing Squid's configuration file. It knows about both ways
+       of Squid-2 cache_dir specifications, and will automatically try
+       to use the correct one.
+
+       default: /usr/local/squid/etc/squid.conf
+
+-C cd  if you want to rescue files from your cache, you need to specify
+       the directory into which the files will be copied. Please note
+       that purge will try to establish the original server's directory 
+       structure. This switch also activates copy-out mode. Please do 
+       not use copy-out mode with any purge mode (-P) other than 0.
+
+       For instance, if you specified "-C /tmp", Purge will try to 
+       recreate /tmp/www.server.1/url/path/file, and so forth. 
+
+       default: off
+       See also: -H, -P
+
+-d l   lets you specify a debug level. Differents bits are reserved for
+       different output.
+
+       default: 0
+
+-e re  the "-e" options let you specify one regular expression at the 
+-E re  commandline. This is useful, if there is only a handful you
+       want to check. Please remember to escape the shell metachars
+       used in your regular expression. The use of single quotes 
+       around your expression is recommended. The capital letter
+       version works case sensitive, the lower caps version does not.
+
+       default: (no default)
+
+-f fn  if you have more than a handful of expression, or want to check
+-F fn  the same set at regular intervals, the file option might be more
+       useful to you. Each line in the text file will be regarded as
+       one regular expression.  Again, the capital letter version works
+       case sensitive, the lower caps version does not.
+
+       default: (no default)
+
+-H     if in copy-out mode (see: -C), you can specify to keep the
+       HTTP Header in the recreated file. 
+
+       default: off
+       See also: -C
+
+-n     by specifying the "-n" switch, you will tell Purge to process 
+       one cache_dir after another, instead of doing things in parallel.
+       If you have more than one cache_dir in your configuration,
+       Purge will fork off a worker process for each cache_dir to
+       do the checks for optimum speed - assuming a decently designed
+       cache. Since parallel execution will put quite some load on the 
+       system and its controllers, it is sometimes preferred to use 
+       less resources, though it will take longer. 
+
+       default: parallel mode for more than one cache_dir
+
+-p h[:p] Some cache admins (i.e. me) use a different port than 3128. The
+       purge tool will need to connect to your cache in order to send
+       the PURGE request (see -P). This option lets you specify the
+       host and port to connect to. The port is optional. The port
+       can be a name (check your /etc/services) or number. It is
+       separated from the host name portion by a single colon, no
+       spaces allowed.
+
+       default: localhost:3128
+
+-P #   If you want to do more than just print your cache content, you
+       will need to specify this option. Each bit is reserved for a
+       different action. Only the use of the LSB is recommended, the
+       rest should be considered experimental.
+
+               no bit set:     just print
+               bit#0 set:      send PURGE for matches
+               bit#1 set:      unlink object file for 404 not found PURGEs
+               bit#2 set:      unlink weird object files
+
+       If you use a value other than 0 or 1, you will need to slow
+       rebuild your cache content. A warning message will remind you
+       of that. If you use bit#1, all unsuccessful PURGEs will result
+       in the object file in your cache directory to be removed, because
+       squid does not seem to know about it any longer. Beware that the
+       asyncio might try to remove it after the purge tool, and thus
+       complains bitterly. Bit#1 only makes sense, if Bit#0 is also
+       set, otherwise it has no effect (since the HTTP status 404 is
+       never returned).
+
+       Bit#2 is reserved for strange files which do not even contain
+       a URL. Beware that these files may indicate a new object squid
+       currently intends to swap onto disk. If the file suddenly went
+       away, or is removed when squid tries to fetch the object, it
+       will complain bitterly. You must slow rebuild your cache, if
+       you use this option.
+
+       It is recommended that if you dare to use bit#1 or bit#2, you
+       should only grant the purge tool access to your squid, e.g. 
+       move the HTTP and ICP listening port of squid to a different
+       non-standard location during the purge.
+
+       default: 0 (just print)
+
+-s     If you specify this switch, all commandline parameters will be
+       shown after they were parsed.
+
+       default: off
+
+-v     be verbose in the things reported about the file. See the output
+       section below.
+
+
+output
+======
+
+In regular mode, the output of purge consists of four columns. If the
+URL contains not encoded whitespaces, it may look as if there are more
+columns, but the last one is the URI.
+
+ # name   meaning
+ - ------ -----------------------------------------------------------
+ 1 file   name of cache file eximed which matches the re.
+ 2 status return result of purge request, "  0" in print mode.
+ 3 size   object size including stored headers, not file size.
+ 4 uri    perceived uri
+
+Example for non-verbose output in print-mode:
+
+/cache3/00/00/0000004A   0     5682 http://graphics.userfriendly.org/images/slovenia.gif
+
+In verbose mode, additional columns are inserted before the uri. Time
+stamps are reported using hexadecimal notation, and Squid's standard
+for reporting "no such timestamp" == -1, and "unparsable timestamp" == -2.
+
+ # name   meaning
+ - ------ -----------------------------------------------------------
+ 1 file   name of cache file eximed which matches the re.
+ 2 status return result of purge request, "  0" in print mode "-P 0".
+ 3 size   object size including stored headers, not file size.
+ 4 md5    MD5 of URI from file, or "(no_md5_data_available)" string.
+ 5 ts     UTC of Value of Date: header in hex notation
+ 6 lr     UTC of last time the object was referenced
+ 7 ex     UTC of Expires: header
+ 8 lr     UTC of Last-Modified: header
+ 9 flags  Value of objects flags field in hex, see: Programmers Guide
+10 refcnt number of times the object was referenced.
+11 uri    STORE_META_URL uri or "strange_file"
+
+Example for verbose output in print-mode:
+
+/cache1/00/00/000000B7   0      406 7CFCB1D319F158ADC9CFD991BB8F6DCE 397d449b 39bf677b ffffffff 3820abfc 0460     1  http://www.netscape.com/images/nc_vera_tile.gif
+
+
+hexd
+====
+
+The hexd tool let's you conveniently hex dump a file both, in hex char and
+display char columns. Hexd only assumes that characters 0-31,127-159,255
+are not printable.
+
+
+$ ./hexd /cache1/00/00/000000B7 | less -r
+
+00000000: 03 00 00 00 6D 03 00 00-00 10 7C FC B1 D3 19 F1  ....m.....|ü±Ó.ñ
+00000010: 58 AD C9 CF D9 91 BB 8F-6D CE 05 00 00 00 18 39  X­ÉÏÙ.».mÎ.....9
+00000020: 7D 44 9B 39 BF 67 7B FF-FF FF FF 38 20 AB FC 00  }D.9¿g{....8 «ü.
+00000030: 00 00 00 00 01 04 60 04-00 00 00 30 68 74 74 70  ......`....0http
+00000040: 3A 2F 2F 77 77 77 2E 6E-65 74 73 63 61 70 65 2E  ://www.netscape.
+00000050: 63 6F 6D 2F 69 6D 61 67-65 73 2F 6E 63 5F 76 65  com/images/nc_ve
+00000060: 72 61 5F 74 69 6C 65 2E-67 69 66 00 08 48 54 54  ra_tile.gif..HTT
+00000070: 50 2F 31 2E 30 20 32 30-30 20 4F 4B 0D 0A 53 65  P/1.0 200 OK..Se
+00000080: 72 76 65 72 3A 20 4E 65-74 73 63 61 70 65 2D 45  rver: Netscape-E
+00000090: 6E 74 65 72 70 72 69 73-65 2F 33 2E 36 0D 0A 44  nterprise/3.6..D
+000000A0: 61 74 65 3A 20 54 75 65-2C 20 32 35 20 4A 75 6C  ate: Tue, 25 Jul
+000000B0: 20 32 30 30 30 20 30 37-3A 34 31 3A 31 35 20 47   2000 07:41:15 G
+000000C0: 4D 54 0D 0A 43 6F 6E 74-65 6E 74 2D 54 79 70 65  MT..Content-Type
+000000D0: 3A 20 69 6D 61 67 65 2F-67 69 66 0D 0A 4C 61 73  : image/gif..Las
+000000E0: 74 2D 4D 6F 64 69 66 69-65 64 3A 20 57 65 64 2C  t-Modified: Wed,
+000000F0: 20 30 33 20 4E 6F 76 20-31 39 39 39 20 32 31 3A   03 Nov 1999 21:
+00000100: 34 31 3A 31 36 20 47 4D-54 0D 0A 43 6F 6E 74 65  41:16 GMT..Conte
+00000110: 6E 74 2D 4C 65 6E 67 74-68 3A 20 36 37 0D 0A 41  nt-Length: 67..A
+00000120: 63 63 65 70 74 2D 52 61-6E 67 65 73 3A 20 62 79  ccept-Ranges: by
+00000130: 74 65 73 0D 0A 41 67 65-3A 20 31 38 32 37 31 33  tes..Age: 182713
+00000140: 0D 0A 58 2D 43 61 63 68-65 3A 20 48 49 54 20 66  ..X-Cache: HIT f
+00000150: 72 6F 6D 20 63 73 2D 68-61 6E 34 2E 77 69 6E 2D  rom cs-han4.win-
+00000160: 69 70 2E 64 66 6E 2E 64-65 0D 0A 58 2D 43 61 63  ip.dfn.de..X-Cac
+00000170: 68 65 2D 4C 6F 6F 6B 75-70 3A 20 48 49 54 20 66  he-Lookup: HIT f
+00000180: 72 6F 6D 20 63 73 2D 68-61 6E 34 2E 77 69 6E 2D  rom cs-han4.win-
+00000190: 69 70 2E 64 66 6E 2E 64-65 3A 38 30 38 31 0D 0A  ip.dfn.de:8081..
+000001A0: 50 72 6F 78 79 2D 43 6F-6E 6E 65 63 74 69 6F 6E  Proxy-Connection
+000001B0: 3A 20 6B 65 65 70 2D 61-6C 69 76 65 0D 0A 0D 0A  : keep-alive....
+000001C0: 47 49 46 38 39 61 01 00-26 00 A2 00 00 00 00 00  GIF89a..&.¢.....
+000001D0: FF FF FF 00 33 66 33 66-99 FF FF FF 00 00 00 00  ....3f3f........
+000001E0: 00 00 00 00 00 21 F9 04-01 00 00 04 00 2C 00 00  .....!ù......,..
+000001F0: 00 00 01 00 26 00 00 03-08 38 A2 BC DE F0 C9 A8  ....&....8¢¼Þðɨ
+00000200: 12 00 3B                                         ..;
+
+
+
+limitations
+===========
+
+o Purge does not slow rebuild the cache for you.
+
+o It is still relatively slow, especially if your machine is low on memory
+and/or unable to hold all OS directory cache entries in main memory.
+
+o should never be used on "busy" caches with purge modes higher than 1.
+
+
+TODO
+====
+
+1) use the stat() result on weird files to have a look at their ctime and 
+   mtime. If they are younger than, lets say 30 seconds, they were just
+   created by squid and should not be removed.
+
+2) Add a query before purging objects or removing files, and add another
+   option to remove nagging for the experienced user.
+
+3) The reported object size may be off by one.
diff --git a/tools/purge/conffile.cc b/tools/purge/conffile.cc
new file mode 100644 (file)
index 0000000..9add58a
--- /dev/null
@@ -0,0 +1,179 @@
+//
+// $Id: conffile.cc,v 1.1 2000/09/21 09:44:53 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    conffile.cc
+//          Fri Sep 15 2000
+//
+// (c) 2000 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: conffile.cc,v $
+// Revision 1.1  2000/09/21 09:44:53  voeckler
+// Initial revision
+//
+//
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#endif
+
+#include "conffile.hh"
+#include <sys/types.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <regex.h>
+
+int
+readConfigFile( CacheDirVector& cachedir, const char* fn, FILE* debug )
+  // purpose: read squid.conf file and extract cache_dir entries
+  // paramtr: cachedir (OUT): vector with an entry for each cache_dir found
+  //          fn (IN): file name of squid.conf to use
+  // returns: number of entries, or negative to warn of errors
+{
+  static const char* expression =
+    "^[ \t]*cache_dir([ \t]+([[:alpha:]]+))?[ \t]+([[:graph:]]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)";
+
+  // try to open file
+  if ( debug ) fprintf( debug, "# trying to open %s\n", fn ? fn : "(null)" );
+  FILE* in = fopen( fn, "r" );
+  if ( in == NULL ) {
+    fprintf( stderr, "fopen %s: %s\n", fn, strerror(errno) );
+    return -1;
+  }
+
+  // prepare regular expression for matching
+  if ( debug ) fprintf( debug, "# trying to compile \"%s\"\n", expression );
+  regex_t rexp;
+  int result = regcomp( &rexp, expression, REG_EXTENDED );
+  if ( result != 0 ) {
+    char buffer[256];
+    regerror( result, &rexp, buffer, sizeof(buffer) );
+    fprintf( stderr, "regular expression \"%s\": %s\n", expression, buffer );
+    return -1;
+  }
+
+  // read line by line
+  if ( debug ) fputs( "# trying to read lines\n", debug );
+
+  regmatch_t subs[8];
+  char *s, line[1024];
+  CacheDir cd;
+  while ( fgets( line, sizeof(line), in ) ) {
+    // FIXME: overly long lines
+
+    // terminate line at start of comment
+    if ( (s = (char*) memchr( line, '#', sizeof(line) )) ) *s = '\0';
+
+    // quick skip
+    if ( *line == '\0' || *line == '\n' ) continue;
+
+    // test line
+    if ( (result=regexec( &rexp, line, 7, subs, 0 )) != 0 ) {
+      // error or no match
+      if ( result != REG_NOMATCH ) {
+       char buffer[256];
+       regerror( result, &rexp, buffer, sizeof(buffer) );
+       fprintf( stderr, "while matching \"%s\" against %s%s\n",
+                expression, line, buffer );
+       regfree(&rexp);
+       fclose(in);
+       return -1;
+      }
+    } else {
+      // match, please record
+      memset( &cd, 0, sizeof(cd) );
+      if ( debug ) fprintf( debug, "# match from %d-%d on line %s", 
+                           subs[0].rm_so, subs[0].rm_eo, line );
+
+      // terminate line after matched expression
+      line[ subs[0].rm_eo ] = '\0';
+
+      // extract information. If 6th parenthesis is filled, this is
+      // a new squid with disk types, otherwise it is an older version
+      int offset = 2;
+      if ( subs[6].rm_so == -1 ) {
+       // old version, disk type at position 2 is always UFS
+       cd.type = CacheDir::CDT_UFS;
+      } else {
+       // new version, disk type at position 2
+       line[ subs[offset].rm_eo ] = '\0';
+       if ( debug ) fprintf( debug, "# match from %d-%d on \"%s\"\n", 
+                             subs[offset].rm_so, subs[offset].rm_eo, 
+                             line+subs[offset].rm_so );
+       if ( strcmp( line + subs[offset].rm_so, "ufs" ) == 0 ) 
+         cd.type = CacheDir::CDT_UFS;
+       else if ( strcmp( line + subs[offset].rm_so, "asyncufs" ) == 0 ) 
+         cd.type = CacheDir::CDT_AUFS;
+       else if ( strcmp( line + subs[offset].rm_so, "diskd" ) == 0 ) 
+         cd.type = CacheDir::CDT_DISKD;
+       else 
+         cd.type = CacheDir::CDT_OTHER;
+       offset++;
+      }
+
+      // extract base directory
+      line[ subs[offset].rm_eo ] = '\0';
+      if ( debug ) fprintf( debug, "# match from %d-%d on \"%s\"\n", 
+                           subs[offset].rm_so, subs[offset].rm_eo, 
+                           line+subs[offset].rm_so );
+      cd.base = strdup( line+subs[offset].rm_so );
+      offset++;
+
+      // extract size information
+      line[ subs[offset].rm_eo ] = '\0';
+      if ( debug ) fprintf( debug, "# match from %d-%d on \"%s\"\n", 
+                           subs[offset].rm_so, subs[offset].rm_eo, 
+                           line+subs[offset].rm_so );
+      cd.size = strtoul( line+subs[offset].rm_so, 0, 10 );
+      offset++;
+
+      // extract 1st level directories
+      line[ subs[offset].rm_eo ] = '\0';
+      if ( debug ) fprintf( debug, "# match from %d-%d on \"%s\"\n", 
+                           subs[offset].rm_so, subs[offset].rm_eo, 
+                           line+subs[offset].rm_so );
+      cd.level[0] = strtoul( line+subs[offset].rm_so, 0, 10 );
+      offset++;
+
+      // extract 2nd level directories
+      line[ subs[offset].rm_eo ] = '\0';
+      if ( debug ) fprintf( debug, "# match from %d-%d on \"%s\"\n", 
+                           subs[offset].rm_so, subs[offset].rm_eo, 
+                           line+subs[offset].rm_so );
+      cd.level[1] = strtoul( line+subs[offset].rm_so, 0, 10 );
+      offset++;
+
+      cachedir.push_back( cd );
+    }
+  }
+
+  fclose(in);
+  regfree(&rexp);
+  return cachedir.size();
+}
diff --git a/tools/purge/conffile.hh b/tools/purge/conffile.hh
new file mode 100644 (file)
index 0000000..3fb919a
--- /dev/null
@@ -0,0 +1,85 @@
+//
+// $Id: conffile.hh,v 1.2 2000/09/21 10:17:17 cached Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    conffile.hh
+//          Fri Sep 15 2000
+//
+// (c) 2000 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: conffile.hh,v $
+// Revision 1.2  2000/09/21 10:17:17  cached
+// namespace std:: needed for Sun WS compiler.
+//
+// Revision 1.1  2000/09/21 09:45:14  voeckler
+// Initial revision
+//
+//
+#ifndef _CONFFILE_HH
+#define _CONFFILE_HH
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma interface
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+
+#ifndef DEFAULT_SQUID_CONF
+#define DEFAULT_SQUID_CONF "/usr/local/squid/etc/squid.conf"
+#endif // DEFAULT_SQUID_CONF
+
+#include <stdio.h>      // FILE*
+#include <vector>      // minimum STL container
+
+struct CacheDir {
+  enum CacheDirType { CDT_NONE, CDT_UFS, CDT_AUFS, CDT_DISKD, CDT_OTHER };
+
+  const char*  base;
+  CacheDirType type;
+  size_t       size;
+  unsigned     level[2];
+};
+
+typedef std::vector<CacheDir> CacheDirVector;
+
+int
+readConfigFile( CacheDirVector& cachedir, 
+               const char* fn = DEFAULT_SQUID_CONF, 
+               FILE* debug = 0 );
+  // purpose: read squid.conf file and extract cache_dir entries
+  // paramtr: cachedir (OUT): vector with an entry for each cache_dir found
+  //          fn (IN): file name of squid.conf to use
+  //          debug (IO): if not null, place debug information there
+  // returns: number of entries, or negative to warn of errors
+
+#endif // _CONFFILE_HH
diff --git a/tools/purge/convert.cc b/tools/purge/convert.cc
new file mode 100644 (file)
index 0000000..e841531
--- /dev/null
@@ -0,0 +1,160 @@
+//
+// $Id: convert.cc,v 1.3 2000/06/20 09:43:01 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    convert.cc
+//          Thu Oct 30 1997
+//
+// (c) 1997 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: convert.cc,v $
+// Revision 1.3  2000/06/20 09:43:01  voeckler
+// added FreeBSD related fixes and support.
+//
+// Revision 1.2  1999/01/19 11:00:50  voeckler
+// Linux glibc2 fixes for sockets.
+//
+// Revision 1.1  1998/08/13 21:38:04  voeckler
+// Initial revision
+//
+//
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#endif
+
+#include "convert.hh"
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef SA
+#define SA struct sockaddr
+#endif
+
+static const char* RCS_ID = 
+"$Id: convert.cc,v 1.3 2000/06/20 09:43:01 voeckler Exp $";
+
+const char*
+my_inet_ntoa( const struct in_addr& a, HostAddress output )
+  // purpose: thread-safely convert IPv4 address -> ASCII representation
+  // paramtr: a (IN): networked representation of IPv4 address
+  //          buffer (OUT): storage area to store representation into.
+  // returns: pointer to buffer
+  // goodies: INADDR_ANY will be converted to "*"
+{
+  if ( a.s_addr == ntohl(INADDR_ANY) ) {
+    // 'default' or '*' or ...
+    output[0] = '*';
+    output[1] = '\0';
+  } else {
+    // ANSI C++ forbids casting to an array type, nag, nag, nag...
+    unsigned char s[sizeof(a.s_addr)];
+    memcpy( s, &a.s_addr, sizeof(a.s_addr) );
+
+    sprintf( output, "%d.%d.%d.%d", s[0], s[1], s[2], s[3] );
+  }
+  return output;
+}
+
+const char*
+my_sock_ntoa( const struct sockaddr_in& a, SockAddress buffer )
+  // purpose: thread-safely convert IPv4 socket pair into ASCII rep.
+  // paramtr: a (IN): sockaddr_in address 
+  //          buffer (OUT): storage area to store representation into.
+  // returns: pointer to buffer
+{
+  HostAddress host;
+  sprintf( buffer, "%s:%u",
+          my_inet_ntoa(a.sin_addr,host), ntohs(a.sin_port) );
+  return buffer;
+}
+
+const char*
+my_sock_fd2a( int fd, SockAddress buffer, bool peer )
+  // purpose: thread-safely convert IPv4 socket FD associated address
+  //          to ASCII representation
+  // paramtr: fd (IN): open socket FD
+  //          buffer (OUT): storage area
+  //          peer (IN): true, use peer (remote) socket pair
+  //                     false, use own (local) socket pair
+  // returns: NULL in case of error, or pointer to buffer otherwise
+  //          Refer to errno in case of error (usually unconnected fd...)
+{
+  struct sockaddr_in socket;
+  SOCKLEN len = sizeof(socket);
+
+  if ( (peer ? getpeername( fd, (SA*) &socket, &len ) :
+       getsockname( fd, (SA*) &socket, &len )) == -1 )
+    return NULL;
+  else
+    return my_sock_ntoa( socket, buffer );
+}
+
+int
+convertHostname( const char* host, in_addr& dst )
+  // purpose: convert a numeric or symbolic hostname
+  // paramtr: host (IN): host description to convert
+  //          dst (OUT): the internet address in network byteorder.
+  // returns: -1 in case of error, see h_errno; 0 otherwise.
+{
+  if ( host == 0 ) return -1;
+  unsigned long int h = inet_addr(host);
+  if ( h == 0xFFFFFFFF && strncmp(host,"255.255.255.255",15) != 0 ) {
+    // symbolic host
+    struct hostent* dns = gethostbyname(host);
+    if ( dns == NULL ) return -1;
+    else memcpy( &dst.s_addr, dns->h_addr, dns->h_length );
+  } else {
+    // numeric host
+    dst.s_addr = h;
+  }
+  return 0;
+}
+
+int
+convertPortname( const char* port, unsigned short& dst )
+  // purpose: convert a numeric or symbolic port number
+  // paramtr: port (IN): port description to convert
+  //          dst (OUT): port number in network byteorder.
+  // returns: -1 in case of error, see errno; 0 otherwise.
+{
+  int p = strtoul(port,0,0);
+
+  if ( p == 0 ) {
+    // symbolic port
+    struct servent* proto = getservbyname( port, "tcp" );
+    if ( proto == NULL ) return -1;
+    else dst = proto->s_port;
+  } else {
+    // numeric port
+    dst = htons(p);
+  }
+  return 0;
+}
diff --git a/tools/purge/convert.hh b/tools/purge/convert.hh
new file mode 100644 (file)
index 0000000..dbc65f6
--- /dev/null
@@ -0,0 +1,102 @@
+//
+// $Id: convert.hh,v 1.2 1999/01/19 11:00:50 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    convert.hh
+//          Thu Oct 30 1997
+//
+// (c) 1997 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: convert.hh,v $
+// Revision 1.2  1999/01/19 11:00:50  voeckler
+// added bool type workaround.
+//
+// Revision 1.1  1998/08/13 21:38:04  voeckler
+// Initial revision
+//
+//
+#ifndef _CONVERT_HH
+#define _CONVERT_HH
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma interface
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+typedef char HostAddress[16]; // strlen("xxx.xxx.xxx.xxx\0") <= 16
+typedef char SockAddress[24]; // strlen("xxx.xxx.xxx.xxx:xxxxx\0" ) < 24
+
+const char*
+my_inet_ntoa( const struct in_addr& a, HostAddress buffer );
+  // purpose: thread-safely convert IPv4 address -> ASCII representation
+  // paramtr: a (IN): networked representation of IPv4 address
+  //          buffer (OUT): storage area to store representation into.
+  // returns: pointer to buffer
+  // goodies: INADDR_ANY will be converted to "*"
+
+const char*
+my_sock_ntoa( const struct sockaddr_in& a, SockAddress buffer );
+  // purpose: thread-safely convert IPv4 socket pair into ASCII rep.
+  // paramtr: a (IN): socket_in address 
+  //          buffer (OUT): storage area to store representation into.
+  // returns: pointer to buffer
+
+const char*
+my_sock_fd2a( int fd, SockAddress buffer, bool peer = true );
+  // purpose: thread-safely convert IPv4 socket FD associated address
+  //          to ASCII representation
+  // paramtr: fd (IN): open socket FD
+  //          buffer (OUT): storage area
+  //          peer (IN): true, use peer (remote) socket pair
+  //                     false, use own (local) socket pair
+  // returns: NULL in case of error, or pointer to buffer otherwise
+  //          Refer to errno in case of error (usually unconnected fd...)
+
+int
+convertHostname( const char* host, struct in_addr& dst );
+  // purpose: convert a numeric or symbolic hostname
+  // paramtr: host (IN): host description to convert
+  //          dst (OUT): the internet address in network byteorder.
+  // returns: -1 in case of error, see h_errno; 0 otherwise.
+
+int
+convertPortname( const char* port, unsigned short& dst );
+  // purpose: convert a numeric or symbolic port number
+  // paramtr: port (IN): port description to convert
+  //          dst (OUT): port number in network byteorder.
+  // returns: -1 in case of error, see errno; 0 otherwise.
+
+#endif // _CONVERT_HH
diff --git a/tools/purge/copyout.cc b/tools/purge/copyout.cc
new file mode 100644 (file)
index 0000000..605750c
--- /dev/null
@@ -0,0 +1,279 @@
+//
+// $Id: copyout.cc,v 1.2 1999/06/16 13:05:26 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    copyout.cc
+//          Tue Jun 15 1999
+//
+// (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: copyout.cc,v $
+// Revision 1.2  1999/06/16 13:05:26  voeckler
+// mmap file copying on Solaris.
+//
+// Revision 1.1  1999/06/15 21:10:47  voeckler
+// Initial revision
+//
+//
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#endif
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif // MAP_FILE
+
+#include "copyout.hh"
+
+static const char* RCS_ID =
+"$Id: copyout.cc,v 1.2 1999/06/16 13:05:26 voeckler Exp $";
+
+int
+assert_copydir( const char* copydir )
+  // purpose: check, if copydir is a directory and that we can write into it.
+  // paramtr: copydir (IN): name of directory for copying bodies.
+  // returns: 0 if ok, -1 otherwise.
+  // further: errors are handled within. If the directory does not exist,
+  //          the assertion function will try to create it.
+{
+  struct stat st;
+  int status = stat( copydir, &st );
+
+  // check, if either "copydir" points to a valid directory,
+  // or if copydir can be created.
+  if ( status == 0 && ! S_ISDIR(st.st_mode) ) {
+    // stat() returned true, but did not point to a directory
+    fprintf( stderr, "copy dir \"%s\" is a file!\n", copydir );
+    return -1;
+  } else if ( S_ISDIR(st.st_mode) &&
+             !( (st.st_uid == geteuid() && ( (st.st_mode & S_IWUSR) > 0 )) ||
+                (st.st_gid == getegid() && ( (st.st_mode & S_IWGRP) > 0 )) ||
+                ((st.st_mode & S_IWOTH) > 0) ) ) {
+    fprintf( stderr, "copy dir \"%s\" is not accessible to me\n", copydir );
+    return -1;
+  } if ( status == -1 ) {
+    // stat() returned with an error. 'File not found' is a legal error.
+    if ( errno != ENOENT ) {
+      // not a 'file not found' error, so this is hard error.
+      fprintf( stderr, "accessing copy-out dir \"%s\": %s\n",
+              copydir, strerror(errno) );
+      return -1;
+    } else {
+      // directory does not exist. Try to create it.
+      if ( mkdir( copydir, 0750 ) == -1 ) {
+       fprintf( stderr, "mkdir(%s): %s\n", copydir, strerror(errno) );
+       return -1;
+      }
+    }
+  }
+
+  // postcondition: copydir exists and is a directory.
+  return 0;
+}
+
+inline
+unsigned
+xlate( char ch )
+{
+  if ( ch == '\r' ) return 0u;
+  else if ( ch == '\n' ) return 1u;
+  else return 2u;
+}
+
+// shortcut for monotoneous typings...
+#define BAUTZ(x) delete[] filename; close(input); close(out); return (x)
+
+bool
+copy_out( size_t filesize, size_t metasize, unsigned debug,
+         const char* fn, const char* url, const char* copydir,
+         bool copyHdr )
+  // purpose: copy content from squid disk file into separate file
+  // paramtr: metasize (IN): size of metadata to skip
+  //          fn (IN): current filename of squid disk file
+  //          url (IN): currently looked at URL to generate separate file
+  //          copydir (IN): base directory where to generate the file
+  //          copyHdr (IN): copy HTTP header, too, if set to true.
+  // returns: true, if successful, false otherwise.
+{
+  static const char* index = "index.html";
+  
+  // find hostname part after the scheme (okok, not counting port, etc.)
+  char* ptr = strstr( url, "://" );
+  if ( ptr == 0 || strlen(ptr) < 4 ) return false;
+
+  // create filename to store contents into
+  char *filename = new char[ strlen(url) + strlen(copydir) + strlen(index) ];
+  assert( filename != 0 );
+  strcpy( filename, copydir );
+  strcat( filename, "/" );
+  char* here = filename + strlen(filename);
+  strcat( filename, ptr+3 );
+
+  // handle server root (e.g. "http://www.focus.de" )
+  if ( strchr( ptr+3, '/' ) == 0 ) strcat( filename, "/" );
+
+  // handle directories (e.g. "http://www.focus.de/A/" )
+  if ( filename[strlen(filename)-1] == '/' ) strcat( filename, index );
+
+  // create subdirectory structure
+  for ( char* t = strchr(here,'/'); t; t = strchr(t,'/') ) {
+    *t = 0;
+    if ( mkdir( filename, 0775 ) == -1 && errno != EEXIST ) {
+      fprintf( stderr, "mkdir(%s): %s\n", filename, strerror(errno) );
+      delete[] filename;
+      return false;
+    } else if ( debug & 0x02 ) {
+      fprintf( stderr, "# creating %s\n", filename );
+    }
+    *t++ = '/';
+  }
+
+  // create file
+  int out = open( filename, O_CREAT | O_RDWR | O_TRUNC, 0664 );
+  if ( out == -1 ) {
+    fprintf( stderr, "open(%s,RDWR): %s\n", filename, strerror(errno) );
+    delete[] filename;
+    return false;
+  } else if ( debug & 0x02 ) {
+    fprintf( stderr, "# creating %s\n", filename );
+  }
+
+  // (re)open cache file
+  int input = open( fn, O_RDONLY );
+  if ( input == -1 ) {
+    fprintf( stderr, "open(%s,RDONLY): %s\n", fn, strerror(errno) );
+    delete[] filename;
+    close(out);
+    return false;
+  }
+
+  // find double CRLF sequence (actually, look at the FSM below)
+  // FIXME: this only looks at the already known buffer read previously,
+  // which is globally passed (yuck)! As a limitation, the content data
+  // *must* begin within the buffer size (that is: 16k)! 
+  if ( ! copyHdr ) {
+    extern char*  linebuffer; // import from purge.cc
+    extern size_t buffersize; // import from purge.cc
+
+    unsigned state = 0;
+    char* s = linebuffer + metasize;
+    while ( s < linebuffer + buffersize && state < 4 ) {
+      // state transition machine
+      static unsigned table[4][3] = { {3,2,0}, {0,4,0}, {1,4,0}, {4,2,0} };
+      //  old || \r | \n |else|
+      // =====++====+====+====+
+      //    0 ||  3 |  2 |  0 |
+      //    1 ||  0 |  4 |  0 |
+      //    2 ||  1 |  4 |  0 |
+      //    3 ||  4 |  2 |  0 | 
+      state = table[ state ][ xlate(*s++) ];
+    }
+
+    if ( state < 4 ) 
+      // complain bitterly, if the HTTP header was too large ( > 16k ).
+      fprintf( stderr, "WARNING: %s will contain partial HTTP header data!\n",
+              filename );
+
+    // adjust to different seek size
+    metasize = s - linebuffer;
+  }
+
+  // no need to copy zero content files
+  if ( filesize - metasize <= 0 ) {
+    BAUTZ( filesize-metasize == 0 );
+  }
+
+#ifdef USE_REGULAR_COPY
+  // position input at start of server answer (contains HTTP headers)
+  if ( lseek( input, metasize, SEEK_SET ) == -1 ) {
+    fprintf( stderr, "lseek(%s,%lu): %s\n", fn, metasize, strerror(errno) );
+    BAUTZ(false);
+  }
+
+  // file copy input into output via buffer (regular io)
+  char buffer[32768];
+  int rsize, wsize;
+  while ( (rsize=read(input,buffer,sizeof(buffer))) > 0 ) {
+    if ( (wsize=write(out,buffer,rsize)) <= 0 ) break;
+  }
+  if ( rsize < 0 || wsize < 0 ) perror( "while copying" );
+#else // use mmap copy (compare: Stevens APUE 12.9)
+  // precondition: filesize-metasize > 0
+  // seek end of output file ...
+  off_t position = lseek( out, filesize-metasize-1, SEEK_SET );
+  if ( position == -1 ) {
+    fprintf( stderr, "lseek(%s,%lu): %s\n", filename, filesize-metasize,
+            strerror(errno) );
+    BAUTZ(false);
+  } else if ( debug & 0x02 ) {
+    fprintf( stderr, "# filesize=%lu, metasize=%lu, filepos=%ld\n",
+            filesize, metasize, position );
+  }
+  
+  // ...and write 1 byte there (create a file that length)
+  if ( write( out, "", 1 ) != 1 ) {
+    perror( "write to output" );
+    BAUTZ(false);
+  }
+
+  // create source mmap to copy from (mmap complete file)
+  caddr_t src = (caddr_t) mmap( 0, filesize, PROT_READ,
+                               MAP_FILE | MAP_SHARED, input, 0 );
+  if ( src == (caddr_t) -1 ) {
+    perror( "mmap input" );
+    BAUTZ(false);
+  }
+
+  // create destination mmap to copy into (mmap data portion)
+  caddr_t dst = (caddr_t) mmap( 0, filesize-metasize, PROT_READ | PROT_WRITE,
+                               MAP_FILE | MAP_SHARED, out, 0 );
+  if ( dst == (caddr_t) -1 ) {
+    perror( "mmap output" );
+    munmap( src, filesize );
+    BAUTZ(false);
+  }
+
+  // copy file (beware of offset into wanted data, skip meta data)
+  memcpy( dst, src+metasize, filesize-metasize );
+
+  // clean up
+  munmap( dst, filesize-metasize );
+  munmap( src, filesize );
+#endif // USE_REGULAR_COPY
+
+  BAUTZ(true);
+}
diff --git a/tools/purge/copyout.hh b/tools/purge/copyout.hh
new file mode 100644 (file)
index 0000000..d99f7e4
--- /dev/null
@@ -0,0 +1,73 @@
+//
+// $Id: copyout.hh,v 1.1 1999/06/15 21:10:47 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    copyout.hh
+//          Tue Jun 15 1999
+//
+// (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: copyout.hh,v $
+// Revision 1.1  1999/06/15 21:10:47  voeckler
+// Initial revision
+//
+#ifndef _COPYOUT_HH
+#define _COPYOUT_HH
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma interface
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+int
+assert_copydir( const char* copydir );
+  // purpose: check, if copydir is a directory and that we can write into it.
+  // paramtr: copydir (IN): name of directory for copying bodies.
+  // returns: 0 if ok, -1 otherwise.
+  // further: errors are handled within. If the directory does not exist,
+  //          the assertion function will try to create it.
+
+bool
+copy_out( size_t filesize, size_t metasize, unsigned debug,
+         const char* fn, const char* url, const char* copydir,
+         bool copyHdr = true );
+  // purpose: copy content from squid disk file into separate file
+  // paramtr: filesize (IN): complete size of input file
+  //          metasize (IN): size of metadata to skip
+  //          fn (IN): current filename of squid disk file
+  //          url (IN): currently looked at URL to generate separate file
+  //          copydir (IN): base directory where to generate the file
+  //          copyHdr (IN): copy HTTP header, too, if set to true.
+  // returns: true, if successful, false otherwise.
+
+#endif // _COPYOUT_HH
diff --git a/tools/purge/hexd.c b/tools/purge/hexd.c
new file mode 100644 (file)
index 0000000..46163cf
--- /dev/null
@@ -0,0 +1,94 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+unsigned char
+xlate( unsigned char ch )
+{
+  return (isprint(ch & 0x7f) ? ch : '.');
+}
+
+typedef struct {
+  int fd, rsize, cursor;
+  size_t bufsize;
+  unsigned char* buffer;
+} InputByByte;
+
+int
+in_open( InputByByte* this, const char* fn, size_t size )
+{
+  if ( (this->fd = open( fn, O_RDONLY )) == -1 ) return -1;
+  if ( (this->buffer=(unsigned char*) malloc(size)) == 0 ) {
+    close(this->fd);
+    return -1;
+  }
+  this->bufsize = size;
+  this->rsize = this->cursor = 0;
+  return 0;
+}
+
+int
+in_get( InputByByte* this )
+     /* 
+      * purpose: read next character
+      * returns: 0..255 as valid character, -1 for error, -2 for EOF
+      */
+{
+  if ( this->cursor >= this->rsize ) {
+    do { 
+      this->rsize = read( this->fd, this->buffer, this->bufsize );
+    } while ( this->rsize == -1 && errno == EINTR );
+    if ( this->rsize > 0 ) this->cursor = 0;
+    else return ((-2) - this->rsize);
+  }
+
+  return this->buffer[this->cursor++];
+}
+
+int
+in_close( InputByByte* this )
+{
+  free((void*) this->buffer);
+  return close(this->fd);
+}
+
+int
+main( int argc, char* argv[] )
+{
+  int ch, i;
+  unsigned line = 0;
+  InputByByte in;
+  char b2[20];
+  
+  if ( argc != 2 ) {
+    fprintf( stderr, "Usage: %s filename\n", argv[0] );
+    return 1;
+  }
+
+  if ( in_open(&in,argv[1],32768) == -1 ) {
+    perror( "open" );
+    return 1;
+  }
+
+  for ( ch = in_get(&in); ch >= 0; ) {
+    printf( "%08X: ", line );
+    memset( b2, 0, sizeof(b2) );
+    for ( i=0; i < 16 && ch >= 0; i++ ) {
+      printf( "%02X%c", ch, ((i==7) ? '-' : ' ' ) );
+      b2[i] = xlate(ch);
+      ch = in_get(&in);
+    }
+    line += i;
+    for ( ; i<16; i++ ) fputs("   ",stdout);
+    printf( " %s\n", b2 );
+  }
+  
+  return in_close(&in);
+}
+
+
diff --git a/tools/purge/purge.cc b/tools/purge/purge.cc
new file mode 100644 (file)
index 0000000..8d7162c
--- /dev/null
@@ -0,0 +1,947 @@
+//
+// $Id: purge.cc,v 1.17 2000/09/21 10:59:53 cached Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    purge.cc
+//          Wed Jan 13 1999
+//
+// (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: purge.cc,v $
+// Revision 1.17  2000/09/21 10:59:53  cached
+// *** empty log message ***
+//
+// Revision 1.16  2000/09/21 09:45:18  cached
+// Fixed some small bugs.
+//
+// Revision 1.15  2000/09/21 09:05:56  cached
+// added multi cache_dir support, thus changing -c cmdline option.
+// modified file reading to support /dev/fd/0 reading for non-disclosed items.
+//
+// Revision 1.14  2000/06/20 09:43:01  voeckler
+// added FreeBSD related fixes and support.
+//
+// Revision 1.13  2000/03/29 08:12:21  voeckler
+// fixed wrong header file.
+//
+// Revision 1.12  2000/03/29 07:54:41  voeckler
+// added mechanism to give a port specification precedence over a host
+// specificiation with the -p option and not colon.
+//
+// Revision 1.11  1999/06/18 13:18:28  voeckler
+// added refcount, fixed missing LF in -s output.
+//
+// Revision 1.10  1999/06/16 13:06:05  voeckler
+// reversed meaning of -M flag.
+//
+// Revision 1.9  1999/06/15 21:11:53  voeckler
+// added extended logging feature which extract the squid meta data available
+// within the disk files. moved the content extraction and squid meta data
+// handling parts into separate files. added options for copy-out and verbose.
+//
+// Revision 1.8  1999/06/14 20:14:46  voeckler
+// intermediate version when adding understanding about the way
+// Squid does log the metadata into the file.
+//
+// Revision 1.7  1999/01/23 21:01:10  root
+// stumbled over libc5 header/lib inconsistency bug....
+//
+// Revision 1.6  1999/01/23 20:47:54  root
+// added Linux specifics for psignal...
+// Hope this helps.
+//
+// Revision 1.5  1999/01/20 09:48:12  voeckler
+// added warning as first line of output.
+//
+// Revision 1.4  1999/01/19 11:53:49  voeckler
+// added psignal() from <siginfo.h> handling.
+//
+// Revision 1.3  1999/01/19 11:00:50  voeckler
+// added keyboard interrupt handling, exit handling, removed C++ strings and
+// regular expression syntax in favour of less source code, added comments,
+// added a reminder to remove swap.state in case of unlinks, added IAA flag,
+// added a few assertions, changed policy to enforce the definition of at
+// least one regular expression, and catch a few signals.
+//
+// Revision 1.2  1999/01/15 23:06:28  voeckler
+// downgraded to simple C strings...
+//
+// Revision 1.1  1999/01/14 12:05:32  voeckler
+// Initial revision
+//
+//
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <signal.h>
+#include <errno.h>
+
+#if defined(HAS_PSIGNAL) && !defined(LINUX) && !defined(FREEBSD)
+#include <siginfo.h>
+#endif // HAS_PSIGNAL
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>  // TCP_NODELAY
+#include <arpa/inet.h>
+#include <netdb.h>        // gethostbyname()
+#include <regex.h>
+
+#include "convert.hh"
+#include "socket.hh"
+#include "signal.hh"
+#include "squid-tlv.hh"
+#include "copyout.hh"
+#include "conffile.hh"
+
+#ifndef DEFAULTHOST
+#define DEFAULTHOST "localhost"
+#endif // DEFAULTHOST
+
+#ifndef DEFAULTPORT
+#define DEFAULTPORT 3128
+#endif // DEFAULTPORT
+
+volatile sig_atomic_t term_flag = 0; // 'terminate' is a gcc 2.8.x internal...
+ char*  linebuffer = 0;
+ size_t buffersize = 16834;
+static char* copydir = 0;
+static unsigned debug = 0;
+static unsigned purgeMode = 0;
+static bool iamalive = false;
+static bool reminder = false;
+static bool verbose  = false;
+static bool envelope = false;
+static bool no_fork  = false;
+static const char* programname = 0;
+static const char* RCS_ID =
+"$Id: purge.cc,v 1.17 2000/09/21 10:59:53 cached Exp $";
+
+// ----------------------------------------------------------------------
+
+struct REList {
+  REList( const char* what, bool doCase );
+  ~REList();
+  bool match( const char* check ) const;
+
+  REList*     next;
+  const char* data;
+  regex_t     rexp;
+};
+
+REList::REList( const char* what, bool doCase )
+  :next(0),data(strdup(what))
+{
+  int result = regcomp( &rexp, what, 
+                       REG_EXTENDED | REG_NOSUB | (doCase ? 0 : REG_ICASE) );
+  if ( result != 0 ) {
+    char buffer[256];
+    regerror( result, &rexp, buffer, 256 );
+    fprintf( stderr, "unable to compile re \"%s\": %s\n", what, buffer );
+    exit(1);
+  }
+}
+
+REList::~REList()
+{ 
+  if ( next ) delete next;
+  if ( data ) free((void*) data);
+  regfree(&rexp);
+}
+
+bool
+REList::match( const char* check ) const
+{
+  int result = regexec( &rexp, check, 0, 0, 0 );
+  if ( result != 0 && result != REG_NOMATCH ) {
+    char buffer[256];
+    regerror( result, &rexp, buffer, 256 );
+    fprintf( stderr, "unable to execute re \"%s\"\n+ on line \"%s\": %s\n",
+            data, check, buffer );
+    exit(1);
+  }
+  return ( result == 0 );
+}
+
+// ----------------------------------------------------------------------
+
+char*
+concat( const char* start, ... )
+  // purpose: concatinate an arbitrary number of C strings.
+  // paramtr: start (IN): first C string
+  //          ... (IN): further C strings, terminated with a NULL pointer
+  // returns: memory allocated via new(), containing the concatinated string.
+{
+  va_list ap;
+  const char* s;
+
+  // first run: determine size
+  unsigned size = strlen(start)+1;
+  va_start( ap, start );
+  while ( (s=va_arg(ap,const char*)) != NULL ) size += strlen(s ? s : "");
+  va_end(ap);
+
+  // allocate
+  char* result = new char[size];
+  if ( result == 0 ) {
+    perror( "string memory allocation" );
+    exit(1);
+  }
+
+  // second run: copy content
+  strcpy( result, start );
+  va_start( ap, start );
+  while ( (s=va_arg(ap,const char*)) != NULL ) strcat( result, s );
+  va_end(ap);
+
+  return result;
+}
+
+bool
+isxstring( const char* s, size_t testlen )
+  // purpose: test a string for conforming to xdigit
+  // paramtr: s (IN): string to test
+  //          testlen (IN): length the string must have
+  // returns: true, iff strlen(s)==testlen && all_x_chars(s), false otherwise
+{
+  if ( strlen(s) != testlen ) return false;
+
+  size_t i=0;
+  while ( i<testlen && isxdigit(s[i]) ) i++;
+  return (i==testlen);
+}
+
+inline
+int
+log_output( const char* fn, int code, long size, const char* url )
+{
+  return printf( "%s %3d %8ld %s\n", fn, code, size, url );
+}
+
+static
+int
+log_extended( const char* fn, int code, long size, const SquidMetaList* meta )
+{
+  static const char hexdigit[] = "0123456789ABCDEF";
+  char md5[34];
+  const SquidTLV* findings = 0;
+
+  if ( meta && (findings = meta->search( STORE_META_KEY_MD5 )) ) {
+    unsigned char* s = (unsigned char*) findings->data;
+    for ( int j=0; j<16; j++, s++ ) {
+      md5[j*2+0] = hexdigit[ *s >> 4 ];
+      md5[j*2+1] = hexdigit[ *s & 15 ];
+    }
+    md5[32] = '\0'; // terminate string
+  } else {
+    sprintf( md5, "%-32s", "(no_md5_data_available)" );
+  }
+
+  char timeb[64];
+  if ( meta && (findings = meta->search( STORE_META_STD )) ) {
+    StoreMetaStd temp;
+    // make data aligned, avoid SIGBUS on RISC machines (ARGH!)
+    memcpy( &temp, findings->data, sizeof(StoreMetaStd) );
+    sprintf( timeb, "%08x %08x %08x %08x %04x %5hu ",
+            temp.timestamp, temp.lastref,
+            temp.expires, temp.lastmod, temp.flags, temp.refcount );
+  } else {
+    sprintf( timeb, "%08x %08x %08x %08x %04x %5hu ", -1, -1, -1, -1, 0, 0 );
+  }
+
+  // make sure that there is just one printf()
+  if ( meta && (findings = meta->search( STORE_META_URL )) ) {
+    return printf( "%s %3d %8ld %s %s %s\n", 
+                  fn, code, size, md5, timeb, findings->data );
+  } else {
+    return printf( "%s %3d %8ld %s %s strange_file\n", 
+                  fn, code, size, md5, timeb );
+  }
+}
+
+// o.k., this is pure lazyness...
+static struct in_addr serverHost;
+static unsigned short serverPort;
+
+bool
+action( int fd, size_t metasize,
+       const char* fn, const char* url, const SquidMetaList& meta )
+  // purpose: if cmdline-requested, send the purge request to the cache
+  // paramtr: fd (IN): open FD for the object file
+  //         metasize (IN): offset into data portion of file (meta data size)
+  //          fn (IN): name of the object file
+  //          url (IN): URL string stored in the object file
+  //         meta (IN): list containing further meta data
+  // returns: true for a successful action, false otherwise. The action
+  //          may just print the file, send the purge request or even
+  //          remove unwanted files.
+  // globals: ::purgeMode (IN):  bit#0 set -> send purge request.
+  //                             bit#1 set -> remove 404 object files.
+  //          ::serverHost (IN): cache host address
+  //          ::serverPort (IN): cache port number
+{
+  static const char* schablone = "PURGE %s HTTP/1.0\r\nAccept: */*\r\n\r\n";
+  struct stat st;
+  long size = ( fstat(fd,&st) == -1 ? -1 : long(st.st_size - metasize) );  
+  int status = 0;
+
+  // if we want to copy out the file, do that first of all.
+  if ( ::copydir && *copydir && size > 0 ) 
+    copy_out( st.st_size, metasize, ::debug, 
+             fn, url, ::copydir, ::envelope );
+  
+  // do we need to PURGE the file, yes, if purgemode bit#0 was set.
+  if ( ::purgeMode & 0x01 ) {
+    unsigned long bufsize = strlen(url) + strlen(schablone) + 4;
+    char* buffer = new char[bufsize];
+
+    sprintf( buffer, schablone, url );
+    int sockfd = connectTo( serverHost, serverPort, true );
+    if ( sockfd == -1 ) {
+      fprintf( stderr, "unable to connect to server: %s\n", strerror(errno) );
+      delete[] buffer;
+      return false;
+    }
+    
+    int size = strlen(buffer);
+    if ( write( sockfd, buffer, size ) != size ) {
+      // error while talking to squid
+      fprintf( stderr, "unable to talk to server: %s\n", strerror(errno) );
+      close(sockfd);
+      delete[] buffer;
+      return false;
+    }
+    memset( buffer+8, 0, 4 );
+    if ( read( sockfd, buffer, bufsize ) < 1 ) {
+      // error while reading squid's answer
+      fprintf( stderr, "unable to read answer: %s\n", strerror(errno) );
+      close(sockfd);
+      delete[] buffer;
+      return false;
+    }
+    close(sockfd);
+    status = strtol(buffer+8,0,10);
+    delete[] buffer;
+  }
+  
+  // log the output of our operation
+  bool flag = true;
+  if ( ::verbose ) flag = ( log_extended( fn, status, size, &meta ) >= 0 );
+  else flag = ( log_output( fn, status, size, url ) >= 0 );
+
+  // remove the file, if purgemode bit#1, and HTTP result status 404).
+  if ( (::purgeMode & 0x02) && status == 404 ) {
+    reminder = true;
+    if ( unlink(fn) == -1 )
+      // error while unlinking file, this may happen due to the cache
+      // unlinking a file while it is still in the readdir() cache of purge.
+      fprintf( stderr, "WARNING: unable to unlink %s: %s\n",
+              fn, strerror(errno) );
+  }
+  
+  return flag;
+}
+
+bool
+match( const char* fn, const REList* list )
+  // purpose: do something with the given cache content filename
+  // paramtr: fn (IN): filename of cache file
+  // returns: true for successful action, false otherwise.
+  // warning: only return false, if you want the loop to terminate!
+{
+  static const size_t addon = sizeof(unsigned char) + sizeof(unsigned int);
+  bool flag = true;
+  
+  if ( debug & 0x01 ) fprintf( stderr, "# [3] %s\n", fn );
+  int fd = open( fn, O_RDONLY );
+  if ( fd != -1 ) {
+    if ( read(fd,::linebuffer,::buffersize-1) > 60 ) {
+      ::linebuffer[ ::buffersize-1 ] = '\0'; // force-terminate string
+
+      // check the offset into the start of object data. The offset is
+      // stored in a host endianess after the first byte.
+      unsigned int datastart;
+      memcpy( &datastart, ::linebuffer + 1, sizeof(unsigned int) );
+      if ( datastart > ::buffersize - addon - 1 ) {
+       // check offset into server reply header (start of cache data).
+       fputs( "WARNING: Using a truncated URL string.\n", stderr );
+       datastart = ::buffersize - addon - 1;
+      }
+
+      // NEW: Parse squid meta data, which is a kind of linked list
+      // flattened out into a file byte stream. Somewhere within is
+      // the URL as part of the list. First, gobble all meta data.
+      unsigned int offset = addon;
+      SquidMetaList meta;
+      while ( offset < datastart && *(offset+linebuffer) != STORE_META_END ) {
+       unsigned int size = 0;
+       memcpy( &size, linebuffer+offset+sizeof(char), sizeof(unsigned int) );
+       meta.append( SquidMetaType(*(linebuffer+offset)),
+                    size, linebuffer+offset+addon );
+       offset += ( addon + size );
+      }
+
+      // Now extract the key URL from the meta data.
+      const SquidTLV* urlmeta = meta.search( STORE_META_URL );
+      if ( urlmeta ) {
+       // found URL in meta data. Try to process the URL
+       if ( list == 0 )
+         flag = action( fd, datastart, fn, (char*) urlmeta->data, meta );
+       else {
+         REList* head = (REList*) list; // YUCK!
+         while ( head != 0 ) {
+           if ( head->match( (char*) urlmeta->data ) ) break;
+           head = head->next;
+         }
+         if ( head != 0 )
+           flag = action( fd, datastart, fn, (char*) urlmeta->data, meta );
+         else flag = true;
+       }
+      }
+
+      // "meta" will be deleted when exiting from this block
+    } else {
+      // weird file, FIXME: stat() it!
+      struct stat st;
+      long size = ( fstat(fd,&st) == -1 ? -1 : st.st_size );
+      if ( ::verbose ) flag = ( log_extended( fn, -1, size, 0 ) >= 0 );
+      else flag = ( log_output( fn, -1, size, "strange file" ) >= 0 );
+
+      if ( (::purgeMode & 0x04) ) {
+       reminder = true;
+       if ( unlink(fn) == -1 )
+         // error while unlinking file, this may happen due to the cache
+         // unlinking a file while it is in the readdir() cache of purge.
+         fprintf( stderr, "WARNING: unable to unlink %s: %s\n",
+                  fn, strerror(errno) );
+      }
+    }
+    close(fd);
+  } else {
+    // error while opening file, this may happen due to the cache
+    // unlinking a file while it is still in the readdir() cache of purge.
+    fprintf( stderr, "WARNING: open \"%s\": %s\n", fn, strerror(errno) );
+  }
+  
+  return flag;
+}
+
+bool
+filelevel( const char* directory, const REList* list )
+  // purpose: from given starting point, look for squid xxxxxxxx files.
+  // example: "/var/spool/cache/08/7F" as input, do action over files
+  // paramtr: directory (IN): starting point
+  //          list (IN): list of rexps to match URLs against
+  // returns: true, if every subdir && action was successful.
+{
+  struct dirent* entry;
+  if ( debug & 0x01 )
+    fprintf( stderr, "# [2] %s\n", directory );
+
+  DIR* dir = opendir( directory );
+  if ( dir == NULL ) {
+    fprintf( stderr, "unable to open directory \"%s\": %s\n", 
+            directory, strerror(errno) );
+    return false;
+  }
+
+  // display a rotating character as "i am alive" signal (slows purge).
+  if ( ::iamalive ) {
+    static char alivelist[4][3] = { "\\\b", "|\b", "/\b", "-\b" };
+    static unsigned short alivecount = 0;
+    assert( write( STDOUT_FILENO, alivelist[alivecount++ & 3], 2 ) == 2 );
+  }
+
+  bool flag = true;
+  while ( (entry=readdir(dir)) && flag ) {
+    if ( isxstring(entry->d_name,8) ) {
+      char* name = concat( directory, "/", entry->d_name, 0 );
+      flag = match( name, list );
+      delete[] name;
+    }
+  }
+
+  closedir(dir);
+  return flag;
+}
+
+bool
+dirlevel( const char* dirname, const REList* list, bool level=false )
+  // purpose: from given starting point, look for squid 00..FF directories.
+  // paramtr: dirname (IN): starting point
+  //          list (IN): list of rexps to match URLs against
+  //          level (IN): false==toplevel, true==1st level
+  // example: "/var/spool/cache", false as input, traverse subdirs w/ action.
+  // example: "/var/spool/cache/08", true as input, traverse subdirs w/ action.
+  // returns: true, if every subdir && action was successful.
+  // warning: this function is once-recursive, no deeper.
+{
+  struct dirent* entry;
+  if ( debug & 0x01 )
+    fprintf( stderr, "# [%d] %s\n", (level ? 1 : 0), dirname );
+
+  DIR* dir = opendir( dirname );
+  if ( dir == NULL ) {
+    fprintf( stderr, "unable to open directory \"%s\": %s\n", 
+            dirname, strerror(errno) );
+    return false;
+  }
+  
+  bool flag = true;
+  while ( (entry=readdir(dir)) && flag ) {
+    if ( strlen(entry->d_name) == 2 &&
+        isxdigit(entry->d_name[0]) &&
+        isxdigit(entry->d_name[1]) ) {
+      char* name = concat( dirname, "/", entry->d_name, 0 );
+      flag = level ? filelevel( name, list ) : dirlevel( name, list, true );
+      delete[] name;
+    }
+  }
+
+  closedir(dir);
+  return flag;
+}
+
+int
+checkForPortOnly( const char* optarg )
+  // purpose: see if somebody just put in a port instead of a hostname
+  // paramtr: optarg (IN): argument from commandline
+  // returns: 0..65535 is the valid port number in network byte order,
+  //          -1 if not a port
+{
+  // if there is a period in there, it must be a valid hostname
+  if ( strchr( optarg, '.' ) != 0 ) return -1;
+
+  // if it is just a number between 0 and 65535, it must be a port
+  char* errstr = 0;
+  unsigned long result = strtoul( optarg, &errstr, 0 );
+  if ( result < 65536 && errstr != optarg ) return htons(result);
+
+#if 0
+  // one last try, test for a symbolical service name
+  struct servent* service = getservbyname( optarg, "tcp" );
+  return service ? service->s_port : -1;
+#else
+  return -1;
+#endif
+}
+
+void
+helpMe( void )
+  // purpuse: write help message and exit
+{
+  printf( "\n%s\nUsage:\t%s\t[-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] "
+         "[-p h[:p]]\n\t\t[-P #] [-s] [-v] [-C dir [-H]] [-n]\n\n",
+         ::RCS_ID, ::programname );
+  printf(
+" -a\tdisplay a little rotating thingy to indicate that I am alive (tty only).\n"
+" -c c\tsquid.conf location, default \"%s\".\n"
+" -C dir\tbase directory for content extraction (copy-out mode).\n"
+" -d l\tdebug level, an OR of different debug options.\n"
+" -e re\tsingle regular expression per -e instance (use quotes!).\n"
+" -E re\tsingle case sensitive regular expression like -e.\n"
+" -f fn\tname of textfile containing one regular expression per line.\n"
+" -F fn\tname of textfile like -f containing case sensitive REs.\n"
+" -H\tprepend HTTP reply header to destination files in copy-out mode.\n"
+" -n\tdo not fork() when using more than one cache_dir.\n"
+" -p h:p\tcache runs on host h and optional port p, default is %s:%u.\n"
+" -P #\tif 0, just print matches; otherwise OR the following purge modes:\n"
+"\t   0x01 really send PURGE to the cache.\n"
+"\t   0x02 remove all caches files reported as 404 (not found).\n"
+"\t   0x04 remove all weird (inaccessible or too small) cache files.\n"
+"\t0 and 1 are recommended - slow rebuild your cache with other modes.\n"
+" -s\tshow all options after option parsing, but before really starting.\n"
+" -v\tshow more information about the file, e.g. MD5, timestamps and flags.\n"
+"\n", DEFAULT_SQUID_CONF, DEFAULTHOST, DEFAULTPORT );
+
+}
+
+void
+parseCommandline( int argc, char* argv[], REList*& head,
+                 char*& conffile, char*& copydir,
+                 struct in_addr& serverHost, unsigned short& serverPort )
+  // paramtr: argc: see ::main().
+  //          argv: see ::main().
+  // returns: Does terminate the program on errors!
+  // purpose: suck in any commandline options, and set the global vars.
+{ int option, port, showme = 0;
+  char* ptr, *colon;
+  FILE* rfile;
+
+  // program basename
+  if ( (ptr = strrchr(argv[0],'/')) == NULL ) ptr=argv[0];
+  else ptr++;
+  ::programname = ptr;
+
+  // extract commandline parameters
+  REList* tail = head = 0;
+  opterr = 0;
+  while ( (option = getopt( argc, argv, "ac:C:d:E:e:F:f:Hnp:P:sv" )) != -1 ) {
+    switch ( option ) {
+    case 'a':
+      ::iamalive = ! ::iamalive;
+      break;
+    case 'C':
+      if ( optarg && *optarg ) {
+       if ( copydir ) free( (void*) copydir );
+       assert( (copydir = strdup(optarg)) );
+      }
+      break;
+    case 'c':
+      if ( optarg && *optarg ) {
+       if ( *conffile ) free((void*) conffile );
+       assert( (conffile = strdup(optarg)) );
+      }
+      break;
+
+    case 'd':
+      ::debug = strtoul( optarg, 0, 0 );
+      break;
+
+    case 'E':
+    case 'e':
+      if ( head == 0 ) tail = head = new REList( optarg, option=='E' );
+      else {
+       tail->next = new REList( optarg, option=='E' );
+       tail = tail->next;
+      }
+      break;
+      
+    case 'f':
+      if ( (rfile = fopen( optarg, "r" )) != NULL ) {
+       unsigned long lineno = 0;
+#define LINESIZE 512
+       char line[LINESIZE];
+       while ( fgets( line, LINESIZE, rfile ) != NULL ) {
+         lineno++;
+         int len = strlen(line)-1;
+         if ( len+2 >= LINESIZE ) {
+           fprintf( stderr, "%s:%lu: line too long, sorry.\n",
+                    optarg, lineno );
+           exit(1);
+         }
+
+         // remove trailing line breaks
+         while ( len > 0 && ( line[len] == '\n' || line[len] == '\r' ) )
+           line[len--] = '\0';
+
+         // insert into list of expressions
+         if ( head == 0 ) tail = head = new REList(line,option=='F');
+         else {
+           tail->next = new REList(line,option=='F');
+           tail = tail->next;
+         }
+       }
+       fclose(rfile);
+      } else
+       fprintf( stderr, "unable to open %s: %s\n", optarg, strerror(errno));
+      break;
+
+    case 'H':
+      ::envelope = ! ::envelope;
+      break;
+    case 'n':
+      ::no_fork = ! ::no_fork;
+      break;
+    case 'p':
+      colon = strchr( optarg, ':' );
+      if ( colon == 0 ) {
+       // no colon, only look at host
+
+       // fix: see if somebody just put in there a port (no periods)
+       // give port number precedence over host names
+       port = checkForPortOnly( optarg );
+       if ( port == -1 ) {
+         // assume that main() did set the default port
+         if ( convertHostname(optarg,serverHost) == -1 ) {
+           fprintf( stderr, "unable to resolve host %s!\n", optarg );
+           exit(1);
+         }
+       } else {
+         // assume that main() did set the default host
+         serverPort = port;
+       }
+      } else {
+       // colon used, port is extra
+       *colon++ = 0;
+       if ( convertHostname(optarg,serverHost) == -1 ) {
+         fprintf( stderr, "unable to resolve host %s!\n", optarg );
+         exit(1);
+       }
+       if ( convertPortname(colon,serverPort) == -1 ) {
+         fprintf( stderr, "unable to resolve port %s!\n", colon );
+         exit(1);
+       }
+      }
+      break;
+    case 'P':
+      ::purgeMode = ( strtol( optarg, 0, 0 ) & 0x07 );
+      break;
+    case 's':
+      showme=1;
+      break;
+    case 'v':
+      ::verbose = ! ::verbose;
+      break;
+    case '?':
+    default:
+      helpMe();
+      exit(1);
+    }
+  }
+
+  // adjust
+  if ( ! isatty(fileno(stdout)) || (::debug & 0x01) ) ::iamalive = false;
+  if ( head == 0 ) {
+    fputs( "There was no regular expression defined. If you intend\n", stderr );
+    fputs( "to match all possible URLs, use \"-e .\" instead.\n", stderr );
+    exit(1);
+  }
+
+  // postcondition: head != 0
+  assert( head != 0 );
+
+  // make sure that the copy out directory is there and accessible
+  if ( copydir && *copydir )
+    if ( assert_copydir( copydir ) != 0 ) exit(1);
+  
+  // show results
+  if ( showme ) {
+    printf( "#\n# Currently active values for %s:\n# %s\n", 
+           ::programname, ::RCS_ID );
+    printf( "# Debug level       : " );
+    if ( ::debug ) printf( "%#6.4hx", ::debug );
+    else printf( "production level" ); // printf omits 0x prefix for 0!
+    printf( " + %s mode", ::no_fork ? "linear" : "parallel" );
+    puts( ::verbose ? " + extra verbosity" : "" );
+
+    printf( "# Copy-out directory: %s ", 
+           copydir ? copydir : "copy-out mode disabled" );
+    if ( copydir ) 
+      printf( "(%s HTTP header)\n", ::envelope ? "prepend" : "no" );
+    else
+      puts("");
+    
+    printf( "# Squid config file : %s\n", conffile );
+    printf( "# Cacheserveraddress: %s:%u\n",
+           inet_ntoa( serverHost ), ntohs( serverPort ) );
+    printf( "# purge mode        : 0x%02x\n", ::purgeMode );
+    printf( "# Regular expression: " );
+
+    unsigned count(0);
+    for ( tail = head; tail != NULL; tail = tail->next ) {
+      if ( count++ ) printf( "#%22u", count );
+#if defined(LINUX) && putc==_IO_putc
+      // I HATE BROKEN LINUX HEADERS!
+      // purge.o(.text+0x1040): undefined reference to `_IO_putc'
+      // If your compilation breaks here, remove the undefinition
+#undef putc      
+#endif
+      else putchar('1');
+      printf( " \"%s\"\n", tail->data );
+    }
+    puts( "#" );
+  }
+  fflush( stdout );
+}
+
+extern "C" {
+
+static
+void
+exiter( void )
+{
+  if ( ::term_flag ) psignal( ::term_flag, "received signal" );
+  delete[] ::linebuffer;
+  if ( ::reminder ) {
+    fputs( 
+"WARNING! Caches files were removed. Please shut down your cache, remove\n"
+"your swap.state files and restart your cache again, i.e. effictively do\n"
+"a slow rebuild your cache! Otherwise your squid *will* choke!\n", stderr );
+  }
+}
+
+static
+void
+handler( int signo )
+{
+  ::term_flag = signo;
+  if ( getpid() == getpgrp() ) kill( -getpgrp(), signo );
+  exit(1);
+}
+
+} // extern "C"
+
+static
+int
+makelinebuffered( FILE* fp, const char* fn = 0 )
+  // purpose: make the given FILE line buffered
+  // paramtr: fp (IO): file pointer which to put into line buffer mode
+  //          fn (IN): name of file to print in case of error
+  // returns: 0 is ok, -1 to indicate an error
+  // warning: error messages will already be printed
+{
+  if ( setvbuf( fp, 0, _IOLBF, 0 ) == 0 ) {
+    // ok
+    return 0;
+  } else {
+    // error
+    fprintf( stderr, "unable to make \"%s\" line buffered: %s\n",
+            fn ? fn : "", strerror(errno) );
+    return -1;
+  }
+}
+
+int
+main( int argc, char* argv[] )
+{
+  // setup variables
+  REList* list = 0;
+  char* conffile = strdup( DEFAULT_SQUID_CONF );
+  serverPort = htons(DEFAULTPORT);
+  if ( convertHostname(DEFAULTHOST,serverHost) == -1 ) {
+    fprintf( stderr, "unable to resolve host %s!\n", DEFAULTHOST );
+    return 1;
+  }
+
+  // setup line buffer
+  ::linebuffer = new char[ ::buffersize ];
+  assert( ::linebuffer != 0 );
+  
+  // parse commandline
+  puts( "### Use at your own risk! No guarantees whatsoever. You were warned. ###");
+  parseCommandline( argc, argv, list, conffile, ::copydir,
+                   serverHost, serverPort );
+
+  // prepare execution
+  if ( atexit( exiter ) != 0 ||
+       Signal( SIGTERM, handler, true ) == SIG_ERR ||
+       Signal( SIGINT, handler, true ) == SIG_ERR ||
+       Signal( SIGHUP, handler, true ) == SIG_ERR ) {
+    perror( "unable to install signal/exit function" );
+    return 1;
+  }
+
+  // try to read squid.conf file to determine all cache_dir locations
+  CacheDirVector cdv(0);
+  if ( readConfigFile( cdv, conffile, debug ? stderr : 0 ) > 0 ) {
+    // there are some valid cache_dir entries. 
+    // unless forking was forbidden by cmdline option, 
+    // for a process for each cache_dir entry to remove files.
+
+    if ( ::no_fork || cdv.size() == 1 ) {
+      // linear mode, one cache_dir after the next
+      for ( CacheDirVector::iterator i = cdv.begin(); i != cdv.end(); ++i ) {
+       // execute OR complain
+       if ( ! dirlevel(i->base,list) )
+         fprintf( stderr, "program terminated due to error: %s", 
+                  strerror(errno) );
+       free((void*) i->base);
+      }
+    } else {
+      // parallel mode, all cache_dir in parallel
+      pid_t* child = new pid_t[ cdv.size() ];
+
+      // make stdout/stderr line bufferd
+      makelinebuffered( stdout, "stdout" );
+      makelinebuffered( stderr, "stderr" );
+
+      // make parent process group leader for easier killings
+      if ( setpgid(getpid(), getpid()) != 0 ) {
+       perror( "unable to set process group leader" );
+       return 1;
+      }
+
+      // -a is mutually exclusive with fork mode
+      if ( ::iamalive ) {
+       puts( "# i-am-alive flag incompatible with fork mode, resetting" );
+       ::iamalive = false;
+      }
+
+      for ( int i=0; i < cdv.size(); ++i ) {
+       if ( getpid() == getpgrp() ) { 
+         // only parent == group leader may fork off new processes
+         if ( (child[i]=fork()) < 0 ) {
+           // fork error, this is bad!
+           perror( "unable to fork" );
+           kill( -getpgrp(), SIGTERM );
+           return 1;
+         } else if ( child[i] == 0 ) {
+           // child mode
+           // execute OR complain
+           if ( ! dirlevel(cdv[i].base,list) )
+             fprintf( stderr, "program terminated due to error: %s\n", 
+                      strerror(errno) );
+           free((void*) cdv[i].base);
+           return 0;
+         } else {
+           // parent mode
+           if ( ::debug ) printf( "forked child %d\n", (int) child[i] );
+         }
+       }
+      } 
+
+      // collect the garbase
+      pid_t temp;
+      int status;
+      for ( int i=0; i < cdv.size(); ++i ) {
+       while ( (temp=waitpid( (pid_t)-1, &status, 0 )) == -1 )
+         if ( errno == EINTR ) continue;
+       if ( ::debug ) printf( "collected child %d\n", (int) temp );
+      }
+      delete[] child;
+    }
+  } else {
+    fprintf( stderr, "no cache_dir or error accessing \"%s\"\n", conffile );
+  }
+
+  // clean up
+  if ( copydir ) free( (void*) copydir );
+  free((void*) conffile);
+  delete list;
+  return 0;
+}
diff --git a/tools/purge/signal.cc b/tools/purge/signal.cc
new file mode 100644 (file)
index 0000000..15e1a3b
--- /dev/null
@@ -0,0 +1,164 @@
+//
+// $Id: signal.cc,v 1.3 1999/01/19 13:11:52 cached Exp $
+//
+// Author:   Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+// File:     signal.cc
+// Date:     Sat Feb 28 1998
+// Compiler: gcc 2.7.2.x series
+// 
+// Books:    W. Richard Steven, "Advanced Programming in the UNIX Environment",
+//           Addison-Wesley, 1992.
+// 
+// (c) 1998 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: signal.cc,v $
+// Revision 1.3  1999/01/19 13:11:52  cached
+// adaptations necessary for AIX.
+//
+// Revision 1.2  1999/01/19 11:00:50  voeckler
+// added psignal(int,const char*) compatibility function.
+//
+// Revision 1.1  1998/08/13 21:51:58  voeckler
+// Initial revision
+//
+//
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#endif
+
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "signal.hh"
+
+static const char* RCS_ID = 
+"$Id: signal.cc,v 1.3 1999/01/19 13:11:52 cached Exp $";
+
+#ifndef HAS_PSIGNAL
+#ifdef AIX
+extern const char* const sys_siglist[];
+#define _sys_nsig 64
+#define _sys_siglist sys_siglist
+#endif // AIX
+
+void
+psignal( int sig, const char* msg )
+  // purpose: print message, colon, space, signal name and LF.
+  // paramtr: sig (IN): signal number
+  //          msg (IN): message to prepend
+{
+  if ( msg && *msg ) fprintf( stderr, "%s: ", msg );
+  if ( sig > 0 && sig < _sys_nsig ) 
+    fprintf( stderr, "%s\n", _sys_siglist[sig] );
+  else
+    fputs( "(unknown)\n", stderr );
+}
+#endif // !HAS_PSIGNAL
+
+SigFunc*
+Signal( int signo, SigFunc* newhandler, bool doInterrupt )
+  // purpose: install reliable signals
+  // paramtr: signo (IN): signal for which a handler is to be installed
+  //          newhandler (IN): function pointer to the signal handler
+  //          doInterrupt (IN): interrupted system calls wanted!
+  // returns: the old signal handler, or SIG_ERR in case of error.
+{
+  struct sigaction action, old;
+
+  memset( &old, 0, sizeof(old) );
+  memset( &action, 0, sizeof(action) );
+
+  // action.sa_handler = newhandler; I HATE TYPE-OVERCORRECTNESS !
+  memmove( &action.sa_handler, &newhandler, sizeof(SigFunc*) );
+  sigemptyset( &action.sa_mask );
+
+  if ( signo == SIGCHLD ) {
+    action.sa_flags |= SA_NOCLDSTOP;
+
+#ifdef SA_NODEFER
+    action.sa_flags |= SA_NODEFER;   // SYSV: don't block current signal
+#endif
+  }
+
+  if ( signo == SIGALRM || doInterrupt ) {
+#ifdef SA_INTERRUPT
+    action.sa_flags |= SA_INTERRUPT; // SunOS, obsoleted by POSIX
+#endif
+  } else {
+#ifdef SA_RESTART
+    action.sa_flags |= SA_RESTART;   // BSD, SVR4
+#endif
+  }
+
+  return ( sigaction( signo, &action, &old ) < 0 ) ? 
+    (SigFunc*) SIG_ERR : 
+    (SigFunc*) old.sa_handler;
+}
+
+SIGRETTYPE
+sigChild( int signo )
+  // purpose: supply ad hoc child handler with output on stderr
+  // paramtr: signo (IN): == SIGCHLD
+  // returns: only if OS uses a return type for signal handler
+  // seealso: Stevens, UNP, figure 5.11 *and* Stevens, APUE, figure 8.3
+{
+  pid_t pid;
+  int  status = signo; // to stop GNU from complaining...
+  char line[128];
+
+  int saveerr = errno;
+  while ( (pid = waitpid( -1, &status, WNOHANG )) > 0 ) {
+    if ( WIFEXITED(status) ) {
+      sprintf( line, "child (pid=%ld) reaped, status %d\n%c", 
+              (long) pid, WEXITSTATUS(status), 0 );
+    } else if ( WIFSIGNALED(status) ) {
+      sprintf( line, "child (pid=%ld) died on signal %d%s\n%c", 
+              (long) pid, WTERMSIG(status),
+#ifdef WCOREDUMP
+              WCOREDUMP(status) ? " (core generated)" : "",
+#else
+              "",
+#endif
+              0 );
+    } else {
+      sprintf( line, "detected dead child (pid=%ld), status %d\n%c",
+              (long) pid, status, 0 );
+    }
+    write( STDERR_FILENO, line, strlen(line) );
+  }
+  errno = saveerr;
+
+#if SIGRETTYPE != void
+  return 0;
+#endif
+}
diff --git a/tools/purge/signal.hh b/tools/purge/signal.hh
new file mode 100644 (file)
index 0000000..562a83f
--- /dev/null
@@ -0,0 +1,103 @@
+//
+// $Id: signal.hh,v 1.4 2000/09/21 10:59:27 cached Exp $
+//
+// Author:   Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+// File:     signal.hh
+// Date:     Sat Feb 28 1998
+// Compiler: gcc 2.7.2.x series
+// 
+// Books:    W. Richard Steven, "Advanced Programming in the UNIX Environment",
+//           Addison-Wesley, 1992.
+// 
+// (c) 1998 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: signal.hh,v $
+// Revision 1.4  2000/09/21 10:59:27  cached
+// introduced extern "C" to function pointer type.
+//
+// Revision 1.3  1999/01/19 11:53:49  voeckler
+// added bool compatibility definitions.
+//
+// Revision 1.2  1999/01/19 11:00:50  voeckler
+// added psignal(int,const char*) compatibility function declaration.
+//
+// Revision 1.1  1998/08/13 21:51:58  voeckler
+// Initial revision
+//
+//
+#ifndef _SIGNAL_HH
+#define _SIGNAL_HH
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma interface
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+#if 1 // so far, all systems I know use void
+# define SIGRETTYPE void
+#else
+# define SIGRETTYPE int
+#endif
+
+#if defined(SUNOS) && defined(SUN)
+# define SIGPARAM void
+#else // SOLARIS, LINUX, IRIX, AIX, SINIXY
+# define SIGPARAM int
+#endif
+
+extern "C" {
+  typedef SIGRETTYPE SigFunc( SIGPARAM );
+}
+
+#ifndef HAS_PSIGNAL
+void
+psignal( int sig, const char* msg );
+  // purpose: print message, colon, space, signal name and LF.
+  // paramtr: sig (IN): signal number
+  //          msg (IN): message to prepend
+#endif // ! HAS_PSIGNAL
+
+SigFunc*
+Signal( int signo, SigFunc* newhandler, bool doInterrupt );
+  // purpose: install reliable signals
+  // paramtr: signo (IN): signal for which a handler is to be installed
+  //          newhandler (IN): function pointer to the signal handler
+  //          doInterrupt (IN): interrupted system calls wanted!
+  // returns: the old signal handler, or SIG_ERR in case of error.
+
+SIGRETTYPE
+sigChild( int signo );
+  // purpose: supply ad hoc child handler with output on stderr
+  // paramtr: signo (IN): == SIGCHLD
+  // returns: only if OS uses a return type for signal handler
+
+#endif // _SIGNAL_HH
diff --git a/tools/purge/socket.cc b/tools/purge/socket.cc
new file mode 100644 (file)
index 0000000..8d99849
--- /dev/null
@@ -0,0 +1,260 @@
+//
+// $Id: socket.cc,v 1.3 1999/01/19 11:00:50 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    socket.hh
+//          Sun May  3 1998
+//
+// (c) 1998 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Books:   W. Richard Steven, "Advanced Programming in the UNIX Environment",
+//          Addison-Wesley, 1992.
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: socket.cc,v $
+// Revision 1.3  1999/01/19 11:00:50  voeckler
+// Linux glibc2 fixes for socket size parameters.
+//
+// Revision 1.2  1998/08/27 15:23:39  voeckler
+// added TCP_NODELAY options at several places.
+//
+// Revision 1.1  1998/08/13 21:52:55  voeckler
+// Initial revision
+//
+//
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#endif
+
+#include "socket.hh"
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "convert.hh"
+
+static const char* RCS_ID =
+"$Id: socket.cc,v 1.3 1999/01/19 11:00:50 voeckler Exp $";
+
+int
+setSocketBuffers( int sockfd, int size )
+  // purpose: set socket buffers for both directions to the specified size
+  // paramtr: sockfd (IN): socket file descriptor
+  //          size (IN): new socket buffer size
+  // returns: -1 on setsockopt() errors, 0 otherwise
+  // warning: prints error message on stderr, errno will be changed
+{
+  if ( setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF,
+                   (char*) &size, sizeof(size) ) == -1 ) {
+    perror( "setsockopt( SO_RCVBUF )" );
+    return -1;
+  }
+
+  if ( setsockopt( sockfd, SOL_SOCKET, SO_SNDBUF,
+                   (char*) &size, sizeof(size) ) == -1 ) {
+    perror( "setsockopt( SO_SNDBUF )" );
+    return -1;
+  }
+
+  return 0;
+}
+
+int
+getSocketNoDelay( int sockfd )
+  // purpose: get state of the TCP_NODELAY of the socket
+  // paramtr: sockfd (IN): socket descriptor
+  // returns: 1, if TCP_NODELAY is set,
+  //          0, if TCP_NODELAY is not set,
+  //         -1, if an error occurred (e.g. datagram socket)
+{
+  int delay = 0;
+  SOCKLEN len = sizeof(delay);
+  if ( getsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, 
+                  (char*) &delay, &len ) == -1 ) {
+    perror( "# getsockopt( TCP_NODELAY ) failed" );
+    return -1;
+  } else
+    return ( delay ? 1 : 0 );
+}
+
+int
+setSocketNoDelay( int sockfd, bool nodelay )
+  // purpose: get state of the TCP_NODELAY of the socket
+  // paramtr: sockfd (IN): socket descriptor
+  //          nodelay (IN): true, if TCP_NODELAY is to be set, false otherwise.
+  // returns: 0, if everything worked out o.k.
+  //         -1, if an error occurred (e.g. datagram socket)
+{
+  const int delay = 1;
+  if ( setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY,
+                  (const char*) &delay, sizeof(delay) ) == -1 ) {
+    perror( "setsockopt( TCP_NODELAY ) failed" );
+    return -1;
+  } else
+    return 0;
+}
+
+
+int
+commonCode( int& sockfd, bool nodelay, int sendBufferSize, int recvBufferSize )
+  // purpose: common code in server sockets and client sockets
+  // paramtr: sockfd (IO): socket filedescriptor
+  //          nodelay (IN): true=set TCP_NODELAY option.
+  //          sendBufferSize (IN): don't set (use sys defaults) if < 0 
+  //          recvBufferSize (IN): don't set (use sys defaults) if < 0 
+  // returns: 0 == if everthing went ok, -1 otherwise
+  // warning: sockfd will be closed, if -1 is returned!
+{
+  // set TCP_NODELAY option, if that is wanted. 
+  // The socket API default is unset.
+  if ( nodelay ) {
+    const int delay = 1;
+    if ( setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY,
+                    (const char*) &delay, sizeof(delay) ) == -1 ) {
+      perror( "setsockopt( TCP_NODELAY ) failed" );
+      close(sockfd);
+      return -1;
+    }
+  }
+
+  // set the socket send buffer size explicitly, or use the system default
+  if ( sendBufferSize >= 0 ) {
+    if ( setsockopt( sockfd, SOL_SOCKET, SO_SNDBUF, (char*) &sendBufferSize,
+                    sizeof(sendBufferSize) ) == -1 ) {
+      perror( "setsockopt( SO_SNDBUF )" );
+      close(sockfd);
+      return -1;
+    }
+  }
+
+  // set the socket recv buffer size explicitly, or use the system default
+  if ( recvBufferSize >= 0 ) {
+    if ( setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF, (char*) &recvBufferSize,
+                    sizeof(recvBufferSize) ) == -1 ) {
+      perror( "setsockopt( SO_RCVBUF )" );
+      close(sockfd);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+int
+connectTo( struct in_addr host, unsigned short port, bool nodelay,
+          int sendBufferSize, int recvBufferSize )
+  // purpose: connect to a server as a client
+  // paramtr: host (IN): address describing the server
+  //          port (IN): port to connect at the server
+  //          nodelay (IN): true=set TCP_NODELAY option.
+  //          sendBufferSize (IN): don't set (use sys defaults) if < 0 
+  //          recvBufferSize (IN): don't set (use sys defaults) if < 0 
+  // returns: >=0 is the descriptor of the opened, connected socket,
+  //          -1  is an indication of an error (errno may have been reset).
+{
+  int sockfd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
+  if ( sockfd == -1 ) {
+    perror( "socket() failed" );
+    return -1;
+  }
+
+  if ( commonCode( sockfd, nodelay, sendBufferSize, recvBufferSize ) == -1 )
+    return -1;
+  
+  struct sockaddr_in server;
+  memset( &server, 0, sizeof(server) );
+  server.sin_family = AF_INET;
+  server.sin_addr   = host;
+  server.sin_port   = port;
+  if ( connect( sockfd, (struct sockaddr*) &server, sizeof(server) ) == -1 ) {
+    perror( "connect() failure" );
+    close(sockfd);
+    return -1;
+  }
+  
+  return sockfd;
+}
+
+int
+serverSocket( struct in_addr host, unsigned short port,
+             int backlog, bool reuse, bool nodelay,
+             int sendBufferSize, int recvBufferSize ) 
+  // purpose: open a server socket for listening
+  // paramtr: host (IN): host to bind locally to, use INADDRY_ANY for *
+  //          port (IN): port to bind to, use 0 for system assigned
+  //          backlog (IN): listen backlog queue length
+  //          reuse (IN): set SO_REUSEADDR option - default usefully
+  //          nodelay (IN): true=set TCP_NODELAY option.
+  //            SETTING TCP_NODELAY ON A SERVER SOCKET DOES NOT MAKE SENSE!
+  //          sendBufferSize (IN): don't set (use sys defaults) if < 0 
+  //          recvBufferSize (IN): don't set (use sys defaults) if < 0 
+  // returns: opened listening fd, or -1 on error.
+  // warning: error message will be printed on stderr and errno reset.
+{
+  int sockfd = socket( AF_INET, SOCK_STREAM, 0 );
+  if ( sockfd == -1 ) {
+    perror( "socket" );
+    return -1;
+  }
+
+  if ( reuse ) {
+    int reuse = 1;
+    if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
+                    (char*) &reuse, sizeof(int) ) == -1) {
+      perror( "setsockopt( SO_REUSEADDR )" );
+      close( sockfd );
+      return -1;
+    }
+  }
+
+  if ( commonCode( sockfd, nodelay, sendBufferSize, recvBufferSize ) == -1 )
+    return -1;
+
+  struct sockaddr_in server;
+  memset( &server, 0, sizeof(server) );
+  server.sin_family = AF_INET;
+  server.sin_port   = port;
+  server.sin_addr   = host;
+  if ( bind( sockfd, (SA*) &server, sizeof(server) ) == -1 ) {
+    SockAddress socket;
+    fprintf( stderr, "bind(%s): %s\n", 
+            my_sock_ntoa(server,socket), strerror(errno) );
+    close(sockfd);
+    return -1;
+  }
+
+  if ( listen( sockfd, backlog ) == -1 ) {
+    perror( "listen" );
+    close(sockfd);
+    return -1;
+  }
+
+  return sockfd;
+}
diff --git a/tools/purge/socket.hh b/tools/purge/socket.hh
new file mode 100644 (file)
index 0000000..a053ad7
--- /dev/null
@@ -0,0 +1,140 @@
+//
+// $Id: socket.hh,v 1.3 1999/01/19 11:00:50 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    socket.hh
+//          Sun May  3 1998
+//
+// (c) 1998 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Books:   W. Richard Steven, "Advanced Programming in the UNIX Environment",
+//          Addison-Wesley, 1992.
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: socket.hh,v $
+// Revision 1.3  1999/01/19 11:00:50  voeckler
+// added bool type workaround.
+//
+// Revision 1.2  1998/08/27 15:23:24  voeckler
+// added TCP_NODELAY options at several places.
+//
+// Revision 1.1  1998/08/13 21:52:55  voeckler
+// Initial revision
+//
+//
+#ifndef _SOCKET_HH
+#define _SOCKET_HH
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma interface
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>        // SOMAXCONN
+#include <netinet/in.h>
+
+#if SOMAXCONN <= 5
+#undef SOMAXCONN
+#endif
+
+#ifndef SOMAXCONN
+#if defined(SOLARIS)
+#if MAJOR < 5
+#define SOMAXCONN 32
+#else
+#define SOMAXCONN 128
+#endif
+#elif defined(LINUX)
+#define SOMAXCONN 128
+#else
+#define SOMAXCONN 5 // BSD
+#endif // OS selection
+#endif // !SOMAXCONN
+
+#ifndef SA
+#define SA struct sockaddr
+#endif
+
+int
+setSocketBuffers( int fd, int size );
+  // purpose: set socket buffers for both directions to the specified size
+  // paramtr: size (IN): new socket buffer size
+  // returns: -1 on setsockopt() errors, 0 otherwise
+  // warning: prints error message on stderr, errno will be changed
+
+int
+getSocketNoDelay( int sockfd );
+  // purpose: get state of the TCP_NODELAY of the socket
+  // paramtr: sockfd (IN): socket descriptor
+  // returns: 1, if TCP_NODELAY is set,
+  //          0, if TCP_NODELAY is not set,
+  //         -1, if an error occurred (e.g. datagram socket)
+
+
+int
+setSocketNoDelay( int sockfd, bool nodelay = true );
+  // purpose: get state of the TCP_NODELAY of the socket
+  // paramtr: sockfd (IN): socket descriptor
+  //          nodelay (IN): true, if TCP_NODELAY is to be set, false otherwise.
+  // returns: 0, if everything worked out o.k.
+  //         -1, if an error occurred (e.g. datagram socket)
+
+int
+connectTo( struct in_addr host, unsigned short port, bool nodelay = false,
+          int sendBufferSize = -1, int recvBufferSize = -1 );
+  // purpose: connect to a server as a client
+  // paramtr: host (IN): address describing the server
+  //          port (IN): port to connect at the server
+  //          nodelay (IN): true=set TCP_NODELAY option.
+  //          sendBufferSize (IN): don't set (use sys defaults) if < 0 
+  //          recvBufferSize (IN): don't set (use sys defaults) if < 0 
+  // returns: >=0 is the descriptor of the opened, connected socket,
+  //          -1  is an indication of an error (errno may have been reset).
+
+int
+serverSocket( struct in_addr host, unsigned short port, 
+             int backlog = SOMAXCONN, bool reuse = true, bool nodelay = false,
+             int sendBufferSize = -1, int recvBufferSize = -1 );
+  // purpose: open a server socket for listening
+  // paramtr: host (IN): host to bind locally to, use INADDRY_ANY for *
+  //          port (IN): port to bind to, use 0 for system assigned
+  //          backlog (IN): listen backlog queue length
+  //          reuse (IN): set SO_REUSEADDR option - default usefully
+  //          nodelay (IN): true=set TCP_NODELAY option.
+  //            SETTING TCP_NODELAY ON A SERVER SOCKET DOES NOT MAKE SENSE!
+  //          sendBufferSize (IN): don't set (use sys defaults) if < 0 
+  //          recvBufferSize (IN): don't set (use sys defaults) if < 0 
+  // returns: opened listening fd, or -1 on error.
+  // warning: error message will be printed on stderr and errno reset.
+
+#endif // _SOCKET_HH
diff --git a/tools/purge/squid-tlv.cc b/tools/purge/squid-tlv.cc
new file mode 100644 (file)
index 0000000..2ca4d6d
--- /dev/null
@@ -0,0 +1,91 @@
+//
+// $Id: squid-tlv.cc,v 1.1 1999/06/15 21:10:16 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    squid-tlv.cc
+//          Tue Jun 15 1999
+//
+// (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: squid-tlv.cc,v $
+// Revision 1.1  1999/06/15 21:10:16  voeckler
+// Initial revision
+//
+//
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma implementation
+#endif
+
+#include <assert.h>
+#include "squid-tlv.hh"
+
+static const char* RCS_ID =
+"$Id: squid-tlv.cc,v 1.1 1999/06/15 21:10:16 voeckler Exp $";
+
+SquidTLV::SquidTLV( SquidMetaType _type, size_t _size, void* _data )
+  :next(0),size(_size)
+{
+  if ( size ) {
+    type = _type;
+    data = (char*) _data;
+  } else {
+    type = STORE_META_END;
+    data = 0;
+  }
+}
+
+SquidMetaList::SquidMetaList()
+{
+  head = tail = 0;
+}
+
+SquidMetaList::~SquidMetaList()
+{
+  for ( SquidTLV* temp = head; temp; temp = head ) {
+    head = temp->next;
+    delete temp;
+  }
+}
+
+void
+SquidMetaList::append( SquidMetaType type, size_t size, void* data )
+{
+  SquidTLV* temp = new SquidTLV( type, size, data );
+  if ( head == 0 ) head = tail = temp;
+  else {
+    tail->next = temp;
+    tail = temp;
+  }
+}
+
+const SquidTLV*
+SquidMetaList::search( SquidMetaType type ) const
+{
+  const SquidTLV* temp = head;
+  while ( temp && temp->type != type ) temp = temp->next;
+  return temp;
+}
diff --git a/tools/purge/squid-tlv.hh b/tools/purge/squid-tlv.hh
new file mode 100644 (file)
index 0000000..17edc41
--- /dev/null
@@ -0,0 +1,107 @@
+//
+// $Id: squid-tlv.hh,v 1.1 1999/06/15 21:10:16 voeckler Exp $
+//
+// Author:  Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+//
+// File:    squid-tlv.hh
+//          Tue Jun 15 1999
+//
+// (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
+//          Universität Hannover, Germany
+//
+// Permission to use, copy, modify, distribute, and sell this software
+// and its documentation for any purpose is hereby granted without fee,
+// provided that (i) the above copyright notices and this permission
+// notice appear in all copies of the software and related documentation,
+// and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
+// Systeme and the University of Hannover may not be used in any
+// advertising or publicity relating to the software without the
+// specific, prior written permission of Lehrgebiet Rechnernetze und
+// Verteilte Systeme and the University of Hannover.
+//
+// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
+// THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
+// ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+// SOFTWARE.
+//
+// $Log: squid-tlv.hh,v $
+// Revision 1.1  1999/06/15 21:10:16  voeckler
+// Initial revision
+//
+#ifndef _SQUID_TLV_HH
+#define _SQUID_TLV_HH
+
+#if defined(__GNUC__) || defined(__GNUG__)
+#pragma interface
+#else
+#ifndef HAS_BOOL
+#define HAS_BOOL
+typedef int bool;
+#define false 0
+#define true  1
+#endif
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h>
+
+// taken from Squid-2.x
+// NOTE!  We must preserve the order of this list!
+enum SquidMetaType {
+  STORE_META_VOID,             // should not come up 
+  STORE_META_KEY_URL,          // key w/ keytype 
+  STORE_META_KEY_SHA,
+  STORE_META_KEY_MD5,
+  STORE_META_URL,              // the url , if not in the header
+  STORE_META_STD,              // standard metadata
+  STORE_META_HITMETERING,      // reserved for hit metering
+  STORE_META_VALID,
+  STORE_META_END
+};
+
+// taken from Squid-2.x
+struct StoreMetaStd {
+  time_t  timestamp;
+  time_t  lastref;
+  time_t  expires;
+  time_t  lastmod;
+  size_t  swap_file_sz;
+  u_short refcount;
+  u_short flags;
+};
+
+struct SquidTLV {
+  // create a shallow reference pointing into the "buffer" variable
+  // do not copy --> saves times, saves memory.
+  SquidTLV( SquidMetaType _type, size_t _size = 0, void* _data = 0 );
+  ~SquidTLV() {}
+
+  SquidTLV*      next;
+  size_t        size;
+  SquidMetaType  type;
+  char*          data;
+};
+
+class SquidMetaList {
+public:
+  SquidMetaList();
+  ~SquidMetaList();
+
+  void append( SquidMetaType type, size_t size, void* data );
+  const SquidTLV* search( SquidMetaType type ) const;
+
+private:
+  SquidTLV* head;
+  SquidTLV* tail;
+};
+
+#endif // _SQUID_TLV_HH