]>
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 fprintf( stderr
, "%c requires a regex pattern argument!\n", option
);
659 if ( *conffile
) xfree((void*) conffile
);
660 conffile
= xstrdup(optarg
);
665 ::debugFlag
= optarg
? 0 : strtoul( optarg
, 0, 0 );
670 if ( !optarg
|| !*optarg
) {
671 fprintf( stderr
, "%c requires a regex pattern argument!\n", option
);
675 tail
= head
= new REList( optarg
, option
=='E' );
677 tail
->next
= new REList( optarg
, option
=='E' );
683 if ( !optarg
|| !*optarg
) {
684 fprintf( stderr
, "%c requires a filename argument!\n", option
);
687 if ( (rfile
= fopen( optarg
, "r" )) != NULL
) {
688 unsigned long lineno
= 0;
691 while ( fgets( line
, LINESIZE
, rfile
) != NULL
) {
693 int len
= strlen(line
)-1;
694 if ( len
+2 >= LINESIZE
) {
695 fprintf( stderr
, "%s:%lu: line too long, sorry.\n",
700 // remove trailing line breaks
701 while ( len
> 0 && ( line
[len
] == '\n' || line
[len
] == '\r' ) ) {
706 // insert into list of expressions
707 if ( head
== 0 ) tail
= head
= new REList(line
,option
=='F');
709 tail
->next
= new REList(line
,option
=='F');
715 fprintf( stderr
, "unable to open %s: %s\n", optarg
, strerror(errno
));
719 ::envelope
= ! ::envelope
;
722 ::no_fork
= ! ::no_fork
;
725 if ( !optarg
|| !*optarg
) {
726 fprintf( stderr
, "%c requires a port argument!\n", option
);
729 colon
= strchr( optarg
, ':' );
731 // no colon, only look at host
733 // fix: see if somebody just put in there a port (no periods)
734 // give port number precedence over host names
735 port
= checkForPortOnly( optarg
);
737 // assume that main() did set the default port
738 if ( convertHostname(optarg
,serverHost
) == -1 ) {
739 fprintf( stderr
, "unable to resolve host %s!\n", optarg
);
743 // assume that main() did set the default host
747 // colon used, port is extra
750 if ( convertHostname(optarg
,serverHost
) == -1 ) {
751 fprintf( stderr
, "unable to resolve host %s!\n", optarg
);
754 if ( convertPortname(colon
,serverPort
) == -1 ) {
755 fprintf( stderr
, "unable to resolve port %s!\n", colon
);
761 if ( !optarg
|| !*optarg
) {
762 fprintf( stderr
, "%c requires a mode argument!\n", option
);
765 ::purgeMode
= ( strtol( optarg
, 0, 0 ) & 0x07 );
771 ::verbose
= ! ::verbose
;
781 if ( ! isatty(fileno(stdout
)) || (::debugFlag
& 0x01) ) ::iamalive
= false;
783 fputs( "There was no regular expression defined. If you intend\n", stderr
);
784 fputs( "to match all possible URLs, use \"-e .\" instead.\n", stderr
);
788 // postcondition: head != 0
791 // make sure that the copy out directory is there and accessible
792 if ( copydir
&& *copydir
)
793 if ( assert_copydir( copydir
) != 0 ) exit(1);
797 printf( "#\n# Currently active values for %s:\n",
799 printf( "# Debug level : " );
800 if ( ::debugFlag
) printf( "%#6.4x", ::debugFlag
);
801 else printf( "production level" ); // printf omits 0x prefix for 0!
802 printf( " + %s mode", ::no_fork
? "linear" : "parallel" );
803 puts( ::verbose
? " + extra verbosity" : "" );
805 printf( "# Copy-out directory: %s ",
806 copydir
? copydir
: "copy-out mode disabled" );
808 printf( "(%s HTTP header)\n", ::envelope
? "prepend" : "no" );
812 printf( "# Squid config file : %s\n", conffile
);
813 printf( "# Cacheserveraddress: %s:%u\n",
814 inet_ntoa( serverHost
), ntohs( serverPort
) );
815 printf( "# purge mode : 0x%02x\n", ::purgeMode
);
816 printf( "# Regular expression: " );
819 for ( tail
= head
; tail
!= NULL
; tail
= tail
->next
) {
821 printf( "#%22u", count
);
822 #if defined(LINUX) && putc==_IO_putc
823 // I HATE BROKEN LINUX HEADERS!
824 // purge.o(.text+0x1040): undefined reference to `_IO_putc'
825 // If your compilation breaks here, remove the undefinition
829 printf( " \"%s\"\n", tail
->data
);
841 if ( ::term_flag
) psignal( ::term_flag
, "received signal" );
842 delete[] ::linebuffer
;
845 "WARNING! Caches files were removed. Please shut down your cache, remove\n"
846 "your swap.state files and restart your cache again, i.e. effictively do\n"
847 "a slow rebuild your cache! Otherwise your squid *will* choke!\n", stderr
);
853 handler( int signo
) {
855 if ( getpid() == getpgrp() ) kill( -getpgrp(), signo
);
863 makelinebuffered( FILE* fp
, const char* fn
= 0 )
864 // purpose: make the given FILE line buffered
865 // paramtr: fp (IO): file pointer which to put into line buffer mode
866 // fn (IN): name of file to print in case of error
867 // returns: 0 is ok, -1 to indicate an error
868 // warning: error messages will already be printed
870 if ( setvbuf( fp
, 0, _IOLBF
, 0 ) == 0 ) {
875 fprintf( stderr
, "unable to make \"%s\" line buffered: %s\n",
876 fn
? fn
: "", strerror(errno
) );
882 main( int argc
, char* argv
[] )
886 char* conffile
= xstrdup( DEFAULT_SQUID_CONF
);
887 serverPort
= htons(DEFAULTPORT
);
888 if ( convertHostname(DEFAULTHOST
,serverHost
) == -1 ) {
889 fprintf( stderr
, "unable to resolve host %s!\n", DEFAULTHOST
);
894 ::linebuffer
= new char[ ::buffersize
];
895 assert( ::linebuffer
!= 0 );
898 puts( "### Use at your own risk! No guarantees whatsoever. You were warned. ###");
899 parseCommandline( argc
, argv
, list
, conffile
, ::copydir
,
900 serverHost
, serverPort
);
903 if ( atexit( exiter
) != 0 ||
904 Signal( SIGTERM
, handler
, true ) == SIG_ERR
||
905 Signal( SIGINT
, handler
, true ) == SIG_ERR
||
906 Signal( SIGHUP
, handler
, true ) == SIG_ERR
) {
907 perror( "unable to install signal/exit function" );
911 // try to read squid.conf file to determine all cache_dir locations
912 CacheDirVector
cdv(0);
913 if ( readConfigFile( cdv
, conffile
, debugFlag
? stderr
: 0 ) > 0 ) {
914 // there are some valid cache_dir entries.
915 // unless forking was forbidden by cmdline option,
916 // for a process for each cache_dir entry to remove files.
918 if ( ::no_fork
|| cdv
.size() == 1 ) {
919 // linear mode, one cache_dir after the next
920 for ( CacheDirVector::iterator i
= cdv
.begin(); i
!= cdv
.end(); ++i
) {
921 // execute OR complain
922 if ( ! dirlevel(i
->base
,list
) )
923 fprintf( stderr
, "program terminated due to error: %s",
925 xfree((void*) i
->base
);
928 // parallel mode, all cache_dir in parallel
929 pid_t
* child
= new pid_t
[ cdv
.size() ];
931 // make stdout/stderr line bufferd
932 makelinebuffered( stdout
, "stdout" );
933 makelinebuffered( stderr
, "stderr" );
935 // make parent process group leader for easier killings
936 if ( setpgid(getpid(), getpid()) != 0 ) {
937 perror( "unable to set process group leader" );
941 // -a is mutually exclusive with fork mode
943 puts( "# i-am-alive flag incompatible with fork mode, resetting" );
947 for ( size_t i
=0; i
< cdv
.size(); ++i
) {
948 if ( getpid() == getpgrp() ) {
949 // only parent == group leader may fork off new processes
950 if ( (child
[i
]=fork()) < 0 ) {
951 // fork error, this is bad!
952 perror( "unable to fork" );
953 kill( -getpgrp(), SIGTERM
);
955 } else if ( child
[i
] == 0 ) {
957 // execute OR complain
958 if ( ! dirlevel(cdv
[i
].base
,list
) )
959 fprintf( stderr
, "program terminated due to error: %s\n",
961 xfree((void*) cdv
[i
].base
);
965 if ( ::debugFlag
) printf( "forked child %d\n", (int) child
[i
] );
970 // collect the garbase
973 for ( size_t i
=0; i
< cdv
.size(); ++i
) {
974 while ( (temp
=waitpid( (pid_t
)-1, &status
, 0 )) == -1 )
975 if ( errno
== EINTR
) continue;
976 if ( ::debugFlag
) printf( "collected child %d\n", (int) temp
);
981 fprintf( stderr
, "no cache_dir or error accessing \"%s\"\n", conffile
);
985 if ( copydir
) xfree( (void*) copydir
);
986 xfree((void*) conffile
);