-//
-// $Id$
-//
// Author: Jens-S. V?ckler <voeckler@rvs.uni-hannover.de>
//
// File: purge.cc
// Initial revision
//
//
-#if defined(__GNUC__) || defined(__GNUG__)
-#pragma implementation
-#endif
-
-#include "config.h"
-// for xstrdup
+#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>
#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> //comes via compat.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 debugFlag = 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$";
// ----------------------------------------------------------------------
// 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 ];
}
(unsigned long)temp.timestamp, (unsigned long)temp.lastref,
(unsigned long)temp.expires, (unsigned long)temp.lastmod, temp.flags, temp.refcount );
} else {
- snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5hu ", (unsigned long)-1, (unsigned long)-1, (unsigned long)-1, (unsigned long)-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 )
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];
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;
}
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 );
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;
}
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 ) xfree( (void*) copydir );
- assert( (copydir = xstrdup(optarg)) );
+ if ( copyDirPath ) xfree( (void*) copyDirPath );
+ copyDirPath = xstrdup(optarg);
+ assert(copyDirPath);
}
break;
case 'c':
- if ( optarg && *optarg ) {
- if ( *conffile ) xfree((void*) conffile );
- assert( (conffile = xstrdup(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':
- ::debugFlag = 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':
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 ( ::debugFlag ) printf( "%#6.4hx", ::debugFlag );
+ 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'