From: Amos Jeffries Date: Thu, 8 Jul 2010 12:28:08 +0000 (+1200) Subject: Author: Jens-S. V�ckler X-Git-Tag: SQUID_3_2_0_1~75 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=eb1f6bfa7c19555c72c72c7102cac62d8417ee2f;p=thirdparty%2Fsquid.git Author: Jens-S. V�ckler 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 --- diff --git a/tools/purge/Makefile b/tools/purge/Makefile new file mode 100644 index 0000000000..946f394c57 --- /dev/null +++ b/tools/purge/Makefile @@ -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 index 0000000000..d41905abd9 --- /dev/null +++ b/tools/purge/README @@ -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 index 0000000000..9add58ae64 --- /dev/null +++ b/tools/purge/conffile.cc @@ -0,0 +1,179 @@ +// +// $Id: conffile.cc,v 1.1 2000/09/21 09:44:53 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include +#include +#include +#include +#include +#include + +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 index 0000000000..3fb919a002 --- /dev/null +++ b/tools/purge/conffile.hh @@ -0,0 +1,85 @@ +// +// $Id: conffile.hh,v 1.2 2000/09/21 10:17:17 cached Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 // FILE* +#include // 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 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 index 0000000000..e8415314d6 --- /dev/null +++ b/tools/purge/convert.cc @@ -0,0 +1,160 @@ +// +// $Id: convert.cc,v 1.3 2000/06/20 09:43:01 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include +#include +#include +#include +#include + +#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 index 0000000000..dbc65f6d23 --- /dev/null +++ b/tools/purge/convert.hh @@ -0,0 +1,102 @@ +// +// $Id: convert.hh,v 1.2 1999/01/19 11:00:50 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include + +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 index 0000000000..605750cc2b --- /dev/null +++ b/tools/purge/copyout.cc @@ -0,0 +1,279 @@ +// +// $Id: copyout.cc,v 1.2 1999/06/16 13:05:26 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#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 index 0000000000..d99f7e4da9 --- /dev/null +++ b/tools/purge/copyout.hh @@ -0,0 +1,73 @@ +// +// $Id: copyout.hh,v 1.1 1999/06/15 21:10:47 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 index 0000000000..46163cfa86 --- /dev/null +++ b/tools/purge/hexd.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000000..8d7162cecf --- /dev/null +++ b/tools/purge/purge.cc @@ -0,0 +1,947 @@ +// +// $Id: purge.cc,v 1.17 2000/09/21 10:59:53 cached Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAS_PSIGNAL) && !defined(LINUX) && !defined(FREEBSD) +#include +#endif // HAS_PSIGNAL + +#include +#include // TCP_NODELAY +#include +#include // gethostbyname() +#include + +#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 ( isearch( 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 index 0000000000..15e1a3b6b3 --- /dev/null +++ b/tools/purge/signal.cc @@ -0,0 +1,164 @@ +// +// $Id: signal.cc,v 1.3 1999/01/19 13:11:52 cached Exp $ +// +// Author: Jens-S. Vöckler +// 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 +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..562a83f001 --- /dev/null +++ b/tools/purge/signal.hh @@ -0,0 +1,103 @@ +// +// $Id: signal.hh,v 1.4 2000/09/21 10:59:27 cached Exp $ +// +// Author: Jens-S. Vöckler +// 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 index 0000000000..8d99849eac --- /dev/null +++ b/tools/purge/socket.cc @@ -0,0 +1,260 @@ +// +// $Id: socket.cc,v 1.3 1999/01/19 11:00:50 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include +#include + +#include +#include +#include +#include + +#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 index 0000000000..a053ad76c2 --- /dev/null +++ b/tools/purge/socket.hh @@ -0,0 +1,140 @@ +// +// $Id: socket.hh,v 1.3 1999/01/19 11:00:50 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include // SOMAXCONN +#include + +#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 index 0000000000..2ca4d6d421 --- /dev/null +++ b/tools/purge/squid-tlv.cc @@ -0,0 +1,91 @@ +// +// $Id: squid-tlv.cc,v 1.1 1999/06/15 21:10:16 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#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 index 0000000000..17edc4116e --- /dev/null +++ b/tools/purge/squid-tlv.hh @@ -0,0 +1,107 @@ +// +// $Id: squid-tlv.hh,v 1.1 1999/06/15 21:10:16 voeckler Exp $ +// +// Author: Jens-S. Vöckler +// +// 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 +#include +#include +#include + +// 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