124#include <netinet/in.h>
125#include <netinet/tcp.h>
126#include <arpa/inet.h>
129#include "conffile.hh"
134#include "squid-tlv.hh"
137#define DEFAULTHOST "localhost"
141#define DEFAULTPORT 3128
160 REList(
const char* what,
bool doCase );
162 bool match(
const char* check )
const;
170 :next(nullptr),data(
xstrdup(what))
172 int result = regcomp( &
rexp, what,
173 REG_EXTENDED | REG_NOSUB | (doCase ? 0 : REG_ICASE) );
176 regerror( result, &
rexp, buffer, 256 );
177 fprintf( stderr,
"unable to compile re \"%s\": %s\n", what, buffer );
192 int result = regexec( &
rexp, check, 0,
nullptr, 0 );
193 if ( result != 0 && result != REG_NOMATCH ) {
195 regerror( result, &
rexp, buffer, 256 );
196 fprintf( stderr,
"unable to execute re \"%s\"\n+ on line \"%s\": %s\n",
197 data, check, buffer );
200 return ( result == 0 );
216 unsigned size = strlen(start)+1;
217 va_start( ap, start );
218 while ( (s=va_arg(ap,
const char*)) !=
nullptr )
223 char* result =
new char[
size];
224 if ( result ==
nullptr ) {
225 perror(
"string memory allocation" );
230 strcpy( result, start );
231 va_start( ap, start );
232 while ( (s=va_arg(ap,
const char*)) !=
nullptr ) strcat( result, s );
245 if ( strlen(s) != testlen )
return false;
248 while ( i<testlen && isxdigit(s[i]) )
257 return printf(
"%s %3d %8ld %s\n", fn,
code,
size, url );
264 static const char hexdigit[] =
"0123456789ABCDEF";
266 const SquidTLV* findings =
nullptr;
269 unsigned char* s = (
unsigned char*) findings->data;
270 for (
int j=0; j<16; ++j, ++s ) {
271 md5[j*2+0] = hexdigit[ *s >> 4 ];
272 md5[j*2+1] = hexdigit[ *s & 15 ];
276 snprintf( md5,
sizeof(md5),
"%-32s",
"(no_md5_data_available)" );
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 StoreMetaStdLFS temp;
290 memcpy( &temp, findings->data,
sizeof(StoreMetaStdLFS) );
291 snprintf( timeb,
sizeof(timeb),
"%08lx %08lx %08lx %08lx %04x %5hu ",
292 (
unsigned long)temp.timestamp, (
unsigned long)temp.lastref,
293 (
unsigned long)temp.expires, (
unsigned long)temp.lastmod, temp.flags, temp.refcount );
295 unsigned long ul = ULONG_MAX;
296 unsigned short hu = 0;
297 snprintf( timeb,
sizeof(timeb),
"%08lx %08lx %08lx %08lx %04x %5d ", ul, ul, ul, ul, 0, hu);
302 return printf(
"%s %3d %8ld %s %s %s\n",
303 fn,
code,
size, md5, timeb, findings->data );
305 return printf(
"%s %3d %8ld %s %s strange_file\n",
316 const char *fn,
const char *url,
const SquidMetaList &meta)
331 static const char* schablone =
"PURGE %s HTTP/1.0\r\nAccept: */*\r\n\r\n";
333 long size = ( fstat(fd,&st) == -1 ? -1 : long(st.st_size - metasize) );
343 unsigned long bufsize = strlen(url) + strlen(schablone) + 4;
344 char* buffer =
new char[bufsize];
346 snprintf( buffer, bufsize, schablone, url );
349 fprintf( stderr,
"unable to connect to server: %s\n",
strerror(errno) );
354 int content_size = strlen(buffer);
355 if ( write(
sockfd, buffer, content_size ) != content_size ) {
357 fprintf( stderr,
"unable to talk to server: %s\n",
strerror(errno) );
362 memset( buffer+8, 0, 4 );
363 int readLen = read(
sockfd, buffer, bufsize);
366 fprintf( stderr,
"unable to read answer: %s\n",
strerror(errno) );
371 buffer[bufsize-1] =
'\0';
373 int64_t s = strtol(buffer+8,
nullptr,10);
374 if (s > 0 && s < 1000)
378 fprintf( stderr,
"invalid HTTP status in reply: %s\n", buffer+8);
391 if ( unlink(fn) == -1 )
394 fprintf( stderr,
"WARNING: unable to unlink %s: %s\n",
408 static const size_t addon =
sizeof(
unsigned char) +
sizeof(
unsigned int);
411 if (
debugFlag & 0x01 ) fprintf( stderr,
"# [3] %s\n", fn );
412 int fd = open( fn, O_RDONLY );
416 if ( readLen > 60 ) {
421 unsigned int datastart;
422 memcpy( &datastart,
::linebuffer + 1,
sizeof(
unsigned int) );
425 fputs(
"WARNING: Using a truncated URL string.\n", stderr );
432 unsigned int offset = addon;
434 while ( offset + addon <= datastart ) {
435 unsigned int size = 0;
436 memcpy( &
size,
linebuffer+offset+
sizeof(
char),
sizeof(
unsigned int) );
438 fputs(
"WARNING: file corruption detected. 32-bit overflow in size field.\n", stderr);
441 if (
size+offset > readLen) {
442 fputs(
"WARNING: Partial meta data loaded.\n", stderr );
445 meta.append( SquidMetaType(*(
linebuffer+offset)),
447 offset += ( addon +
size );
454 if ( list ==
nullptr )
455 flag =
action( fd, datastart, fn, (
char*) urlmeta->data, meta );
458 while (
head !=
nullptr ) {
459 if (
head->match( (
char*) urlmeta->data ) )
break;
462 if (
head !=
nullptr )
463 flag =
action( fd, datastart, fn, (
char*) urlmeta->data, meta );
472 long size = ( fstat(fd,&st) == -1 ? -1 : st.st_size );
478 if ( unlink(fn) == -1 )
481 fprintf( stderr,
"WARNING: unable to unlink %s: %s\n",
489 fprintf( stderr,
"WARNING: open \"%s\": %s\n", fn,
strerror(errno) );
505 fprintf( stderr,
"# [2] %s\n", directory );
507 DIR* dir = opendir( directory );
508 if ( dir ==
nullptr ) {
509 fprintf( stderr,
"unable to open directory \"%s\": %s\n",
516 static char alivelist[4][3] = {
"\\\b",
"|\b",
"/\b",
"-\b" };
517 static unsigned short alivecount = 0;
518 const int write_success = write(STDOUT_FILENO, alivelist[alivecount++ & 3], 2);
519 assert(write_success == 2);
523 while ( (entry=readdir(dir)) && flag ) {
525 char* name =
concat( directory,
"/", entry->d_name, 0 );
526 flag =
match( name, list );
548 fprintf( stderr,
"# [%d] %s\n", (level ? 1 : 0), dirname );
550 DIR* dir = opendir( dirname );
551 if ( dir ==
nullptr ) {
552 fprintf( stderr,
"unable to open directory \"%s\": %s\n",
558 while ( (entry=readdir(dir)) && flag ) {
559 if ( strlen(entry->d_name) == 2 &&
560 isxdigit(entry->d_name[0]) &&
561 isxdigit(entry->d_name[1]) ) {
562 char* name =
concat( dirname,
"/", entry->d_name, 0 );
580 if ( strchr( arg,
'.' ) !=
nullptr )
return -1;
584 unsigned long result = strtoul( arg, &
errstr, 0 );
585 if ( result < 65536 &&
errstr != arg )
return htons(result);
594 printf(
"\nUsage:\t%s\t[-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] "
595 "[-p h[:p]]\n\t\t[-P #] [-s] [-v] [-C dir [-H]] [-n]\n\n",
598 " -a\tdisplay a little rotating thingy to indicate that I am alive (tty only).\n"
599 " -c c\tsquid.conf location, default \"%s\".\n"
600 " -C dir\tbase directory for content extraction (copy-out mode).\n"
601 " -d l\tdebug level, an OR mask of different debug options.\n"
602 " -e re\tsingle regular expression per -e instance (use quotes!).\n"
603 " -E re\tsingle case sensitive regular expression like -e.\n"
604 " -f fn\tname of textfile containing one regular expression per line.\n"
605 " -F fn\tname of textfile like -f containing case sensitive REs.\n"
606 " -H\tprepend HTTP reply header to destination files in copy-out mode.\n"
607 " -n\tdo not fork() when using more than one cache_dir.\n"
608 " -p h:p\tcache runs on host h and optional port p, default is %s:%u.\n"
609 " -P #\tif 0, just print matches; otherwise OR the following purge modes:\n"
610 "\t 0x01 really send PURGE to the cache.\n"
611 "\t 0x02 remove all caches files reported as 404 (not found).\n"
612 "\t 0x04 remove all weird (inaccessible or too small) cache files.\n"
613 "\t0 and 1 are recommended - slow rebuild your cache with other modes.\n"
614 " -s\tshow all options after option parsing, but before really starting.\n"
615 " -v\tshow more information about the file, e.g. MD5, timestamps and flags.\n"
622 char *&conffile,
char *©DirPath,
623 struct in_addr &serverHostIp,
unsigned short &serverHostPort)
629 int option,
port, showme = 0;
634 if ( (ptr = strrchr(argv[0],
'/')) ==
nullptr )
643 while ( (option =
getopt( argc, argv,
"ac:C:d:E:e:F:f:Hnp:P:sv" )) != -1 ) {
650 if ( copyDirPath )
xfree( (
void*) copyDirPath );
657 fprintf( stderr,
"%c requires a regex pattern argument!\n", option );
660 if ( *conffile )
xfree((
void*) conffile);
667 fprintf( stderr,
"%c expects a mask parameter. Debug disabled.\n", option );
676 fprintf( stderr,
"%c requires a regex pattern argument!\n", option );
679 if (
head ==
nullptr )
689 fprintf( stderr,
"%c requires a filename argument!\n", option );
692 if ( (rfile = fopen(
optarg,
"r" )) !=
nullptr ) {
693 unsigned long lineno = 0;
696 while ( fgets( line,
LINESIZE, rfile ) !=
nullptr ) {
698 int len = strlen(line)-1;
700 fprintf( stderr,
"%s:%lu: line too long, sorry.\n",
706 while ( len > 0 && ( line[len] ==
'\n' || line[len] ==
'\r' ) ) {
712 if (
head ==
nullptr ) tail =
head =
new REList(line,option==
'F');
720 fprintf( stderr,
"unable to open %s: %s\n",
optarg,
strerror(errno));
731 fprintf( stderr,
"%c requires a port argument!\n", option );
734 colon = strchr(
optarg,
':' );
735 if ( colon ==
nullptr ) {
744 fprintf( stderr,
"unable to resolve host %s!\n",
optarg );
749 serverHostPort =
port;
756 fprintf( stderr,
"unable to resolve host %s!\n",
optarg );
760 fprintf( stderr,
"unable to resolve port %s!\n", colon );
767 fprintf( stderr,
"%c requires a mode argument!\n", option );
787 if (
head ==
nullptr ) {
788 fputs(
"There was no regular expression defined. If you intend\n", stderr );
789 fputs(
"to match all possible URLs, use \"-e .\" instead.\n", stderr );
797 if ( copyDirPath && *copyDirPath )
802 printf(
"#\n# Currently active values for %s:\n",
804 printf(
"# Debug level : " );
806 else printf(
"production level" );
807 printf(
" + %s mode",
::no_fork ?
"linear" :
"parallel" );
808 puts(
::verbose ?
" + extra verbosity" :
"" );
810 printf(
"# Copy-out directory: %s ",
811 copyDirPath ? copyDirPath :
"copy-out mode disabled" );
813 printf(
"(%s HTTP header)\n",
::envelope ?
"prepend" :
"no" );
817 printf(
"# Squid config file : %s\n", conffile );
818 printf(
"# Cacheserveraddress: %s:%u\n",
819 inet_ntoa( serverHostIp ), ntohs( serverHostPort ) );
821 printf(
"# Regular expression: " );
824 for ( tail =
head; tail !=
nullptr; tail = tail->
next ) {
826 printf(
"#%22u", count );
827#if defined(LINUX) && putc==_IO_putc
834 printf(
" \"%s\"\n", tail->
data );
850 "WARNING! Caches files were removed. Please shut down your cache, remove\n"
851 "your swap.state files and restart your cache again, i.e. effictively do\n"
852 "a slow rebuild your cache! Otherwise your squid *will* choke!\n", stderr );
860 if ( getpid() == getpgrp() ) kill( -getpgrp(), signo );
875 if ( setvbuf( fp,
nullptr, _IOLBF, 0 ) == 0 ) {
880 fprintf( stderr,
"unable to make \"%s\" line buffered: %s\n",
891 char* conffile =
xstrdup(DEFAULT_CONFIG_FILE);
894 fprintf( stderr,
"unable to resolve host %s!\n",
DEFAULTHOST );
903 puts(
"### Use at your own risk! No guarantees whatsoever. You were warned. ###");
908 if ( atexit(
exiter ) != 0 ||
912 perror(
"unable to install signal/exit function" );
917 CacheDirVector cdv(0);
925 for ( CacheDirVector::iterator i = cdv.begin(); i != cdv.end(); ++i ) {
928 fprintf( stderr,
"program terminated due to error: %s",
930 xfree((
void*) i->base);
934 pid_t* child =
new pid_t[ cdv.size() ];
941 if ( setpgid(getpid(), getpid()) != 0 ) {
942 perror(
"unable to set process group leader" );
948 puts(
"# i-am-alive flag incompatible with fork mode, resetting" );
952 for (
size_t i=0; i < cdv.size(); ++i ) {
953 if ( getpid() == getpgrp() ) {
955 if ( (child[i]=fork()) < 0 ) {
957 perror(
"unable to fork" );
958 kill( -getpgrp(), SIGTERM );
960 }
else if ( child[i] == 0 ) {
964 fprintf( stderr,
"program terminated due to error: %s\n",
966 xfree((
void*) cdv[i].base);
970 if (
::debugFlag ) printf(
"forked child %d\n", (
int) child[i] );
978 for (
size_t i=0; i < cdv.size(); ++i ) {
979 while ( (temp=waitpid( (pid_t)-1, &status, 0 )) == -1 )
980 if ( errno == EINTR )
continue;
981 if (
::debugFlag ) printf(
"collected child %d\n", (
int) temp );
986 fprintf( stderr,
"no cache_dir or error accessing \"%s\"\n", conffile );
991 xfree((
void*) conffile);
squidaio_request_t * head
int readConfigFile(CacheDirVector &cachedir, const char *fn, FILE *debug)
int convertHostname(const char *host, in_addr &dst)
int convertPortname(const char *port, unsigned short &dst)
int assert_copydir(const char *copydir)
bool copy_out(size_t filesize, size_t metasize, unsigned debug, const char *fn, const char *url, const char *copydir, bool copyHdr)
int getopt(int nargc, char *const *nargv, const char *ostr)
void psignal(int sig, const char *msg)
static int checkForPortOnly(const char *arg)
int main(int argc, char *argv[])
static int makelinebuffered(FILE *fp, const char *fn=nullptr)
static void handler(int signo)
static uint32_t debugFlag
static int log_extended(const char *fn, int code, long size, const SquidMetaList *meta)
static bool filelevel(const char *directory, const REList *list)
static bool dirlevel(const char *dirname, const REList *list, bool level=false)
static bool isxstring(const char *s, size_t testlen)
static char * concat(const char *start,...)
volatile sig_atomic_t term_flag
static bool action(int fd, size_t metasize, const char *fn, const char *url, const SquidMetaList &meta)
static unsigned short serverPort
static const char * programname
static bool match(const char *fn, const REList *list)
int log_output(const char *fn, int code, long size, const char *url)
static void parseCommandline(int argc, char *argv[], REList *&head, char *&conffile, char *©DirPath, struct in_addr &serverHostIp, unsigned short &serverHostPort)
static unsigned purgeMode
static struct in_addr serverHost
SigFunc * Signal(int signo, SigFunc *newhandler, bool doInterrupt)
int connectTo(struct in_addr host, unsigned short port, bool nodelay, int sendBufferSize, int recvBufferSize)
REList(const char *what, bool doCase)
bool match(const char *check) const
struct squidaio_request_t * next