-//
-// $Id: purge.cc,v 1.17 2000/09/21 10:59:53 cached Exp $
-//
-// Author: Jens-S. Vöckler <voeckler@rvs.uni-hannover.de>
+// Author: Jens-S. V?ckler <voeckler@rvs.uni-hannover.de>
//
// File: purge.cc
// Wed Jan 13 1999
//
// (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme
-// Universität Hannover, Germany
+// 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,
// 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 ***
//
// 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 "squid.h"
+#include "util.h"
-#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <dirent.h>
-#include <ctype.h>
#include <string.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
-#if defined(HAS_PSIGNAL) && !defined(LINUX) && !defined(FREEBSD)
+#if HAVE_SIGINFO_H
#include <siginfo.h>
-#endif // HAS_PSIGNAL
+#endif
#include <netinet/in.h>
-#include <netinet/tcp.h> // TCP_NODELAY
+#include <netinet/tcp.h>
#include <arpa/inet.h>
-#include <netdb.h> // gethostbyname()
-#include <regex.h>
+#include <netdb.h>
+#include "conffile.hh"
#include "convert.hh"
-#include "socket.hh"
+#include "copyout.hh"
#include "signal.hh"
+#include "socket.hh"
#include "squid-tlv.hh"
-#include "copyout.hh"
-#include "conffile.hh"
#ifndef DEFAULTHOST
#define DEFAULTHOST "localhost"
volatile sig_atomic_t term_flag = 0; // 'terminate' is a gcc 2.8.x internal...
char* linebuffer = 0;
-size_t buffersize = 16834;
+size_t buffersize = 128*1024;
static char* copydir = 0;
-static unsigned debug = 0;
+static uint32_t debugFlag = 0;
static unsigned purgeMode = 0;
static bool iamalive = false;
static bool reminder = 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 $";
// ----------------------------------------------------------------------
};
REList::REList( const char* what, bool doCase )
- :next(0),data(strdup(what))
+ :next(0),data(xstrdup(what))
{
int result = regcomp( &rexp, what,
REG_EXTENDED | REG_NOSUB | (doCase ? 0 : REG_ICASE) );
REList::~REList()
{
if ( next ) delete next;
- if ( data ) free((void*) data);
+ if ( data ) xfree((void*) data);
regfree(&rexp);
}
// 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 : "");
+ while ( (s=va_arg(ap,const char*)) != NULL )
+ size += strlen(s);
va_end(ap);
// allocate
if ( strlen(s) != testlen ) return false;
size_t i=0;
- while ( i<testlen && isxdigit(s[i]) ) i++;
+ while ( i<testlen && isxdigit(s[i]) )
+ ++i;
return (i==testlen);
}
if ( meta && (findings = meta->search( STORE_META_KEY_MD5 )) ) {
unsigned char* s = (unsigned char*) findings->data;
- for ( int j=0; j<16; j++, s++ ) {
+ 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)" );
+ snprintf( md5, sizeof(md5), "%-32s", "(no_md5_data_available)" );
}
char timeb[64];
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 );
+ snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5hu ",
+ (unsigned long)temp.timestamp, (unsigned long)temp.lastref,
+ (unsigned long)temp.expires, (unsigned long)temp.lastmod, temp.flags, temp.refcount );
} else if ( meta && (findings = meta->search( STORE_META_STD_LFS )) ) {
StoreMetaStdLFS 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 );
+ snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5hu ",
+ (unsigned long)temp.timestamp, (unsigned long)temp.lastref,
+ (unsigned long)temp.expires, (unsigned long)temp.lastmod, temp.flags, temp.refcount );
} else {
- sprintf( timeb, "%08x %08x %08x %08x %04x %5hu ", -1, -1, -1, -1, 0, 0 );
+ unsigned long ul = ULONG_MAX; // Match type of StoreMetaTLV fields
+ unsigned short hu = 0; // Match type of StoreMetaTLV refcount fields
+ snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5d ", ul, ul, ul, ul, 0, hu);
}
// make sure that there is just one printf()
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,
+ copy_out( st.st_size, metasize, ::debugFlag,
fn, url, ::copydir, ::envelope );
// do we need to PURGE the file, yes, if purgemode bit#0 was set.
+ int status = 0;
if ( ::purgeMode & 0x01 ) {
unsigned long bufsize = strlen(url) + strlen(schablone) + 4;
char* buffer = new char[bufsize];
- sprintf( buffer, schablone, url );
+ snprintf( buffer, bufsize, schablone, url );
int sockfd = connectTo( serverHost, serverPort, true );
if ( sockfd == -1 ) {
fprintf( stderr, "unable to connect to server: %s\n", strerror(errno) );
return false;
}
- int size = strlen(buffer);
- if ( write( sockfd, buffer, size ) != size ) {
+ int content_size = strlen(buffer);
+ if ( write( sockfd, buffer, content_size ) != content_size ) {
// error while talking to squid
fprintf( stderr, "unable to talk to server: %s\n", strerror(errno) );
close(sockfd);
return false;
}
memset( buffer+8, 0, 4 );
- if ( read( sockfd, buffer, bufsize ) < 1 ) {
+ int readLen = read(sockfd, buffer, bufsize);
+ if (readLen < 1) {
// error while reading squid's answer
fprintf( stderr, "unable to read answer: %s\n", strerror(errno) );
close(sockfd);
delete[] buffer;
return false;
}
+ buffer[bufsize-1] = '\0';
close(sockfd);
- status = strtol(buffer+8,0,10);
+ int64_t s = strtol(buffer+8,0,10);
+ if (s > 0 && s < 1000)
+ status = s;
+ else {
+ // error while reading squid's answer
+ fprintf( stderr, "invalid HTTP status in reply: %s\n", buffer+8);
+ }
delete[] buffer;
}
static const size_t addon = sizeof(unsigned char) + sizeof(unsigned int);
bool flag = true;
- if ( debug & 0x01 ) fprintf( stderr, "# [3] %s\n", fn );
+ if ( debugFlag & 0x01 ) fprintf( stderr, "# [3] %s\n", fn );
int fd = open( fn, O_RDONLY );
if ( fd != -1 ) {
- if ( read(fd,::linebuffer,::buffersize-1) > 60 ) {
+ memset(::linebuffer, 0, ::buffersize);
+ size_t readLen = read(fd,::linebuffer,::buffersize-1);
+ if ( readLen > 60 ) {
::linebuffer[ ::buffersize-1 ] = '\0'; // force-terminate string
// check the offset into the start of object data. The offset is
while ( offset + addon <= datastart ) {
unsigned int size = 0;
memcpy( &size, linebuffer+offset+sizeof(char), sizeof(unsigned int) );
+ if (size+offset < size) {
+ fputs("WARNING: file corruption detected. 32-bit overflow in size field.\n", stderr);
+ break;
+ }
+ if (size+offset > readLen) {
+ fputs( "WARNING: Partial meta data loaded.\n", stderr );
+ break;
+ }
meta.append( SquidMetaType(*(linebuffer+offset)),
size, linebuffer+offset+addon );
offset += ( addon + size );
// list (IN): list of rexps to match URLs against
// returns: true, if every subdir && action was successful.
{
- struct dirent* entry;
- if ( debug & 0x01 )
+ dirent_t * entry;
+ if ( debugFlag & 0x01 )
fprintf( stderr, "# [2] %s\n", directory );
DIR* dir = opendir( directory );
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 );
+ const int write_success = write(STDOUT_FILENO, alivelist[alivecount++ & 3], 2);
+ assert(write_success == 2);
}
bool flag = true;
// returns: true, if every subdir && action was successful.
// warning: this function is once-recursive, no deeper.
{
- struct dirent* entry;
- if ( debug & 0x01 )
+ dirent_t* entry;
+ if ( debugFlag & 0x01 )
fprintf( stderr, "# [%d] %s\n", (level ? 1 : 0), dirname );
DIR* dir = opendir( dirname );
}
int
-checkForPortOnly( const char* optarg )
+checkForPortOnly( const char* arg )
// 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 ( strchr( arg, '.' ) != 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);
+ unsigned long result = strtoul( arg, &errstr, 0 );
+ if ( result < 65536 && errstr != arg ) return htons(result);
#if 0
// one last try, test for a symbolical service name
- struct servent* service = getservbyname( optarg, "tcp" );
+ struct servent* service = getservbyname( arg, "tcp" );
return service ? service->s_port : -1;
#else
return -1;
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] "
+ printf( "\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 );
+ ::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"
+ " -d l\tdebug level, an OR mask 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"
void
parseCommandline( int argc, char* argv[], REList*& head,
- char*& conffile, char*& copydir,
- struct in_addr& serverHost, unsigned short& serverPort )
+ char*& conffile, char*& copyDirPath,
+ struct in_addr& serverHostIp, unsigned short& serverHostPort )
// paramtr: argc: see ::main().
// argv: see ::main().
// returns: Does terminate the program on errors!
FILE* rfile;
// program basename
- if ( (ptr = strrchr(argv[0],'/')) == NULL ) ptr=argv[0];
- else ptr++;
+ if ( (ptr = strrchr(argv[0],'/')) == NULL )
+ ptr=argv[0];
+ else
+ ++ptr;
::programname = ptr;
// extract commandline parameters
break;
case 'C':
if ( optarg && *optarg ) {
- if ( copydir ) free( (void*) copydir );
- assert( (copydir = strdup(optarg)) );
+ if ( copyDirPath ) xfree( (void*) copyDirPath );
+ copyDirPath = xstrdup(optarg);
+ assert(copyDirPath);
}
break;
case 'c':
- if ( optarg && *optarg ) {
- if ( *conffile ) free((void*) conffile );
- assert( (conffile = strdup(optarg)) );
+ if ( !optarg || !*optarg ) {
+ fprintf( stderr, "%c requires a regex pattern argument!\n", option );
+ exit(1);
}
+ if ( *conffile ) xfree((void*) conffile);
+ conffile = xstrdup(optarg);
+ assert(conffile);
break;
case 'd':
- ::debug = strtoul( optarg, 0, 0 );
+ if ( !optarg || !*optarg ) {
+ fprintf( stderr, "%c expects a mask parameter. Debug disabled.\n", option );
+ ::debugFlag = 0;
+ } else
+ ::debugFlag = (strtoul(optarg, NULL, 0) & 0xFFFFFFFF);
break;
case 'E':
case 'e':
- if ( head == 0 ) tail = head = new REList( optarg, option=='E' );
+ if ( !optarg || !*optarg ) {
+ fprintf( stderr, "%c requires a regex pattern argument!\n", option );
+ exit(1);
+ }
+ 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 ( !optarg || !*optarg ) {
+ fprintf( stderr, "%c requires a filename argument!\n", option );
+ exit(1);
+ }
if ( (rfile = fopen( optarg, "r" )) != NULL ) {
unsigned long lineno = 0;
#define LINESIZE 512
char line[LINESIZE];
while ( fgets( line, LINESIZE, rfile ) != NULL ) {
- lineno++;
+ ++lineno;
int len = strlen(line)-1;
if ( len+2 >= LINESIZE ) {
fprintf( stderr, "%s:%lu: line too long, sorry.\n",
}
// remove trailing line breaks
- while ( len > 0 && ( line[len] == '\n' || line[len] == '\r' ) )
- line[len--] = '\0';
+ while ( len > 0 && ( line[len] == '\n' || line[len] == '\r' ) ) {
+ line[len] = '\0';
+ --len;
+ }
// insert into list of expressions
if ( head == 0 ) tail = head = new REList(line,option=='F');
::no_fork = ! ::no_fork;
break;
case 'p':
+ if ( !optarg || !*optarg ) {
+ fprintf( stderr, "%c requires a port argument!\n", option );
+ exit(1);
+ }
colon = strchr( optarg, ':' );
if ( colon == 0 ) {
// no colon, only look at host
port = checkForPortOnly( optarg );
if ( port == -1 ) {
// assume that main() did set the default port
- if ( convertHostname(optarg,serverHost) == -1 ) {
+ if ( convertHostname(optarg,serverHostIp) == -1 ) {
fprintf( stderr, "unable to resolve host %s!\n", optarg );
exit(1);
}
} else {
// assume that main() did set the default host
- serverPort = port;
+ serverHostPort = port;
}
} else {
// colon used, port is extra
- *colon++ = 0;
- if ( convertHostname(optarg,serverHost) == -1 ) {
+ *colon = 0;
+ ++colon;
+ if ( convertHostname(optarg,serverHostIp) == -1 ) {
fprintf( stderr, "unable to resolve host %s!\n", optarg );
exit(1);
}
- if ( convertPortname(colon,serverPort) == -1 ) {
+ if ( convertPortname(colon,serverHostPort) == -1 ) {
fprintf( stderr, "unable to resolve port %s!\n", colon );
exit(1);
}
}
break;
case 'P':
+ if ( !optarg || !*optarg ) {
+ fprintf( stderr, "%c requires a mode argument!\n", option );
+ exit(1);
+ }
::purgeMode = ( strtol( optarg, 0, 0 ) & 0x07 );
break;
case 's':
}
// adjust
- if ( ! isatty(fileno(stdout)) || (::debug & 0x01) ) ::iamalive = false;
+ if ( ! isatty(fileno(stdout)) || (::debugFlag & 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 );
assert( head != 0 );
// make sure that the copy out directory is there and accessible
- if ( copydir && *copydir )
- if ( assert_copydir( copydir ) != 0 ) exit(1);
+ if ( copyDirPath && *copyDirPath )
+ if ( assert_copydir( copyDirPath ) != 0 ) exit(1);
// show results
if ( showme ) {
- printf( "#\n# Currently active values for %s:\n# %s\n",
- ::programname, ::RCS_ID );
+ printf( "#\n# Currently active values for %s:\n",
+ ::programname);
printf( "# Debug level : " );
- if ( ::debug ) printf( "%#6.4hx", ::debug );
+ if ( ::debugFlag ) printf( "%#6.4x", ::debugFlag );
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 )
+ copyDirPath ? copyDirPath : "copy-out mode disabled" );
+ if ( copyDirPath )
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 ) );
+ inet_ntoa( serverHostIp ), ntohs( serverHostPort ) );
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 ( count++ )
+ printf( "#%22u", count );
#if defined(LINUX) && putc==_IO_putc
// I HATE BROKEN LINUX HEADERS!
// purge.o(.text+0x1040): undefined reference to `_IO_putc'
{
// setup variables
REList* list = 0;
- char* conffile = strdup( DEFAULT_SQUID_CONF );
+ char* conffile = xstrdup( DEFAULT_SQUID_CONF );
serverPort = htons(DEFAULTPORT);
if ( convertHostname(DEFAULTHOST,serverHost) == -1 ) {
fprintf( stderr, "unable to resolve host %s!\n", DEFAULTHOST );
// try to read squid.conf file to determine all cache_dir locations
CacheDirVector cdv(0);
- if ( readConfigFile( cdv, conffile, debug ? stderr : 0 ) > 0 ) {
+ if ( readConfigFile( cdv, conffile, debugFlag ? 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 ( ! dirlevel(i->base,list) )
fprintf( stderr, "program terminated due to error: %s",
strerror(errno) );
- free((void*) i->base);
+ xfree((void*) i->base);
}
} else {
// parallel mode, all cache_dir in parallel
::iamalive = false;
}
- for ( int i=0; i < cdv.size(); ++i ) {
+ for ( size_t i=0; i < cdv.size(); ++i ) {
if ( getpid() == getpgrp() ) {
// only parent == group leader may fork off new processes
if ( (child[i]=fork()) < 0 ) {
if ( ! dirlevel(cdv[i].base,list) )
fprintf( stderr, "program terminated due to error: %s\n",
strerror(errno) );
- free((void*) cdv[i].base);
+ xfree((void*) cdv[i].base);
return 0;
} else {
// parent mode
- if ( ::debug ) printf( "forked child %d\n", (int) child[i] );
+ if ( ::debugFlag ) printf( "forked child %d\n", (int) child[i] );
}
}
}
// collect the garbase
pid_t temp;
int status;
- for ( int i=0; i < cdv.size(); ++i ) {
+ for ( size_t 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 );
+ if ( ::debugFlag ) printf( "collected child %d\n", (int) temp );
}
delete[] child;
}
}
// clean up
- if ( copydir ) free( (void*) copydir );
- free((void*) conffile);
+ if ( copydir ) xfree( (void*) copydir );
+ xfree((void*) conffile);
delete list;
return 0;
}