842ae4bd224140319ae7feec1872b93dfd491143fielding/* Licensed to the Apache Software Foundation (ASF) under one or more
842ae4bd224140319ae7feec1872b93dfd491143fielding * contributor license agreements. See the NOTICE file distributed with
842ae4bd224140319ae7feec1872b93dfd491143fielding * this work for additional information regarding copyright ownership.
842ae4bd224140319ae7feec1872b93dfd491143fielding * The ASF licenses this file to You under the Apache License, Version 2.0
842ae4bd224140319ae7feec1872b93dfd491143fielding * (the "License"); you may not use this file except in compliance with
842ae4bd224140319ae7feec1872b93dfd491143fielding * the License. You may obtain a copy of the License at
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * Unless required by applicable law or agreed to in writing, software
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * distributed under the License is distributed on an "AS IS" BASIS,
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * See the License for the specific language governing permissions and
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * limitations under the License.
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * htcacheclean.c: simple program for cleaning of
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * the disk cache of the Apache HTTP server
2d728410bcf9e6e53698bb035f516e18efd76d8bnd * Contributed by Andreas Steinmetz <ast domdv.de>
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * 8 Oct 2004
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz/* define the following for debugging */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * Note: on Linux delays <= 2ms are busy waits without
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * scheduling, so never use a delay <= 2ms below
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz#define DELETE_NICE 10 /* be nice after this amount of delete ops */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz#define STAT_ATTEMPTS 10 /* maximum stat attempts for a file */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz#define DIRINFO (APR_FINFO_MTIME|APR_FINFO_SIZE|APR_FINFO_TYPE|APR_FINFO_LINK)
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd int type; /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd apr_time_t response_time; /* cache entry time of last response to client */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int delcount; /* file deletion count for nice mode */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int interrupted; /* flag: true if SIGINT or SIGTERM occurred */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int realclean; /* flag: true means user said apache is not running */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int verbose; /* flag: true means print statistics */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int benice; /* flag: true means nice mode is activated */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int dryrun; /* flag: true means dry run, don't actually delete
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd anything */
663c339c8a4663cf1977f890481f8b7e251d3221jerenkrantzstatic int deldirs; /* flag: true means directories should be deleted */
5876f43a746f688a32b7201bced8591ddf19bd43minfrinstatic int listurls; /* flag: true means list cached urls */
5876f43a746f688a32b7201bced8591ddf19bd43minfrinstatic int listextended;/* flag: true means list cached urls */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic int baselen; /* string length of the path to the proxy directory */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic apr_time_t now; /* start time of this processing run */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic apr_off_t unsolicited; /* file size summary for deleted unsolicited
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9ndstatic APR_RING_ENTRY(_entry) root; /* ENTRY ring anchor */
2d728410bcf9e6e53698bb035f516e18efd76d8bnd/* short program name as called */
9fe23388f983cb652b5d68e2bd92aa9f0568c574minfrin/* what did we clean? */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * fake delete for debug purposes
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantzstatic void fake_file_remove(char *pathname, apr_pool_t *p)
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* stat and printing to simulate some deletion system load and to
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz display what would actually have happened */
a51acf58d9d82f52e0ee56106cd9282030f3f3bend apr_file_printf(errfile, "would delete %s" APR_EOL_STR, pathname);
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * called on SIGINT or SIGTERM
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * called in out of memory condition
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* be careful to call exit() only once */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * print purge statistics
9fe23388f983cb652b5d68e2bd92aa9f0568c574minfrin apr_file_printf(errfile, "Cleaned %s. Statistics:" APR_EOL_STR, path);
a51acf58d9d82f52e0ee56106cd9282030f3f3bend apr_file_printf(errfile, "unsolicited size %d.%d%c" APR_EOL_STR,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin apr_file_printf(errfile, "size limit %" APR_OFF_T_FMT ".0%c" APR_EOL_STR,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin apr_file_printf(errfile, "inodes limit %" APR_OFF_T_FMT APR_EOL_STR,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin "total size was %" APR_OFF_T_FMT ".%" APR_OFF_T_FMT "%c, total size now "
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin "%" APR_OFF_T_FMT ".%" APR_OFF_T_FMT "%c" APR_EOL_STR, s->total,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin apr_file_printf(errfile, "total inodes was %" APR_OFF_T_FMT
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin ", total %sinodes now "
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin "%" APR_OFF_T_FMT APR_EOL_STR, s->ntotal, dryrun && deldirs ? "estimated "
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin "total entries was %" APR_OFF_T_FMT ", total entries now %" APR_OFF_T_FMT
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin "%" APR_OFF_T_FMT " entries deleted (%" APR_OFF_T_FMT " from future, %"
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin APR_OFF_T_FMT " expired, %" APR_OFF_T_FMT " fresh)" APR_EOL_STR,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin (s->etotal - s->entries), s->dfuture, s->dexpired, s->dfresh);
fac37c9794a18c24d187f4e0f97a9476c4344118minfrin * Round the value up to the given threshold.
fac37c9794a18c24d187f4e0f97a9476c4344118minfrinstatic apr_size_t round_up(apr_size_t val, apr_off_t round) {
289a94d4157967ac04dc5c865b94e361400eae0afuankg return (apr_size_t)(((val + round - 1) / round) * round);
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin * delete parent directories
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrinstatic void delete_parent(const char *path, const char *basename,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin /* temp pool, otherwise lots of memory could be allocated */
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin /* If asked to delete dirs, do so now. We don't care if it fails.
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin * If it fails, it likely means there was something else there.
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin /* remove the directory */
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin /* vary directory found? */
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin nextpath = apr_pstrcat(p, path, "/", apr_pstrndup(p, name, vary
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin * delete a single file
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrinstatic void delete_file(char *path, char *basename, apr_off_t *nodes,
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd /* temp pool, otherwise lots of memory could be allocated */
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if (!apr_stat(&finfo, nextpath, APR_FINFO_NLINK, p)) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * delete cache file set
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrinstatic void delete_entry(char *path, char *basename, apr_off_t *nodes,
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd /* temp pool, otherwise lots of memory could be allocated */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if (!apr_stat(&finfo, nextpath, APR_FINFO_NLINK, p)) {
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if (!apr_stat(&finfo, nextpath, APR_FINFO_NLINK, p)) {
5876f43a746f688a32b7201bced8591ddf19bd43minfrin * list the cache directory tree
5876f43a746f688a32b7201bced8591ddf19bd43minfrinstatic int list_urls(char *path, apr_pool_t *pool, apr_off_t round)
717473b4288c54ef043f016a4ee801aba477303erjung while (apr_dir_read(&info, APR_FINFO_TYPE, dir) == APR_SUCCESS && !interrupted) {
5876f43a746f688a32b7201bced8591ddf19bd43minfrin if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
5876f43a746f688a32b7201bced8591ddf19bd43minfrin if (list_urls(apr_pstrcat(p, path, "/", info.name, NULL), pool, round)) {
5876f43a746f688a32b7201bced8591ddf19bd43minfrin nextpath = apr_pstrcat(p, path, "/", info.name, NULL);
5876f43a746f688a32b7201bced8591ddf19bd43minfrin | APR_FOPEN_BINARY, APR_OS_DEFAULT, p) == APR_SUCCESS) {
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* stat the header file */
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* ignore the file */
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* ignore the file */
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* ignore the file */
5876f43a746f688a32b7201bced8591ddf19bd43minfrin " %d %d\n",
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* stat the data file */
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* ignore the file */
5876f43a746f688a32b7201bced8591ddf19bd43minfrin /* ignore the file */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * walk the cache directory tree
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrinstatic int process_dir(char *path, apr_pool_t *pool, apr_off_t *nodes)
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd DIRENTRY *d, *t, *n;
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd while (apr_dir_read(&info, 0, dir) == APR_SUCCESS && !interrupted) {
0ecc05d02d9ee3d110cb9e263080aaa2b77a12f9jim if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd d->basename = apr_pstrcat(p, path, "/", info.name, NULL);
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz !interrupted && d != APR_RING_SENTINEL(&anchor, _direntry, link);
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd /* there may be temporary files which may be gone before
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * processing, always skip these if not in realclean mode
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
6850c836b8c7c688ef8a8ec280e798e1d0bfbf01trawick /* this may look strange but apr_stat() may return an error which
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * is system dependent and there may be transient failures,
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * so just blindly retry for a short while
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd } while (status != APR_SUCCESS && !interrupted && --retries);
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd /* what may happen here is that apache did create a file which
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * we did detect but then does delete the file before we can
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * get file information, so if we don't get any file information
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * we will ignore the file in this case
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd /* if a user manually creates a '.header' file */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd /* if a user manually creates a '.data' file */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz for (i = apr_hash_first(p, h); i && !interrupted; i = apr_hash_next(i)) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz switch(d->type) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz nextpath = apr_pstrcat(p, path, "/", d->basename,
e8f95a682820a599fe41b22977010636be5c2717jim if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
e8f95a682820a599fe41b22977010636be5c2717jim /* This must be a URL that added Vary headers later,
13be69a729c912bea7d0aa22596b92717df92551pquerna * so kill the orphaned .data file
af91703d7657e52921bb36c44ff093b2d2077d0cminfrin /* We didn't recognise the format, kill the files */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* we have a somehow unreadable headers file which is associated
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * with a data file. this may be caused by apache currently
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * rewriting the headers file. thus we may delete the file set
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * either in realclean mode or if the headers file modification
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * timestamp is not within a specified positive or negative offset
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * to the current time.
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* single data and header files may be deleted either in realclean
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * mode or if their modification timestamp is not within a
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * specified positive or negative offset to the current time.
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * this handling is necessary due to possible race conditions
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * between apache and this process
85fa7868b2734d79c4e27b74d54b6846a1f9176fcolm if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* temp files may only be deleted in realclean mode which
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * is asserted above if a tempfile is in the hash array
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * purge cache entries
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrinstatic void purge(char *path, apr_pool_t *pool, apr_off_t max,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* process all entries with a timestamp in the future, this may
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * happen if a wrong system time is corrected
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz if (e->response_time > now || e->htime > now || e->dtime > now) {
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* process all entries with are expired */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz if (e->expire != APR_DATE_BAD && e->expire < now) {
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* process remaining entries oldest to newest, the check for an emtpy
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * ring actually isn't necessary except when the compiler does
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * corrupt 64bit arithmetics which happend to me once, so better safe
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * than sorry
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin while (!((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes))
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin && !interrupted && !APR_RING_EMPTY(&root, _entry, link)) {
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin delete_entry(path, oldest->basename, &s.nodes, pool);
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrinstatic apr_status_t remove_directory(apr_pool_t *pool, const char *dir)
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc apr_file_printf(errfile, "Could not open directory %s: %pm" APR_EOL_STR,
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp)
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin if (strcmp(dirent.name, ".") && strcmp(dirent.name, "..")) {
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin rv = remove_directory(pool, apr_pstrcat(pool, dir, "/",
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin /* tolerate the directory not being empty, the cache may have
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin * attempted to recreate the directory in the mean time.
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin const char *file = apr_pstrcat(pool, dir, "/", dirent.name, NULL);
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc "Could not remove file '%s': %pm" APR_EOL_STR, file,
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc apr_file_printf(errfile, "Could not remove directory %s: %pm" APR_EOL_STR,
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrinstatic apr_status_t find_directory(apr_pool_t *pool, const char *base,
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin const char *rest)
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin const char *header = apr_pstrcat(pool, rest, CACHE_HEADER_SUFFIX, NULL);
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin const char *data = apr_pstrcat(pool, rest, CACHE_DATA_SUFFIX, NULL);
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin const char *vdir = apr_pstrcat(pool, rest, CACHE_HEADER_SUFFIX,
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc apr_file_printf(errfile, "Could not open directory %s: %pm" APR_EOL_STR,
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp)
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin if (dirent.filetype == APR_DIR && !strncmp(rest, dirent.name, len)) {
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin dirname = apr_pstrcat(pool, base, "/", dirent.name, NULL);
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin rv = find_directory(pool, dirname, rest + (len < restlen ? len
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin if (!strcmp(dirent.name, header) || !strcmp(dirent.name, data)) {
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin remove = apr_pstrcat(pool, base, "/", header, NULL);
2c2ced9d18f0b065f620b0ead09ba25e955c3cc7sf if (status != APR_SUCCESS && !APR_STATUS_IS_ENOENT(status)) {
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc apr_file_printf(errfile, "Could not remove file %s: %pm" APR_EOL_STR,
2c2ced9d18f0b065f620b0ead09ba25e955c3cc7sf if (status != APR_SUCCESS && !APR_STATUS_IS_ENOENT(status)) {
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc apr_file_printf(errfile, "Could not remove file %s: %pm" APR_EOL_STR,
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin status = remove_directory(pool, apr_pstrcat(pool, base, "/", vdir, NULL));
2c2ced9d18f0b065f620b0ead09ba25e955c3cc7sf if (status != APR_SUCCESS && !APR_STATUS_IS_ENOENT(status)) {
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin /* If asked to delete dirs, do so now. We don't care if it fails.
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin * If it fails, it likely means there was something else there.
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin * Delete a specific URL from the cache.
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrinstatic apr_status_t delete_url(apr_pool_t *pool, const char *proxypath, const char *url)
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin unsigned int x;
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin apr_md5_update(&context, (const unsigned char *) url, strlen(url));
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin /* encode 128 bits as 22 characters, using a modified uuencoding
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin * the encoding is 3 bytes -> 4 characters* i.e. 128 bits is
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin * 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin /* one byte left */
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin /* automatically find the directory levels */
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * usage info
9333012aa2af9e669db651f0ade3e044ccc3ec84rpluem apr_file_printf(errfile, "%s error: %s\n", shortname, error);
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin "Usage: %s [-Dvtrn] -pPATH [-lLIMIT|-LLIMIT] [-PPIDFILE]" NL
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin " %s [-nti] -dINTERVAL -pPATH [-lLIMIT|-LLIMIT] [-PPIDFILE]" NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -d Daemonize and repeat cache cleaning every INTERVAL minutes." NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " This option is mutually exclusive with the -D, -v and -r" NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -D Do a dry run and don't delete anything. This option is mutually" NL
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin " exclusive with the -d option. When doing a dry run and deleting" NL
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin " directories with -t, the inodes reported deleted in the stats" NL
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin " cannot take into account the directories deleted, and will be" NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -v Be verbose and print statistics. This option is mutually" NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -r Clean thoroughly. This assumes that the Apache web server is " NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " not running. This option is mutually exclusive with the -d" NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -n Be nice. This causes slower processing in favour of other" NL
663c339c8a4663cf1977f890481f8b7e251d3221jerenkrantz " -t Delete all empty directories. By default only cache files are" NL
663c339c8a4663cf1977f890481f8b7e251d3221jerenkrantz " removed, however with some configurations the large number of" NL
663c339c8a4663cf1977f890481f8b7e251d3221jerenkrantz " directories created may require attention." NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -p Specify PATH as the root directory of the disk cache." NL
4dd97ffc9c529494e0ec820fe9de80b39b589d42minfrin " -P Specify PIDFILE as the file to write the pid to." NL
487d8b4e063696dd9698792a13c8e426635f1072nd " -l Specify LIMIT as the total disk cache size limit. Attach 'K'" NL
487d8b4e063696dd9698792a13c8e426635f1072nd " or 'M' to the number for specifying KBytes or MBytes." NL
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin " -L Specify LIMIT as the total disk cache inode limit." NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " -i Be intelligent and run only when there was a modification of" NL
2d728410bcf9e6e53698bb035f516e18efd76d8bnd " the disk cache. This option is only possible together with the" NL
5876f43a746f688a32b7201bced8591ddf19bd43minfrin " -a List the URLs currently stored in the cache. Variants of the" NL
5876f43a746f688a32b7201bced8591ddf19bd43minfrin " same URL will be listed once for each variant." NL
5876f43a746f688a32b7201bced8591ddf19bd43minfrin " -A List the URLs currently stored in the cache, along with their" NL
5876f43a746f688a32b7201bced8591ddf19bd43minfrin " attributes in the following order: url, header size, body size," NL
5876f43a746f688a32b7201bced8591ddf19bd43minfrin " status, entity version, date, expiry, request time," NL
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "Should an URL be provided on the command line, the URL will be" NL
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "deleted from the cache. A reverse proxied URL is made up as follows:" NL
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "http://<hostname>:<port><path>?[query]. So, for the path \"/\" on the" NL
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "host \"localhost\" and port 80, the URL to delete becomes" NL
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "\"http://localhost:80/?\". Note the '?' in the URL must always be" NL
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin "specified explicitly, whether a query string is present or not." NL,
4e1b035de81cbd18cfaea3604d40209bc6919bd9trawickstatic void usage_repeated_arg(apr_pool_t *pool, char option) {
4e1b035de81cbd18cfaea3604d40209bc6919bd9trawick "The option '%c' cannot be specified more than once",
fc1206972499ab73c4dfded43cef279d8bf01ad9minfrinstatic void log_pid(apr_pool_t *pool, const char *pidfilename, apr_file_t **pidfile)
5c345baea74f2eb2d74548bed271370b972499e5minfrin if (APR_SUCCESS == (status = apr_file_open(pidfile, pidfilename,
5c345baea74f2eb2d74548bed271370b972499e5minfrin APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE |
5c345baea74f2eb2d74548bed271370b972499e5minfrin APR_FOPEN_DELONCLOSE, APR_FPROT_UREAD | APR_FPROT_UWRITE |
fc1206972499ab73c4dfded43cef279d8bf01ad9minfrin apr_file_printf(*pidfile, "%" APR_PID_T_FMT APR_EOL_STR, mypid);
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc "Could not write the pid file '%s': %pm" APR_EOL_STR,
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin int retries, isdaemon, limit_found, inodes_found, intelligent, dowork;
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin status = apr_getopt(o, "iDnvrtd:l:L:p:P:R:aA", &opt, &arg);
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd switch (opt) {
487d8b4e063696dd9698792a13c8e426635f1072nd } while(0);
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin } while(0);
1018201f5223624476334c6e23aead02db7c4040minfrin if ((status = apr_filepath_set(proxypath, pool)) != APR_SUCCESS) {
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc usage(apr_psprintf(pool, "Could not set filepath to '%s': %pm",
fac37c9794a18c24d187f4e0f97a9476c4344118minfrin else if (round < 0) {
fac37c9794a18c24d187f4e0f97a9476c4344118minfrin usage(apr_psprintf(pool, "Round value must be positive: %s"
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd } /* switch */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd } /* else */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd } /* while */
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin usage("Option -d cannot be used with URL arguments, aborting");
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin usage("Option -i cannot be used with URL arguments, aborting");
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin usage("Option -l cannot be used with URL arguments, aborting");
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin apr_file_printf(errfile, "Not cached: %s" APR_EOL_STR,
6fee4e2faa2e45fe2636d01e35d03c2cf0c9d431minfrin apr_file_printf(errfile, "Error while removed: %s" APR_EOL_STR,
5876f43a746f688a32b7201bced8591ddf19bd43minfrin if (isdaemon && (verbose || realclean || dryrun || listurls)) {
5876f43a746f688a32b7201bced8591ddf19bd43minfrin usage("Option -d cannot be used with -v, -r, -L or -D");
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin usage("At least one of option -l or -L must be greater than zero");
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz if (apr_filepath_get(&path, 0, pool) != APR_SUCCESS) {
46a4dc3f1dfeb9c4cb76727c56ed0a2230acb7cdjailletc usage(apr_psprintf(pool, "Could not get the filepath: %pm", &status));
fc1206972499ab73c4dfded43cef279d8bf01ad9minfrin log_pid(pool, pidfilename, &pidfile); /* before daemonizing, so we
fc1206972499ab73c4dfded43cef279d8bf01ad9minfrin * can report errors
5876f43a746f688a32b7201bced8591ddf19bd43minfrin return (interrupted != 0);
fc1206972499ab73c4dfded43cef279d8bf01ad9minfrin apr_file_close(pidfile); /* delete original pidfile only in parent */
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz } while (status != APR_SUCCESS && !interrupted && --retries);
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd } while (status != APR_SUCCESS && !interrupted && --retries);
f58bb3da705eb7ec926f4883597fc2eb1336a360minfrin if (!process_dir(path, instance, &nodes) && !interrupted) {
a51acf58d9d82f52e0ee56106cd9282030f3f3bend apr_file_printf(errfile, "An error occurred, cache cleaning "
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz } while (status != APR_SUCCESS && !interrupted && --retries);
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz /* we can't sleep the whole delay time here apiece as this is racy
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * with respect to interrupt delivery - think about what happens
9fe74ffcdea85800f04a7222f716f78ae60cce51jerenkrantz * if we have tested for an interrupt, then get scheduled
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * before the apr_sleep() call and while waiting for the cpu
03bdb4fb430b0d4e502ddfc75f7e9dbd91db72e9nd * we do get an interrupt
a51acf58d9d82f52e0ee56106cd9282030f3f3bend apr_file_printf(errfile, "Cache cleaning aborted due to user "