Blame support/htcacheclean.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * htcacheclean.c: simple program for cleaning of
Packit 90a5c9
 * the disk cache of the Apache HTTP server
Packit 90a5c9
 *
Packit 90a5c9
 * Contributed by Andreas Steinmetz <ast domdv.de>
Packit 90a5c9
 * 8 Oct 2004
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "apr.h"
Packit 90a5c9
#include "apr_lib.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_file_io.h"
Packit 90a5c9
#include "apr_file_info.h"
Packit 90a5c9
#include "apr_pools.h"
Packit 90a5c9
#include "apr_hash.h"
Packit 90a5c9
#include "apr_thread_proc.h"
Packit 90a5c9
#include "apr_signal.h"
Packit 90a5c9
#include "apr_getopt.h"
Packit 90a5c9
#include "apr_md5.h"
Packit 90a5c9
#include "apr_ring.h"
Packit 90a5c9
#include "apr_date.h"
Packit 90a5c9
#include "apr_buckets.h"
Packit 90a5c9
Packit 90a5c9
#include "../modules/cache/cache_common.h"
Packit 90a5c9
#include "../modules/cache/cache_disk_common.h"
Packit 90a5c9
Packit 90a5c9
#if APR_HAVE_UNISTD_H
Packit 90a5c9
#include <unistd.h>
Packit 90a5c9
#endif
Packit 90a5c9
#if APR_HAVE_STDLIB_H
Packit 90a5c9
#include <stdlib.h>
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
/* define the following for debugging */
Packit 90a5c9
#undef DEBUG
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Note: on Linux delays <= 2ms are busy waits without
Packit 90a5c9
 *       scheduling, so never use a delay <= 2ms below
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#define NICE_DELAY    10000     /* usecs */
Packit 90a5c9
#define DELETE_NICE   10        /* be nice after this amount of delete ops */
Packit 90a5c9
#define STAT_ATTEMPTS 10        /* maximum stat attempts for a file */
Packit 90a5c9
#define STAT_DELAY    5000      /* usecs */
Packit 90a5c9
#define HEADER        1         /* headers file */
Packit 90a5c9
#define DATA          2         /* body file */
Packit 90a5c9
#define TEMP          4         /* temporary file */
Packit 90a5c9
#define HEADERDATA    (HEADER|DATA)
Packit 90a5c9
#define MAXDEVIATION  3600      /* secs */
Packit 90a5c9
#define SECS_PER_MIN  60
Packit 90a5c9
#define KBYTE         1024
Packit 90a5c9
#define MBYTE         1048576
Packit 90a5c9
#define GBYTE         1073741824
Packit 90a5c9
Packit 90a5c9
#define DIRINFO (APR_FINFO_MTIME|APR_FINFO_SIZE|APR_FINFO_TYPE|APR_FINFO_LINK)
Packit 90a5c9
Packit 90a5c9
typedef struct _direntry {
Packit 90a5c9
    APR_RING_ENTRY(_direntry) link;
Packit 90a5c9
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
Packit 90a5c9
    apr_time_t htime; /* headers file modification time */
Packit 90a5c9
    apr_time_t dtime; /* body file modification time */
Packit 90a5c9
    apr_off_t hsize;  /* headers file size */
Packit 90a5c9
    apr_off_t dsize;  /* body or temporary file size */
Packit 90a5c9
    char *basename;   /* file/fileset base name */
Packit 90a5c9
} DIRENTRY;
Packit 90a5c9
Packit 90a5c9
typedef struct _entry {
Packit 90a5c9
    APR_RING_ENTRY(_entry) link;
Packit 90a5c9
    apr_time_t expire;        /* cache entry exiration time */
Packit 90a5c9
    apr_time_t response_time; /* cache entry time of last response to client */
Packit 90a5c9
    apr_time_t htime;         /* headers file modification time */
Packit 90a5c9
    apr_time_t dtime;         /* body file modification time */
Packit 90a5c9
    apr_off_t hsize;          /* headers file size */
Packit 90a5c9
    apr_off_t dsize;          /* body or temporary file size */
Packit 90a5c9
    char *basename;           /* fileset base name */
Packit 90a5c9
} ENTRY;
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static int delcount;    /* file deletion count for nice mode */
Packit 90a5c9
static int interrupted; /* flag: true if SIGINT or SIGTERM occurred */
Packit 90a5c9
static int realclean;   /* flag: true means user said apache is not running */
Packit 90a5c9
static int verbose;     /* flag: true means print statistics */
Packit 90a5c9
static int benice;      /* flag: true means nice mode is activated */
Packit 90a5c9
static int dryrun;      /* flag: true means dry run, don't actually delete
Packit 90a5c9
                                 anything */
Packit 90a5c9
static int deldirs;     /* flag: true means directories should be deleted */
Packit 90a5c9
static int listurls;    /* flag: true means list cached urls */
Packit 90a5c9
static int listextended;/* flag: true means list cached urls */
Packit 90a5c9
static int baselen;     /* string length of the path to the proxy directory */
Packit 90a5c9
static apr_time_t now;  /* start time of this processing run */
Packit 90a5c9
Packit 90a5c9
static apr_file_t *errfile;   /* stderr file handle */
Packit 90a5c9
static apr_file_t *outfile;   /* stdout file handle */
Packit 90a5c9
static apr_off_t unsolicited; /* file size summary for deleted unsolicited
Packit 90a5c9
                                 files */
