htcacheclean.c revision 07d000c26f08bb7d590da25057748b28b8b59741
af84459fbf938e508fd10b01cb8d699c79083813takashi/* Copyright 2001-2004 The Apache Software Foundation
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * Licensed under the Apache License, Version 2.0 (the "License");
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * you may not use this file except in compliance with the License.
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * You may obtain a copy of the License at
af84459fbf938e508fd10b01cb8d699c79083813takashi * Unless required by applicable law or agreed to in writing, software
96ad5d81ee4a2cc66a4ae19893efc8aa6d06fae7jailletc * distributed under the License is distributed on an "AS IS" BASIS,
af84459fbf938e508fd10b01cb8d699c79083813takashi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
af84459fbf938e508fd10b01cb8d699c79083813takashi * See the License for the specific language governing permissions and
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen * limitations under the License.
af84459fbf938e508fd10b01cb8d699c79083813takashi * htcacheclean.c: simple program for cleaning of
af84459fbf938e508fd10b01cb8d699c79083813takashi * the disk cache of the Apache HTTP server
3f08db06526d6901aa08c110b5bc7dde6bc39905nd * Contributed by Andreas Steinmetz <ast domdv.de>
af84459fbf938e508fd10b01cb8d699c79083813takashi * 8 Oct 2004
af84459fbf938e508fd10b01cb8d699c79083813takashi/* mod_disk_cache.c extract start */
af84459fbf938e508fd10b01cb8d699c79083813takashitypedef struct {
2d39a41e98476f5235b7c37ce745a4aa0904b1cbrbowen /* Indicates the format of the header struct stored on-disk. */
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh /* The HTTP status code returned for this response. */
af84459fbf938e508fd10b01cb8d699c79083813takashi /* The size of the entity name that follows. */
af84459fbf938e508fd10b01cb8d699c79083813takashi /* The number of times we've cached this entity. */
af84459fbf938e508fd10b01cb8d699c79083813takashi /* Miscellaneous time values. */
af84459fbf938e508fd10b01cb8d699c79083813takashi/* mod_disk_cache.c extract end */
af84459fbf938e508fd10b01cb8d699c79083813takashi/* mod_disk_cache.c related definitions start */
af84459fbf938e508fd10b01cb8d699c79083813takashi * this is based on #define AP_TEMPFILE "/aptmpXXXXXX"
af84459fbf938e508fd10b01cb8d699c79083813takashi * the above definition could be reworked into the following:
af84459fbf938e508fd10b01cb8d699c79083813takashi * #define AP_TEMPFILE_PREFIX "/"
af84459fbf938e508fd10b01cb8d699c79083813takashi * #define AP_TEMPFILE_BASE "aptmp"
af84459fbf938e508fd10b01cb8d699c79083813takashi * #define AP_TEMPFILE_SUFFIX "XXXXXX"
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh * #define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh * #define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh * #define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh * these definitions would then match the definitions below:
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh#define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh/* mod_disk_cache.c related definitions end */
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh/* define the following for debugging */
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh * Note: on Linux delays <= 2ms are busy waits without
57e80b64d12f1dcfbfec8670967ce52fe507d0d7humbedooh * scheduling, so never use a delay <= 2ms below
af84459fbf938e508fd10b01cb8d699c79083813takashi#define DELETE_NICE 10 /* be nice after this amount of delete ops */
af84459fbf938e508fd10b01cb8d699c79083813takashi#define STAT_ATTEMPTS 10 /* maximum stat attempts for a file */
af84459fbf938e508fd10b01cb8d699c79083813takashi#define DIRINFO (APR_FINFO_MTIME|APR_FINFO_SIZE|APR_FINFO_TYPE|APR_FINFO_LINK)
af84459fbf938e508fd10b01cb8d699c79083813takashitypedef struct _direntry {
af84459fbf938e508fd10b01cb8d699c79083813takashi int type; /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_time_t htime; /* headers file modification time */
af84459fbf938e508fd10b01cb8d699c79083813takashitypedef struct _entry {
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_time_t response_time; /* cache entry time of last response to client */
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_time_t htime; /* headers file modification time */
888cb40bdeec5abf452bd85d6bf63b26d5913d4chumbedooh apr_time_t dtime; /* body file modification time */
af84459fbf938e508fd10b01cb8d699c79083813takashistatic int delcount; /* file deletion count for nice mode */
282b62d8e9a4edbc2da22ba2d876ec94afc48084ndstatic int interrupted; /* flag: true if SIGINT or SIGTERM occurred */
af84459fbf938e508fd10b01cb8d699c79083813takashistatic int realclean; /* flag: true means user said apache is not running */
af84459fbf938e508fd10b01cb8d699c79083813takashistatic int verbose; /* flag: true means print statistics */
af84459fbf938e508fd10b01cb8d699c79083813takashistatic int benice; /* flag: true means nice mode is activated */
af84459fbf938e508fd10b01cb8d699c79083813takashistatic int dryrun; /* flag: true means dry run, don't actually delete
af84459fbf938e508fd10b01cb8d699c79083813takashi anything */
282b62d8e9a4edbc2da22ba2d876ec94afc48084ndstatic int baselen; /* string length of the path to the proxy directory */
af84459fbf938e508fd10b01cb8d699c79083813takashistatic apr_time_t now; /* start time of this processing run */
888cb40bdeec5abf452bd85d6bf63b26d5913d4chumbedoohstatic apr_off_t unsolicited; /* file size summary for deleted unsolicited
af84459fbf938e508fd10b01cb8d699c79083813takashistatic APR_RING_ENTRY(_entry) root; /* ENTRY ring anchor */
af84459fbf938e508fd10b01cb8d699c79083813takashi/* short program name as called */
af84459fbf938e508fd10b01cb8d699c79083813takashi * fake delete for debug purposes
af84459fbf938e508fd10b01cb8d699c79083813takashistatic void fake_file_remove(char *pathname, apr_pool_t *p)
af84459fbf938e508fd10b01cb8d699c79083813takashi /* stat and printing to simulate some deletion system load and to
af84459fbf938e508fd10b01cb8d699c79083813takashi display what would actually have happened */
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_file_printf(errfile, "would delete %s" APR_EOL_STR, pathname);
af84459fbf938e508fd10b01cb8d699c79083813takashi * called on SIGINT or SIGTERM
af84459fbf938e508fd10b01cb8d699c79083813takashi * called in out of memory condition
af84459fbf938e508fd10b01cb8d699c79083813takashi static int called = 0;
af84459fbf938e508fd10b01cb8d699c79083813takashi /* be careful to call exit() only once */
af84459fbf938e508fd10b01cb8d699c79083813takashi * print purge statistics
af84459fbf938e508fd10b01cb8d699c79083813takashistatic void printstats(apr_off_t total, apr_off_t sum, apr_off_t max,
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_file_printf(errfile, "Statistics:" APR_EOL_STR);
2d39a41e98476f5235b7c37ce745a4aa0904b1cbrbowen apr_file_printf(errfile, "unsolicited size %d.%d%c" APR_EOL_STR,
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_file_printf(errfile, "size limit %d.0%c" APR_EOL_STR,
af84459fbf938e508fd10b01cb8d699c79083813takashi apr_file_printf(errfile, "total size was %d.%d%c, total size now "
4aa603e6448b99f9371397d439795c91a93637eand apr_file_printf(errfile, "total entries was %d, total entries now %d"
af84459fbf938e508fd10b01cb8d699c79083813takashi * delete a single file
af84459fbf938e508fd10b01cb8d699c79083813takashistatic void delete_file(char *path, char *basename, apr_pool_t *pool)
af84459fbf938e508fd10b01cb8d699c79083813takashi /* temp pool, otherwise lots of memory could be allocated */
af84459fbf938e508fd10b01cb8d699c79083813takashi nextpath = apr_pstrcat(p, path, "/", basename, NULL);
af84459fbf938e508fd10b01cb8d699c79083813takashi * delete cache file set
af84459fbf938e508fd10b01cb8d699c79083813takashistatic void delete_entry(char *path, char *basename, apr_pool_t *pool)
af84459fbf938e508fd10b01cb8d699c79083813takashi /* temp pool, otherwise lots of memory could be allocated */
af84459fbf938e508fd10b01cb8d699c79083813takashi nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
af84459fbf938e508fd10b01cb8d699c79083813takashi nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
af84459fbf938e508fd10b01cb8d699c79083813takashi * walk the cache directory tree
2d39a41e98476f5235b7c37ce745a4aa0904b1cbrbowen while (apr_dir_read(&info, 0, dir) == APR_SUCCESS && !interrupted) {
2d39a41e98476f5235b7c37ce745a4aa0904b1cbrbowen if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
2d39a41e98476f5235b7c37ce745a4aa0904b1cbrbowen d->basename = apr_pstrcat(p, path, "/", info.name, NULL);
2d39a41e98476f5235b7c37ce745a4aa0904b1cbrbowen !interrupted && d != APR_RING_SENTINEL(&anchor, _direntry, link);
af84459fbf938e508fd10b01cb8d699c79083813takashi /* there may be temporary files which may be gone before
af84459fbf938e508fd10b01cb8d699c79083813takashi * processing, always skip these if not in realclean mode
0d0ba3a410038e179b695446bb149cce6264e0abnd if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
0d0ba3a410038e179b695446bb149cce6264e0abnd /* this may look strange but apr_stat() may return errno which
0d0ba3a410038e179b695446bb149cce6264e0abnd * is system dependent and there may be transient failures,
0d0ba3a410038e179b695446bb149cce6264e0abnd * so just blindly retry for a short while
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh status = apr_stat(&info, d->basename, DIRINFO, p);
205f749042ed530040a4f0080dbcb47ceae8a374rjung } while (status != APR_SUCCESS && !interrupted && --retries);
0d0ba3a410038e179b695446bb149cce6264e0abnd /* what may happen here is that apache did create a file which
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd * we did detect but then does delete the file before we can
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd * get file information, so if we don't get any file information
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd * we will ignore the file in this case
if (!ext) {
if (interrupted) {
void *hvalue;
d = hvalue;
switch(d->type) {
case HEADERDATA:
p) == APR_SUCCESS) {
case HEADER:
case DATA:
case TEMP:
if (interrupted) {
apr_pool_destroy(p);
if (benice) {
if (interrupted) {
sum = 0;
entries = 0;
entries++;
entries--;
if (!interrupted) {
if (interrupted) {
entries--;
if (!interrupted) {
if (interrupted) {
oldest = e;
entries--;
if (!interrupted) {
static void usage(void)
apr_getopt_t *o;
char opt;
const char *arg;
interrupted = 0;
repeat = 0;
isdaemon = 0;
dryrun = 0;
limit_found = 0;
max = 0;
verbose = 0;
realclean = 0;
benice = 0;
intelligent = 0;
if (argc) {
usage();
switch (opt) {
if (intelligent) {
usage();
if (dryrun) {
usage();
if (benice) {
usage();
if (verbose) {
usage();
if (realclean) {
usage();
if (isdaemon) {
usage();
if (limit_found) {
usage();
char *end;
usage();
if (proxypath) {
usage();
usage();
usage();
usage();
usage();
usage();
usage();
#ifndef DEBUG
if (isdaemon) {
delcount = 0;
unsolicited = 0;
dowork = 0;
switch (intelligent) {
if (isdaemon) {
delay = 0;