]>
git.ipfire.org Git - thirdparty/squid.git/blob - tools/purge/purge.cc
1 // Author: Jens-S. V?ckler <voeckler@rvs.uni-hannover.de>
6 // (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
7 // Universit?t Hannover, Germany
9 // Permission to use, copy, modify, distribute, and sell this software
10 // and its documentation for any purpose is hereby granted without fee,
11 // provided that (i) the above copyright notices and this permission
12 // notice appear in all copies of the software and related documentation,
13 // and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte
14 // Systeme and the University of Hannover may not be used in any
15 // advertising or publicity relating to the software without the
16 // specific, prior written permission of Lehrgebiet Rechnernetze und
17 // Verteilte Systeme and the University of Hannover.
19 // THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
21 // WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
23 // IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR
24 // THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
25 // INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
26 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
27 // ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
28 // ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 // Revision 1.17 2000/09/21 10:59:53 cached
32 // *** empty log message ***
34 // Revision 1.16 2000/09/21 09:45:18 cached
35 // Fixed some small bugs.
37 // Revision 1.15 2000/09/21 09:05:56 cached
38 // added multi cache_dir support, thus changing -c cmdline option.
39 // modified file reading to support /dev/fd/0 reading for non-disclosed items.
41 // Revision 1.14 2000/06/20 09:43:01 voeckler
42 // added FreeBSD related fixes and support.
44 // Revision 1.13 2000/03/29 08:12:21 voeckler
45 // fixed wrong header file.
47 // Revision 1.12 2000/03/29 07:54:41 voeckler
48 // added mechanism to give a port specification precedence over a host
49 // specificiation with the -p option and not colon.
51 // Revision 1.11 1999/06/18 13:18:28 voeckler
52 // added refcount, fixed missing LF in -s output.
54 // Revision 1.10 1999/06/16 13:06:05 voeckler
55 // reversed meaning of -M flag.
57 // Revision 1.9 1999/06/15 21:11:53 voeckler
58 // added extended logging feature which extract the squid meta data available
59 // within the disk files. moved the content extraction and squid meta data
60 // handling parts into separate files. added options for copy-out and verbose.
62 // Revision 1.8 1999/06/14 20:14:46 voeckler
63 // intermediate version when adding understanding about the way
64 // Squid does log the metadata into the file.
66 // Revision 1.7 1999/01/23 21:01:10 root
67 // stumbled over libc5 header/lib inconsistency bug....
69 // Revision 1.6 1999/01/23 20:47:54 root
70 // added Linux specifics for psignal...
73 // Revision 1.5 1999/01/20 09:48:12 voeckler
74 // added warning as first line of output.
76 // Revision 1.4 1999/01/19 11:53:49 voeckler
77 // added psignal() from <siginfo.h> handling.
79 // Revision 1.3 1999/01/19 11:00:50 voeckler
80 // added keyboard interrupt handling, exit handling, removed C++ strings and
81 // regular expression syntax in favour of less source code, added comments,
82 // added a reminder to remove swap.state in case of unlinks, added IAA flag,
83 // added a few assertions, changed policy to enforce the definition of at
84 // least one regular expression, and catch a few signals.
86 // Revision 1.2 1999/01/15 23:06:28 voeckler
87 // downgraded to simple C strings...
89 // Revision 1.1 1999/01/14 12:05:32 voeckler
93 #if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__)
94 #pragma implementation
104 #include <sys/stat.h>
105 #include <sys/wait.h>
117 #include <netinet/in.h>
118 #include <netinet/tcp.h>
119 #include <arpa/inet.h>
122 #include "convert.hh"
125 #include "squid-tlv.hh"
126 #include "copyout.hh"
127 #include "conffile.hh"
130 #define DEFAULTHOST "localhost"
131 #endif // DEFAULTHOST
134 #define DEFAULTPORT 3128
135 #endif // DEFAULTPORT
137 volatile sig_atomic_t term_flag
= 0; // 'terminate' is a gcc 2.8.x internal...
138 char* linebuffer
= 0;
139 size_t buffersize
= 128*1024;
140 static char* copydir
= 0;
141 static unsigned debugFlag
= 0;
142 static unsigned purgeMode
= 0;
143 static bool iamalive
= false;
144 static bool reminder
= false;
145 static bool verbose
= false;
146 static bool envelope
= false;
147 static bool no_fork
= false;
148 static const char* programname
= 0;
150 // ----------------------------------------------------------------------
153 REList( const char* what
, bool doCase
);
155 bool match( const char* check
) const;
162 REList::REList( const char* what
, bool doCase
)
163 :next(0),data(xstrdup(what
))
165 int result
= regcomp( &rexp
, what
,
166 REG_EXTENDED
| REG_NOSUB
| (doCase
? 0 : REG_ICASE
) );
169 regerror( result
, &rexp
, buffer
, 256 );
170 fprintf( stderr
, "unable to compile re \"%s\": %s\n", what
, buffer
);
177 if ( next
) delete next
;
178 if ( data
) xfree((void*) data
);
183 REList::match( const char* check
) const
185 int result
= regexec( &rexp
, check
, 0, 0, 0 );
186 if ( result
!= 0 && result
!= REG_NOMATCH
) {
188 regerror( result
, &rexp
, buffer
, 256 );
189 fprintf( stderr
, "unable to execute re \"%s\"\n+ on line \"%s\": %s\n",
190 data
, check
, buffer
);
193 return ( result
== 0 );
196 // ----------------------------------------------------------------------
199 concat( const char* start
, ... )
200 // purpose: concatinate an arbitrary number of C strings.
201 // paramtr: start (IN): first C string
202 // ... (IN): further C strings, terminated with a NULL pointer
203 // returns: memory allocated via new(), containing the concatinated string.
208 // first run: determine size
209 unsigned size
= strlen(start
)+1;
210 va_start( ap
, start
);
211 while ( (s
=va_arg(ap
,const char*)) != NULL
)
216 char* result
= new char[size
];
218 perror( "string memory allocation" );
222 // second run: copy content
223 strcpy( result
, start
);
224 va_start( ap
, start
);
225 while ( (s
=va_arg(ap
,const char*)) != NULL
) strcat( result
, s
);
232 isxstring( const char* s
, size_t testlen
)
233 // purpose: test a string for conforming to xdigit
234 // paramtr: s (IN): string to test
235 // testlen (IN): length the string must have
236 // returns: true, iff strlen(s)==testlen && all_x_chars(s), false otherwise
238 if ( strlen(s
) != testlen
) return false;
241 while ( i
<testlen
&& isxdigit(s
[i
]) )
248 log_output( const char* fn
, int code
, long size
, const char* url
)
250 return printf( "%s %3d %8ld %s\n", fn
, code
, size
, url
);
255 log_extended( const char* fn
, int code
, long size
, const SquidMetaList
* meta
)
257 static const char hexdigit
[] = "0123456789ABCDEF";
259 const SquidTLV
* findings
= 0;
261 if ( meta
&& (findings
= meta
->search( STORE_META_KEY_MD5
)) ) {
262 unsigned char* s
= (unsigned char*) findings
->data
;
263 for ( int j
=0; j
<16; ++j
, ++s
) {
264 md5
[j
*2+0] = hexdigit
[ *s
>> 4 ];
265 md5
[j
*2+1] = hexdigit
[ *s
& 15 ];
267 md5
[32] = '\0'; // terminate string
269 snprintf( md5
, sizeof(md5
), "%-32s", "(no_md5_data_available)" );
273 if ( meta
&& (findings
= meta
->search( STORE_META_STD
)) ) {
275 // make data aligned, avoid SIGBUS on RISC machines (ARGH!)
276 memcpy( &temp
, findings
->data
, sizeof(StoreMetaStd
) );
277 snprintf( timeb
, sizeof(timeb
), "%08lx %08lx %08lx %08lx %04x %5hu ",
278 (unsigned long)temp
.timestamp
, (unsigned long)temp
.lastref
,
279 (unsigned long)temp
.expires
, (unsigned long)temp
.lastmod
, temp
.flags
, temp
.refcount
);
280 } else if ( meta
&& (findings
= meta
->search( STORE_META_STD_LFS
)) ) {
281 StoreMetaStdLFS temp
;
282 // make data aligned, avoid SIGBUS on RISC machines (ARGH!)
283 memcpy( &temp
, findings
->data
, sizeof(StoreMetaStd
) );
284 snprintf( timeb
, sizeof(timeb
), "%08lx %08lx %08lx %08lx %04x %5hu ",
285 (unsigned long)temp
.timestamp
, (unsigned long)temp
.lastref
,
286 (unsigned long)temp
.expires
, (unsigned long)temp
.lastmod
, temp
.flags
, temp
.refcount
);
288 unsigned long ul
= ULONG_MAX
; // Match type of StoreMetaTLV fields
289 unsigned short hu
= 0; // Match type of StoreMetaTLV refcount fields
290 snprintf( timeb
, sizeof(timeb
), "%08lx %08lx %08lx %08lx %04x %5d ", ul
, ul
, ul
, ul
, 0, hu
);
293 // make sure that there is just one printf()
294 if ( meta
&& (findings
= meta
->search( STORE_META_URL
)) ) {
295 return printf( "%s %3d %8ld %s %s %s\n",
296 fn
, code
, size
, md5
, timeb
, findings
->data
);
298 return printf( "%s %3d %8ld %s %s strange_file\n",
299 fn
, code
, size
, md5
, timeb
);
303 // o.k., this is pure lazyness...
304 static struct in_addr serverHost
;
305 static unsigned short serverPort
;
308 action( int fd
, size_t metasize
,
309 const char* fn
, const char* url
, const SquidMetaList
& meta
)
310 // purpose: if cmdline-requested, send the purge request to the cache
311 // paramtr: fd (IN): open FD for the object file
312 // metasize (IN): offset into data portion of file (meta data size)
313 // fn (IN): name of the object file
314 // url (IN): URL string stored in the object file
315 // meta (IN): list containing further meta data
316 // returns: true for a successful action, false otherwise. The action
317 // may just print the file, send the purge request or even
318 // remove unwanted files.
319 // globals: ::purgeMode (IN): bit#0 set -> send purge request.
320 // bit#1 set -> remove 404 object files.
321 // ::serverHost (IN): cache host address
322 // ::serverPort (IN): cache port number
324 static const char* schablone
= "PURGE %s HTTP/1.0\r\nAccept: */*\r\n\r\n";
326 long size
= ( fstat(fd
,&st
) == -1 ? -1 : long(st
.st_size
- metasize
) );
328 // if we want to copy out the file, do that first of all.
329 if ( ::copydir
&& *copydir
&& size
> 0 )
330 copy_out( st
.st_size
, metasize
, ::debugFlag
,
331 fn
, url
, ::copydir
, ::envelope
);
333 // do we need to PURGE the file, yes, if purgemode bit#0 was set.
335 if ( ::purgeMode
& 0x01 ) {
336 unsigned long bufsize
= strlen(url
) + strlen(schablone
) + 4;
337 char* buffer
= new char[bufsize
];
339 snprintf( buffer
, bufsize
, schablone
, url
);
340 int sockfd
= connectTo( serverHost
, serverPort
, true );
341 if ( sockfd
== -1 ) {
342 fprintf( stderr
, "unable to connect to server: %s\n", strerror(errno
) );
347 int size
= strlen(buffer
);
348 if ( write( sockfd
, buffer
, size
) != size
) {
349 // error while talking to squid
350 fprintf( stderr
, "unable to talk to server: %s\n", strerror(errno
) );
355 memset( buffer
+8, 0, 4 );
356 int readLen
= read(sockfd
, buffer
, bufsize
);
358 // error while reading squid's answer
359 fprintf( stderr
, "unable to read answer: %s\n", strerror(errno
) );
364 buffer
[bufsize
-1] = '\0';
366 int64_t s
= strtol(buffer
+8,0,10);
367 if (s
> 0 && s
< 1000)
370 // error while reading squid's answer
371 fprintf( stderr
, "invalid HTTP status in reply: %s\n", buffer
+8);
376 // log the output of our operation
378 if ( ::verbose
) flag
= ( log_extended( fn
, status
, size
, &meta
) >= 0 );
379 else flag
= ( log_output( fn
, status
, size
, url
) >= 0 );
381 // remove the file, if purgemode bit#1, and HTTP result status 404).
382 if ( (::purgeMode
& 0x02) && status
== 404 ) {
384 if ( unlink(fn
) == -1 )
385 // error while unlinking file, this may happen due to the cache
386 // unlinking a file while it is still in the readdir() cache of purge.
387 fprintf( stderr
, "WARNING: unable to unlink %s: %s\n",
388 fn
, strerror(errno
) );
395 match( const char* fn
, const REList
* list
)
396 // purpose: do something with the given cache content filename
397 // paramtr: fn (IN): filename of cache file
398 // returns: true for successful action, false otherwise.
399 // warning: only return false, if you want the loop to terminate!
401 static const size_t addon
= sizeof(unsigned char) + sizeof(unsigned int);
404 if ( debugFlag
& 0x01 ) fprintf( stderr
, "# [3] %s\n", fn
);
405 int fd
= open( fn
, O_RDONLY
);
407 memset(::linebuffer
, 0, ::buffersize
);
408 size_t readLen
= read(fd
,::linebuffer
,::buffersize
-1);
409 if ( readLen
> 60 ) {
410 ::linebuffer
[ ::buffersize
-1 ] = '\0'; // force-terminate string
412 // check the offset into the start of object data. The offset is
413 // stored in a host endianess after the first byte.
414 unsigned int datastart
;
415 memcpy( &datastart
, ::linebuffer
+ 1, sizeof(unsigned int) );
416 if ( datastart
> ::buffersize
- addon
- 1 ) {
417 // check offset into server reply header (start of cache data).
418 fputs( "WARNING: Using a truncated URL string.\n", stderr
);
419 datastart
= ::buffersize
- addon
- 1;
422 // NEW: Parse squid meta data, which is a kind of linked list
423 // flattened out into a file byte stream. Somewhere within is
424 // the URL as part of the list. First, gobble all meta data.
425 unsigned int offset
= addon
;
427 while ( offset
+ addon
<= datastart
) {
428 unsigned int size
= 0;
429 memcpy( &size
, linebuffer
+offset
+sizeof(char), sizeof(unsigned int) );
430 if (size
+offset
< size
) {
431 fputs("WARNING: file corruption detected. 32-bit overflow in size field.\n", stderr
);
434 if (size
+offset
> readLen
) {
435 fputs( "WARNING: Partial meta data loaded.\n", stderr
);
438 meta
.append( SquidMetaType(*(linebuffer
+offset
)),
439 size
, linebuffer
+offset
+addon
);
440 offset
+= ( addon
+ size
);
443 // Now extract the key URL from the meta data.
444 const SquidTLV
* urlmeta
= meta
.search( STORE_META_URL
);
446 // found URL in meta data. Try to process the URL
448 flag
= action( fd
, datastart
, fn
, (char*) urlmeta
->data
, meta
);
450 REList
* head
= (REList
*) list
; // YUCK!
451 while ( head
!= 0 ) {
452 if ( head
->match( (char*) urlmeta
->data
) ) break;
456 flag
= action( fd
, datastart
, fn
, (char*) urlmeta
->data
, meta
);
461 // "meta" will be deleted when exiting from this block
463 // weird file, FIXME: stat() it!
465 long size
= ( fstat(fd
,&st
) == -1 ? -1 : st
.st_size
);
466 if ( ::verbose
) flag
= ( log_extended( fn
, -1, size
, 0 ) >= 0 );
467 else flag
= ( log_output( fn
, -1, size
, "strange file" ) >= 0 );
469 if ( (::purgeMode
& 0x04) ) {
471 if ( unlink(fn
) == -1 )
472 // error while unlinking file, this may happen due to the cache
473 // unlinking a file while it is in the readdir() cache of purge.
474 fprintf( stderr
, "WARNING: unable to unlink %s: %s\n",
475 fn
, strerror(errno
) );
480 // error while opening file, this may happen due to the cache
481 // unlinking a file while it is still in the readdir() cache of purge.
482 fprintf( stderr
, "WARNING: open \"%s\": %s\n", fn
, strerror(errno
) );
489 filelevel( const char* directory
, const REList
* list
)
490 // purpose: from given starting point, look for squid xxxxxxxx files.
491 // example: "/var/spool/cache/08/7F" as input, do action over files
492 // paramtr: directory (IN): starting point
493 // list (IN): list of rexps to match URLs against
494 // returns: true, if every subdir && action was successful.
497 if ( debugFlag
& 0x01 )
498 fprintf( stderr
, "# [2] %s\n", directory
);
500 DIR* dir
= opendir( directory
);
502 fprintf( stderr
, "unable to open directory \"%s\": %s\n",
503 directory
, strerror(errno
) );
507 // display a rotating character as "i am alive" signal (slows purge).
509 static char alivelist
[4][3] = { "\\\b", "|\b", "/\b", "-\b" };
510 static unsigned short alivecount
= 0;
511 const int write_success
= write(STDOUT_FILENO
, alivelist
[alivecount
++ & 3], 2);
512 assert(write_success
== 2);
516 while ( (entry
=readdir(dir
)) && flag
) {
517 if ( isxstring(entry
->d_name
,8) ) {
518 char* name
= concat( directory
, "/", entry
->d_name
, 0 );
519 flag
= match( name
, list
);
529 dirlevel( const char* dirname
, const REList
* list
, bool level
=false )
530 // purpose: from given starting point, look for squid 00..FF directories.
531 // paramtr: dirname (IN): starting point
532 // list (IN): list of rexps to match URLs against
533 // level (IN): false==toplevel, true==1st level
534 // example: "/var/spool/cache", false as input, traverse subdirs w/ action.
535 // example: "/var/spool/cache/08", true as input, traverse subdirs w/ action.
536 // returns: true, if every subdir && action was successful.
537 // warning: this function is once-recursive, no deeper.
540 if ( debugFlag
& 0x01 )
541 fprintf( stderr
, "# [%d] %s\n", (level
? 1 : 0), dirname
);
543 DIR* dir
= opendir( dirname
);
545 fprintf( stderr
, "unable to open directory \"%s\": %s\n",
546 dirname
, strerror(errno
) );
551 while ( (entry
=readdir(dir
)) && flag
) {
552 if ( strlen(entry
->d_name
) == 2 &&
553 isxdigit(entry
->d_name
[0]) &&
554 isxdigit(entry
->d_name
[1]) ) {
555 char* name
= concat( dirname
, "/", entry
->d_name
, 0 );
556 flag
= level
? filelevel( name
, list
) : dirlevel( name
, list
, true );
566 checkForPortOnly( const char* optarg
)
567 // purpose: see if somebody just put in a port instead of a hostname
568 // paramtr: optarg (IN): argument from commandline
569 // returns: 0..65535 is the valid port number in network byte order,
572 // if there is a period in there, it must be a valid hostname
573 if ( strchr( optarg
, '.' ) != 0 ) return -1;
575 // if it is just a number between 0 and 65535, it must be a port
577 unsigned long result
= strtoul( optarg
, &errstr
, 0 );
578 if ( result
< 65536 && errstr
!= optarg
) return htons(result
);
581 // one last try, test for a symbolical service name
582 struct servent
* service
= getservbyname( optarg
, "tcp" );
583 return service
? service
->s_port
: -1;
591 // purpuse: write help message and exit
593 printf( "\nUsage:\t%s\t[-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] "
594 "[-p h[:p]]\n\t\t[-P #] [-s] [-v] [-C dir [-H]] [-n]\n\n",
597 " -a\tdisplay a little rotating thingy to indicate that I am alive (tty only).\n"
598 " -c c\tsquid.conf location, default \"%s\".\n"
599 " -C dir\tbase directory for content extraction (copy-out mode).\n"
600 " -d l\tdebug level, an OR of different debug options.\n"
601 " -e re\tsingle regular expression per -e instance (use quotes!).\n"
602 " -E re\tsingle case sensitive regular expression like -e.\n"
603 " -f fn\tname of textfile containing one regular expression per line.\n"
604 " -F fn\tname of textfile like -f containing case sensitive REs.\n"
605 " -H\tprepend HTTP reply header to destination files in copy-out mode.\n"
606 " -n\tdo not fork() when using more than one cache_dir.\n"
607 " -p h:p\tcache runs on host h and optional port p, default is %s:%u.\n"
608 " -P #\tif 0, just print matches; otherwise OR the following purge modes:\n"
609 "\t 0x01 really send PURGE to the cache.\n"
610 "\t 0x02 remove all caches files reported as 404 (not found).\n"
611 "\t 0x04 remove all weird (inaccessible or too small) cache files.\n"
612 "\t0 and 1 are recommended - slow rebuild your cache with other modes.\n"
613 " -s\tshow all options after option parsing, but before really starting.\n"
614 " -v\tshow more information about the file, e.g. MD5, timestamps and flags.\n"
615 "\n", DEFAULT_SQUID_CONF
, DEFAULTHOST
, DEFAULTPORT
);
620 parseCommandline( int argc
, char* argv
[], REList
*& head
,
621 char*& conffile
, char*& copydir
,
622 struct in_addr
& serverHost
, unsigned short& serverPort
)
623 // paramtr: argc: see ::main().
624 // argv: see ::main().
625 // returns: Does terminate the program on errors!
626 // purpose: suck in any commandline options, and set the global vars.
628 int option
, port
, showme
= 0;
633 if ( (ptr
= strrchr(argv
[0],'/')) == NULL
)
639 // extract commandline parameters
640 REList
* tail
= head
= 0;
642 while ( (option
= getopt( argc
, argv
, "ac:C:d:E:e:F:f:Hnp:P:sv" )) != -1 ) {
645 ::iamalive
= ! ::iamalive
;
648 if ( optarg
&& *optarg
) {
649 if ( copydir
) xfree( (void*) copydir
);
650 copydir
= xstrdup(optarg
);
655 if ( optarg
&& *optarg
) {
656 if ( *conffile
) xfree((void*) conffile
);
657 conffile
= xstrdup(optarg
);
663 ::debugFlag
= strtoul( optarg
, 0, 0 );
668 if ( head
== 0 ) tail
= head
= new REList( optarg
, option
=='E' );
670 tail
->next
= new REList( optarg
, option
=='E' );
676 if ( (rfile
= fopen( optarg
, "r" )) != NULL
) {
677 unsigned long lineno
= 0;
680 while ( fgets( line
, LINESIZE
, rfile
) != NULL
) {
682 int len
= strlen(line
)-1;
683 if ( len
+2 >= LINESIZE
) {
684 fprintf( stderr
, "%s:%lu: line too long, sorry.\n",
689 // remove trailing line breaks
690 while ( len
> 0 && ( line
[len
] == '\n' || line
[len
] == '\r' ) ) {
695 // insert into list of expressions
696 if ( head
== 0 ) tail
= head
= new REList(line
,option
=='F');
698 tail
->next
= new REList(line
,option
=='F');
704 fprintf( stderr
, "unable to open %s: %s\n", optarg
, strerror(errno
));
708 ::envelope
= ! ::envelope
;
711 ::no_fork
= ! ::no_fork
;
714 colon
= strchr( optarg
, ':' );
716 // no colon, only look at host
718 // fix: see if somebody just put in there a port (no periods)
719 // give port number precedence over host names
720 port
= checkForPortOnly( optarg
);
722 // assume that main() did set the default port
723 if ( convertHostname(optarg
,serverHost
) == -1 ) {
724 fprintf( stderr
, "unable to resolve host %s!\n", optarg
);
728 // assume that main() did set the default host
732 // colon used, port is extra
735 if ( convertHostname(optarg
,serverHost
) == -1 ) {
736 fprintf( stderr
, "unable to resolve host %s!\n", optarg
);
739 if ( convertPortname(colon
,serverPort
) == -1 ) {
740 fprintf( stderr
, "unable to resolve port %s!\n", colon
);
746 ::purgeMode
= ( strtol( optarg
, 0, 0 ) & 0x07 );
752 ::verbose
= ! ::verbose
;
762 if ( ! isatty(fileno(stdout
)) || (::debugFlag
& 0x01) ) ::iamalive
= false;
764 fputs( "There was no regular expression defined. If you intend\n", stderr
);
765 fputs( "to match all possible URLs, use \"-e .\" instead.\n", stderr
);
769 // postcondition: head != 0
772 // make sure that the copy out directory is there and accessible
773 if ( copydir
&& *copydir
)
774 if ( assert_copydir( copydir
) != 0 ) exit(1);
778 printf( "#\n# Currently active values for %s:\n",
780 printf( "# Debug level : " );
781 if ( ::debugFlag
) printf( "%#6.4x", ::debugFlag
);
782 else printf( "production level" ); // printf omits 0x prefix for 0!
783 printf( " + %s mode", ::no_fork
? "linear" : "parallel" );
784 puts( ::verbose
? " + extra verbosity" : "" );
786 printf( "# Copy-out directory: %s ",
787 copydir
? copydir
: "copy-out mode disabled" );
789 printf( "(%s HTTP header)\n", ::envelope
? "prepend" : "no" );
793 printf( "# Squid config file : %s\n", conffile
);
794 printf( "# Cacheserveraddress: %s:%u\n",
795 inet_ntoa( serverHost
), ntohs( serverPort
) );
796 printf( "# purge mode : 0x%02x\n", ::purgeMode
);
797 printf( "# Regular expression: " );
800 for ( tail
= head
; tail
!= NULL
; tail
= tail
->next
) {
802 printf( "#%22u", count
);
803 #if defined(LINUX) && putc==_IO_putc
804 // I HATE BROKEN LINUX HEADERS!
805 // purge.o(.text+0x1040): undefined reference to `_IO_putc'
806 // If your compilation breaks here, remove the undefinition
810 printf( " \"%s\"\n", tail
->data
);
822 if ( ::term_flag
) psignal( ::term_flag
, "received signal" );
823 delete[] ::linebuffer
;
826 "WARNING! Caches files were removed. Please shut down your cache, remove\n"
827 "your swap.state files and restart your cache again, i.e. effictively do\n"
828 "a slow rebuild your cache! Otherwise your squid *will* choke!\n", stderr
);
834 handler( int signo
) {
836 if ( getpid() == getpgrp() ) kill( -getpgrp(), signo
);
844 makelinebuffered( FILE* fp
, const char* fn
= 0 )
845 // purpose: make the given FILE line buffered
846 // paramtr: fp (IO): file pointer which to put into line buffer mode
847 // fn (IN): name of file to print in case of error
848 // returns: 0 is ok, -1 to indicate an error
849 // warning: error messages will already be printed
851 if ( setvbuf( fp
, 0, _IOLBF
, 0 ) == 0 ) {
856 fprintf( stderr
, "unable to make \"%s\" line buffered: %s\n",
857 fn
? fn
: "", strerror(errno
) );
863 main( int argc
, char* argv
[] )
867 char* conffile
= xstrdup( DEFAULT_SQUID_CONF
);
868 serverPort
= htons(DEFAULTPORT
);
869 if ( convertHostname(DEFAULTHOST
,serverHost
) == -1 ) {
870 fprintf( stderr
, "unable to resolve host %s!\n", DEFAULTHOST
);
875 ::linebuffer
= new char[ ::buffersize
];
876 assert( ::linebuffer
!= 0 );
879 puts( "### Use at your own risk! No guarantees whatsoever. You were warned. ###");
880 parseCommandline( argc
, argv
, list
, conffile
, ::copydir
,
881 serverHost
, serverPort
);
884 if ( atexit( exiter
) != 0 ||
885 Signal( SIGTERM
, handler
, true ) == SIG_ERR
||
886 Signal( SIGINT
, handler
, true ) == SIG_ERR
||
887 Signal( SIGHUP
, handler
, true ) == SIG_ERR
) {
888 perror( "unable to install signal/exit function" );
892 // try to read squid.conf file to determine all cache_dir locations
893 CacheDirVector
cdv(0);
894 if ( readConfigFile( cdv
, conffile
, debugFlag
? stderr
: 0 ) > 0 ) {
895 // there are some valid cache_dir entries.
896 // unless forking was forbidden by cmdline option,
897 // for a process for each cache_dir entry to remove files.
899 if ( ::no_fork
|| cdv
.size() == 1 ) {
900 // linear mode, one cache_dir after the next
901 for ( CacheDirVector::iterator i
= cdv
.begin(); i
!= cdv
.end(); ++i
) {
902 // execute OR complain
903 if ( ! dirlevel(i
->base
,list
) )
904 fprintf( stderr
, "program terminated due to error: %s",
906 xfree((void*) i
->base
);
909 // parallel mode, all cache_dir in parallel
910 pid_t
* child
= new pid_t
[ cdv
.size() ];
912 // make stdout/stderr line bufferd
913 makelinebuffered( stdout
, "stdout" );
914 makelinebuffered( stderr
, "stderr" );
916 // make parent process group leader for easier killings
917 if ( setpgid(getpid(), getpid()) != 0 ) {
918 perror( "unable to set process group leader" );
922 // -a is mutually exclusive with fork mode
924 puts( "# i-am-alive flag incompatible with fork mode, resetting" );
928 for ( size_t i
=0; i
< cdv
.size(); ++i
) {
929 if ( getpid() == getpgrp() ) {
930 // only parent == group leader may fork off new processes
931 if ( (child
[i
]=fork()) < 0 ) {
932 // fork error, this is bad!
933 perror( "unable to fork" );
934 kill( -getpgrp(), SIGTERM
);
936 } else if ( child
[i
] == 0 ) {
938 // execute OR complain
939 if ( ! dirlevel(cdv
[i
].base
,list
) )
940 fprintf( stderr
, "program terminated due to error: %s\n",
942 xfree((void*) cdv
[i
].base
);
946 if ( ::debugFlag
) printf( "forked child %d\n", (int) child
[i
] );
951 // collect the garbase
954 for ( size_t i
=0; i
< cdv
.size(); ++i
) {
955 while ( (temp
=waitpid( (pid_t
)-1, &status
, 0 )) == -1 )
956 if ( errno
== EINTR
) continue;
957 if ( ::debugFlag
) printf( "collected child %d\n", (int) temp
);
962 fprintf( stderr
, "no cache_dir or error accessing \"%s\"\n", conffile
);
966 if ( copydir
) xfree( (void*) copydir
);
967 xfree((void*) conffile
);