Packit 90a5c9
static APR_RING_ENTRY(_entry) root; /* ENTRY ring anchor */
Packit 90a5c9
Packit 90a5c9
/* short program name as called */
Packit 90a5c9
static const char *shortname = "htcacheclean";
Packit 90a5c9
Packit 90a5c9
/* what did we clean? */
Packit 90a5c9
struct stats {
Packit 90a5c9
    apr_off_t total;
Packit 90a5c9
    apr_off_t sum;
Packit 90a5c9
    apr_off_t max;
Packit 90a5c9
    apr_off_t ntotal;
Packit 90a5c9
    apr_off_t nodes;
Packit 90a5c9
    apr_off_t inodes;
Packit 90a5c9
    apr_off_t etotal;
Packit 90a5c9
    apr_off_t entries;
Packit 90a5c9
    apr_off_t dfuture;
Packit 90a5c9
    apr_off_t dexpired;
Packit 90a5c9
    apr_off_t dfresh;
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
#ifdef DEBUG
Packit 90a5c9
/*
Packit 90a5c9
 * fake delete for debug purposes
Packit 90a5c9
 */
Packit 90a5c9
#define apr_file_remove fake_file_remove
Packit 90a5c9
static void fake_file_remove(char *pathname, apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    apr_finfo_t info;
Packit 90a5c9
Packit 90a5c9
    /* stat and printing to simulate some deletion system load and to
Packit 90a5c9
       display what would actually have happened */
Packit 90a5c9
    apr_stat(&info, pathname, DIRINFO, p);
Packit 90a5c9
    apr_file_printf(errfile, "would delete %s" APR_EOL_STR, pathname);
Packit 90a5c9
}
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * called on SIGINT or SIGTERM
Packit 90a5c9
 */
Packit 90a5c9
static void setterm(int unused)
Packit 90a5c9
{
Packit 90a5c9
#ifdef DEBUG
Packit 90a5c9
    apr_file_printf(errfile, "interrupt" APR_EOL_STR);
Packit 90a5c9
#endif
Packit 90a5c9
    interrupted = 1;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * called in out of memory condition
Packit 90a5c9
 */
Packit 90a5c9
static int oom(int unused)
Packit 90a5c9
{
Packit 90a5c9
    static int called = 0;
Packit 90a5c9
Packit 90a5c9
    /* be careful to call exit() only once */
Packit 90a5c9
    if (!called) {
Packit 90a5c9
        called = 1;
Packit 90a5c9
        exit(1);
Packit 90a5c9
    }
Packit 90a5c9
    return APR_ENOMEM;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * print purge statistics
Packit 90a5c9
 */
Packit 90a5c9
static void printstats(char *path, struct stats *s)
Packit 90a5c9
{
Packit 90a5c9
    char ttype, stype, mtype, utype;
Packit 90a5c9
    apr_off_t tfrag, sfrag, ufrag;
Packit 90a5c9
Packit 90a5c9
    if (!verbose) {
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ttype = 'K';
Packit 90a5c9
    tfrag = ((s->total * 10) / KBYTE) % 10;
Packit 90a5c9
    s->total /= KBYTE;
Packit 90a5c9
    if (s->total >= KBYTE) {
Packit 90a5c9
        ttype = 'M';
Packit 90a5c9
        tfrag = ((s->total * 10) / KBYTE) % 10;
Packit 90a5c9
        s->total /= KBYTE;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    stype = 'K';
Packit 90a5c9
    sfrag = ((s->sum * 10) / KBYTE) % 10;
Packit 90a5c9
    s->sum /= KBYTE;
Packit 90a5c9
    if (s->sum >= KBYTE) {
Packit 90a5c9
        stype = 'M';
Packit 90a5c9
        sfrag = ((s->sum * 10) / KBYTE) % 10;
Packit 90a5c9
        s->sum /= KBYTE;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    mtype = 'K';
Packit 90a5c9
    s->max /= KBYTE;
Packit 90a5c9
    if (s->max >= KBYTE) {
Packit 90a5c9
        mtype = 'M';
Packit 90a5c9
        s->max /= KBYTE;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_file_printf(errfile, "Cleaned %s. Statistics:" APR_EOL_STR, path);
Packit 90a5c9
    if (unsolicited) {
Packit 90a5c9
        utype = 'K';
Packit 90a5c9
        ufrag = ((unsolicited * 10) / KBYTE) % 10;
Packit 90a5c9
        unsolicited /= KBYTE;
Packit 90a5c9
        if (unsolicited >= KBYTE) {
Packit 90a5c9
            utype = 'M';
Packit 90a5c9
            ufrag = ((unsolicited * 10) / KBYTE) % 10;
Packit 90a5c9
            unsolicited /= KBYTE;
Packit 90a5c9
        }
Packit 90a5c9
        if (!unsolicited && !ufrag) {
Packit 90a5c9
            ufrag = 1;
Packit 90a5c9
        }
Packit 90a5c9
        apr_file_printf(errfile, "unsolicited size %d.%d%c" APR_EOL_STR,
Packit 90a5c9
                        (int)(unsolicited), (int)(ufrag), utype);
Packit 90a5c9
    }
Packit 90a5c9
    apr_file_printf(errfile, "size limit %" APR_OFF_T_FMT ".0%c" APR_EOL_STR,
Packit 90a5c9
            s->max, mtype);
Packit 90a5c9
    apr_file_printf(errfile, "inodes limit %" APR_OFF_T_FMT APR_EOL_STR,
Packit 90a5c9
            s->inodes);
Packit 90a5c9
    apr_file_printf(
Packit 90a5c9
            errfile,
Packit 90a5c9
            "total size was %" APR_OFF_T_FMT ".%" APR_OFF_T_FMT "%c, total size now "
Packit 90a5c9
            "%" APR_OFF_T_FMT ".%" APR_OFF_T_FMT "%c" APR_EOL_STR, s->total,
Packit 90a5c9
            tfrag, ttype, s->sum, sfrag, stype);
Packit 90a5c9
    apr_file_printf(errfile, "total inodes was %" APR_OFF_T_FMT
Packit 90a5c9
            ", total %sinodes now "
Packit 90a5c9
            "%" APR_OFF_T_FMT APR_EOL_STR, s->ntotal, dryrun && deldirs ? "estimated "
Packit 90a5c9
            : "", s->nodes);
Packit 90a5c9
    apr_file_printf(
Packit 90a5c9
            errfile,
Packit 90a5c9
            "total entries was %" APR_OFF_T_FMT ", total entries now %" APR_OFF_T_FMT
Packit 90a5c9
            APR_EOL_STR, s->etotal, s->entries);
Packit 90a5c9
    apr_file_printf(
Packit 90a5c9
            errfile,
Packit 90a5c9
            "%" APR_OFF_T_FMT " entries deleted (%" APR_OFF_T_FMT " from future, %"
Packit 90a5c9
            APR_OFF_T_FMT " expired, %" APR_OFF_T_FMT " fresh)" APR_EOL_STR,
Packit 90a5c9
            (s->etotal - s->entries), s->dfuture, s->dexpired, s->dfresh);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/**
Packit 90a5c9
 * Round the value up to the given threshold.
Packit 90a5c9
 */
Packit 90a5c9
static apr_size_t round_up(apr_size_t val, apr_off_t round) {
Packit 90a5c9
    if (round > 1) {
Packit 90a5c9
        return (apr_size_t)(((val + round - 1) / round) * round);
Packit 90a5c9
    }
Packit 90a5c9
    return val;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * delete parent directories
Packit 90a5c9
 */
Packit 90a5c9
static void delete_parent(const char *path, const char *basename,
Packit 90a5c9
        apr_off_t *nodes, apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    char *nextpath, *name;
Packit 90a5c9
    apr_pool_t *p;
Packit 90a5c9
Packit 90a5c9
    /* temp pool, otherwise lots of memory could be allocated */
Packit 90a5c9
    apr_pool_create(&p, pool);
Packit 90a5c9
    name = apr_pstrdup(p, basename);
Packit 90a5c9
Packit 90a5c9
    /* If asked to delete dirs, do so now. We don't care if it fails.
Packit 90a5c9
     * If it fails, it likely means there was something else there.
Packit 90a5c9
     */
Packit 90a5c9
    if (deldirs && !dryrun) {
Packit 90a5c9
        const char *vary;
Packit 90a5c9
        char *end = strrchr(name, '/');
Packit 90a5c9
        while (end) {
Packit 90a5c9
            *end = 0;
Packit 90a5c9
Packit 90a5c9
            /* remove the directory */
Packit 90a5c9
            nextpath = apr_pstrcat(p, path, "/", name, NULL);
Packit 90a5c9
            if (!apr_dir_remove(nextpath, p)) {
Packit 90a5c9
                (*nodes)--;
Packit 90a5c9
Packit 90a5c9
                /* vary directory found? */
Packit 90a5c9
                vary = strstr(name, CACHE_VDIR_SUFFIX);
Packit 90a5c9
                if (vary && !vary[sizeof(CACHE_VDIR_SUFFIX) - 1]) {
Packit 90a5c9
                    nextpath = apr_pstrcat(p, path, "/", apr_pstrndup(p, name, vary
Packit 90a5c9
                            - name), NULL);
Packit 90a5c9
                    if (!apr_file_remove(nextpath, p)) {
Packit 90a5c9
                        (*nodes)--;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            end = strrchr(name, '/');
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_pool_destroy(p);
Packit 90a5c9
Packit 90a5c9
    if (benice) {
Packit 90a5c9
        if (++delcount >= DELETE_NICE) {
Packit 90a5c9
            apr_sleep(NICE_DELAY);
Packit 90a5c9
            delcount = 0;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * delete a single file
Packit 90a5c9
 */
Packit 90a5c9
static void delete_file(char *path, char *basename, apr_off_t *nodes,
Packit 90a5c9
        apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    char *nextpath;
Packit 90a5c9
    apr_pool_t *p;
Packit 90a5c9
Packit 90a5c9
    /* temp pool, otherwise lots of memory could be allocated */
Packit 90a5c9
    apr_pool_create(&p, pool);
Packit 90a5c9
    nextpath = apr_pstrcat(p, path, "/", basename, NULL);
Packit 90a5c9
Packit 90a5c9
    if (dryrun) {
Packit 90a5c9
        apr_finfo_t finfo;
Packit 90a5c9
        if (!apr_stat(&finfo, nextpath, APR_FINFO_NLINK, p)) {
Packit 90a5c9
            (*nodes)--;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (!apr_file_remove(nextpath, p)) {
Packit 90a5c9
        (*nodes)--;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_pool_destroy(p);
Packit 90a5c9
Packit 90a5c9
    if (benice) {
Packit 90a5c9
        if (++delcount >= DELETE_NICE) {
Packit 90a5c9
            apr_sleep(NICE_DELAY);
Packit 90a5c9
            delcount = 0;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    delete_parent(path, basename, nodes, pool);
Packit 90a5c9
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * delete cache file set
Packit 90a5c9
 */
Packit 90a5c9
static void delete_entry(char *path, char *basename, apr_off_t *nodes,
Packit 90a5c9
        apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    char *nextpath;
Packit 90a5c9
    apr_pool_t *p;
Packit 90a5c9
Packit 90a5c9
    /* temp pool, otherwise lots of memory could be allocated */
Packit 90a5c9
    apr_pool_create(&p, pool);
Packit 90a5c9
Packit 90a5c9
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
Packit 90a5c9
    if (dryrun) {
Packit 90a5c9
        apr_finfo_t finfo;
Packit 90a5c9
        if (!apr_stat(&finfo, nextpath, APR_FINFO_NLINK, p)) {
Packit 90a5c9
            (*nodes)--;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (!apr_file_remove(nextpath, p)) {
Packit 90a5c9
        (*nodes)--;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
Packit 90a5c9
    if (dryrun) {
Packit 90a5c9
        apr_finfo_t finfo;
Packit 90a5c9
        if (!apr_stat(&finfo, nextpath, APR_FINFO_NLINK, p)) {
Packit 90a5c9
            (*nodes)--;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (!apr_file_remove(nextpath, p)) {
Packit 90a5c9
        (*nodes)--;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_pool_destroy(p);
Packit 90a5c9
Packit 90a5c9
    if (benice) {
Packit 90a5c9
        delcount += 2;
Packit 90a5c9
        if (delcount >= DELETE_NICE) {
Packit 90a5c9
            apr_sleep(NICE_DELAY);
Packit 90a5c9
            delcount = 0;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    delete_parent(path, basename, nodes, pool);
Packit 90a5c9
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * list the cache directory tree
Packit 90a5c9
 */
Packit 90a5c9
static int list_urls(char *path, apr_pool_t *pool, apr_off_t round)
Packit 90a5c9
{
Packit 90a5c9
    apr_dir_t *dir;
Packit 90a5c9
    apr_finfo_t info;
Packit 90a5c9
    apr_size_t len;
Packit 90a5c9
    apr_pool_t *p;
Packit 90a5c9
    apr_file_t *fd;
Packit 90a5c9
    const char *ext, *nextpath;
Packit 90a5c9
    char *url;
Packit 90a5c9
    apr_uint32_t format;
Packit 90a5c9
    disk_cache_info_t disk_info;
Packit 90a5c9
Packit 90a5c9
    apr_pool_create(&p, pool);
Packit 90a5c9
Packit 90a5c9
    if (apr_dir_open(&dir, path, p) != APR_SUCCESS) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (apr_dir_read(&info, APR_FINFO_TYPE, dir) == APR_SUCCESS && !interrupted) {
Packit 90a5c9
Packit 90a5c9
        if (info.filetype == APR_DIR) {
Packit 90a5c9
            if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
Packit 90a5c9
                continue;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (list_urls(apr_pstrcat(p, path, "/", info.name, NULL), pool, round)) {
Packit 90a5c9
                return 1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        else if (info.filetype == APR_REG) {
Packit 90a5c9
Packit 90a5c9
            ext = strchr(info.name, '.');
Packit 90a5c9
Packit 90a5c9
            if (ext && !strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
Packit 90a5c9
Packit 90a5c9
                nextpath = apr_pstrcat(p, path, "/", info.name, NULL);
Packit 90a5c9
Packit 90a5c9
                if (apr_file_open(&fd, nextpath, APR_FOPEN_READ
Packit 90a5c9
                        | APR_FOPEN_BINARY, APR_OS_DEFAULT, p) == APR_SUCCESS) {
Packit 90a5c9
                    len = sizeof(format);
Packit 90a5c9
                    if (apr_file_read_full(fd, &format, len, &len)
Packit 90a5c9
                            == APR_SUCCESS) {
Packit 90a5c9
                        if (format == DISK_FORMAT_VERSION) {
Packit 90a5c9
                            apr_off_t offset = 0;
Packit 90a5c9
Packit 90a5c9
                            apr_file_seek(fd, APR_SET, &offset);
Packit 90a5c9
Packit 90a5c9
                            len = sizeof(disk_cache_info_t);
Packit 90a5c9
Packit 90a5c9
                            if (apr_file_read_full(fd, &disk_info, len, &len)
Packit 90a5c9
                                    == APR_SUCCESS) {
Packit 90a5c9
                                len = disk_info.name_len;
Packit 90a5c9
                                url = apr_palloc(p, len + 1);
Packit 90a5c9
                                url[len] = 0;
Packit 90a5c9
Packit 90a5c9
                                if (apr_file_read_full(fd, url, len, &len)
Packit 90a5c9
                                        == APR_SUCCESS) {
Packit 90a5c9
Packit 90a5c9
                                    if (listextended) {
Packit 90a5c9
                                        apr_finfo_t hinfo, dinfo;
Packit 90a5c9
Packit 90a5c9
                                        /* stat the header file */
Packit 90a5c9
                                        if (APR_SUCCESS != apr_file_info_get(
Packit 90a5c9
                                                &hinfo, APR_FINFO_SIZE, fd)) {
Packit 90a5c9
                                            /* ignore the file */
Packit 90a5c9
                                        }
Packit 90a5c9
                                        else if (disk_info.has_body && APR_SUCCESS
Packit 90a5c9
                                                != apr_stat(
Packit 90a5c9
                                                        &dinfo,
Packit 90a5c9
                                                        apr_pstrcat(
Packit 90a5c9
                                                                p,
Packit 90a5c9
                                                                path,
Packit 90a5c9
                                                                "/",
Packit 90a5c9
                                                                apr_pstrndup(
Packit 90a5c9
                                                                        p,
Packit 90a5c9
                                                                        info.name,
Packit 90a5c9
                                                                        ext
Packit 90a5c9
                                                                                - info.name),
Packit 90a5c9
                                                                CACHE_DATA_SUFFIX,
Packit 90a5c9
                                                                NULL),
Packit 90a5c9
                                                        APR_FINFO_SIZE
Packit 90a5c9
                                                                | APR_FINFO_IDENT,
Packit 90a5c9
                                                        p)) {
Packit 90a5c9
                                            /* ignore the file */
Packit 90a5c9
                                        }
Packit 90a5c9
                                        else if (disk_info.has_body && (dinfo.device
Packit 90a5c9
                                                != disk_info.device
Packit 90a5c9
                                                || dinfo.inode
Packit 90a5c9
                                                        != disk_info.inode)) {
Packit 90a5c9
                                            /* ignore the file */
Packit 90a5c9
                                        }
Packit 90a5c9
                                        else {
Packit 90a5c9
Packit 90a5c9
                                            apr_file_printf(
Packit 90a5c9
                                                    outfile,
Packit 90a5c9
                                                    "%s %" APR_SIZE_T_FMT
Packit 90a5c9
                                                    " %" APR_SIZE_T_FMT
Packit 90a5c9
                                                    " %d %" APR_SIZE_T_FMT
Packit 90a5c9
                                                    " %" APR_TIME_T_FMT
Packit 90a5c9
                                                    " %" APR_TIME_T_FMT
Packit 90a5c9
                                                    " %" APR_TIME_T_FMT
Packit 90a5c9
                                                    " %" APR_TIME_T_FMT
Packit 90a5c9
                                                    " %d %d\n",
Packit 90a5c9
                                                    url,
Packit 90a5c9
                                                    round_up((apr_size_t)hinfo.size, round),
Packit 90a5c9
                                                    round_up(
Packit 90a5c9
                                                            disk_info.has_body ? (apr_size_t)dinfo.size
Packit 90a5c9
                                                                    : 0, round),
Packit 90a5c9
                                                    disk_info.status,
Packit 90a5c9
                                                    disk_info.entity_version,
Packit 90a5c9
                                                    disk_info.date,
Packit 90a5c9
                                                    disk_info.expire,
Packit 90a5c9
                                                    disk_info.request_time,
Packit 90a5c9
                                                    disk_info.response_time,
Packit 90a5c9
                                                    disk_info.has_body,
Packit 90a5c9
                                                    disk_info.header_only);
Packit 90a5c9
                                        }
Packit 90a5c9
                                    }
Packit 90a5c9
                                    else {
Packit 90a5c9
                                        apr_finfo_t dinfo;
Packit 90a5c9
Packit 90a5c9
                                        /* stat the data file */
Packit 90a5c9
                                        if (disk_info.has_body && APR_SUCCESS
Packit 90a5c9
                                                != apr_stat(
Packit 90a5c9
                                                        &dinfo,
Packit 90a5c9
                                                        apr_pstrcat(
Packit 90a5c9
                                                                p,
Packit 90a5c9
                                                                path,
Packit 90a5c9
                                                                "/",
Packit 90a5c9
                                                                apr_pstrndup(
Packit 90a5c9
                                                                        p,
Packit 90a5c9
                                                                        info.name,
Packit 90a5c9
                                                                        ext
Packit 90a5c9
                                                                                - info.name),
Packit 90a5c9
                                                                CACHE_DATA_SUFFIX,
Packit 90a5c9
                                                                NULL),
Packit 90a5c9
                                                        APR_FINFO_SIZE
Packit 90a5c9
                                                                | APR_FINFO_IDENT,
Packit 90a5c9
                                                        p)) {
Packit 90a5c9
                                            /* ignore the file */
Packit 90a5c9
                                        }
Packit 90a5c9
                                        else if (disk_info.has_body && (dinfo.device
Packit 90a5c9
                                                != disk_info.device
Packit 90a5c9
                                                || dinfo.inode
Packit 90a5c9
                                                        != disk_info.inode)) {
Packit 90a5c9
                                            /* ignore the file */
Packit 90a5c9
                                        }
Packit 90a5c9
                                        else {
Packit 90a5c9
                                            apr_file_printf(outfile, "%s\n",
Packit 90a5c9
                                                    url);
Packit 90a5c9
                                        }
Packit 90a5c9
                                    }
Packit 90a5c9
                                }
Packit 90a5c9
Packit 90a5c9
                                break;
Packit 90a5c9
                            }
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    apr_file_close(fd);
Packit 90a5c9
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_dir_close(dir);
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_pool_destroy(p);
Packit 90a5c9
Packit 90a5c9
    if (benice) {
Packit 90a5c9
        apr_sleep(NICE_DELAY);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * walk the cache directory tree
Packit 90a5c9
 */
Packit 90a5c9
static int process_dir(char *path, apr_pool_t *pool, apr_off_t *nodes)
Packit 90a5c9
{
Packit 90a5c9
    apr_dir_t *dir;
Packit 90a5c9
    apr_pool_t *p;
Packit 90a5c9
    apr_hash_t *h;
Packit 90a5c9
    apr_hash_index_t *i;
Packit 90a5c9
    apr_file_t *fd;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    apr_finfo_t info;
Packit 90a5c9
    apr_size_t len;
Packit 90a5c9
    apr_time_t current, deviation;
Packit 90a5c9
    char *nextpath, *base, *ext;
Packit 90a5c9
    APR_RING_ENTRY(_direntry) anchor;
Packit 90a5c9
    DIRENTRY *d, *t, *n;
Packit 90a5c9
    ENTRY *e;
Packit 90a5c9
    int skip, retries;
Packit 90a5c9
    disk_cache_info_t disk_info;
Packit 90a5c9
Packit 90a5c9
    APR_RING_INIT(&anchor, _direntry, link);
Packit 90a5c9
    apr_pool_create(&p, pool);
Packit 90a5c9
    h = apr_hash_make(p);
Packit 90a5c9
    fd = NULL;
Packit 90a5c9
    deviation = MAXDEVIATION * APR_USEC_PER_SEC;
Packit 90a5c9
Packit 90a5c9
    if (apr_dir_open(&dir, path, p) != APR_SUCCESS) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (apr_dir_read(&info, 0, dir) == APR_SUCCESS && !interrupted) {
Packit 90a5c9
        if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
        d = apr_pcalloc(p, sizeof(DIRENTRY));
Packit 90a5c9
        d->basename = apr_pstrcat(p, path, "/", info.name, NULL);
Packit 90a5c9
        APR_RING_INSERT_TAIL(&anchor, d, _direntry, link);
Packit 90a5c9
        (*nodes)++;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_dir_close(dir);
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    skip = baselen + 1;
Packit 90a5c9
Packit 90a5c9
    for (d = APR_RING_FIRST(&anchor);
Packit 90a5c9
         !interrupted && d != APR_RING_SENTINEL(&anchor, _direntry, link);
Packit 90a5c9
         d=n) {
Packit 90a5c9
        n = APR_RING_NEXT(d, link);
Packit 90a5c9
        base = strrchr(d->basename, '/');
Packit 90a5c9
        if (!base++) {
Packit 90a5c9
            base = d->basename;
Packit 90a5c9
        }
Packit 90a5c9
        ext = strchr(base, '.');
Packit 90a5c9
Packit 90a5c9
        /* there may be temporary files which may be gone before
Packit 90a5c9
         * processing, always skip these if not in realclean mode
Packit 90a5c9
         */
Packit 90a5c9
        if (!ext && !realclean) {
Packit 90a5c9
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
Packit 90a5c9
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
Packit 90a5c9
                continue;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* this may look strange but apr_stat() may return an error which
Packit 90a5c9
         * is system dependent and there may be transient failures,
Packit 90a5c9
         * so just blindly retry for a short while
Packit 90a5c9
         */
Packit 90a5c9
        retries = STAT_ATTEMPTS;
Packit 90a5c9
        status = APR_SUCCESS;
Packit 90a5c9
        do {
Packit 90a5c9
            if (status != APR_SUCCESS) {
Packit 90a5c9
                apr_sleep(STAT_DELAY);
Packit 90a5c9
            }
Packit 90a5c9
            status = apr_stat(&info, d->basename, DIRINFO, p);
Packit 90a5c9
        } while (status != APR_SUCCESS && !interrupted && --retries);
Packit 90a5c9
Packit 90a5c9
        /* what may happen here is that apache did create a file which
Packit 90a5c9
         * we did detect but then does delete the file before we can
Packit 90a5c9
         * get file information, so if we don't get any file information
Packit 90a5c9
         * we will ignore the file in this case
Packit 90a5c9
         */
Packit 90a5c9
        if (status != APR_SUCCESS) {
Packit 90a5c9
            if (!realclean && !interrupted) {
Packit 90a5c9
                continue;
Packit 90a5c9
            }
Packit 90a5c9
            return 1;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (info.filetype == APR_DIR) {
Packit 90a5c9
            if (process_dir(d->basename, pool, nodes)) {
Packit 90a5c9
                return 1;
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (info.filetype != APR_REG) {
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!ext) {
Packit 90a5c9
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
Packit 90a5c9
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
Packit 90a5c9
                d->basename += skip;
Packit 90a5c9
                d->type = TEMP;
Packit 90a5c9
                d->dsize = info.size;
Packit 90a5c9
                apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
Packit 90a5c9
            *ext = '\0';
Packit 90a5c9
            d->basename += skip;
Packit 90a5c9
            /* if a user manually creates a '.header' file */
Packit 90a5c9
            if (d->basename[0] == '\0') {
Packit 90a5c9
                continue;
Packit 90a5c9
            }
Packit 90a5c9
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
Packit 90a5c9
            if (t) {
Packit 90a5c9
                d = t;
Packit 90a5c9
            }
Packit 90a5c9
            d->type |= HEADER;
Packit 90a5c9
            d->htime = info.mtime;
Packit 90a5c9
            d->hsize = info.size;
Packit 90a5c9
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(ext, CACHE_DATA_SUFFIX)) {
Packit 90a5c9
            *ext = '\0';
Packit 90a5c9
            d->basename += skip;
Packit 90a5c9
            /* if a user manually creates a '.data' file */
Packit 90a5c9
            if (d->basename[0] == '\0') {
Packit 90a5c9
                continue;
Packit 90a5c9
            }
Packit 90a5c9
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
Packit 90a5c9
            if (t) {
Packit 90a5c9
                d = t;
Packit 90a5c9
            }
Packit 90a5c9
            d->type |= DATA;
Packit 90a5c9
            d->dtime = info.mtime;
Packit 90a5c9
            d->dsize = info.size;
Packit 90a5c9
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    path[baselen] = '\0';
Packit 90a5c9
Packit 90a5c9
    for (i = apr_hash_first(p, h); i && !interrupted; i = apr_hash_next(i)) {
Packit 90a5c9
        void *hvalue;
Packit 90a5c9
        apr_uint32_t format;
Packit 90a5c9
Packit 90a5c9
        apr_hash_this(i, NULL, NULL, &hvalue);
Packit 90a5c9
        d = hvalue;
Packit 90a5c9
Packit 90a5c9
        switch(d->type) {
Packit 90a5c9
        case HEADERDATA:
Packit 90a5c9
            nextpath = apr_pstrcat(p, path, "/", d->basename,
Packit 90a5c9
                                   CACHE_HEADER_SUFFIX, NULL);
Packit 90a5c9
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
Packit 90a5c9
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
Packit 90a5c9
                len = sizeof(format);
Packit 90a5c9
                if (apr_file_read_full(fd, &format, len,
Packit 90a5c9
                                       &len) == APR_SUCCESS) {
Packit 90a5c9
                    if (format == DISK_FORMAT_VERSION) {
Packit 90a5c9
                        apr_off_t offset = 0;
Packit 90a5c9
Packit 90a5c9
                        apr_file_seek(fd, APR_SET, &offset);
Packit 90a5c9
Packit 90a5c9
                        len = sizeof(disk_cache_info_t);
Packit 90a5c9
Packit 90a5c9
                        if (apr_file_read_full(fd, &disk_info, len,
Packit 90a5c9
                                               &len) == APR_SUCCESS) {
Packit 90a5c9
                            apr_file_close(fd);
Packit 90a5c9
                            e = apr_palloc(pool, sizeof(ENTRY));
Packit 90a5c9
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
Packit 90a5c9
                            e->expire = disk_info.expire;
Packit 90a5c9
                            e->response_time = disk_info.response_time;
Packit 90a5c9
                            e->htime = d->htime;
Packit 90a5c9
                            e->dtime = d->dtime;
Packit 90a5c9
                            e->hsize = d->hsize;
Packit 90a5c9
                            e->dsize = d->dsize;
Packit 90a5c9
                            e->basename = apr_pstrdup(pool, d->basename);
Packit 90a5c9
                            if (!disk_info.has_body) {
Packit 90a5c9
                                delete_file(path, apr_pstrcat(p, path, "/",
Packit 90a5c9
                                        d->basename, CACHE_DATA_SUFFIX, NULL),
Packit 90a5c9
                                        nodes, p);
Packit 90a5c9
                            }
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                        else {
Packit 90a5c9
                            apr_file_close(fd);
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    else if (format == VARY_FORMAT_VERSION) {
Packit 90a5c9
                        apr_finfo_t finfo;
Packit 90a5c9
Packit 90a5c9
                        /* This must be a URL that added Vary headers later,
Packit 90a5c9
                         * so kill the orphaned .data file
Packit 90a5c9
                         */
Packit 90a5c9
                        apr_file_close(fd);
Packit 90a5c9
Packit 90a5c9
                        if (apr_stat(&finfo, apr_pstrcat(p, nextpath,
Packit 90a5c9
                                CACHE_VDIR_SUFFIX, NULL), APR_FINFO_TYPE, p)
Packit 90a5c9
                                || finfo.filetype != APR_DIR) {
Packit 90a5c9
                            delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                        }
Packit 90a5c9
                        else {
Packit 90a5c9
                            delete_file(path, apr_pstrcat(p, path, "/",
Packit 90a5c9
                                    d->basename, CACHE_DATA_SUFFIX, NULL),
Packit 90a5c9
                                    nodes, p);
Packit 90a5c9
                        }
Packit 90a5c9
                        break;
Packit 90a5c9
                    }
Packit 90a5c9
                    else {
Packit 90a5c9
                        /* We didn't recognise the format, kill the files */
Packit 90a5c9
                        apr_file_close(fd);
Packit 90a5c9
                        delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                        break;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    apr_file_close(fd);
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
            }
Packit 90a5c9
            /* we have a somehow unreadable headers file which is associated
Packit 90a5c9
             * with a data file. this may be caused by apache currently
Packit 90a5c9
             * rewriting the headers file. thus we may delete the file set
Packit 90a5c9
             * either in realclean mode or if the headers file modification
Packit 90a5c9
             * timestamp is not within a specified positive or negative offset
Packit 90a5c9
             * to the current time.
Packit 90a5c9
             */
Packit 90a5c9
            current = apr_time_now();
Packit 90a5c9
            if (realclean || d->htime < current - deviation
Packit 90a5c9
                || d->htime > current + deviation) {
Packit 90a5c9
                delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                unsolicited += d->hsize;
Packit 90a5c9
                unsolicited += d->dsize;
Packit 90a5c9
            }
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        /* single data and header files may be deleted either in realclean
Packit 90a5c9
         * mode or if their modification timestamp is not within a
Packit 90a5c9
         * specified positive or negative offset to the current time.
Packit 90a5c9
         * this handling is necessary due to possible race conditions
Packit 90a5c9
         * between apache and this process
Packit 90a5c9
         */
Packit 90a5c9
        case HEADER:
Packit 90a5c9
            current = apr_time_now();
Packit 90a5c9
            nextpath = apr_pstrcat(p, path, "/", d->basename,
Packit 90a5c9
                                   CACHE_HEADER_SUFFIX, NULL);
Packit 90a5c9
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
Packit 90a5c9
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
Packit 90a5c9
                len = sizeof(format);
Packit 90a5c9
                if (apr_file_read_full(fd, &format, len,
Packit 90a5c9
                                       &len) == APR_SUCCESS) {
Packit 90a5c9
                    if (format == VARY_FORMAT_VERSION) {
Packit 90a5c9
                        apr_time_t expires;
Packit 90a5c9
Packit 90a5c9
                        len = sizeof(expires);
Packit 90a5c9
Packit 90a5c9
                        if (apr_file_read_full(fd, &expires, len,
Packit 90a5c9
                                               &len) == APR_SUCCESS) {
Packit 90a5c9
                            apr_finfo_t finfo;
Packit 90a5c9
Packit 90a5c9
                            apr_file_close(fd);
Packit 90a5c9
Packit 90a5c9
                            if (apr_stat(&finfo, apr_pstrcat(p, nextpath,
Packit 90a5c9
                                    CACHE_VDIR_SUFFIX, NULL), APR_FINFO_TYPE, p)
Packit 90a5c9
                                    || finfo.filetype != APR_DIR) {
Packit 90a5c9
                                delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                            }
Packit 90a5c9
                            else if (expires < current) {
Packit 90a5c9
                                delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                            }
Packit 90a5c9
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    else if (format == DISK_FORMAT_VERSION) {
Packit 90a5c9
                        apr_off_t offset = 0;
Packit 90a5c9
Packit 90a5c9
                        apr_file_seek(fd, APR_SET, &offset);
Packit 90a5c9
Packit 90a5c9
                        len = sizeof(disk_cache_info_t);
Packit 90a5c9
Packit 90a5c9
                        if (apr_file_read_full(fd, &disk_info, len,
Packit 90a5c9
                                               &len) == APR_SUCCESS) {
Packit 90a5c9
                            apr_file_close(fd);
Packit 90a5c9
                            e = apr_palloc(pool, sizeof(ENTRY));
Packit 90a5c9
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
Packit 90a5c9
                            e->expire = disk_info.expire;
Packit 90a5c9
                            e->response_time = disk_info.response_time;
Packit 90a5c9
                            e->htime = d->htime;
Packit 90a5c9
                            e->dtime = d->dtime;
Packit 90a5c9
                            e->hsize = d->hsize;
Packit 90a5c9
                            e->dsize = d->dsize;
Packit 90a5c9
                            e->basename = apr_pstrdup(pool, d->basename);
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                        else {
Packit 90a5c9
                            apr_file_close(fd);
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    else {
Packit 90a5c9
                        apr_file_close(fd);
Packit 90a5c9
                        delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                        break;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    apr_file_close(fd);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (realclean || d->htime < current - deviation
Packit 90a5c9
                || d->htime > current + deviation) {
Packit 90a5c9
                delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                unsolicited += d->hsize;
Packit 90a5c9
            }
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        case DATA:
Packit 90a5c9
            current = apr_time_now();
Packit 90a5c9
            if (realclean || d->dtime < current - deviation
Packit 90a5c9
                || d->dtime > current + deviation) {
Packit 90a5c9
                delete_entry(path, d->basename, nodes, p);
Packit 90a5c9
                unsolicited += d->dsize;
Packit 90a5c9
            }
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        /* temp files may only be deleted in realclean mode which
Packit 90a5c9
         * is asserted above if a tempfile is in the hash array
Packit 90a5c9
         */
Packit 90a5c9
        case TEMP:
Packit 90a5c9
            delete_file(path, d->basename, nodes, p);
Packit 90a5c9
            unsolicited += d->dsize;
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_pool_destroy(p);
Packit 90a5c9
Packit 90a5c9
    if (benice) {
Packit 90a5c9
        apr_sleep(NICE_DELAY);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * purge cache entries
Packit 90a5c9
 */
Packit 90a5c9
static void purge(char *path, apr_pool_t *pool, apr_off_t max,
Packit 90a5c9
        apr_off_t inodes, apr_off_t nodes, apr_off_t round)
Packit 90a5c9
{
Packit 90a5c9
    ENTRY *e, *n, *oldest;
Packit 90a5c9
Packit 90a5c9
    struct stats s;
Packit 90a5c9
    s.sum = 0;
Packit 90a5c9
    s.entries = 0;
Packit 90a5c9
    s.dfuture = 0;
Packit 90a5c9
    s.dexpired = 0;
Packit 90a5c9
    s.dfresh = 0;
Packit 90a5c9
    s.max = max;
Packit 90a5c9
    s.nodes = nodes;
Packit 90a5c9
    s.inodes = inodes;
Packit 90a5c9
    s.ntotal = nodes;
Packit 90a5c9
Packit 90a5c9
    for (e = APR_RING_FIRST(&root);
Packit 90a5c9
         e != APR_RING_SENTINEL(&root, _entry, link);
Packit 90a5c9
         e = APR_RING_NEXT(e, link)) {
Packit 90a5c9
        s.sum += round_up((apr_size_t)e->hsize, round);
Packit 90a5c9
        s.sum += round_up((apr_size_t)e->dsize, round);
Packit 90a5c9
        s.entries++;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    s.total = s.sum;
Packit 90a5c9
    s.etotal = s.entries;
Packit 90a5c9
Packit 90a5c9
    if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) {
Packit 90a5c9
        printstats(path, &s);
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* process all entries with a timestamp in the future, this may
Packit 90a5c9
     * happen if a wrong system time is corrected
Packit 90a5c9
     */
Packit 90a5c9
Packit 90a5c9
    for (e = APR_RING_FIRST(&root);
Packit 90a5c9
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
Packit 90a5c9
        n = APR_RING_NEXT(e, link);
Packit 90a5c9
        if (e->response_time > now || e->htime > now || e->dtime > now) {
Packit 90a5c9
            delete_entry(path, e->basename, &s.nodes, pool);
Packit 90a5c9
            s.sum -= round_up((apr_size_t)e->hsize, round);
Packit 90a5c9
            s.sum -= round_up((apr_size_t)e->dsize, round);
Packit 90a5c9
            s.entries--;
Packit 90a5c9
            s.dfuture++;
Packit 90a5c9
            APR_RING_REMOVE(e, link);
Packit 90a5c9
            if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) {
Packit 90a5c9
                if (!interrupted) {
Packit 90a5c9
                    printstats(path, &s);
Packit 90a5c9
                }
Packit 90a5c9
                return;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        e = n;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* process all entries with are expired */
Packit 90a5c9
    for (e = APR_RING_FIRST(&root);
Packit 90a5c9
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
Packit 90a5c9
        n = APR_RING_NEXT(e, link);
Packit 90a5c9
        if (e->expire != APR_DATE_BAD && e->expire < now) {
Packit 90a5c9
            delete_entry(path, e->basename, &s.nodes, pool);
Packit 90a5c9
            s.sum -= round_up((apr_size_t)e->hsize, round);
Packit 90a5c9
            s.sum -= round_up((apr_size_t)e->dsize, round);
Packit 90a5c9
            s.entries--;
Packit 90a5c9
            s.dexpired++;
Packit 90a5c9
            APR_RING_REMOVE(e, link);
Packit 90a5c9
            if ((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes)) {
Packit 90a5c9
                if (!interrupted) {
Packit 90a5c9
                    printstats(path, &s);
Packit 90a5c9
                }
Packit 90a5c9
                return;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        e = n;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (interrupted) {
Packit 90a5c9
         return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* process remaining entries oldest to newest, the check for an empty
Packit 90a5c9
     * ring actually isn't necessary except when the compiler does
Packit 90a5c9
     * corrupt 64bit arithmetics which happened to me once, so better safe
Packit 90a5c9
     * than sorry
Packit 90a5c9
     */
Packit 90a5c9
    while (!((!s.max || s.sum <= s.max) && (!s.inodes || s.nodes <= s.inodes))
Packit 90a5c9
            && !interrupted && !APR_RING_EMPTY(&root, _entry, link)) {
Packit 90a5c9
        oldest = APR_RING_FIRST(&root);
Packit 90a5c9
Packit 90a5c9
        for (e = APR_RING_NEXT(oldest, link);
Packit 90a5c9
             e != APR_RING_SENTINEL(&root, _entry, link);
Packit 90a5c9
             e = APR_RING_NEXT(e, link)) {
Packit 90a5c9
            if (e->dtime < oldest->dtime) {
Packit 90a5c9
                oldest = e;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        delete_entry(path, oldest->basename, &s.nodes, pool);
Packit 90a5c9
        s.sum -= round_up((apr_size_t)oldest->hsize, round);
Packit 90a5c9
        s.sum -= round_up((apr_size_t)oldest->dsize, round);
Packit 90a5c9
        s.entries--;
Packit 90a5c9
        s.dfresh++;
Packit 90a5c9
        APR_RING_REMOVE(oldest, link);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!interrupted) {
Packit 90a5c9
        printstats(path, &s);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t remove_directory(apr_pool_t *pool, const char *dir)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    apr_dir_t *dirp;
Packit 90a5c9
    apr_finfo_t dirent;
Packit 90a5c9
Packit 90a5c9
    rv = apr_dir_open(&dirp, dir, pool);
Packit 90a5c9
    if (APR_STATUS_IS_ENOENT(rv)) {
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
    if (rv != APR_SUCCESS) {
Packit 90a5c9
        apr_file_printf(errfile, "Could not open directory %s: %pm" APR_EOL_STR,
Packit 90a5c9
                dir, &rv;;
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp)
Packit 90a5c9
            == APR_SUCCESS) {
Packit 90a5c9
        if (dirent.filetype == APR_DIR) {
Packit 90a5c9
            if (strcmp(dirent.name, ".") && strcmp(dirent.name, "..")) {
Packit 90a5c9
                rv = remove_directory(pool, apr_pstrcat(pool, dir, "/",
Packit 90a5c9
                        dirent.name, NULL));
Packit 90a5c9
                /* tolerate the directory not being empty, the cache may have
Packit 90a5c9
                 * attempted to recreate the directory in the mean time.
Packit 90a5c9
                 */
Packit 90a5c9
                if (APR_SUCCESS != rv && APR_ENOTEMPTY != rv) {
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        } else {
Packit 90a5c9
            const char *file = apr_pstrcat(pool, dir, "/", dirent.name, NULL);
Packit 90a5c9
            rv = apr_file_remove(file, pool);
Packit 90a5c9
            if (APR_SUCCESS != rv) {
Packit 90a5c9
                apr_file_printf(errfile,
Packit 90a5c9
                        "Could not remove file '%s': %pm" APR_EOL_STR, file,
Packit 90a5c9
                        &rv;;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_dir_close(dirp);
Packit 90a5c9
Packit 90a5c9
    if (rv == APR_SUCCESS) {
Packit 90a5c9
        rv = apr_dir_remove(dir, pool);
Packit 90a5c9
        if (APR_ENOTEMPTY == rv) {
Packit 90a5c9
            rv = APR_SUCCESS;
Packit 90a5c9
        }
Packit 90a5c9
        if (rv != APR_SUCCESS) {
Packit 90a5c9
            apr_file_printf(errfile, "Could not remove directory %s: %pm" APR_EOL_STR,
Packit 90a5c9
                    dir, &rv;;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t find_directory(apr_pool_t *pool, const char *base,
Packit 90a5c9
        const char *rest)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    apr_dir_t *dirp;
Packit 90a5c9
    apr_finfo_t dirent;
Packit 90a5c9
    int found = 0, files = 0;
Packit 90a5c9
    const char *header = apr_pstrcat(pool, rest, CACHE_HEADER_SUFFIX, NULL);
Packit 90a5c9
    const char *data = apr_pstrcat(pool, rest, CACHE_DATA_SUFFIX, NULL);
Packit 90a5c9
    const char *vdir = apr_pstrcat(pool, rest, CACHE_HEADER_SUFFIX,
Packit 90a5c9
            CACHE_VDIR_SUFFIX, NULL);
Packit 90a5c9
    const char *dirname = NULL;
Packit 90a5c9
Packit 90a5c9
    rv = apr_dir_open(&dirp, base, pool);
Packit 90a5c9
    if (rv != APR_SUCCESS) {
Packit 90a5c9
        apr_file_printf(errfile, "Could not open directory %s: %pm" APR_EOL_STR,
Packit 90a5c9
                base, &rv;;
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = APR_ENOENT;
Packit 90a5c9
Packit 90a5c9
    while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp)
Packit 90a5c9
            == APR_SUCCESS) {
Packit 90a5c9
        int len = strlen(dirent.name);
Packit 90a5c9
        int restlen = strlen(rest);
Packit 90a5c9
        if (dirent.filetype == APR_DIR && !strncmp(rest, dirent.name, len)) {
Packit 90a5c9
            dirname = apr_pstrcat(pool, base, "/", dirent.name, NULL);
Packit 90a5c9
            rv = find_directory(pool, dirname, rest + (len < restlen ? len
Packit 90a5c9
                    : restlen));
Packit 90a5c9
            if (APR_SUCCESS == rv) {
Packit 90a5c9
                found = 1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        if (dirent.filetype == APR_DIR) {
Packit 90a5c9
            if (!strcmp(dirent.name, vdir)) {
Packit 90a5c9
                files = 1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        if (dirent.filetype == APR_REG) {
Packit 90a5c9
            if (!strcmp(dirent.name, header) || !strcmp(dirent.name, data)) {
Packit 90a5c9
                files = 1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_dir_close(dirp);
Packit 90a5c9
Packit 90a5c9
    if (files) {
Packit 90a5c9
        rv = APR_SUCCESS;
Packit 90a5c9
        if (!dryrun) {
Packit 90a5c9
            const char *remove;
Packit 90a5c9
            apr_status_t status;
Packit 90a5c9
Packit 90a5c9
            remove = apr_pstrcat(pool, base, "/", header, NULL);
Packit 90a5c9
            status = apr_file_remove(remove, pool);
Packit 90a5c9
            if (status != APR_SUCCESS && !APR_STATUS_IS_ENOENT(status)) {
Packit 90a5c9
                apr_file_printf(errfile, "Could not remove file %s: %pm" APR_EOL_STR,
Packit 90a5c9
                        remove, &status);
Packit 90a5c9
                rv = status;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            remove = apr_pstrcat(pool, base, "/", data, NULL);
Packit 90a5c9
            status = apr_file_remove(remove, pool);
Packit 90a5c9
            if (status != APR_SUCCESS && !APR_STATUS_IS_ENOENT(status)) {
Packit 90a5c9
                apr_file_printf(errfile, "Could not remove file %s: %pm" APR_EOL_STR,
Packit 90a5c9
                        remove, &status);
Packit 90a5c9
                rv = status;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            status = remove_directory(pool, apr_pstrcat(pool, base, "/", vdir, NULL));
Packit 90a5c9
            if (status != APR_SUCCESS && !APR_STATUS_IS_ENOENT(status)) {
Packit 90a5c9
                rv = status;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* If asked to delete dirs, do so now. We don't care if it fails.
Packit 90a5c9
     * If it fails, it likely means there was something else there.
Packit 90a5c9
     */
Packit 90a5c9
    if (dirname && deldirs && !dryrun) {
Packit 90a5c9
        apr_dir_remove(dirname, pool);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (found) {
Packit 90a5c9
        return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/**
Packit 90a5c9
 * Delete a specific URL from the cache.
Packit 90a5c9
 */
Packit 90a5c9
static apr_status_t delete_url(apr_pool_t *pool, const char *proxypath, const char *url)
Packit 90a5c9
{
Packit 90a5c9
    apr_md5_ctx_t context;
Packit 90a5c9
    unsigned char digest[16];
Packit 90a5c9
    char tmp[23];
Packit 90a5c9
    int i, k;
Packit 90a5c9
    unsigned int x;
Packit 90a5c9
    static const char enc_table[64] =
Packit 90a5c9
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
Packit 90a5c9
Packit 90a5c9
    apr_md5_init(&context);
Packit 90a5c9
    apr_md5_update(&context, (const unsigned char *) url, strlen(url));
Packit 90a5c9
    apr_md5_final(digest, &context);
Packit 90a5c9
Packit 90a5c9
    /* encode 128 bits as 22 characters, using a modified uuencoding
Packit 90a5c9
     * the encoding is 3 bytes -> 4 characters* i.e. 128 bits is
Packit 90a5c9
     * 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
Packit 90a5c9
     */
Packit 90a5c9
    for (i = 0, k = 0; i < 15; i += 3) {
Packit 90a5c9
        x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
Packit 90a5c9
        tmp[k++] = enc_table[x >> 18];
Packit 90a5c9
        tmp[k++] = enc_table[(x >> 12) & 0x3f];
Packit 90a5c9
        tmp[k++] = enc_table[(x >> 6) & 0x3f];
Packit 90a5c9
        tmp[k++] = enc_table[x & 0x3f];
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* one byte left */
Packit 90a5c9
    x = digest[15];
Packit 90a5c9
    tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */
Packit 90a5c9
    tmp[k++] = enc_table[(x << 4) & 0x3f];
Packit 90a5c9
    tmp[k] = 0;
Packit 90a5c9
Packit 90a5c9
    /* automatically find the directory levels */
Packit 90a5c9
    return find_directory(pool, proxypath, tmp);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * usage info
Packit 90a5c9
 */
Packit 90a5c9
#define NL APR_EOL_STR
Packit 90a5c9
static void usage(const char *error)
Packit 90a5c9
{
Packit 90a5c9
    if (error) {
Packit 90a5c9
        apr_file_printf(errfile, "%s error: %s\n", shortname, error);
Packit 90a5c9
    }
Packit 90a5c9
    apr_file_printf(errfile,
Packit 90a5c9
    "%s -- program for cleaning the disk cache."                             NL
Packit 90a5c9
    "Usage: %s [-Dvtrn] -pPATH [-lLIMIT|-LLIMIT] [-PPIDFILE]"                NL
Packit 90a5c9
    "       %s [-nti] -dINTERVAL -pPATH [-lLIMIT|-LLIMIT] [-PPIDFILE]"       NL
Packit 90a5c9
    "       %s [-Dvt] -pPATH URL ..."                                        NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "Options:"                                                               NL
Packit 90a5c9
    "  -d   Daemonize and repeat cache cleaning every INTERVAL minutes."     NL
Packit 90a5c9
    "       This option is mutually exclusive with the -D, -v and -r"        NL
Packit 90a5c9
    "       options."                                                        NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -D   Do a dry run and don't delete anything. This option is mutually" NL
Packit 90a5c9
    "       exclusive with the -d option. When doing a dry run and deleting" NL
Packit 90a5c9
    "       directories with -t, the inodes reported deleted in the stats"   NL
Packit 90a5c9
    "       cannot take into account the directories deleted, and will be"   NL
Packit 90a5c9
    "       marked as an estimate."                                          NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -v   Be verbose and print statistics. This option is mutually"        NL
Packit 90a5c9
    "       exclusive with the -d option."                                   NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -r   Clean thoroughly. This assumes that the Apache web server is "   NL
Packit 90a5c9
    "       not running. This option is mutually exclusive with the -d"      NL
Packit 90a5c9
    "       option and implies -t."                                          NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -n   Be nice. This causes slower processing in favour of other"       NL
Packit 90a5c9
    "       processes."                                                      NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -t   Delete all empty directories. By default only cache files are"   NL
Packit 90a5c9
    "       removed, however with some configurations the large number of"   NL
Packit 90a5c9
    "       directories created may require attention."                      NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -p   Specify PATH as the root directory of the disk cache."           NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -P   Specify PIDFILE as the file to write the pid to."                NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -R   Specify amount to round sizes up to."                            NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -l   Specify LIMIT as the total disk cache size limit. Attach 'K'"    NL
Packit 90a5c9
    "       or 'M' to the number for specifying KBytes or MBytes."           NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -L   Specify LIMIT as the total disk cache inode limit."              NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -i   Be intelligent and run only when there was a modification of"    NL
Packit 90a5c9
    "       the disk cache. This option is only possible together with the"  NL
Packit 90a5c9
    "       -d option."                                                      NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -a   List the URLs currently stored in the cache. Variants of the"    NL
Packit 90a5c9
    "       same URL will be listed once for each variant."                  NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "  -A   List the URLs currently stored in the cache, along with their"   NL
Packit 90a5c9
    "       attributes in the following order: url, header size, body size," NL
Packit 90a5c9
    "       status, entity version, date, expiry, request time,"             NL
Packit 90a5c9
    "       response time, body present, head request."                      NL
Packit 90a5c9
                                                                             NL
Packit 90a5c9
    "Should an URL be provided on the command line, the URL will be"         NL
Packit 90a5c9
    "deleted from the cache. A reverse proxied URL is made up as follows:"   NL
Packit 90a5c9
    "http://<hostname>:<port><path>?[query]. So, for the path \"/\" on the"  NL
Packit 90a5c9
    "host \"localhost\" and port 80, the URL to delete becomes"              NL
Packit 90a5c9
    "\"http://localhost:80/?\". Note the '?' in the URL must always be"      NL
Packit 90a5c9
    "specified explicitly, whether a query string is present or not."        NL,
Packit 90a5c9
    shortname,
Packit 90a5c9
    shortname,
Packit 90a5c9
    shortname,
Packit 90a5c9
    shortname
Packit 90a5c9
    );
Packit 90a5c9
Packit 90a5c9
    exit(1);
Packit 90a5c9
}
Packit 90a5c9
#undef NL
Packit 90a5c9
Packit 90a5c9
static void usage_repeated_arg(apr_pool_t *pool, char option) {
Packit 90a5c9
    usage(apr_psprintf(pool,
Packit 90a5c9
                       "The option '%c' cannot be specified more than once",
Packit 90a5c9
                       option));
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void log_pid(apr_pool_t *pool, const char *pidfilename, apr_file_t **pidfile)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    pid_t mypid = getpid();
Packit 90a5c9
Packit 90a5c9
    if (APR_SUCCESS == (status = apr_file_open(pidfile, pidfilename,
Packit 90a5c9
                APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE |
Packit 90a5c9
                APR_FOPEN_DELONCLOSE, APR_FPROT_UREAD | APR_FPROT_UWRITE |
Packit 90a5c9
                APR_FPROT_GREAD | APR_FPROT_WREAD, pool))) {
Packit 90a5c9
        apr_file_printf(*pidfile, "%" APR_PID_T_FMT APR_EOL_STR, mypid);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        if (errfile) {
Packit 90a5c9
            apr_file_printf(errfile,
Packit 90a5c9
                            "Could not write the pid file '%s': %pm" APR_EOL_STR,
Packit 90a5c9
                            pidfilename, &status);
Packit 90a5c9
        }
Packit 90a5c9
        exit(1);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * main
Packit 90a5c9
 */
Packit 90a5c9
int main(int argc, const char * const argv[])
Packit 90a5c9
{
Packit 90a5c9
    apr_off_t max, inodes, round;
Packit 90a5c9
    apr_time_t current, repeat, delay, previous;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    apr_pool_t *pool, *instance;
Packit 90a5c9
    apr_getopt_t *o;
Packit 90a5c9
    apr_finfo_t info;
Packit 90a5c9
    apr_file_t *pidfile;
Packit 90a5c9
    int retries, isdaemon, limit_found, inodes_found, intelligent, dowork;
Packit 90a5c9
    char opt;
Packit 90a5c9
    const char *arg;
Packit 90a5c9
    char *proxypath, *path, *pidfilename;
Packit 90a5c9
Packit 90a5c9
    interrupted = 0;
Packit 90a5c9
    repeat = 0;
Packit 90a5c9
    isdaemon = 0;
Packit 90a5c9
    dryrun = 0;
Packit 90a5c9
    limit_found = 0;
Packit 90a5c9
    inodes_found = 0;
Packit 90a5c9
    max = 0;
Packit 90a5c9
    inodes = 0;
Packit 90a5c9
    round = 0;
Packit 90a5c9
    verbose = 0;
Packit 90a5c9
    realclean = 0;
Packit 90a5c9
    benice = 0;
Packit 90a5c9
    deldirs = 0;
Packit 90a5c9
    intelligent = 0;
Packit 90a5c9
    previous = 0; /* avoid compiler warning */
Packit 90a5c9
    proxypath = NULL;
Packit 90a5c9
    pidfilename = NULL;
Packit 90a5c9
Packit 90a5c9
    if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
    atexit(apr_terminate);
Packit 90a5c9
Packit 90a5c9
    if (argc) {
Packit 90a5c9
        shortname = apr_filepath_name_get(argv[0]);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
    apr_pool_abort_set(oom, pool);
Packit 90a5c9
    apr_file_open_stderr(&errfile, pool);
Packit 90a5c9
    apr_file_open_stdout(&outfile, pool);
Packit 90a5c9
    apr_signal(SIGINT, setterm);
Packit 90a5c9
    apr_signal(SIGTERM, setterm);
Packit 90a5c9
Packit 90a5c9
    apr_getopt_init(&o, pool, argc, argv);
Packit 90a5c9
Packit 90a5c9
    while (1) {
Packit 90a5c9
        status = apr_getopt(o, "iDnvrtd:l:L:p:P:R:aA", &opt, &arg;;
Packit 90a5c9
        if (status == APR_EOF) {
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
        else if (status != APR_SUCCESS) {
Packit 90a5c9
            usage(NULL);
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            char *end;
Packit 90a5c9
            apr_status_t rv;
Packit 90a5c9
            switch (opt) {
Packit 90a5c9
            case 'i':
Packit 90a5c9
                if (intelligent) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                intelligent = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'D':
Packit 90a5c9
                if (dryrun) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                dryrun = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'n':
Packit 90a5c9
                if (benice) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                benice = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 't':
Packit 90a5c9
                if (deldirs) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                deldirs = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'v':
Packit 90a5c9
                if (verbose) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                verbose = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'r':
Packit 90a5c9
                if (realclean) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                realclean = 1;
Packit 90a5c9
                deldirs = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'd':
Packit 90a5c9
                if (isdaemon) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                isdaemon = 1;
Packit 90a5c9
                repeat = apr_atoi64(arg);
Packit 90a5c9
                repeat *= SECS_PER_MIN;
Packit 90a5c9
                repeat *= APR_USEC_PER_SEC;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'l':
Packit 90a5c9
                if (limit_found) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                limit_found = 1;
Packit 90a5c9
Packit 90a5c9
                do {
Packit 90a5c9
                    rv = apr_strtoff(&max, arg, &end, 10);
Packit 90a5c9
                    if (rv == APR_SUCCESS) {
Packit 90a5c9
                        if ((*end == 'K' || *end == 'k') && !end[1]) {
Packit 90a5c9
                            max *= KBYTE;
Packit 90a5c9
                        }
Packit 90a5c9
                        else if ((*end == 'M' || *end == 'm') && !end[1]) {
Packit 90a5c9
                            max *= MBYTE;
Packit 90a5c9
                        }
Packit 90a5c9
                        else if ((*end == 'G' || *end == 'g') && !end[1]) {
Packit 90a5c9
                            max *= GBYTE;
Packit 90a5c9
                        }
Packit 90a5c9
                        else if (*end &&        /* neither empty nor [Bb] */
Packit 90a5c9
                                 ((*end != 'B' && *end != 'b') || end[1])) {
Packit 90a5c9
                            rv = APR_EGENERAL;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    if (rv != APR_SUCCESS) {
Packit 90a5c9
                        usage(apr_psprintf(pool, "Invalid limit: %s"
Packit 90a5c9
                                                 APR_EOL_STR APR_EOL_STR, arg));
Packit 90a5c9
                    }
Packit 90a5c9
                } while(0);
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'L':
Packit 90a5c9
                if (inodes_found) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                inodes_found = 1;
Packit 90a5c9
Packit 90a5c9
                do {
Packit 90a5c9
                    rv = apr_strtoff(&inodes, arg, &end, 10);
Packit 90a5c9
                    if (rv == APR_SUCCESS) {
Packit 90a5c9
                        if ((*end == 'K' || *end == 'k') && !end[1]) {
Packit 90a5c9
                            inodes *= KBYTE;
Packit 90a5c9
                        }
Packit 90a5c9
                        else if ((*end == 'M' || *end == 'm') && !end[1]) {
Packit 90a5c9
                            inodes *= MBYTE;
Packit 90a5c9
                        }
Packit 90a5c9
                        else if ((*end == 'G' || *end == 'g') && !end[1]) {
Packit 90a5c9
                            inodes *= GBYTE;
Packit 90a5c9
                        }
Packit 90a5c9
                        else if (*end &&        /* neither empty nor [Bb] */
Packit 90a5c9
                                 ((*end != 'B' && *end != 'b') || end[1])) {
Packit 90a5c9
                            rv = APR_EGENERAL;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    if (rv != APR_SUCCESS) {
Packit 90a5c9
                        usage(apr_psprintf(pool, "Invalid limit: %s"
Packit 90a5c9
                                                 APR_EOL_STR APR_EOL_STR, arg));
Packit 90a5c9
                    }
Packit 90a5c9
                } while(0);
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'a':
Packit 90a5c9
                if (listurls) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                listurls = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'A':
Packit 90a5c9
                if (listurls) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                listurls = 1;
Packit 90a5c9
                listextended = 1;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'p':
Packit 90a5c9
                if (proxypath) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                proxypath = apr_pstrdup(pool, arg);
Packit 90a5c9
                if ((status = apr_filepath_set(proxypath, pool)) != APR_SUCCESS) {
Packit 90a5c9
                    usage(apr_psprintf(pool, "Could not set filepath to '%s': %pm",
Packit 90a5c9
                                       proxypath, &status));
Packit 90a5c9
                }
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'P':
Packit 90a5c9
                if (pidfilename) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                pidfilename = apr_pstrdup(pool, arg);
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'R':
Packit 90a5c9
                if (round) {
Packit 90a5c9
                    usage_repeated_arg(pool, opt);
Packit 90a5c9
                }
Packit 90a5c9
                rv = apr_strtoff(&round, arg, &end, 10);
Packit 90a5c9
                if (rv == APR_SUCCESS) {
Packit 90a5c9
                    if (*end) {
Packit 90a5c9
                        usage(apr_psprintf(pool, "Invalid round value: %s"
Packit 90a5c9
                                                 APR_EOL_STR APR_EOL_STR, arg));
Packit 90a5c9
                    }
Packit 90a5c9
                    else if (round < 0) {
Packit 90a5c9
                        usage(apr_psprintf(pool, "Round value must be positive: %s"
Packit 90a5c9
                                                 APR_EOL_STR APR_EOL_STR, arg));
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                if (rv != APR_SUCCESS) {
Packit 90a5c9
                    usage(apr_psprintf(pool, "Invalid round value: %s"
Packit 90a5c9
                                             APR_EOL_STR APR_EOL_STR, arg));
Packit 90a5c9
                }
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            } /* switch */
Packit 90a5c9
        } /* else */
Packit 90a5c9
    } /* while */
Packit 90a5c9
Packit 90a5c9
    if (argc <= 1) {
Packit 90a5c9
        usage(NULL);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!proxypath) {
Packit 90a5c9
         usage("Option -p must be specified");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (o->ind < argc) {
Packit 90a5c9
        int deleted = 0;
Packit 90a5c9
        int error = 0;
Packit 90a5c9
        if (isdaemon) {
Packit 90a5c9
            usage("Option -d cannot be used with URL arguments, aborting");
Packit 90a5c9
        }
Packit 90a5c9
        if (intelligent) {
Packit 90a5c9
            usage("Option -i cannot be used with URL arguments, aborting");
Packit 90a5c9
        }
Packit 90a5c9
        if (limit_found) {
Packit 90a5c9
            usage("Option -l cannot be used with URL arguments, aborting");
Packit 90a5c9
        }
Packit 90a5c9
        while (o->ind < argc) {
Packit 90a5c9
            status = delete_url(pool, proxypath, argv[o->ind]);
Packit 90a5c9
            if (APR_SUCCESS == status) {
Packit 90a5c9
                if (verbose) {
Packit 90a5c9
                    apr_file_printf(errfile, "Removed: %s" APR_EOL_STR,
Packit 90a5c9
                            argv[o->ind]);
Packit 90a5c9
                }
Packit 90a5c9
                deleted = 1;
Packit 90a5c9
            }
Packit 90a5c9
            else if (APR_ENOENT == status) {
Packit 90a5c9
                if (verbose) {
Packit 90a5c9
                    apr_file_printf(errfile, "Not cached: %s" APR_EOL_STR,
Packit 90a5c9
                            argv[o->ind]);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                if (verbose) {
Packit 90a5c9
                    apr_file_printf(errfile, "Error while removed: %s" APR_EOL_STR,
Packit 90a5c9
                            argv[o->ind]);
Packit 90a5c9
                }
Packit 90a5c9
                error = 1;
Packit 90a5c9
            }
Packit 90a5c9
            o->ind++;
Packit 90a5c9
        }
Packit 90a5c9
        return error ? 1 : deleted ? 0 : 2;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (isdaemon && repeat <= 0) {
Packit 90a5c9
         usage("Option -d must be greater than zero");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (isdaemon && (verbose || realclean || dryrun || listurls)) {
Packit 90a5c9
         usage("Option -d cannot be used with -v, -r, -L or -D");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!isdaemon && intelligent) {
Packit 90a5c9
         usage("Option -i cannot be used without -d");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!listurls && max <= 0 && inodes <= 0) {
Packit 90a5c9
         usage("At least one of option -l or -L must be greater than zero");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (apr_filepath_get(&path, 0, pool) != APR_SUCCESS) {
Packit 90a5c9
        usage(apr_psprintf(pool, "Could not get the filepath: %pm", &status));
Packit 90a5c9
    }
Packit 90a5c9
    baselen = strlen(path);
Packit 90a5c9
Packit 90a5c9
    if (pidfilename) {
Packit 90a5c9
        log_pid(pool, pidfilename, &pidfile); /* before daemonizing, so we
Packit 90a5c9
                                               * can report errors
Packit 90a5c9
                                               */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (listurls) {
Packit 90a5c9
        list_urls(path, pool, round);
Packit 90a5c9
        return (interrupted != 0);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
#ifndef DEBUG
Packit 90a5c9
    if (isdaemon) {
Packit 90a5c9
        apr_file_close(errfile);
Packit 90a5c9
        errfile = NULL;
Packit 90a5c9
        if (pidfilename) {
Packit 90a5c9
            apr_file_close(pidfile); /* delete original pidfile only in parent */
Packit 90a5c9
        }
Packit 90a5c9
        apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
Packit 90a5c9
        if (pidfilename) {
Packit 90a5c9
            log_pid(pool, pidfilename, &pidfile);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    do {
Packit 90a5c9
        apr_pool_create(&instance, pool);
Packit 90a5c9
Packit 90a5c9
        now = apr_time_now();
Packit 90a5c9
        APR_RING_INIT(&root, _entry, link);
Packit 90a5c9
        delcount = 0;
Packit 90a5c9
        unsolicited = 0;
Packit 90a5c9
        dowork = 0;
Packit 90a5c9
Packit 90a5c9
        switch (intelligent) {
Packit 90a5c9
        case 0:
Packit 90a5c9
            dowork = 1;
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        case 1:
Packit 90a5c9
            retries = STAT_ATTEMPTS;
Packit 90a5c9
            status = APR_SUCCESS;
Packit 90a5c9
Packit 90a5c9
            do {
Packit 90a5c9
                if (status != APR_SUCCESS) {
Packit 90a5c9
                    apr_sleep(STAT_DELAY);
Packit 90a5c9
                }
Packit 90a5c9
                status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
Packit 90a5c9
            } while (status != APR_SUCCESS && !interrupted && --retries);
Packit 90a5c9
Packit 90a5c9
            if (status == APR_SUCCESS) {
Packit 90a5c9
                previous = info.mtime;
Packit 90a5c9
                intelligent = 2;
Packit 90a5c9
            }
Packit 90a5c9
            dowork = 1;
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        case 2:
Packit 90a5c9
            retries = STAT_ATTEMPTS;
Packit 90a5c9
            status = APR_SUCCESS;
Packit 90a5c9
Packit 90a5c9
            do {
Packit 90a5c9
                if (status != APR_SUCCESS) {
Packit 90a5c9
                    apr_sleep(STAT_DELAY);
Packit 90a5c9
                }
Packit 90a5c9
                status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
Packit 90a5c9
            } while (status != APR_SUCCESS && !interrupted && --retries);
Packit 90a5c9
Packit 90a5c9
            if (status == APR_SUCCESS) {
Packit 90a5c9
                if (previous != info.mtime) {
Packit 90a5c9
                    dowork = 1;
Packit 90a5c9
                }
Packit 90a5c9
                previous = info.mtime;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            intelligent = 1;
Packit 90a5c9
            dowork = 1;
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (dowork && !interrupted) {
Packit 90a5c9
            apr_off_t nodes = 0;
Packit 90a5c9
            if (!process_dir(path, instance, &nodes) && !interrupted) {
Packit 90a5c9
                purge(path, instance, max, inodes, nodes, round);
Packit 90a5c9
            }
Packit 90a5c9
            else if (!isdaemon && !interrupted) {
Packit 90a5c9
                apr_file_printf(errfile, "An error occurred, cache cleaning "
Packit 90a5c9
                                         "aborted." APR_EOL_STR);
Packit 90a5c9
                return 1;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (intelligent && !interrupted) {
Packit 90a5c9
                retries = STAT_ATTEMPTS;
Packit 90a5c9
                status = APR_SUCCESS;
Packit 90a5c9
                do {
Packit 90a5c9
                    if (status != APR_SUCCESS) {
Packit 90a5c9
                        apr_sleep(STAT_DELAY);
Packit 90a5c9
                    }
Packit 90a5c9
                    status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
Packit 90a5c9
                } while (status != APR_SUCCESS && !interrupted && --retries);
Packit 90a5c9
Packit 90a5c9
                if (status == APR_SUCCESS) {
Packit 90a5c9
                    previous = info.mtime;
Packit 90a5c9
                    intelligent = 2;
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    intelligent = 1;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        apr_pool_destroy(instance);
Packit 90a5c9
Packit 90a5c9
        current = apr_time_now();
Packit 90a5c9
        if (current < now) {
Packit 90a5c9
            delay = repeat;
Packit 90a5c9
        }
Packit 90a5c9
        else if (current - now >= repeat) {
Packit 90a5c9
            delay = repeat;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            delay = now + repeat - current;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* we can't sleep the whole delay time here apiece as this is racy
Packit 90a5c9
         * with respect to interrupt delivery - think about what happens
Packit 90a5c9
         * if we have tested for an interrupt, then get scheduled
Packit 90a5c9
         * before the apr_sleep() call and while waiting for the cpu
Packit 90a5c9
         * we do get an interrupt
Packit 90a5c9
         */
Packit 90a5c9
        if (isdaemon) {
Packit 90a5c9
            while (delay && !interrupted) {
Packit 90a5c9
                if (delay > APR_USEC_PER_SEC) {
Packit 90a5c9
                    apr_sleep(APR_USEC_PER_SEC);
Packit 90a5c9
                    delay -= APR_USEC_PER_SEC;
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    apr_sleep(delay);
Packit 90a5c9
                    delay = 0;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    } while (isdaemon && !interrupted);
Packit 90a5c9
Packit 90a5c9
    if (!isdaemon && interrupted) {
Packit 90a5c9
        apr_file_printf(errfile, "Cache cleaning aborted due to user "
Packit 90a5c9
                                 "request." APR_EOL_STR);
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return 0;
Packit 90a5c9
}