]>
Commit | Line | Data |
---|---|---|
5f623035 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
5f623035 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
0b96a9b3 | 9 | // Author: Jens-S. V?ckler <voeckler@rvs.uni-hannover.de> |
eb1f6bfa AJ |
10 | // |
11 | // File: purge.cc | |
12 | // Wed Jan 13 1999 | |
13 | // | |
14 | // (c) 1999 Lehrgebiet Rechnernetze und Verteilte Systeme | |
0b96a9b3 | 15 | // Universit?t Hannover, Germany |
eb1f6bfa AJ |
16 | // |
17 | // Permission to use, copy, modify, distribute, and sell this software | |
18 | // and its documentation for any purpose is hereby granted without fee, | |
19 | // provided that (i) the above copyright notices and this permission | |
20 | // notice appear in all copies of the software and related documentation, | |
21 | // and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte | |
22 | // Systeme and the University of Hannover may not be used in any | |
23 | // advertising or publicity relating to the software without the | |
24 | // specific, prior written permission of Lehrgebiet Rechnernetze und | |
25 | // Verteilte Systeme and the University of Hannover. | |
26 | // | |
27 | // THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, | |
28 | // EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY | |
29 | // WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. | |
30 | // | |
31 | // IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR | |
32 | // THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL, | |
33 | // INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES | |
34 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT | |
35 | // ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, | |
36 | // ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
37 | // SOFTWARE. | |
38 | // | |
eb1f6bfa AJ |
39 | // Revision 1.17 2000/09/21 10:59:53 cached |
40 | // *** empty log message *** | |
41 | // | |
42 | // Revision 1.16 2000/09/21 09:45:18 cached | |
43 | // Fixed some small bugs. | |
44 | // | |
45 | // Revision 1.15 2000/09/21 09:05:56 cached | |
46 | // added multi cache_dir support, thus changing -c cmdline option. | |
47 | // modified file reading to support /dev/fd/0 reading for non-disclosed items. | |
48 | // | |
49 | // Revision 1.14 2000/06/20 09:43:01 voeckler | |
50 | // added FreeBSD related fixes and support. | |
51 | // | |
52 | // Revision 1.13 2000/03/29 08:12:21 voeckler | |
53 | // fixed wrong header file. | |
54 | // | |
55 | // Revision 1.12 2000/03/29 07:54:41 voeckler | |
56 | // added mechanism to give a port specification precedence over a host | |
57 | // specificiation with the -p option and not colon. | |
58 | // | |
59 | // Revision 1.11 1999/06/18 13:18:28 voeckler | |
60 | // added refcount, fixed missing LF in -s output. | |
61 | // | |
62 | // Revision 1.10 1999/06/16 13:06:05 voeckler | |
63 | // reversed meaning of -M flag. | |
64 | // | |
65 | // Revision 1.9 1999/06/15 21:11:53 voeckler | |
66 | // added extended logging feature which extract the squid meta data available | |
67 | // within the disk files. moved the content extraction and squid meta data | |
68 | // handling parts into separate files. added options for copy-out and verbose. | |
69 | // | |
70 | // Revision 1.8 1999/06/14 20:14:46 voeckler | |
71 | // intermediate version when adding understanding about the way | |
72 | // Squid does log the metadata into the file. | |
73 | // | |
74 | // Revision 1.7 1999/01/23 21:01:10 root | |
75 | // stumbled over libc5 header/lib inconsistency bug.... | |
76 | // | |
77 | // Revision 1.6 1999/01/23 20:47:54 root | |
78 | // added Linux specifics for psignal... | |
79 | // Hope this helps. | |
80 | // | |
81 | // Revision 1.5 1999/01/20 09:48:12 voeckler | |
82 | // added warning as first line of output. | |
83 | // | |
84 | // Revision 1.4 1999/01/19 11:53:49 voeckler | |
85 | // added psignal() from <siginfo.h> handling. | |
86 | // | |
87 | // Revision 1.3 1999/01/19 11:00:50 voeckler | |
88 | // added keyboard interrupt handling, exit handling, removed C++ strings and | |
89 | // regular expression syntax in favour of less source code, added comments, | |
90 | // added a reminder to remove swap.state in case of unlinks, added IAA flag, | |
91 | // added a few assertions, changed policy to enforce the definition of at | |
92 | // least one regular expression, and catch a few signals. | |
93 | // | |
94 | // Revision 1.2 1999/01/15 23:06:28 voeckler | |
95 | // downgraded to simple C strings... | |
96 | // | |
97 | // Revision 1.1 1999/01/14 12:05:32 voeckler | |
98 | // Initial revision | |
99 | // | |
100 | // | |
f7f3304a | 101 | #include "squid.h" |
2ccf2eb2 AJ |
102 | #include "util.h" |
103 | ||
074d6a40 AJ |
104 | #include <cerrno> |
105 | #include <climits> | |
106 | #include <csignal> | |
107 | #include <cstdarg> | |
108 | #include <cstdlib> | |
109 | #include <cstring> | |
eb1f6bfa | 110 | #include <dirent.h> |
eb1f6bfa AJ |
111 | #include <sys/stat.h> |
112 | #include <sys/wait.h> | |
113 | #include <fcntl.h> | |
114 | #include <unistd.h> | |
eb1f6bfa | 115 | |
2ccf2eb2 | 116 | #if HAVE_SIGINFO_H |
eb1f6bfa | 117 | #include <siginfo.h> |
2ccf2eb2 | 118 | #endif |
eb1f6bfa AJ |
119 | |
120 | #include <netinet/in.h> | |
b0fb853f | 121 | #include <netinet/tcp.h> |
eb1f6bfa | 122 | #include <arpa/inet.h> |
582c2af2 | 123 | #include <netdb.h> |
eb1f6bfa | 124 | |
602d9612 | 125 | #include "conffile.hh" |
eb1f6bfa | 126 | #include "convert.hh" |
602d9612 | 127 | #include "copyout.hh" |
eb1f6bfa | 128 | #include "signal.hh" |
602d9612 | 129 | #include "socket.hh" |
eb1f6bfa | 130 | #include "squid-tlv.hh" |
eb1f6bfa AJ |
131 | |
132 | #ifndef DEFAULTHOST | |
133 | #define DEFAULTHOST "localhost" | |
134 | #endif // DEFAULTHOST | |
135 | ||
136 | #ifndef DEFAULTPORT | |
137 | #define DEFAULTPORT 3128 | |
138 | #endif // DEFAULTPORT | |
139 | ||
140 | volatile sig_atomic_t term_flag = 0; // 'terminate' is a gcc 2.8.x internal... | |
feec68a0 | 141 | char* linebuffer = 0; |
e65cf8d5 | 142 | size_t buffersize = 128*1024; |
eb1f6bfa | 143 | static char* copydir = 0; |
052aecef | 144 | static uint32_t debugFlag = 0; |
eb1f6bfa AJ |
145 | static unsigned purgeMode = 0; |
146 | static bool iamalive = false; | |
147 | static bool reminder = false; | |
148 | static bool verbose = false; | |
149 | static bool envelope = false; | |
150 | static bool no_fork = false; | |
151 | static const char* programname = 0; | |
eb1f6bfa AJ |
152 | |
153 | // ---------------------------------------------------------------------- | |
154 | ||
c2afddd8 AJ |
155 | struct REList { |
156 | REList( const char* what, bool doCase ); | |
157 | ~REList(); | |
158 | bool match( const char* check ) const; | |
eb1f6bfa | 159 | |
597504bf AJ |
160 | REList* next; |
161 | const char* data; | |
c2afddd8 | 162 | regex_t rexp; |
597504bf | 163 | }; |
eb1f6bfa | 164 | |
c2afddd8 AJ |
165 | REList::REList( const char* what, bool doCase ) |
166 | :next(0),data(xstrdup(what)) | |
167 | { | |
168 | int result = regcomp( &rexp, what, | |
169 | REG_EXTENDED | REG_NOSUB | (doCase ? 0 : REG_ICASE) ); | |
170 | if ( result != 0 ) { | |
171 | char buffer[256]; | |
172 | regerror( result, &rexp, buffer, 256 ); | |
173 | fprintf( stderr, "unable to compile re \"%s\": %s\n", what, buffer ); | |
24885773 | 174 | exit(EXIT_FAILURE); |
c2afddd8 AJ |
175 | } |
176 | } | |
177 | ||
178 | REList::~REList() | |
179 | { | |
180 | if ( next ) delete next; | |
181 | if ( data ) xfree((void*) data); | |
182 | regfree(&rexp); | |
183 | } | |
184 | ||
185 | bool | |
186 | REList::match( const char* check ) const | |
187 | { | |
188 | int result = regexec( &rexp, check, 0, 0, 0 ); | |
189 | if ( result != 0 && result != REG_NOMATCH ) { | |
190 | char buffer[256]; | |
191 | regerror( result, &rexp, buffer, 256 ); | |
192 | fprintf( stderr, "unable to execute re \"%s\"\n+ on line \"%s\": %s\n", | |
193 | data, check, buffer ); | |
24885773 | 194 | exit(EXIT_FAILURE); |
c2afddd8 AJ |
195 | } |
196 | return ( result == 0 ); | |
197 | } | |
198 | ||
eb1f6bfa AJ |
199 | // ---------------------------------------------------------------------- |
200 | ||
201 | char* | |
202 | concat( const char* start, ... ) | |
feec68a0 A |
203 | // purpose: concatinate an arbitrary number of C strings. |
204 | // paramtr: start (IN): first C string | |
205 | // ... (IN): further C strings, terminated with a NULL pointer | |
206 | // returns: memory allocated via new(), containing the concatinated string. | |
eb1f6bfa | 207 | { |
feec68a0 A |
208 | va_list ap; |
209 | const char* s; | |
210 | ||
211 | // first run: determine size | |
212 | unsigned size = strlen(start)+1; | |
213 | va_start( ap, start ); | |
2a9c12b3 AJ |
214 | while ( (s=va_arg(ap,const char*)) != NULL ) |
215 | size += strlen(s); | |
feec68a0 A |
216 | va_end(ap); |
217 | ||
218 | // allocate | |
219 | char* result = new char[size]; | |
220 | if ( result == 0 ) { | |
221 | perror( "string memory allocation" ); | |
24885773 | 222 | exit(EXIT_FAILURE); |
feec68a0 A |
223 | } |
224 | ||
225 | // second run: copy content | |
226 | strcpy( result, start ); | |
227 | va_start( ap, start ); | |
228 | while ( (s=va_arg(ap,const char*)) != NULL ) strcat( result, s ); | |
229 | va_end(ap); | |
230 | ||
231 | return result; | |
eb1f6bfa AJ |
232 | } |
233 | ||
234 | bool | |
235 | isxstring( const char* s, size_t testlen ) | |
feec68a0 A |
236 | // purpose: test a string for conforming to xdigit |
237 | // paramtr: s (IN): string to test | |
238 | // testlen (IN): length the string must have | |
239 | // returns: true, iff strlen(s)==testlen && all_x_chars(s), false otherwise | |
eb1f6bfa | 240 | { |
feec68a0 | 241 | if ( strlen(s) != testlen ) return false; |
eb1f6bfa | 242 | |
feec68a0 | 243 | size_t i=0; |
14942edd FC |
244 | while ( i<testlen && isxdigit(s[i]) ) |
245 | ++i; | |
feec68a0 | 246 | return (i==testlen); |
eb1f6bfa AJ |
247 | } |
248 | ||
249 | inline | |
250 | int | |
251 | log_output( const char* fn, int code, long size, const char* url ) | |
252 | { | |
feec68a0 | 253 | return printf( "%s %3d %8ld %s\n", fn, code, size, url ); |
eb1f6bfa AJ |
254 | } |
255 | ||
256 | static | |
257 | int | |
258 | log_extended( const char* fn, int code, long size, const SquidMetaList* meta ) | |
259 | { | |
feec68a0 A |
260 | static const char hexdigit[] = "0123456789ABCDEF"; |
261 | char md5[34]; | |
262 | const SquidTLV* findings = 0; | |
263 | ||
264 | if ( meta && (findings = meta->search( STORE_META_KEY_MD5 )) ) { | |
265 | unsigned char* s = (unsigned char*) findings->data; | |
cbebe602 | 266 | for ( int j=0; j<16; ++j, ++s ) { |
feec68a0 A |
267 | md5[j*2+0] = hexdigit[ *s >> 4 ]; |
268 | md5[j*2+1] = hexdigit[ *s & 15 ]; | |
269 | } | |
270 | md5[32] = '\0'; // terminate string | |
271 | } else { | |
6e2aefad | 272 | snprintf( md5, sizeof(md5), "%-32s", "(no_md5_data_available)" ); |
feec68a0 A |
273 | } |
274 | ||
2d51fa42 | 275 | char timeb[256]; |
feec68a0 A |
276 | if ( meta && (findings = meta->search( STORE_META_STD )) ) { |
277 | StoreMetaStd temp; | |
278 | // make data aligned, avoid SIGBUS on RISC machines (ARGH!) | |
279 | memcpy( &temp, findings->data, sizeof(StoreMetaStd) ); | |
6e2aefad | 280 | snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5hu ", |
8978bd9d A |
281 | (unsigned long)temp.timestamp, (unsigned long)temp.lastref, |
282 | (unsigned long)temp.expires, (unsigned long)temp.lastmod, temp.flags, temp.refcount ); | |
feec68a0 A |
283 | } else if ( meta && (findings = meta->search( STORE_META_STD_LFS )) ) { |
284 | StoreMetaStdLFS temp; | |
285 | // make data aligned, avoid SIGBUS on RISC machines (ARGH!) | |
2d51fa42 | 286 | memcpy( &temp, findings->data, sizeof(StoreMetaStdLFS) ); |
6e2aefad | 287 | snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5hu ", |
8978bd9d A |
288 | (unsigned long)temp.timestamp, (unsigned long)temp.lastref, |
289 | (unsigned long)temp.expires, (unsigned long)temp.lastmod, temp.flags, temp.refcount ); | |
feec68a0 | 290 | } else { |
8baf6ea3 | 291 | unsigned long ul = ULONG_MAX; // Match type of StoreMetaTLV fields |
d8b258a9 PW |
292 | unsigned short hu = 0; // Match type of StoreMetaTLV refcount fields |
293 | snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5d ", ul, ul, ul, ul, 0, hu); | |
feec68a0 A |
294 | } |
295 | ||
296 | // make sure that there is just one printf() | |
297 | if ( meta && (findings = meta->search( STORE_META_URL )) ) { | |
298 | return printf( "%s %3d %8ld %s %s %s\n", | |
299 | fn, code, size, md5, timeb, findings->data ); | |
300 | } else { | |
301 | return printf( "%s %3d %8ld %s %s strange_file\n", | |
302 | fn, code, size, md5, timeb ); | |
eb1f6bfa | 303 | } |
eb1f6bfa AJ |
304 | } |
305 | ||
306 | // o.k., this is pure lazyness... | |
307 | static struct in_addr serverHost; | |
308 | static unsigned short serverPort; | |
309 | ||
310 | bool | |
311 | action( int fd, size_t metasize, | |
feec68a0 A |
312 | const char* fn, const char* url, const SquidMetaList& meta ) |
313 | // purpose: if cmdline-requested, send the purge request to the cache | |
314 | // paramtr: fd (IN): open FD for the object file | |
f53969cc | 315 | // metasize (IN): offset into data portion of file (meta data size) |
feec68a0 A |
316 | // fn (IN): name of the object file |
317 | // url (IN): URL string stored in the object file | |
f53969cc | 318 | // meta (IN): list containing further meta data |
feec68a0 A |
319 | // returns: true for a successful action, false otherwise. The action |
320 | // may just print the file, send the purge request or even | |
321 | // remove unwanted files. | |
322 | // globals: ::purgeMode (IN): bit#0 set -> send purge request. | |
323 | // bit#1 set -> remove 404 object files. | |
324 | // ::serverHost (IN): cache host address | |
325 | // ::serverPort (IN): cache port number | |
eb1f6bfa | 326 | { |
feec68a0 A |
327 | static const char* schablone = "PURGE %s HTTP/1.0\r\nAccept: */*\r\n\r\n"; |
328 | struct stat st; | |
329 | long size = ( fstat(fd,&st) == -1 ? -1 : long(st.st_size - metasize) ); | |
feec68a0 A |
330 | |
331 | // if we want to copy out the file, do that first of all. | |
332 | if ( ::copydir && *copydir && size > 0 ) | |
59a09b98 | 333 | copy_out( st.st_size, metasize, ::debugFlag, |
feec68a0 A |
334 | fn, url, ::copydir, ::envelope ); |
335 | ||
336 | // do we need to PURGE the file, yes, if purgemode bit#0 was set. | |
e65cf8d5 | 337 | int status = 0; |
feec68a0 A |
338 | if ( ::purgeMode & 0x01 ) { |
339 | unsigned long bufsize = strlen(url) + strlen(schablone) + 4; | |
340 | char* buffer = new char[bufsize]; | |
341 | ||
6e2aefad | 342 | snprintf( buffer, bufsize, schablone, url ); |
feec68a0 A |
343 | int sockfd = connectTo( serverHost, serverPort, true ); |
344 | if ( sockfd == -1 ) { | |
345 | fprintf( stderr, "unable to connect to server: %s\n", strerror(errno) ); | |
346 | delete[] buffer; | |
347 | return false; | |
348 | } | |
349 | ||
9dca980d AJ |
350 | int content_size = strlen(buffer); |
351 | if ( write( sockfd, buffer, content_size ) != content_size ) { | |
feec68a0 A |
352 | // error while talking to squid |
353 | fprintf( stderr, "unable to talk to server: %s\n", strerror(errno) ); | |
354 | close(sockfd); | |
355 | delete[] buffer; | |
356 | return false; | |
357 | } | |
358 | memset( buffer+8, 0, 4 ); | |
3efeeda2 AJ |
359 | int readLen = read(sockfd, buffer, bufsize); |
360 | if (readLen < 1) { | |
feec68a0 A |
361 | // error while reading squid's answer |
362 | fprintf( stderr, "unable to read answer: %s\n", strerror(errno) ); | |
363 | close(sockfd); | |
364 | delete[] buffer; | |
365 | return false; | |
366 | } | |
3efeeda2 | 367 | buffer[bufsize-1] = '\0'; |
feec68a0 | 368 | close(sockfd); |
e65cf8d5 AJ |
369 | int64_t s = strtol(buffer+8,0,10); |
370 | if (s > 0 && s < 1000) | |
371 | status = s; | |
372 | else { | |
373 | // error while reading squid's answer | |
374 | fprintf( stderr, "invalid HTTP status in reply: %s\n", buffer+8); | |
375 | } | |
feec68a0 | 376 | delete[] buffer; |
eb1f6bfa | 377 | } |
feec68a0 A |
378 | |
379 | // log the output of our operation | |
380 | bool flag = true; | |
381 | if ( ::verbose ) flag = ( log_extended( fn, status, size, &meta ) >= 0 ); | |
382 | else flag = ( log_output( fn, status, size, url ) >= 0 ); | |
383 | ||
384 | // remove the file, if purgemode bit#1, and HTTP result status 404). | |
385 | if ( (::purgeMode & 0x02) && status == 404 ) { | |
386 | reminder = true; | |
387 | if ( unlink(fn) == -1 ) | |
388 | // error while unlinking file, this may happen due to the cache | |
389 | // unlinking a file while it is still in the readdir() cache of purge. | |
390 | fprintf( stderr, "WARNING: unable to unlink %s: %s\n", | |
391 | fn, strerror(errno) ); | |
eb1f6bfa | 392 | } |
feec68a0 A |
393 | |
394 | return flag; | |
eb1f6bfa AJ |
395 | } |
396 | ||
397 | bool | |
398 | match( const char* fn, const REList* list ) | |
feec68a0 A |
399 | // purpose: do something with the given cache content filename |
400 | // paramtr: fn (IN): filename of cache file | |
401 | // returns: true for successful action, false otherwise. | |
402 | // warning: only return false, if you want the loop to terminate! | |
eb1f6bfa | 403 | { |
feec68a0 A |
404 | static const size_t addon = sizeof(unsigned char) + sizeof(unsigned int); |
405 | bool flag = true; | |
406 | ||
59a09b98 | 407 | if ( debugFlag & 0x01 ) fprintf( stderr, "# [3] %s\n", fn ); |
feec68a0 A |
408 | int fd = open( fn, O_RDONLY ); |
409 | if ( fd != -1 ) { | |
e65cf8d5 AJ |
410 | memset(::linebuffer, 0, ::buffersize); |
411 | size_t readLen = read(fd,::linebuffer,::buffersize-1); | |
412 | if ( readLen > 60 ) { | |
feec68a0 A |
413 | ::linebuffer[ ::buffersize-1 ] = '\0'; // force-terminate string |
414 | ||
415 | // check the offset into the start of object data. The offset is | |
416 | // stored in a host endianess after the first byte. | |
417 | unsigned int datastart; | |
418 | memcpy( &datastart, ::linebuffer + 1, sizeof(unsigned int) ); | |
419 | if ( datastart > ::buffersize - addon - 1 ) { | |
420 | // check offset into server reply header (start of cache data). | |
421 | fputs( "WARNING: Using a truncated URL string.\n", stderr ); | |
422 | datastart = ::buffersize - addon - 1; | |
423 | } | |
424 | ||
425 | // NEW: Parse squid meta data, which is a kind of linked list | |
426 | // flattened out into a file byte stream. Somewhere within is | |
427 | // the URL as part of the list. First, gobble all meta data. | |
428 | unsigned int offset = addon; | |
429 | SquidMetaList meta; | |
430 | while ( offset + addon <= datastart ) { | |
431 | unsigned int size = 0; | |
432 | memcpy( &size, linebuffer+offset+sizeof(char), sizeof(unsigned int) ); | |
3efeeda2 AJ |
433 | if (size+offset < size) { |
434 | fputs("WARNING: file corruption detected. 32-bit overflow in size field.\n", stderr); | |
435 | break; | |
436 | } | |
e65cf8d5 AJ |
437 | if (size+offset > readLen) { |
438 | fputs( "WARNING: Partial meta data loaded.\n", stderr ); | |
439 | break; | |
440 | } | |
feec68a0 A |
441 | meta.append( SquidMetaType(*(linebuffer+offset)), |
442 | size, linebuffer+offset+addon ); | |
443 | offset += ( addon + size ); | |
444 | } | |
445 | ||
446 | // Now extract the key URL from the meta data. | |
447 | const SquidTLV* urlmeta = meta.search( STORE_META_URL ); | |
448 | if ( urlmeta ) { | |
449 | // found URL in meta data. Try to process the URL | |
450 | if ( list == 0 ) | |
451 | flag = action( fd, datastart, fn, (char*) urlmeta->data, meta ); | |
452 | else { | |
c2afddd8 AJ |
453 | REList* head = (REList*) list; // YUCK! |
454 | while ( head != 0 ) { | |
feec68a0 A |
455 | if ( head->match( (char*) urlmeta->data ) ) break; |
456 | head = head->next; | |
457 | } | |
c2afddd8 | 458 | if ( head != 0 ) |
feec68a0 A |
459 | flag = action( fd, datastart, fn, (char*) urlmeta->data, meta ); |
460 | else flag = true; | |
461 | } | |
462 | } | |
463 | ||
464 | // "meta" will be deleted when exiting from this block | |
465 | } else { | |
466 | // weird file, FIXME: stat() it! | |
467 | struct stat st; | |
468 | long size = ( fstat(fd,&st) == -1 ? -1 : st.st_size ); | |
469 | if ( ::verbose ) flag = ( log_extended( fn, -1, size, 0 ) >= 0 ); | |
470 | else flag = ( log_output( fn, -1, size, "strange file" ) >= 0 ); | |
471 | ||
472 | if ( (::purgeMode & 0x04) ) { | |
473 | reminder = true; | |
474 | if ( unlink(fn) == -1 ) | |
475 | // error while unlinking file, this may happen due to the cache | |
476 | // unlinking a file while it is in the readdir() cache of purge. | |
477 | fprintf( stderr, "WARNING: unable to unlink %s: %s\n", | |
478 | fn, strerror(errno) ); | |
479 | } | |
480 | } | |
481 | close(fd); | |
eb1f6bfa | 482 | } else { |
feec68a0 A |
483 | // error while opening file, this may happen due to the cache |
484 | // unlinking a file while it is still in the readdir() cache of purge. | |
485 | fprintf( stderr, "WARNING: open \"%s\": %s\n", fn, strerror(errno) ); | |
eb1f6bfa | 486 | } |
feec68a0 A |
487 | |
488 | return flag; | |
eb1f6bfa AJ |
489 | } |
490 | ||
491 | bool | |
492 | filelevel( const char* directory, const REList* list ) | |
feec68a0 A |
493 | // purpose: from given starting point, look for squid xxxxxxxx files. |
494 | // example: "/var/spool/cache/08/7F" as input, do action over files | |
495 | // paramtr: directory (IN): starting point | |
496 | // list (IN): list of rexps to match URLs against | |
497 | // returns: true, if every subdir && action was successful. | |
eb1f6bfa | 498 | { |
fb151769 | 499 | dirent_t * entry; |
59a09b98 | 500 | if ( debugFlag & 0x01 ) |
feec68a0 A |
501 | fprintf( stderr, "# [2] %s\n", directory ); |
502 | ||
503 | DIR* dir = opendir( directory ); | |
504 | if ( dir == NULL ) { | |
505 | fprintf( stderr, "unable to open directory \"%s\": %s\n", | |
506 | directory, strerror(errno) ); | |
507 | return false; | |
508 | } | |
509 | ||
510 | // display a rotating character as "i am alive" signal (slows purge). | |
511 | if ( ::iamalive ) { | |
512 | static char alivelist[4][3] = { "\\\b", "|\b", "/\b", "-\b" }; | |
513 | static unsigned short alivecount = 0; | |
7f56277d AJ |
514 | const int write_success = write(STDOUT_FILENO, alivelist[alivecount++ & 3], 2); |
515 | assert(write_success == 2); | |
feec68a0 A |
516 | } |
517 | ||
518 | bool flag = true; | |
519 | while ( (entry=readdir(dir)) && flag ) { | |
520 | if ( isxstring(entry->d_name,8) ) { | |
521 | char* name = concat( directory, "/", entry->d_name, 0 ); | |
522 | flag = match( name, list ); | |
523 | delete[] name; | |
524 | } | |
eb1f6bfa | 525 | } |
eb1f6bfa | 526 | |
feec68a0 A |
527 | closedir(dir); |
528 | return flag; | |
eb1f6bfa AJ |
529 | } |
530 | ||
531 | bool | |
532 | dirlevel( const char* dirname, const REList* list, bool level=false ) | |
feec68a0 A |
533 | // purpose: from given starting point, look for squid 00..FF directories. |
534 | // paramtr: dirname (IN): starting point | |
535 | // list (IN): list of rexps to match URLs against | |
536 | // level (IN): false==toplevel, true==1st level | |
537 | // example: "/var/spool/cache", false as input, traverse subdirs w/ action. | |
538 | // example: "/var/spool/cache/08", true as input, traverse subdirs w/ action. | |
539 | // returns: true, if every subdir && action was successful. | |
540 | // warning: this function is once-recursive, no deeper. | |
eb1f6bfa | 541 | { |
fb151769 | 542 | dirent_t* entry; |
59a09b98 | 543 | if ( debugFlag & 0x01 ) |
feec68a0 A |
544 | fprintf( stderr, "# [%d] %s\n", (level ? 1 : 0), dirname ); |
545 | ||
546 | DIR* dir = opendir( dirname ); | |
547 | if ( dir == NULL ) { | |
548 | fprintf( stderr, "unable to open directory \"%s\": %s\n", | |
549 | dirname, strerror(errno) ); | |
550 | return false; | |
eb1f6bfa | 551 | } |
eb1f6bfa | 552 | |
feec68a0 A |
553 | bool flag = true; |
554 | while ( (entry=readdir(dir)) && flag ) { | |
555 | if ( strlen(entry->d_name) == 2 && | |
556 | isxdigit(entry->d_name[0]) && | |
557 | isxdigit(entry->d_name[1]) ) { | |
558 | char* name = concat( dirname, "/", entry->d_name, 0 ); | |
559 | flag = level ? filelevel( name, list ) : dirlevel( name, list, true ); | |
560 | delete[] name; | |
561 | } | |
562 | } | |
563 | ||
564 | closedir(dir); | |
565 | return flag; | |
eb1f6bfa AJ |
566 | } |
567 | ||
568 | int | |
9dca980d | 569 | checkForPortOnly( const char* arg ) |
feec68a0 A |
570 | // purpose: see if somebody just put in a port instead of a hostname |
571 | // paramtr: optarg (IN): argument from commandline | |
572 | // returns: 0..65535 is the valid port number in network byte order, | |
573 | // -1 if not a port | |
eb1f6bfa | 574 | { |
feec68a0 | 575 | // if there is a period in there, it must be a valid hostname |
9dca980d | 576 | if ( strchr( arg, '.' ) != 0 ) return -1; |
eb1f6bfa | 577 | |
feec68a0 A |
578 | // if it is just a number between 0 and 65535, it must be a port |
579 | char* errstr = 0; | |
9dca980d AJ |
580 | unsigned long result = strtoul( arg, &errstr, 0 ); |
581 | if ( result < 65536 && errstr != arg ) return htons(result); | |
eb1f6bfa AJ |
582 | |
583 | #if 0 | |
feec68a0 | 584 | // one last try, test for a symbolical service name |
9dca980d | 585 | struct servent* service = getservbyname( arg, "tcp" ); |
feec68a0 | 586 | return service ? service->s_port : -1; |
eb1f6bfa | 587 | #else |
feec68a0 | 588 | return -1; |
eb1f6bfa AJ |
589 | #endif |
590 | } | |
591 | ||
592 | void | |
593 | helpMe( void ) | |
feec68a0 | 594 | // purpuse: write help message and exit |
eb1f6bfa | 595 | { |
6d7a7410 | 596 | printf( "\nUsage:\t%s\t[-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] " |
feec68a0 | 597 | "[-p h[:p]]\n\t\t[-P #] [-s] [-v] [-C dir [-H]] [-n]\n\n", |
6d7a7410 | 598 | ::programname ); |
feec68a0 A |
599 | printf( |
600 | " -a\tdisplay a little rotating thingy to indicate that I am alive (tty only).\n" | |
601 | " -c c\tsquid.conf location, default \"%s\".\n" | |
602 | " -C dir\tbase directory for content extraction (copy-out mode).\n" | |
052aecef | 603 | " -d l\tdebug level, an OR mask of different debug options.\n" |
feec68a0 A |
604 | " -e re\tsingle regular expression per -e instance (use quotes!).\n" |
605 | " -E re\tsingle case sensitive regular expression like -e.\n" | |
606 | " -f fn\tname of textfile containing one regular expression per line.\n" | |
607 | " -F fn\tname of textfile like -f containing case sensitive REs.\n" | |
608 | " -H\tprepend HTTP reply header to destination files in copy-out mode.\n" | |
609 | " -n\tdo not fork() when using more than one cache_dir.\n" | |
610 | " -p h:p\tcache runs on host h and optional port p, default is %s:%u.\n" | |
611 | " -P #\tif 0, just print matches; otherwise OR the following purge modes:\n" | |
612 | "\t 0x01 really send PURGE to the cache.\n" | |
613 | "\t 0x02 remove all caches files reported as 404 (not found).\n" | |
614 | "\t 0x04 remove all weird (inaccessible or too small) cache files.\n" | |
615 | "\t0 and 1 are recommended - slow rebuild your cache with other modes.\n" | |
616 | " -s\tshow all options after option parsing, but before really starting.\n" | |
617 | " -v\tshow more information about the file, e.g. MD5, timestamps and flags.\n" | |
618 | "\n", DEFAULT_SQUID_CONF, DEFAULTHOST, DEFAULTPORT ); | |
eb1f6bfa AJ |
619 | |
620 | } | |
621 | ||
622 | void | |
623 | parseCommandline( int argc, char* argv[], REList*& head, | |
9dca980d AJ |
624 | char*& conffile, char*& copyDirPath, |
625 | struct in_addr& serverHostIp, unsigned short& serverHostPort ) | |
feec68a0 A |
626 | // paramtr: argc: see ::main(). |
627 | // argv: see ::main(). | |
628 | // returns: Does terminate the program on errors! | |
629 | // purpose: suck in any commandline options, and set the global vars. | |
630 | { | |
631 | int option, port, showme = 0; | |
632 | char* ptr, *colon; | |
633 | FILE* rfile; | |
634 | ||
635 | // program basename | |
14942edd FC |
636 | if ( (ptr = strrchr(argv[0],'/')) == NULL ) |
637 | ptr=argv[0]; | |
638 | else | |
639 | ++ptr; | |
feec68a0 A |
640 | ::programname = ptr; |
641 | ||
642 | // extract commandline parameters | |
c2afddd8 | 643 | REList* tail = head = 0; |
feec68a0 A |
644 | opterr = 0; |
645 | while ( (option = getopt( argc, argv, "ac:C:d:E:e:F:f:Hnp:P:sv" )) != -1 ) { | |
646 | switch ( option ) { | |
647 | case 'a': | |
648 | ::iamalive = ! ::iamalive; | |
649 | break; | |
650 | case 'C': | |
651 | if ( optarg && *optarg ) { | |
9dca980d AJ |
652 | if ( copyDirPath ) xfree( (void*) copyDirPath ); |
653 | copyDirPath = xstrdup(optarg); | |
654 | assert(copyDirPath); | |
feec68a0 A |
655 | } |
656 | break; | |
657 | case 'c': | |
b2d7d4be AJ |
658 | if ( !optarg || !*optarg ) { |
659 | fprintf( stderr, "%c requires a regex pattern argument!\n", option ); | |
24885773 | 660 | exit(EXIT_FAILURE); |
feec68a0 | 661 | } |
b2d7d4be AJ |
662 | if ( *conffile ) xfree((void*) conffile); |
663 | conffile = xstrdup(optarg); | |
664 | assert(conffile); | |
feec68a0 A |
665 | break; |
666 | ||
667 | case 'd': | |
052aecef AJ |
668 | if ( !optarg || !*optarg ) { |
669 | fprintf( stderr, "%c expects a mask parameter. Debug disabled.\n", option ); | |
670 | ::debugFlag = 0; | |
671 | } else | |
672 | ::debugFlag = (strtoul(optarg, NULL, 0) & 0xFFFFFFFF); | |
feec68a0 A |
673 | break; |
674 | ||
675 | case 'E': | |
676 | case 'e': | |
b2d7d4be AJ |
677 | if ( !optarg || !*optarg ) { |
678 | fprintf( stderr, "%c requires a regex pattern argument!\n", option ); | |
24885773 | 679 | exit(EXIT_FAILURE); |
b2d7d4be | 680 | } |
c2afddd8 AJ |
681 | if ( head == 0 ) |
682 | tail = head = new REList( optarg, option=='E' ); | |
683 | else { | |
684 | tail->next = new REList( optarg, option=='E' ); | |
685 | tail = tail->next; | |
feec68a0 A |
686 | } |
687 | break; | |
688 | ||
689 | case 'f': | |
b2d7d4be AJ |
690 | if ( !optarg || !*optarg ) { |
691 | fprintf( stderr, "%c requires a filename argument!\n", option ); | |
24885773 | 692 | exit(EXIT_FAILURE); |
b2d7d4be | 693 | } |
feec68a0 A |
694 | if ( (rfile = fopen( optarg, "r" )) != NULL ) { |
695 | unsigned long lineno = 0; | |
eb1f6bfa | 696 | #define LINESIZE 512 |
feec68a0 A |
697 | char line[LINESIZE]; |
698 | while ( fgets( line, LINESIZE, rfile ) != NULL ) { | |
14942edd | 699 | ++lineno; |
feec68a0 A |
700 | int len = strlen(line)-1; |
701 | if ( len+2 >= LINESIZE ) { | |
702 | fprintf( stderr, "%s:%lu: line too long, sorry.\n", | |
703 | optarg, lineno ); | |
24885773 | 704 | exit(EXIT_FAILURE); |
feec68a0 A |
705 | } |
706 | ||
707 | // remove trailing line breaks | |
f412b2d6 FC |
708 | while ( len > 0 && ( line[len] == '\n' || line[len] == '\r' ) ) { |
709 | line[len] = '\0'; | |
710 | --len; | |
711 | } | |
feec68a0 | 712 | |
c2afddd8 AJ |
713 | // insert into list of expressions |
714 | if ( head == 0 ) tail = head = new REList(line,option=='F'); | |
715 | else { | |
716 | tail->next = new REList(line,option=='F'); | |
717 | tail = tail->next; | |
feec68a0 A |
718 | } |
719 | } | |
720 | fclose(rfile); | |
721 | } else | |
722 | fprintf( stderr, "unable to open %s: %s\n", optarg, strerror(errno)); | |
723 | break; | |
724 | ||
725 | case 'H': | |
726 | ::envelope = ! ::envelope; | |
727 | break; | |
728 | case 'n': | |
729 | ::no_fork = ! ::no_fork; | |
730 | break; | |
731 | case 'p': | |
b2d7d4be AJ |
732 | if ( !optarg || !*optarg ) { |
733 | fprintf( stderr, "%c requires a port argument!\n", option ); | |
24885773 | 734 | exit(EXIT_FAILURE); |
b2d7d4be | 735 | } |
feec68a0 A |
736 | colon = strchr( optarg, ':' ); |
737 | if ( colon == 0 ) { | |
738 | // no colon, only look at host | |
739 | ||
740 | // fix: see if somebody just put in there a port (no periods) | |
741 | // give port number precedence over host names | |
742 | port = checkForPortOnly( optarg ); | |
743 | if ( port == -1 ) { | |
744 | // assume that main() did set the default port | |
9dca980d | 745 | if ( convertHostname(optarg,serverHostIp) == -1 ) { |
feec68a0 | 746 | fprintf( stderr, "unable to resolve host %s!\n", optarg ); |
24885773 | 747 | exit(EXIT_FAILURE); |
feec68a0 A |
748 | } |
749 | } else { | |
750 | // assume that main() did set the default host | |
9dca980d | 751 | serverHostPort = port; |
feec68a0 A |
752 | } |
753 | } else { | |
754 | // colon used, port is extra | |
14942edd FC |
755 | *colon = 0; |
756 | ++colon; | |
9dca980d | 757 | if ( convertHostname(optarg,serverHostIp) == -1 ) { |
feec68a0 | 758 | fprintf( stderr, "unable to resolve host %s!\n", optarg ); |
24885773 | 759 | exit(EXIT_FAILURE); |
feec68a0 | 760 | } |
9dca980d | 761 | if ( convertPortname(colon,serverHostPort) == -1 ) { |
feec68a0 | 762 | fprintf( stderr, "unable to resolve port %s!\n", colon ); |
24885773 | 763 | exit(EXIT_FAILURE); |
feec68a0 A |
764 | } |
765 | } | |
766 | break; | |
767 | case 'P': | |
b2d7d4be AJ |
768 | if ( !optarg || !*optarg ) { |
769 | fprintf( stderr, "%c requires a mode argument!\n", option ); | |
24885773 | 770 | exit(EXIT_FAILURE); |
b2d7d4be | 771 | } |
feec68a0 A |
772 | ::purgeMode = ( strtol( optarg, 0, 0 ) & 0x07 ); |
773 | break; | |
774 | case 's': | |
775 | showme=1; | |
776 | break; | |
777 | case 'v': | |
778 | ::verbose = ! ::verbose; | |
779 | break; | |
780 | case '?': | |
781 | default: | |
782 | helpMe(); | |
24885773 | 783 | exit(EXIT_FAILURE); |
feec68a0 A |
784 | } |
785 | } | |
786 | ||
787 | // adjust | |
59a09b98 | 788 | if ( ! isatty(fileno(stdout)) || (::debugFlag & 0x01) ) ::iamalive = false; |
feec68a0 A |
789 | if ( head == 0 ) { |
790 | fputs( "There was no regular expression defined. If you intend\n", stderr ); | |
791 | fputs( "to match all possible URLs, use \"-e .\" instead.\n", stderr ); | |
24885773 | 792 | exit(EXIT_FAILURE); |
eb1f6bfa | 793 | } |
feec68a0 A |
794 | |
795 | // postcondition: head != 0 | |
796 | assert( head != 0 ); | |
797 | ||
798 | // make sure that the copy out directory is there and accessible | |
9dca980d AJ |
799 | if ( copyDirPath && *copyDirPath ) |
800 | if ( assert_copydir( copyDirPath ) != 0 ) exit(1); | |
feec68a0 A |
801 | |
802 | // show results | |
803 | if ( showme ) { | |
6d7a7410 FC |
804 | printf( "#\n# Currently active values for %s:\n", |
805 | ::programname); | |
feec68a0 | 806 | printf( "# Debug level : " ); |
d83197e3 | 807 | if ( ::debugFlag ) printf( "%#6.4x", ::debugFlag ); |
feec68a0 A |
808 | else printf( "production level" ); // printf omits 0x prefix for 0! |
809 | printf( " + %s mode", ::no_fork ? "linear" : "parallel" ); | |
810 | puts( ::verbose ? " + extra verbosity" : "" ); | |
811 | ||
812 | printf( "# Copy-out directory: %s ", | |
9dca980d AJ |
813 | copyDirPath ? copyDirPath : "copy-out mode disabled" ); |
814 | if ( copyDirPath ) | |
feec68a0 A |
815 | printf( "(%s HTTP header)\n", ::envelope ? "prepend" : "no" ); |
816 | else | |
817 | puts(""); | |
818 | ||
819 | printf( "# Squid config file : %s\n", conffile ); | |
820 | printf( "# Cacheserveraddress: %s:%u\n", | |
9dca980d | 821 | inet_ntoa( serverHostIp ), ntohs( serverHostPort ) ); |
feec68a0 A |
822 | printf( "# purge mode : 0x%02x\n", ::purgeMode ); |
823 | printf( "# Regular expression: " ); | |
824 | ||
825 | unsigned count(0); | |
826 | for ( tail = head; tail != NULL; tail = tail->next ) { | |
14942edd FC |
827 | if ( count++ ) |
828 | printf( "#%22u", count ); | |
eb1f6bfa | 829 | #if defined(LINUX) && putc==_IO_putc |
feec68a0 A |
830 | // I HATE BROKEN LINUX HEADERS! |
831 | // purge.o(.text+0x1040): undefined reference to `_IO_putc' | |
832 | // If your compilation breaks here, remove the undefinition | |
833 | #undef putc | |
eb1f6bfa | 834 | #endif |
feec68a0 A |
835 | else putchar('1'); |
836 | printf( " \"%s\"\n", tail->data ); | |
837 | } | |
838 | puts( "#" ); | |
eb1f6bfa | 839 | } |
feec68a0 | 840 | fflush( stdout ); |
eb1f6bfa AJ |
841 | } |
842 | ||
843 | extern "C" { | |
844 | ||
feec68a0 A |
845 | static |
846 | void | |
847 | exiter( void ) { | |
848 | if ( ::term_flag ) psignal( ::term_flag, "received signal" ); | |
849 | delete[] ::linebuffer; | |
850 | if ( ::reminder ) { | |
851 | fputs( | |
852 | "WARNING! Caches files were removed. Please shut down your cache, remove\n" | |
853 | "your swap.state files and restart your cache again, i.e. effictively do\n" | |
854 | "a slow rebuild your cache! Otherwise your squid *will* choke!\n", stderr ); | |
855 | } | |
856 | } | |
eb1f6bfa | 857 | |
feec68a0 A |
858 | static |
859 | void | |
860 | handler( int signo ) { | |
861 | ::term_flag = signo; | |
862 | if ( getpid() == getpgrp() ) kill( -getpgrp(), signo ); | |
24885773 | 863 | exit(EXIT_FAILURE); |
feec68a0 | 864 | } |
eb1f6bfa AJ |
865 | |
866 | } // extern "C" | |
867 | ||
868 | static | |
869 | int | |
870 | makelinebuffered( FILE* fp, const char* fn = 0 ) | |
feec68a0 A |
871 | // purpose: make the given FILE line buffered |
872 | // paramtr: fp (IO): file pointer which to put into line buffer mode | |
873 | // fn (IN): name of file to print in case of error | |
874 | // returns: 0 is ok, -1 to indicate an error | |
875 | // warning: error messages will already be printed | |
eb1f6bfa | 876 | { |
feec68a0 A |
877 | if ( setvbuf( fp, 0, _IOLBF, 0 ) == 0 ) { |
878 | // ok | |
879 | return 0; | |
880 | } else { | |
881 | // error | |
882 | fprintf( stderr, "unable to make \"%s\" line buffered: %s\n", | |
883 | fn ? fn : "", strerror(errno) ); | |
884 | return -1; | |
885 | } | |
eb1f6bfa AJ |
886 | } |
887 | ||
888 | int | |
889 | main( int argc, char* argv[] ) | |
890 | { | |
feec68a0 | 891 | // setup variables |
c2afddd8 | 892 | REList* list = 0; |
2ccf2eb2 | 893 | char* conffile = xstrdup( DEFAULT_SQUID_CONF ); |
feec68a0 A |
894 | serverPort = htons(DEFAULTPORT); |
895 | if ( convertHostname(DEFAULTHOST,serverHost) == -1 ) { | |
896 | fprintf( stderr, "unable to resolve host %s!\n", DEFAULTHOST ); | |
24885773 | 897 | exit(EXIT_FAILURE); |
feec68a0 A |
898 | } |
899 | ||
900 | // setup line buffer | |
901 | ::linebuffer = new char[ ::buffersize ]; | |
902 | assert( ::linebuffer != 0 ); | |
903 | ||
904 | // parse commandline | |
905 | puts( "### Use at your own risk! No guarantees whatsoever. You were warned. ###"); | |
906 | parseCommandline( argc, argv, list, conffile, ::copydir, | |
907 | serverHost, serverPort ); | |
908 | ||
909 | // prepare execution | |
910 | if ( atexit( exiter ) != 0 || | |
911 | Signal( SIGTERM, handler, true ) == SIG_ERR || | |
912 | Signal( SIGINT, handler, true ) == SIG_ERR || | |
913 | Signal( SIGHUP, handler, true ) == SIG_ERR ) { | |
914 | perror( "unable to install signal/exit function" ); | |
24885773 | 915 | exit(EXIT_FAILURE); |
feec68a0 A |
916 | } |
917 | ||
918 | // try to read squid.conf file to determine all cache_dir locations | |
919 | CacheDirVector cdv(0); | |
59a09b98 | 920 | if ( readConfigFile( cdv, conffile, debugFlag ? stderr : 0 ) > 0 ) { |
feec68a0 A |
921 | // there are some valid cache_dir entries. |
922 | // unless forking was forbidden by cmdline option, | |
923 | // for a process for each cache_dir entry to remove files. | |
924 | ||
925 | if ( ::no_fork || cdv.size() == 1 ) { | |
926 | // linear mode, one cache_dir after the next | |
927 | for ( CacheDirVector::iterator i = cdv.begin(); i != cdv.end(); ++i ) { | |
928 | // execute OR complain | |
929 | if ( ! dirlevel(i->base,list) ) | |
930 | fprintf( stderr, "program terminated due to error: %s", | |
931 | strerror(errno) ); | |
6e2aefad | 932 | xfree((void*) i->base); |
feec68a0 A |
933 | } |
934 | } else { | |
935 | // parallel mode, all cache_dir in parallel | |
936 | pid_t* child = new pid_t[ cdv.size() ]; | |
937 | ||
938 | // make stdout/stderr line bufferd | |
939 | makelinebuffered( stdout, "stdout" ); | |
940 | makelinebuffered( stderr, "stderr" ); | |
941 | ||
942 | // make parent process group leader for easier killings | |
943 | if ( setpgid(getpid(), getpid()) != 0 ) { | |
944 | perror( "unable to set process group leader" ); | |
24885773 | 945 | exit(EXIT_FAILURE); |
feec68a0 A |
946 | } |
947 | ||
948 | // -a is mutually exclusive with fork mode | |
949 | if ( ::iamalive ) { | |
950 | puts( "# i-am-alive flag incompatible with fork mode, resetting" ); | |
951 | ::iamalive = false; | |
952 | } | |
953 | ||
2ccf2eb2 | 954 | for ( size_t i=0; i < cdv.size(); ++i ) { |
feec68a0 A |
955 | if ( getpid() == getpgrp() ) { |
956 | // only parent == group leader may fork off new processes | |
957 | if ( (child[i]=fork()) < 0 ) { | |
958 | // fork error, this is bad! | |
959 | perror( "unable to fork" ); | |
960 | kill( -getpgrp(), SIGTERM ); | |
24885773 | 961 | exit(EXIT_FAILURE); |
feec68a0 A |
962 | } else if ( child[i] == 0 ) { |
963 | // child mode | |
964 | // execute OR complain | |
965 | if ( ! dirlevel(cdv[i].base,list) ) | |
966 | fprintf( stderr, "program terminated due to error: %s\n", | |
967 | strerror(errno) ); | |
6e2aefad | 968 | xfree((void*) cdv[i].base); |
24885773 | 969 | exit(EXIT_SUCCESS); |
feec68a0 A |
970 | } else { |
971 | // parent mode | |
59a09b98 | 972 | if ( ::debugFlag ) printf( "forked child %d\n", (int) child[i] ); |
feec68a0 A |
973 | } |
974 | } | |
975 | } | |
976 | ||
977 | // collect the garbase | |
978 | pid_t temp; | |
979 | int status; | |
2ccf2eb2 | 980 | for ( size_t i=0; i < cdv.size(); ++i ) { |
feec68a0 A |
981 | while ( (temp=waitpid( (pid_t)-1, &status, 0 )) == -1 ) |
982 | if ( errno == EINTR ) continue; | |
59a09b98 | 983 | if ( ::debugFlag ) printf( "collected child %d\n", (int) temp ); |
feec68a0 A |
984 | } |
985 | delete[] child; | |
986 | } | |
eb1f6bfa | 987 | } else { |
feec68a0 | 988 | fprintf( stderr, "no cache_dir or error accessing \"%s\"\n", conffile ); |
eb1f6bfa | 989 | } |
feec68a0 A |
990 | |
991 | // clean up | |
6e2aefad HN |
992 | if ( copydir ) xfree( (void*) copydir ); |
993 | xfree((void*) conffile); | |
feec68a0 | 994 | delete list; |
24885773 | 995 | return EXIT_SUCCESS; |
eb1f6bfa | 996 | } |
f53969cc | 997 |