httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jerenkra...@apache.org
Subject cvs commit: httpd-2.0/support htcacheclean.c .cvsignore Makefile.in
Date Sat, 06 Nov 2004 07:45:21 GMT
jerenkrantz    2004/11/05 23:45:21

  Modified:    .        CHANGES
               support  .cvsignore Makefile.in
  Added:       support  htcacheclean.c
  Log:
  Add htcacheclean to function as cleanup utility (daemonizable) for
  mod_disk_cache.
  
  (Justin did a whole bunch of style tweaks, and some minor functionality tweaks
  to get it to function on Solaris.)
  
  Submitted by:	Andreas Steinmetz
  Reviewed by:	Justin Erenkrantz
  
  Revision  Changes    Path
  1.1628    +5 -2      httpd-2.0/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/CHANGES,v
  retrieving revision 1.1627
  retrieving revision 1.1628
  diff -u -u -r1.1627 -r1.1628
  --- CHANGES	5 Nov 2004 18:48:15 -0000	1.1627
  +++ CHANGES	6 Nov 2004 07:45:21 -0000	1.1628
  @@ -2,11 +2,14 @@
   
     [Remove entries to the current 2.0 section below, when backported]
   
  +  *) Add htcacheclean to support/ for assistance with mod_disk_cache.
  +     [Andreas Steinmetz]
  +
     *) mod_authnz_ldap: Added the directive "Requires ldap-filter" that
        allows the module to authorize a user based on a complex LDAP
        search filter.
        [Brad Nicholes]
  -     
  +
     *) SECURITY: CAN-2004-0942, Fix for memory consumption DoS.
        [Joe Orton]
   
  @@ -17,7 +20,7 @@
        allows the module to only authorize a user if the attribute value
        specified matches the value of the user object. PR 31913
        [Ryan Morgan <rmorgan pobox.com>]
  -     
  +
     *) Allow mod_authnz_ldap authorization functionality to be used 
        without requiring the user to also be authenticated through 
        mod_authnz_ldap. This allows other authentication modules to 
  
  
  
  1.18      +1 -0      httpd-2.0/support/.cvsignore
  
  Index: .cvsignore
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/support/.cvsignore,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -u -r1.17 -r1.18
  --- .cvsignore	5 Jun 2002 14:56:46 -0000	1.17
  +++ .cvsignore	6 Nov 2004 07:45:21 -0000	1.18
  @@ -7,6 +7,7 @@
   htpasswd
   htdbm
   htdigest
  +htcacheclean
   unescape
   inc2shtml
   httpd_monitor
  
  
  
  1.41      +4 -1      httpd-2.0/support/Makefile.in
  
  Index: Makefile.in
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/support/Makefile.in,v
  retrieving revision 1.40
  retrieving revision 1.41
  diff -u -u -r1.40 -r1.41
  --- Makefile.in	6 Mar 2004 16:47:41 -0000	1.40
  +++ Makefile.in	6 Nov 2004 07:45:21 -0000	1.41
  @@ -3,7 +3,7 @@
   
   CLEAN_TARGETS = suexec
   
  -PROGRAMS = htpasswd htdigest rotatelogs logresolve ab checkgid htdbm
  +PROGRAMS = htpasswd htdigest rotatelogs logresolve ab checkgid htdbm htcacheclean
   TARGETS  = $(PROGRAMS)
   
   PROGRAM_LDADD        = $(UTIL_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS) $(AP_LIBS)
  @@ -62,3 +62,6 @@
   suexec: $(suexec_OBJECTS)
   	$(LINK) $(suexec_OBJECTS)
   
  +htcacheclean_OBJECTS = htcacheclean.lo
  +htcacheclean: $(htcacheclean_OBJECTS)
  +	$(LINK) $(htcacheclean_LTFLAGS) $(htcacheclean_OBJECTS) $(PROGRAM_LDADD)
  
  
  
  1.1                  httpd-2.0/support/htcacheclean.c
  
  Index: htcacheclean.c
  ===================================================================
  /* Copyright 2001-2004 The Apache Software Foundation
   *
   * Licensed 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
   */
  
  #include "apr.h"
  #include "apr_lib.h"
  #include "apr_strings.h"
  #include "apr_file_io.h"
  #include "apr_file_info.h"
  #include "apr_pools.h"
  #include "apr_hash.h"
  #include "apr_thread_proc.h"
  #include "apr_signal.h"
  #include "apr_getopt.h"
  #include "apr_ring.h"
  #include "apr_date.h"
  
  #if APR_HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #if APR_HAVE_STDLIB_H
  #include <stdlib.h>
  #endif
  
  /* mod_disk_cache.c extract start */
  
  #define DISK_FORMAT_VERSION 0
  typedef struct {
      /* Indicates the format of the header struct stored on-disk. */
      int format;
      /* The HTTP status code returned for this response.  */
      int status;
      /* The size of the entity name that follows. */
      apr_size_t name_len;
      /* The number of times we've cached this entity. */
      apr_size_t entity_version;
      /* Miscellaneous time values. */
      apr_time_t date;
      apr_time_t expire;
      apr_time_t request_time;
      apr_time_t response_time;
  } disk_cache_info_t;
  
  #define CACHE_HEADER_SUFFIX ".header"
  #define CACHE_DATA_SUFFIX   ".data"
  /* mod_disk_cache.c extract end */
  
  /* mod_disk_cache.c related definitions start */
  
  /*
   * this is based on #define AP_TEMPFILE "/aptmpXXXXXX"
   *
   * the above definition could be reworked into the following:
   *
   * #define AP_TEMPFILE_PREFIX "/"
   * #define AP_TEMPFILE_BASE   "aptmp"
   * #define AP_TEMPFILE_SUFFIX "XXXXXX"
   * #define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
   * #define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
   * #define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
   *
   * these definitions would then match the definitions below:
   */
  
  #define AP_TEMPFILE_BASE    "aptmp"
  #define AP_TEMPFILE_SUFFIX  "XXXXXX"
  #define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
  #define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
  
  /* mod_disk_cache.c related definitions end */
  
  /* define the following for debugging */
  #undef DEBUG
  
  /*
   * Note: on Linux delays <= 2ms are busy waits without
   *       scheduling, so never use a delay <= 2ms below
   */
  
  #define NICE_DELAY    10000     /* usecs */
  #define DELETE_NICE   10        /* be nice after this amount of delete ops */
  #define STAT_ATTEMPTS 10        /* maximum stat attempts for a file */
  #define STAT_DELAY    5000      /* usecs */
  #define HEADER        1         /* headers file */
  #define DATA          2         /* body file */
  #define TEMP          4         /* temporary file */
  #define HEADERDATA    (HEADER|DATA)
  #define MAXDEVIATION  3600      /* secs */
  #define SECS_PER_MIN  60
  #define KBYTE         1024
  #define MBYTE         1048576
  
  #define DIRINFO (APR_FINFO_MTIME|APR_FINFO_SIZE|APR_FINFO_TYPE|APR_FINFO_LINK)
  
  typedef struct _direntry
  {
      APR_RING_ENTRY(_direntry) link;
      /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
      int type;
      /* headers file modification time */
      apr_time_t htime;
      /* body file modification time */
      apr_time_t dtime;
      /* headers file size */
      apr_off_t hsize;
      /* body or temporary file size */
      apr_off_t dsize;
      /* file/fileset base name */
      char *basename;
  } DIRENTRY;
  
  typedef struct _entry
  {
      APR_RING_ENTRY(_entry) link;
      /* cache entry exiration time */
      apr_time_t expire;
      /* cache entry time of last response to client */
      apr_time_t response_time;
      /* headers file modification time */
      apr_time_t htime;
      /* body file modification time */
      apr_time_t dtime;
      /* headers file size */
      apr_off_t hsize;
      /* body or temporary file size */
      apr_off_t dsize;
      /* fileset base name */
      char *basename;
  } ENTRY;
  
  /* file deletion count for nice mode */
  static int delcount;
  /* flag: true if SIGINT or SIGTERM occurred */
  static int interrupted;
  /* flag: true means user said apache is not running */
  static int realclean;
  /* flag: true means print statistics */
  static int verbose;
  /* flag: true means nice mode is activated */
  static int benice;
  /* flag: true means dry run, don't actually delete anything */
  static int dryrun;
  /* string length of the path to the proxy directory */
  static int baselen;
  /* start time of this processing run */
  static apr_time_t now;
  /* stderr file handle */
  static apr_file_t *errfile;
  /* file size summary for deleted unsolicited files */
  static apr_off_t unsolicited;
  /* ENTRY ring anchor */
  static APR_RING_ENTRY(_entry) root;
  
  /*
   * fake delete for debug purposes
   */
  
  #ifdef DEBUG
  #define apr_file_remove fake_file_remove
  static void fake_file_remove(char *pathname, apr_pool_t *p)
  {
      apr_finfo_t info;
  
      /* 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\n", pathname);
  }
  #endif
  
  /*
   * called on SIGINT or SIGTERM
   */
  
  static void setterm(int unused)
  {
  #ifdef DEBUG
      apr_file_printf(errfile, "interrupt\n");
  #endif
      interrupted = 1;
  }
  
  /*
   * called in out of memory condition
   */
  static int oom(int unused)
  {
      static int called = 0;
  
      /* be careful to call exit() only once */
      if (!called) {
          called = 1;
          exit(1);
      }
      return APR_ENOMEM;
  }
  
  /*
   * print purge statistics
   */
  static void printstats(apr_off_t total, apr_off_t sum, apr_off_t max,
                         apr_off_t etotal, apr_off_t entries)
  {
      char ttype;
      char stype;
      char mtype;
      char utype;
      apr_off_t tfrag;
      apr_off_t sfrag;
      apr_off_t ufrag;
  
      if (!verbose) {
          return;
      }
  
      ttype = 'K';
      tfrag = ((total * 10) / KBYTE) % 10;
      total /= KBYTE;
      if (total >= KBYTE) {
          ttype = 'M';
          tfrag = ((total * 10) / KBYTE) % 10;
          total /= KBYTE;
      }
  
      stype = 'K';
      sfrag = ((sum * 10) / KBYTE) % 10;
      sum /= KBYTE;
      if (sum >= KBYTE) {
          stype = 'M';
          sfrag = ((sum * 10) / KBYTE) % 10;
          sum /= KBYTE;
      }
  
      mtype = 'K';
      max /= KBYTE;
      if (max >= KBYTE) {
          mtype = 'M';
          max /= KBYTE;
      }
  
      apr_file_printf(errfile, "Statistics:\n");
      if (unsolicited) {
          utype = 'K';
          ufrag = ((unsolicited * 10) / KBYTE) % 10;
          unsolicited /= KBYTE;
          if (unsolicited >= KBYTE) {
              utype = 'M';
              ufrag = ((unsolicited * 10) / KBYTE) % 10;
              unsolicited /= KBYTE;
          }
          if (!unsolicited && !ufrag) {
              ufrag = 1;
          }
          apr_file_printf(errfile, "unsolicited size %d.%d%c\n",
              (int)(unsolicited), (int)(ufrag), utype);
       }
       apr_file_printf(errfile, "size limit %d.0%c\n", (int)(max), mtype);
       apr_file_printf(errfile,
           "total size was %d.%d%c, total size now %d.%d%c\n",
           (int)(total), (int)(tfrag), ttype, (int)(sum), (int)(sfrag), stype);
       apr_file_printf(errfile, "total entries was %d, total entries now %d\n",
           (int)(etotal), (int)(entries));
  }
  
  /*
   * delete a single file
   */
  static void delete_file(char *path, char *basename, apr_pool_t *pool)
  {
       char *nextpath;
       apr_pool_t *p;
  
       if (dryrun) {
           return;
       }
  
       /* temp pool, otherwise lots of memory could be allocated */
       apr_pool_create(&p, pool);
       nextpath = apr_pstrcat(p, path, "/", basename, NULL);
       apr_file_remove(nextpath, p);
       apr_pool_destroy(p);
  
       if (benice) {
           if (++delcount >= DELETE_NICE) {
               apr_sleep(NICE_DELAY);
               delcount = 0;
           }
       }
  }
  
  /*
   * delete cache file set
   */
  static void delete_entry(char *path, char *basename, apr_pool_t *pool)
  {
       char *nextpath;
       apr_pool_t *p;
  
       if (dryrun) {
           return;
       }
  
       /* temp pool, otherwise lots of memory could be allocated */
       apr_pool_create(&p, pool);
  
       nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
       apr_file_remove(nextpath, p);
  
       nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
       apr_file_remove(nextpath, p);
  
       apr_pool_destroy(p);
  
       if (benice) {
           delcount += 2;
           if (delcount >= DELETE_NICE) {
               apr_sleep(NICE_DELAY);
               delcount = 0;
           }
       }
  }
  
  /*
   * walk the cache directory tree
   */
  static int process_dir(char *path, apr_pool_t *pool)
  {
       apr_dir_t *dir;
       apr_pool_t *p;
       apr_hash_t *h;
       apr_hash_index_t *i;
       apr_file_t *fd;
       apr_status_t status;
       apr_finfo_t info;
       apr_size_t len;
       apr_time_t current;
       apr_time_t deviation;
       char *nextpath;
       char *base;
       char *ext;
       APR_RING_ENTRY(_direntry) anchor;
       DIRENTRY *d;
       DIRENTRY *t;
       DIRENTRY *n;
       ENTRY *e;
       int skip;
       int retries;
       disk_cache_info_t disk_info;
  
       APR_RING_INIT(&anchor, _direntry, link);
       apr_pool_create(&p, pool);
       h = apr_hash_make(p);
       fd = NULL;
       skip = 0;
       deviation = MAXDEVIATION * APR_USEC_PER_SEC;
  
       if (apr_dir_open(&dir, path, p) != APR_SUCCESS) {
           return 1;
       }
  
       while (apr_dir_read(&info, 0, dir) == APR_SUCCESS && !interrupted) {
           /* skip first two entries which will always be '.' and '..' */
           if (skip < 2) {
               skip++;
               continue;
           }
           d = apr_pcalloc(p, sizeof(DIRENTRY));
           d->basename = apr_pstrcat(p, path, "/", info.name, NULL);
           APR_RING_INSERT_TAIL(&anchor, d, _direntry, link);
       }
  
       apr_dir_close(dir);
  
       if (interrupted) {
           return 1;
       }
  
       skip = baselen + 1;
  
       for(d = APR_RING_FIRST(&anchor);
           !interrupted && d != APR_RING_SENTINEL(&anchor, _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 errno 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) {
               if (process_dir(d->basename, pool)) {
                   return 1;
               }
               continue;
           }
  
           if (info.filetype != APR_REG) {
               continue;
           }
  
           if (!ext) {
               if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN) &&
                 strlen(base) == AP_TEMPFILE_NAMELEN) {
                   d->basename += skip;
                   d->type = TEMP;
                   d->dsize = info.size;
                   apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
               }
               continue;
           }
  
           if (!strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
               *ext = '\0';
               d->basename += skip;
               /* if a user manually creates a '.header' file */
               if (d->basename[0] == '\0')
                   continue;
               t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
               if (t)
                   d = t;
               d->type |= HEADER;
               d->htime = info.mtime;
               d->hsize = info.size;
               apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
               continue;
           }
  
           if (!strcasecmp(ext, CACHE_DATA_SUFFIX)) {
               *ext = '\0';
               d->basename += skip;
               /* if a user manually creates a '.data' file */
               if (d->basename[0] == '\0')
                   continue;
               t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
               if (t) {
                   d = t;
               }
               d->type |= DATA;
               d->dtime = info.mtime;
               d->dsize = info.size;
               apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
           }
       }
  
      if (interrupted) {
          return 1;
      }
  
      path[baselen] = '\0';
  
      for (i = apr_hash_first(p, h); i && !interrupted; i = apr_hash_next(i)) {
          apr_hash_this(i, NULL, NULL, (void **)(&d));
          switch(d->type) {
          case HEADERDATA:
              nextpath = apr_pstrcat(p, path, "/", d->basename,
                  CACHE_HEADER_SUFFIX, NULL);
              if (apr_file_open(&fd, nextpath, APR_READ, APR_OS_DEFAULT, p)
                == APR_SUCCESS) {
                  len = sizeof(disk_cache_info_t);
                  if (apr_file_read_full(fd, &disk_info, len, &len)
                    == APR_SUCCESS) {
                      apr_file_close(fd);
                      if (disk_info.format == DISK_FORMAT_VERSION) {
                          e = apr_palloc(pool, sizeof(ENTRY));
                          APR_RING_INSERT_TAIL(&root, e, _entry, link);
                          e->expire = disk_info.expire;
                          e->response_time = disk_info.response_time;
                          e->htime = d->htime;
                          e->dtime = d->dtime;
                          e->hsize = d->hsize;
                          e->dsize = d->dsize;
                          e->basename = apr_palloc(pool,
                                                   strlen(d->basename) + 1);
                          strcpy(e->basename, d->basename);
                          break;
                      }
                  }
                  else {
                      apr_file_close(fd);
                  }
              }
              /* 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, 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();
              if (realclean || d->htime < current - deviation ||
                   d->htime > current + deviation) {
                   delete_entry(path, d->basename, 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, 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, p);
              unsolicited += d->dsize;
              break;
          }
      }
  
      if (interrupted) {
          return 1;
      }
  
      apr_pool_destroy(p);
  
      if (benice) {
           apr_sleep(NICE_DELAY);
      }
  
      if (interrupted) {
           return 1;
      }
  
      return 0;
  }
  
  /*
   * purge cache entries
   */
  static void purge(char *path, apr_pool_t *pool, apr_off_t max)
  {
      apr_off_t sum;
      apr_off_t total;
      apr_off_t entries;
      apr_off_t etotal;
      ENTRY *e;
      ENTRY *n;
      ENTRY *oldest;
  
      sum = 0;
      entries = 0;
  
      for (e = APR_RING_FIRST(&root);
           e != APR_RING_SENTINEL(&root, _entry, link);
           e = APR_RING_NEXT(e, link)) {
          sum += e->hsize;
          sum += e->dsize;
          entries++;
      }
  
      total = sum;
      etotal = entries;
  
      if (sum <= max) {
          printstats(total, sum, max, etotal, entries);
          return;
      }
  
      /* 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);
           e != APR_RING_SENTINEL(&root, _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, pool);
              sum -= e->hsize;
              sum -= e->dsize;
              entries--;
              APR_RING_REMOVE(e, link);
              if (sum <= max) {
                  if (!interrupted) {
                      printstats(total, sum, max, etotal, entries);
                  }
                  return;
              }
          }
          e = n;
      }
  
      if (interrupted) {
           return;
      }
  
      /* process all entries with are expired */
      for (e = APR_RING_FIRST(&root);
            e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
          n = APR_RING_NEXT(e, link);
          if (e->expire != APR_DATE_BAD && e->expire < now) {
              delete_entry(path, e->basename, pool);
              sum -= e->hsize;
              sum -= e->dsize;
              entries--;
              APR_RING_REMOVE(e, link);
              if (sum <= max) {
                  if (!interrupted)
                      printstats(total, sum, max, etotal, entries);
                  return;
              }
          }
          e = n;
      }
  
      if (interrupted) {
           return;
      }
  
      /* process remaining entries oldest to newest, the check for an emtpy
       * ring actually isn't necessary except when the compiler does
       * corrupt 64bit arithmetics which happend to me once, so better safe
       * than sorry
       */
      while (sum > max && !interrupted && !APR_RING_EMPTY(&root, _entry,
link)) {
          oldest = APR_RING_FIRST(&root);
  
          for(e = APR_RING_NEXT(oldest, link);
              e != APR_RING_SENTINEL(&root, _entry, link);
              e = APR_RING_NEXT(e, link)) {
              if (e->dtime < oldest->dtime) {
                  oldest = e;
              }
          }
  
          delete_entry(path, oldest->basename, pool);
          sum -= oldest->hsize;
          sum -= oldest->dsize;
          entries--;
          APR_RING_REMOVE(oldest, link);
      }
  
      if (!interrupted) {
          printstats(total, sum, max, etotal, entries);
      }
  }
  
  /*
   * usage info
   */
  static void usage(void)
  {
      apr_file_printf(errfile, "htcacheclean -- program for cleaning the "
                               "disk cache.\n");
      apr_file_printf(errfile, "Usage: htcacheclean [-Dvrn] -pPATH -lLIMIT\n");
      apr_file_printf(errfile, "       htcacheclean [-Dvrn] -pPATH -LLIMIT\n");
      apr_file_printf(errfile, "       htcacheclean [-ni] -dINTERVAL -pPATH "
                               "-lLIMIT\n");
      apr_file_printf(errfile, "       htcacheclean [-ni] -dINTERVAL -pPATH "
                               "-LLIMIT\n");
      apr_file_printf(errfile, "Options:\n");
      apr_file_printf(errfile, "   -d   Daemonize and repeat cache cleaning "
                               "every INTERVAL minutes. This\n"
                               "        option is mutually exclusive with "
                               "the -D, -v and -r options.\n");
      apr_file_printf(errfile, "   -D   Do a dry run and don't delete anything. "
                               "This option is mutually\n"
                               "        exclusive with the -d option.\n");
      apr_file_printf(errfile, "   -v   Be verbose and print statistics. "
                               "This option is mutually exclusive\n"
                               "        with the -d option.\n");
      apr_file_printf(errfile, "   -r   Clean thoroughly. This assumes that "
                               "the Apache web server\n"
                               "        is not running. This option is "
                               "mutually exclusive with the -d option.\n");
      apr_file_printf(errfile, "   -n   Be nice. This causes slower processing "
                               "in favour of other processes.\n");
      apr_file_printf(errfile, "   -p   Specify PATH as the root directory of "
                               "the disk cache.\n");
      apr_file_printf(errfile, "   -l   Specify LIMIT as the total disk cache "
                               "size limit in KBytes.\n");
      apr_file_printf(errfile, "   -L   Specify LIMIT as the total disk cache "
                               "size limit in MBytes.\n");
      apr_file_printf(errfile, "   -i   Be intelligent and run only when there "
                               "was a modification\n"
                               "        of the disk cache. This option is only "
                               "possible together with\n"
                               "        the -d option.\n");
      exit(1);
  }
  
  /*
   * main
   */
  int main(int argc, const char * const argv[])
  {
      apr_off_t max;
      apr_time_t current;
      apr_time_t repeat;
      apr_time_t delay;
      apr_time_t previous;
      apr_status_t status;
      apr_pool_t *pool;
      apr_pool_t *instance;
      apr_getopt_t *o;
      apr_finfo_t info;
      int retries;
      int isdaemon;
      int limit_found;
      int intelligent;
      int dowork;
      char opt;
      const char *arg;
      char *proxypath;
      char *path;
  
      interrupted = 0;
      repeat = 0;
      isdaemon = 0;
      dryrun = 0;
      limit_found = 0;
      max = 0;
      verbose = 0;
      realclean = 0;
      benice = 0;
      intelligent = 0;
      proxypath = NULL;
  
      if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
          return 1;
      }
      atexit(apr_terminate);
  
      if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
          return 1;
      }
      apr_pool_abort_set(oom, pool);
      apr_file_open_stderr(&errfile, pool);
      apr_signal(SIGINT, setterm);
      apr_signal(SIGTERM, setterm);
  
      apr_getopt_init(&o, pool, argc, argv);
  
      while (1) {
          status = apr_getopt(o, "iDnvrd:l:L:p:", &opt, &arg);
          if (status == APR_EOF)
              break;
          else if (status == APR_SUCCESS)
            switch (opt) {
              case 'i':
                  if (intelligent)
                      usage();
                  intelligent = 1;
                  break;
              case 'D':
                  if (dryrun)
                      usage();
                   dryrun = 1;
                 break;
              case 'n':
                  if (benice)
                      usage();
                  benice = 1;
                   break;
             case 'v':
                   if (verbose)
                      usage();
                  verbose = 1;
                 break;
               case 'r':
                 if (realclean)
                       usage();
                  realclean = 1;
                  break;
              case 'd':
                  if (isdaemon)
                      usage();
                  isdaemon = 1;
                  repeat = apr_atoi64(arg);
                  repeat *= SECS_PER_MIN;
                  repeat *= APR_USEC_PER_SEC;
                  break;
              case 'l':
                  if (limit_found)
                      usage();
                  limit_found = 1;
                  max = apr_atoi64(arg);
                  max *= KBYTE;
                  break;
              case 'L':
                  if (limit_found)
                      usage();
                  limit_found = 1;
                  max = apr_atoi64(arg);
                  max *= MBYTE;
                 break;
               case 'p':
                  if (proxypath)
                      usage();
                 proxypath = apr_pstrdup(pool, arg);
                   if (apr_filepath_set(proxypath, pool) != APR_SUCCESS)
                      usage();
                  break;
          }
          else usage();
      }
  
      if (o->ind != argc) {
           usage();
      }
  
      if (isdaemon && (repeat <= 0 || verbose || realclean || dryrun)) {
           usage();
      }
  
      if (!isdaemon && intelligent) {
           usage();
      }
  
      if (!proxypath || max <= 0) {
           usage();
      }
  
      if (apr_filepath_get(&path, 0, pool) != APR_SUCCESS) {
          usage();
      }
      baselen = strlen(path);
  
  #ifndef DEBUG
      if (isdaemon) {
          apr_file_close(errfile);
          apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
      }
  #endif
  
      do
      {
          apr_pool_create(&instance, pool);
  
          now = apr_time_now();
          APR_RING_INIT(&root, _entry, link);
          delcount = 0;
          unsolicited = 0;
          dowork = 0;
  
          switch (intelligent) {
          case 0:
              dowork = 1;
              break;
  
          case 1:
              retries = STAT_ATTEMPTS;
              status = APR_SUCCESS;
              do
              {
                  if (status != APR_SUCCESS)
                      apr_sleep(STAT_DELAY);
                   status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
              } while (status != APR_SUCCESS && !interrupted && --retries);
              if (status == APR_SUCCESS) {
                  previous = info.mtime;
                  intelligent = 2;
              }
              dowork = 1;
              break;
  
          case 2:
              retries = STAT_ATTEMPTS;
              status = APR_SUCCESS;
              do
              {
                  if (status != APR_SUCCESS)
                      apr_sleep(STAT_DELAY);
                  status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
             } while (status != APR_SUCCESS && !interrupted && --retries);
              if (status == APR_SUCCESS) {
                  if (previous != info.mtime)
                       dowork = 1;
                 previous = info.mtime;
                  break;
              }
              intelligent = 1;
              dowork = 1;
              break;
          }
  
          if (dowork && !interrupted) {
              if (!process_dir(path, instance) && !interrupted) {
                 purge(path, instance, max);
              } else if (!isdaemon && !interrupted) {
                  apr_file_printf(errfile,
                       "An error occurred, cache cleaning aborted.\n");
                  return 1;
             }
  
             if (intelligent && !interrupted) {
                  retries = STAT_ATTEMPTS;
                  status = APR_SUCCESS;
                  do
                  {
                      if (status != APR_SUCCESS)
                          apr_sleep(STAT_DELAY);
                      status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
                  } while (status != APR_SUCCESS && !interrupted && --retries);
                  if (status == APR_SUCCESS) {
                      previous = info.mtime;
                      intelligent = 2;
                  }
                  else
                      intelligent = 1;
              }
          }
  
          apr_pool_destroy(instance);
  
          current = apr_time_now();
          if (current < now) {
              delay = repeat;
          } else if (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.\n");
          return 1;
      }
  
      return 0;
  }
  
  
  

Mime
View raw message