/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * htcacheclean.c: simple program for cleaning of * the disk cache of the Apache HTTP server * * Contributed by Andreas Steinmetz <ast domdv.de> * 8 Oct 2004
*/
typedefstruct _direntry {
APR_RING_ENTRY(_direntry) link; int type; /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
apr_time_t htime; /* headers file modification time */
apr_time_t dtime; /* body file modification time */
apr_off_t hsize; /* headers file size */
apr_off_t dsize; /* body or temporary file size */ char *basename; /* file/fileset base name */
} DIRENTRY;
typedefstruct _entry {
APR_RING_ENTRY(_entry) link;
apr_time_t expire; /* cache entry exiration time */
apr_time_t response_time; /* cache entry time of last response to client */
apr_time_t htime; /* headers file modification time */
apr_time_t dtime; /* body file modification time */
apr_off_t hsize; /* headers file size */
apr_off_t dsize; /* body or temporary file size */ char *basename; /* fileset base name */
} ENTRY;
staticint delcount; /* file deletion count for nice mode */ staticint interrupted; /* flag: true if SIGINT or SIGTERM occurred */ staticint realclean; /* flag: true means user said apache is not running */ staticint verbose; /* flag: true means print statistics */ staticint benice; /* flag: true means nice mode is activated */ staticint dryrun; /* flag: true means dry run, don't actually delete
anything */ staticint deldirs; /* flag: true means directories should be deleted */ staticint listurls; /* flag: true means list cached urls */ staticint listextended;/* flag: true means list cached urls */ staticint baselen; /* string length of the path to the proxy directory */ static apr_time_t now; /* start time of this processing run */
/* stat and printing to simulate some deletion system load and to
display what would actually have happened */
apr_stat(&info, pathname, DIRINFO, p);
apr_file_printf(errfile, "would delete %s" APR_EOL_STR, pathname);
} #endif
/* * called on SIGINT or SIGTERM
*/ staticvoid setterm(int unused)
{ #ifdef DEBUG
apr_file_printf(errfile, "interrupt" APR_EOL_STR); #endif
interrupted = 1;
}
/* * called in out of memory condition
*/ staticint oom(int unused)
{ staticint called = 0;
/* be careful to call exit() only once */ if (!called) {
called = 1; exit(1);
} return APR_ENOMEM;
}
/* temp pool, otherwise lots of memory could be allocated */
apr_pool_create(&p, pool);
name = apr_pstrdup(p, basename);
/* If asked to delete dirs, do so now. We don't care if it fails. * If it fails, it likely means there was something else there.
*/ if (deldirs && !dryrun) { constchar *vary; char *end = strrchr(name, '/'); while (end) {
*end = 0;
/* remove the directory */
nextpath = apr_pstrcat(p, path, "/", name, NULL); if (!apr_dir_remove(nextpath, p)) {
(*nodes)--;
for (d = APR_RING_FIRST(&anchor.link);
!interrupted && d != APR_RING_SENTINEL(&anchor.link, _direntry, link);
d=n) {
n = APR_RING_NEXT(d, link);
base = strrchr(d->basename, '/'); if (!base++) {
base = d->basename;
}
ext = strchr(base, '.');
/* there may be temporary files which may be gone before * processing, always skip these if not in realclean mode
*/ if (!ext && !realclean) { if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
&& strlen(base) == AP_TEMPFILE_NAMELEN) { continue;
}
}
/* this may look strange but apr_stat() may return an error which * is system dependent and there may be transient failures, * so just blindly retry for a short while
*/
retries = STAT_ATTEMPTS;
status = APR_SUCCESS; do { if (status != APR_SUCCESS) {
apr_sleep(STAT_DELAY);
}
status = apr_stat(&info, d->basename, DIRINFO, p);
} while (status != APR_SUCCESS && !interrupted && --retries);
/* what may happen here is that apache did create a file which * we did detect but then does delete the file before we can * get file information, so if we don't get any file information * we will ignore the file in this case
*/ if (status != APR_SUCCESS) { if (!realclean && !interrupted) { continue;
} return 1;
}
if (info.filetype == APR_DIR) { char *dirpath = apr_pstrdup(p, d->basename);
if (process_dir(d->basename, pool, nodes)) { return 1;
} /* When given the -t option htcacheclean does not * delete directories that are already empty, so we'll do that here * since process_dir checks all the directories. * If it fails, it likely means there was something else there.
*/ if (deldirs && !dryrun) {
apr_dir_remove(dirpath, p);
} continue;
}
} /* we have a somehow unreadable headers file which is associated * with a data file. this may be caused by apache currently * rewriting the headers file. thus we may delete the file set * either in realclean mode or if the headers file modification * timestamp is not within a specified positive or negative offset * to the current time.
*/
current = apr_time_now(); if (realclean || d->htime < current - deviation
|| d->htime > current + deviation) {
delete_entry(path, d->basename, nodes, p);
unsolicited += d->hsize;
unsolicited += d->dsize;
} break;
/* single data and header files may be deleted either in realclean * mode or if their modification timestamp is not within a * specified positive or negative offset to the current time. * this handling is necessary due to possible race conditions * between apache and this process
*/ case HEADER:
current = apr_time_now();
nextpath = apr_pstrcat(p, path, "/", d->basename,
CACHE_HEADER_SUFFIX, NULL); if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
APR_OS_DEFAULT, p) == APR_SUCCESS) {
len = sizeof(format); if (apr_file_read_full(fd, &format, len,
&len) == APR_SUCCESS) { if (format == VARY_FORMAT_VERSION) {
apr_time_t expires;
len = sizeof(expires);
if (apr_file_read_full(fd, &expires, len,
&len) == APR_SUCCESS) {
apr_finfo_t finfo;
if (realclean || d->htime < current - deviation
|| d->htime > current + deviation) {
delete_entry(path, d->basename, nodes, p);
unsolicited += d->hsize;
} break;
case DATA:
current = apr_time_now(); if (realclean || d->dtime < current - deviation
|| d->dtime > current + deviation) {
delete_entry(path, d->basename, nodes, p);
unsolicited += d->dsize;
} break;
/* temp files may only be deleted in realclean mode which * is asserted above if a tempfile is in the hash array
*/ case TEMP:
delete_file(path, d->basename, nodes, p);
unsolicited += d->dsize; break;
}
}
/* process all entries with a timestamp in the future, this may * happen if a wrong system time is corrected
*/
for (e = APR_RING_FIRST(&root.link);
e != APR_RING_SENTINEL(&root.link, _entry, link) && !interrupted;) {
n = APR_RING_NEXT(e, link); if (e->response_time > now || e->htime > now || e->dtime > now) {
delete_entry(path, e->basename, &s.nodes, pool);
s.sum -= round_up((apr_size_t)e->hsize, round);
s.sum -= round_up((apr_size_t)e->dsize, round);
s.entries--;
s.dfuture++;
APR_RING_REMOVE(e, link); if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) { if (!interrupted) {
printstats(path, &s);
} return;
}
}
e = n;
}
if (interrupted) { return;
}
/* process all entries which are expired */ for (e = APR_RING_FIRST(&root.link);
e != APR_RING_SENTINEL(&root.link, _entry, link) && !interrupted;) {
n = APR_RING_NEXT(e, link); if (e->expire != APR_DATE_BAD && e->expire < now) {
delete_entry(path, e->basename, &s.nodes, pool);
s.sum -= round_up((apr_size_t)e->hsize, round);
s.sum -= round_up((apr_size_t)e->dsize, round);
s.entries--;
s.dexpired++;
APR_RING_REMOVE(e, link); if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) { if (!interrupted) {
printstats(path, &s);
} return;
}
}
e = n;
}
if (interrupted) { return;
}
/* process remaining entries oldest to newest, the check for an empty * ring actually isn't necessary except when the compiler does * corrupt 64bit arithmetics which happened to me once, so better safe * than sorry
*/ while (!((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes))
&& !interrupted && !APR_RING_EMPTY(&root.link, _entry, link)) {
oldest = APR_RING_FIRST(&root.link);
for (e = APR_RING_NEXT(oldest, link);
e != APR_RING_SENTINEL(&root.link, _entry, link);
e = APR_RING_NEXT(e, link)) { if (e->dtime < oldest->dtime) {
oldest = e;
}
}
/* If asked to delete dirs, do so now. We don't care if it fails. * If it fails, it likely means there was something else there.
*/ if (dirname && deldirs && !dryrun) {
apr_dir_remove(dirname, pool);
}
if (found) { return APR_SUCCESS;
}
return rv;
}
/** * Delete a specific URL from the cache.
*/ static apr_status_t delete_url(apr_pool_t *pool, constchar *proxypath, constchar *url)
{
apr_md5_ctx_t context; unsignedchar digest[16]; char tmp[23]; int i, k; unsignedint x; staticconstchar enc_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
/* * usage info
*/ #define NL APR_EOL_STR staticvoid usage(constchar *error)
{ if (error) {
apr_file_printf(errfile, "%s error: %s\n", shortname, error);
}
apr_file_printf(errfile, "%s -- program for cleaning the disk cache." NL "Usage: %s [-Dvtrn] -pPATH [-lLIMIT] [-LLIMIT] [-PPIDFILE]" NL " %s [-nti] -dINTERVAL -pPATH [-lLIMIT] [-LLIMIT] [-PPIDFILE]" NL " %s [-Dvt] -pPATH URL ..." NL
NL "Options:" NL " -d Daemonize and repeat cache cleaning every INTERVAL minutes." NL " This option is mutually exclusive with the -D, -v and -r" NL " options." NL
NL " -D Do a dry run and don't delete anything. This option is mutually" NL " exclusive with the -d option. When doing a dry run and deleting" NL " directories with -t, the inodes reported deleted in the stats" NL " cannot take into account the directories deleted, and will be" NL " marked as an estimate." NL
NL " -v Be verbose and print statistics. This option is mutually" NL " exclusive with the -d option." NL
NL " -r Clean thoroughly. This assumes that the Apache web server is " NL " not running. This option is mutually exclusive with the -d" NL " option and implies -t." NL
NL " -n Be nice. This causes slower processing in favour of other" NL " processes." NL
NL " -t Delete all empty directories. By default only cache files are" NL " removed, however with some configurations the large number of" NL " directories created may require attention." NL
NL " -p Specify PATH as the root directory of the disk cache." NL
NL " -P Specify PIDFILE as the file to write the pid to." NL
NL " -R Specify amount to round sizes up to." NL
NL " -l Specify LIMIT as the total disk cache size limit. Attach 'K'," NL " 'M' or 'G' to the number for specifying KBytes, MBytes or" NL " GBytes." NL
NL " -L Specify LIMIT as the total disk cache inode limit. 'K', 'M' or" NL " 'G' suffix can also be used." NL
NL " -i Be intelligent and run only when there was a modification of" NL " the disk cache. This option is only possible together with the" NL " -d option." NL
NL " -a List the URLs currently stored in the cache. Variants of the" NL " same URL will be listed once for each variant." NL
NL " -A List the URLs currently stored in the cache, along with their" NL " attributes in the following order: url, header size, body size," NL " status, entity version, date, expiry, request time," NL " response time, body present, head request." NL
NL "Should an URL be provided on the command line, the URL will be" NL "deleted from the cache. A reverse proxied URL is made up as follows:" NL "http://>:?[query]. So, for the path \"/\" on the" NL "host \"localhost\" and port 80, the URL to delete becomes" NL "\"http://localhost:80/?\". Note the '?' in the URL must always be" NL "specified explicitly, whether a query string is present or not." NL,
shortname,
shortname,
shortname,
shortname
);
exit(1);
} #undef NL
staticvoid usage_repeated_arg(apr_pool_t *pool, char option)
{
usage(apr_psprintf(pool, "The option '%c' cannot be specified more than once",
option));
}
case'R': if (round) {
usage_repeated_arg(pool, opt);
}
rv = apr_strtoff(&round, arg, &end, 10); if (rv == APR_SUCCESS) { if (*end) {
usage(apr_psprintf(pool, "Invalid round value: %s"
APR_EOL_STR APR_EOL_STR, arg));
} elseif (round < 0) {
usage(apr_psprintf(pool, "Round value must be positive: %s"
APR_EOL_STR APR_EOL_STR, arg));
}
} if (rv != APR_SUCCESS) {
usage(apr_psprintf(pool, "Invalid round value: %s"
APR_EOL_STR APR_EOL_STR, arg));
} break;
} /* switch */
} /* else */
} /* while */
if (argc <= 1) {
usage(NULL);
}
if (!proxypath) {
usage("Option -p must be specified");
}
if (o->ind < argc) { int deleted = 0; int error = 0; if (isdaemon) {
usage("Option -d cannot be used with URL arguments, aborting");
} if (intelligent) {
usage("Option -i cannot be used with URL arguments, aborting");
} if (limit_found) {
usage("Option -l and -L cannot be used with URL arguments, aborting");
} while (o->ind < argc) {
status = delete_url(pool, proxypath, argv[o->ind]); if (APR_SUCCESS == status) { if (verbose) {
apr_file_printf(errfile, "Removed: %s" APR_EOL_STR,
argv[o->ind]);
}
deleted = 1;
} elseif (APR_ENOENT == status) { if (verbose) {
apr_file_printf(errfile, "Not cached: %s" APR_EOL_STR,
argv[o->ind]);
}
} else { if (verbose) {
apr_file_printf(errfile, "Error while removed: %s" APR_EOL_STR,
argv[o->ind]);
}
error = 1;
}
o->ind++;
} return error ? 1 : deleted ? 0 : 2;
}
if (isdaemon && repeat <= 0) {
usage("Option -d must be greater than zero");
}
if (isdaemon && (verbose || realclean || dryrun || listurls)) {
usage("Option -d cannot be used with -v, -r, -L or -D");
}
if (!isdaemon && intelligent) {
usage("Option -i cannot be used without -d");
}
if (!listurls && max <= 0 && inodes <= 0) {
usage("At least one of option -l or -L must be greater than zero");
}
if (apr_filepath_get(&path, 0, pool) != APR_SUCCESS) {
usage(apr_psprintf(pool, "Could not get the filepath: %pm", &status));
}
baselen = strlen(path);
if (pidfilename) {
log_pid(pool, pidfilename, &pidfile); /* before daemonizing, so we * can report errors
*/
}
if (listurls) {
list_urls(path, pool, round); return (interrupted != 0);
}
#ifndef DEBUG if (isdaemon) {
apr_file_close(errfile);
errfile = NULL; if (pidfilename) {
apr_file_close(pidfile); /* delete original pidfile only in parent */
}
apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); if (pidfilename) {
log_pid(pool, pidfilename, &pidfile);
}
} #endif
current = apr_time_now(); if (current < now) {
delay = repeat;
} elseif (current - now >= repeat) {
delay = repeat;
} else {
delay = now + repeat - current;
}
/* we can't sleep the whole delay time here apiece as this is racy * with respect to interrupt delivery - think about what happens * if we have tested for an interrupt, then get scheduled * before the apr_sleep() call and while waiting for the cpu * we do get an interrupt
*/ if (isdaemon) { while (delay && !interrupted) { if (delay > APR_USEC_PER_SEC) {
apr_sleep(APR_USEC_PER_SEC);
delay -= APR_USEC_PER_SEC;
} else {
apr_sleep(delay);
delay = 0;
}
}
}
} while (isdaemon && !interrupted);
if (!isdaemon && interrupted) {
apr_file_printf(errfile, "Cache cleaning aborted due to user " "request." APR_EOL_STR); return 1;
}
return 0;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.37Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.