purge.c
上传用户:seven77cht
上传日期:2007-01-04
资源大小:486k
文件大小:17k
源码类别:

浏览器

开发平台:

Unix_Linux

  1. /***************************************
  2.   $Header: /home/amb/wwwoffle/RCS/purge.c 2.26 2000/01/12 22:37:38 amb Exp $
  3.   WWWOFFLE - World Wide Web Offline Explorer - Version 2.5d.
  4.   Purge old files from the cache.
  5.   ******************/ /******************
  6.   Written by Andrew M. Bishop
  7.   This file Copyright 1996,97,98,99,2000 Andrew M. Bishop
  8.   It may be distributed under the GNU Public License, version 2, or
  9.   any higher version.  See section COPYING of the GNU Public license
  10.   for conditions under which this file may be redistributed.
  11.   ***************************************/
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <time.h>
  16. #include <utime.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <time.h>
  20. #include <dirent.h>
  21. #include <unistd.h>
  22. #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
  23. #include <sys/param.h>
  24. #include <sys/mount.h>
  25. #elif (defined(__sun__) && defined(__svr4__)) || defined(__irix__)
  26. #include <sys/statvfs.h>
  27. #elif defined(__osf__)
  28. #include <sys/mount.h>
  29. #else
  30. #include <sys/vfs.h>
  31. #endif
  32. #if (defined(__sun__) && defined(__svr4__)) || defined(__irix__)
  33. #define STATFS statvfs
  34. #else
  35. #define STATFS statfs
  36. #endif
  37. #include "wwwoffle.h"
  38. #include "misc.h"
  39. #include "proto.h"
  40. #include "config.h"
  41. #include "errors.h"
  42. /* Local functions */
  43. static long PurgeFiles(char *proto,char *host);
  44. /*+ Set this to 0 for debugging so that nothing is deleted. +*/
  45. #define DO_DELETE 1
  46. /*+ The current time. +*/
  47. static time_t now;
  48. /*+ The number of blocks left of each age. +*/
  49. static int *blocks_by_age;
  50. /*+ The scaling factor for the ages in the second pass. +*/
  51. static double age_scale;
  52. /*+ The pass in the purge (1 or 2). +*/
  53. static int pass;
  54. /*+ The blocksize. +*/
  55. static long int blocksize;
  56. /*
  57.  Note: Since some UNIX versions (Solaris) have st_size/st_blocks different for
  58.        different files I have reverted to using 1+(st_size/blocksze) instead
  59.        of st_blocks for counting the number of blocks in a file and using
  60.        st_size/blocksize for directories (since they are multiples of blocks).
  61.        This gives a small error in some cases but is fast to calculate compared
  62.        to the more accurate value of st_size/blocksize+!!(st_size%blocksize).
  63. */
  64. /*++++++++++++++++++++++++++++++++++++++
  65.   Purge files from the cache that meet the age criteria.
  66.   int fd the file descriptor of the wwwoffle client.
  67.   ++++++++++++++++++++++++++++++++++++++*/
  68. void PurgeCache(int fd)
  69. {
  70.  int i,p;
  71.  long diskfree;
  72.  struct STATFS sbuf;
  73.  struct stat buf;
  74.  now=time(NULL)+600;
  75.  age_scale=-1;
  76.  blocks_by_age=(int*)malloc((DefaultPurgeAge+2)*sizeof(int));
  77.  for(i=0;i<=DefaultPurgeAge+1;i++)
  78.     blocks_by_age[i]=0;
  79.  if(stat(".",&buf) || buf.st_size==-1 || buf.st_blocks==-1)
  80.    {
  81.     PrintMessage(Warning,"Cannot determine the disk block size [%!s]; using 1024 instead.");
  82.     blocksize=1024;
  83.    }
  84.  else if(buf.st_blocks==0)
  85.    {
  86.     PrintMessage(Warning,"The number of blocks (0) looks wrong; using 1024 for blocksize.");
  87.     blocksize=1024;
  88.    }
  89.  else
  90.    {
  91.     blocksize=buf.st_size/buf.st_blocks;
  92.     if(blocksize!=512 && blocksize!=1024 && blocksize!=2048)
  93.       {
  94.        PrintMessage(Warning,"The blocksize (%d) looks wrong; using 1024 instead.",blocksize);
  95.        blocksize=1024;
  96.       }
  97.    }
  98.  /* Note: blocksize can be only 512, 1024 or 2048. */
  99.  /* Handle this carefully to avoid overflows from doing blocks*blocksize/1024
  100.                                 or any errors from doing (blocks/1024)*blocksize */
  101. #define Blocks_to_kB(blocks) 
  102.  (long)((blocksize==512)?((blocks)/2): 
  103.        ((blocksize==1024)?(blocks): 
  104.         (blocks)*2)) 
  105.  if(STATFS(".",&sbuf) || sbuf.f_bsize==-1 || sbuf.f_bavail==-1)
  106.    {
  107.     PrintMessage(Warning,"Cannot determine the disk free space [%!s]; assuming 0.");
  108.     diskfree=0;
  109.    }
  110.  else
  111.    {
  112.     int bs=blocksize,dbs=sbuf.f_bsize;
  113.     /* Do this carefully to stop overflow and reduce errors. */
  114.     while(!(dbs&1) && !(bs&1))  /* remove powers of 2. */
  115.       {dbs>>=1;bs>>=1;}
  116.     /* If both were powers of 2 then there should be no problem (either dbs or bs is now 1).
  117.        If not then I am assuming that sbuf.f_bavail is larger than dbs so the error is smaller. */
  118.     diskfree=dbs*(sbuf.f_bavail/bs);
  119.    }
  120.  for(pass=1;pass<=2;pass++)
  121.    {
  122.     int total_blocks=0,total_dirs=0,dir_blocks;
  123.     write_string(fd,"n");
  124.     if(PurgeCacheSize)
  125.        if(pass==1)
  126.           write_string(fd,"Pass 1: Checking dates and sizes of files:n");
  127.        else
  128.           write_string(fd,"Pass 2: Purging files down to specified size:n");
  129.     else
  130.        write_string(fd,"Checking dates of files:n");
  131.     if(pass==1)
  132.       {
  133.        if(PurgeUseMTime)
  134.           write_string(fd,"  (Using modification time.)n");
  135.        else
  136.           write_string(fd,"  (Using last access time.)n");
  137.        if(PurgeUseURL)
  138.           write_string(fd,"  (Using the full URL.)n");
  139.        else
  140.           write_string(fd,"  (Using the hostname and protocol only.)n");
  141.       }
  142.     write_string(fd,"n");
  143.     for(p=0;p<NProtocols;p++)
  144.       {
  145.        DIR *dir;
  146.        struct dirent* ent;
  147.        struct stat buf;
  148.        char *proto=Protocols[p].name;
  149.        /* Open the spool directory. */
  150.        if(stat(proto,&buf))
  151.          {PrintMessage(Inform,"Cannot stat directory '%s' [%!s]; not purged",proto);continue;}
  152.        dir_blocks=buf.st_size/blocksize;
  153.        total_blocks+=dir_blocks;
  154.        blocks_by_age[DefaultPurgeAge+1]+=dir_blocks;
  155.        if(chdir(proto))
  156.          {PrintMessage(Warning,"Cannot change to directory '%s' [%!s]; not purged.",proto);continue;}
  157.        dir=opendir(".");
  158.        if(!dir)
  159.          {PrintMessage(Warning,"Cannot open directory '%s' [%!s]; not purged.",proto);chdir("..");continue;}
  160.        ent=readdir(dir);  /* skip .  */
  161.        if(!ent)
  162.          {PrintMessage(Warning,"Cannot read directory '%s' [%!s]; not purged.",proto);closedir(dir);chdir("..");continue;}
  163.        ent=readdir(dir);  /* skip .. */
  164.        /* Search through all of the sub directories. */
  165.        while((ent=readdir(dir)))
  166.          {
  167.           struct stat buf;
  168.           if(lstat(ent->d_name,&buf))
  169.              PrintMessage(Inform,"Cannot stat directory '%s/%s' [%!s]; race condition?",proto,ent->d_name);
  170.           else if(S_ISDIR(buf.st_mode))
  171.             {
  172.              long file_blocks=PurgeFiles(proto,ent->d_name);
  173.              dir_blocks=buf.st_size/blocksize;
  174.              if(file_blocks==-1)
  175.                {
  176. #if DO_DELETE
  177.                 if(rmdir(ent->d_name))
  178.                    PrintMessage(Warning,"Cannot delete what should be an empty directory '%s/%s' [%!s].",proto,ent->d_name);
  179. #else
  180.                 PrintMessage(Debug,"rmdir(%s/%s).",proto,ent->d_name);
  181. #endif
  182.                }
  183.              else
  184.                {
  185.                 struct utimbuf utbuf;
  186.                 utbuf.actime=buf.st_atime;
  187.                 utbuf.modtime=buf.st_mtime;
  188.                 utime(ent->d_name,&utbuf);
  189.                 blocks_by_age[DefaultPurgeAge+1]+=dir_blocks;
  190.                 total_blocks+=file_blocks+dir_blocks;
  191.                 total_dirs++;
  192.                }
  193.              if(PurgeUseURL)
  194.                {
  195.                 if(file_blocks==-1)
  196.                    write_formatted(fd,"Purged %6s://%-32s ; (empty) - deletedn",proto,ent->d_name);
  197.                 else
  198.                    write_formatted(fd,"Purged %6s://%-32s ; %5ld kBn",proto,ent->d_name,Blocks_to_kB(file_blocks+dir_blocks));
  199.                }
  200.              else
  201.                {
  202.                 int age=WhatPurgeAge(proto,ent->d_name,"/",NULL);
  203.                 if(pass==2 && age>0)
  204.                    age=(int)(age*age_scale+0.5);
  205.                 if(age<0)
  206.                    write_formatted(fd,"Not Purged       %6s://%-32s ; %5ld kBn",proto,ent->d_name,Blocks_to_kB(file_blocks+dir_blocks));
  207.                 else if(file_blocks==-1)
  208.                    write_formatted(fd,"Purged (%2d days) %6s://%-32s ; (empty) - deletedn",age,proto,ent->d_name);
  209.                 else
  210.                    write_formatted(fd,"Purged (%2d days) %6s://%-32s ; %5ld kBn",age,proto,ent->d_name,Blocks_to_kB(file_blocks+dir_blocks));
  211.                }
  212.             }
  213.           else
  214.             {
  215.              PrintMessage(Warning,"Found an unexpected file instead of a directory '%s/%s' [%!s]; deleting it.",proto,ent->d_name);
  216. #if DO_DELETE
  217.              if(unlink(ent->d_name))
  218.                 PrintMessage(Warning,"Cannot delete the non-directory '%s/%s' [%!s].",proto,ent->d_name);
  219. #else
  220.              PrintMessage(Debug,"unlink(%s/%s).",proto,ent->d_name);
  221. #endif
  222.             }
  223.          }
  224.        closedir(dir);
  225.        chdir("..");
  226.       }
  227.     write_string(fd,"n");
  228.     write_formatted(fd,"Total of %d directories ; %ld kBn",total_dirs,Blocks_to_kB(total_blocks));
  229.     if(pass==1)
  230.       {
  231.        int age_for_size=-1,age_for_free=-1;
  232.        int age_blocks_used=blocks_by_age[DefaultPurgeAge+1];
  233.        int age_blocks_free=diskfree+total_blocks-blocks_by_age[DefaultPurgeAge+1];
  234.        write_string(fd,"n");
  235.        write_string(fd,"Age Profile of cached pages:n");
  236.        write_string(fd,"  (All ages scaled to the range 0 -> default age.)n");
  237.        write_string(fd,"n");
  238.        write_formatted(fd,"Total not purged   ; %5ld kB (%6ld kB free)n",
  239.                        Blocks_to_kB(age_blocks_used),Blocks_to_kB(age_blocks_free));
  240.        write_string(fd,"n");
  241.        for(i=0;i<=DefaultPurgeAge;i++)
  242.          {
  243.           age_blocks_used+=blocks_by_age[i];
  244.           age_blocks_free-=blocks_by_age[i];
  245.           if(PurgeCacheSize && age_for_size<0 && Blocks_to_kB(age_blocks_used)>(1024*PurgeCacheSize))
  246.             {
  247.              age_for_size=i;
  248.              write_formatted(fd,"Cutoff Age is %2d days for %3d MB cache sizen",age_for_size,PurgeCacheSize);
  249.             }
  250.           if(PurgeDiskFree && diskfree && age_for_free<0 && Blocks_to_kB(age_blocks_free)<(1024*PurgeDiskFree))
  251.             {
  252.              age_for_free=i;
  253.              write_formatted(fd,"Cutoff Age is %2d days for %3d MB disk freen",age_for_free,PurgeDiskFree);
  254.             }
  255.           if(i==DefaultPurgeAge)
  256.              write_formatted(fd,"Total all ages     ; %5ld kB (%6ld kB free)n",
  257.                              Blocks_to_kB(age_blocks_used-blocks_by_age[DefaultPurgeAge+1]),
  258.                              diskfree?Blocks_to_kB(age_blocks_free):0);
  259.           else
  260.              write_formatted(fd,"Newer than %2d day%c ; %5ld kB (%6ld kB free)n",i+1,i?'s':' ',
  261.                              Blocks_to_kB(age_blocks_used-blocks_by_age[DefaultPurgeAge+1]),
  262.                              diskfree?Blocks_to_kB(age_blocks_free):0);
  263.          }
  264.        if(DefaultPurgeAge)
  265.          {
  266.           if(age_for_size!=-1 && (age_for_size<=age_for_free || age_for_free==-1))
  267.              age_scale=(double)age_for_size/(double)DefaultPurgeAge;
  268.           else if(age_for_free!=-1 && (age_for_free<age_for_size || age_for_size==-1))
  269.              age_scale=(double)age_for_free/(double)DefaultPurgeAge;
  270.          }
  271.        else if(age_for_size!=-1 || age_for_free!=-1)
  272.           age_scale=0;
  273.       }
  274.     if(age_scale==-1)
  275.        break;
  276.    }
  277.  write_string(fd,"n");
  278.  free(blocks_by_age);
  279.  /* Purge the tmp.* files in outgoing. */
  280.  if(chdir("outgoing"))
  281.     PrintMessage(Warning,"Cannot change to directory 'outgoing' [%!s]; not purged.");
  282.  else
  283.    {
  284.     DIR *dir;
  285.     struct dirent* ent;
  286.     dir=opendir(".");
  287.     if(!dir)
  288.       PrintMessage(Warning,"Cannot open directory 'outgoing' [%!s]; not purged.");
  289.     else
  290.       {
  291.        ent=readdir(dir);  /* skip .  */
  292.        if(!ent)
  293.           PrintMessage(Warning,"Cannot read directory 'outgoing' [%!s]; not purged.");
  294.        else
  295.          {
  296.           ent=readdir(dir);  /* skip .. */
  297.           while((ent=readdir(dir)))
  298.              if(!strncmp(ent->d_name,"tmp.",4))
  299.                {
  300.                 struct stat buf;
  301.                 if(!stat(ent->d_name,&buf) && buf.st_mtime<(now-60))
  302.                   {
  303. #if DO_DELETE
  304.                    if(unlink(ent->d_name))
  305.                       PrintMessage(Warning,"Cannot unlink file 'outgoing/%s' [%!s].",ent->d_name);
  306. #else
  307.                    PrintMessage(Debug,"unlink(outgoing/%s).",ent->d_name);
  308. #endif
  309.                   }
  310.                }
  311.          }
  312.        closedir(dir);
  313.       }
  314.    }
  315.  chdir("..");
  316.  /* Purge the tmp.* files in temp. */
  317.  if(chdir("temp"))
  318.     PrintMessage(Warning,"Cannot change to directory 'temp' [%!s]; not purged.");
  319.  else
  320.    {
  321.     DIR *dir;
  322.     struct dirent* ent;
  323.     dir=opendir(".");
  324.     if(!dir)
  325.       PrintMessage(Warning,"Cannot open directory 'temp' [%!s]; not purged.");
  326.     else
  327.       {
  328.        ent=readdir(dir);  /* skip .  */
  329.        if(!ent)
  330.           PrintMessage(Warning,"Cannot read directory 'temp' [%!s]; not purged.");
  331.        else
  332.          {
  333.           ent=readdir(dir);  /* skip .. */
  334.           while((ent=readdir(dir)))
  335.              if(!strncmp(ent->d_name,"tmp.",4))
  336.                {
  337.                 struct stat buf;
  338.                 if(!stat(ent->d_name,&buf) && buf.st_mtime<(now-60))
  339.                   {
  340. #if DO_DELETE
  341.                    if(unlink(ent->d_name))
  342.                       PrintMessage(Warning,"Cannot unlink file 'temp/%s' [%!s].",ent->d_name);
  343. #else
  344.                    PrintMessage(Debug,"unlink(temp/%s).",ent->d_name);
  345. #endif
  346.                   }
  347.                }
  348.          }
  349.        closedir(dir);
  350.       }
  351.    }
  352.  chdir("..");
  353. }
  354. /*++++++++++++++++++++++++++++++++++++++
  355.   Delete the file in the current directory that are older than the specified age.
  356.   long PurgeFiles Returns the number of blocks in files that are left.
  357.   char *proto The name of the protocol directory to purge.
  358.   char *host The name of the host directory to purge.
  359.   ++++++++++++++++++++++++++++++++++++++*/
  360. static long PurgeFiles(char *proto,char *host)
  361. {
  362.  long blocks_left=-1;
  363.  DIR *dir;
  364.  struct dirent* ent;
  365.  int def_age=WhatPurgeAge(proto,host,"/",NULL);
  366.  /* Open the spool subdirectory. */
  367.  if(chdir(host))
  368.    {PrintMessage(Warning,"Cannot change to directory '%s/%s' [%!s]; not purged.",proto,host);return(1);}
  369.  dir=opendir(".");
  370.  if(!dir)
  371.    {PrintMessage(Warning,"Cannot open directory '%s/%s' [%!s]; not purged.",proto,host);chdir("..");return(1);}
  372.  ent=readdir(dir);  /* skip .  */
  373.  if(!ent)
  374.    {PrintMessage(Warning,"Cannot read directory '%s/%s' [%!s]; not purged.",proto,host);closedir(dir);chdir("..");return(1);}
  375.  ent=readdir(dir);  /* skip .. */
  376.  /* Check all of the files for age, and delete as needed. */
  377.  while((ent=readdir(dir)))
  378.    {
  379.     struct stat buf,buf2;
  380.     if(stat(ent->d_name,&buf))
  381.        ;
  382. /*
  383.        PrintMessage(Inform,"Cannot stat file '%s/%s/%s' [%!s]; race condition?",proto,host,ent->d_name);
  384. */
  385.     else
  386.       {
  387.        int age=DefaultPurgeAge;
  388.        time_t t=now;
  389.        if(buf.st_mtime>now || buf.st_atime>now)
  390.          {
  391.           PrintMessage(Inform,"Cached file '%s/%s/%s' has a future timestamp; changing timestamp.",proto,host,ent->d_name);
  392. #if DO_DELETE
  393.           utime(ent->d_name,NULL);
  394. #else
  395.           PrintMessage(Debug,"utime(%s/%s/%s).",proto,host,ent->d_name);
  396. #endif
  397.          }
  398.        if(*ent->d_name=='U' || *ent->d_name=='D')
  399.          {
  400.           int s;
  401.           *ent->d_name^='U'^'D';
  402.           s=stat(ent->d_name,&buf2);
  403.           *ent->d_name^='U'^'D';
  404.           if(s)
  405.             {
  406.              PrintMessage(Inform,"Cached file '%s/%s/%s' is not complete (U* and D* files); deleting it.",proto,host,ent->d_name);
  407.              age=0;
  408.             }
  409.           else if(*ent->d_name=='U')
  410.              continue;
  411.           else if(PurgeUseURL)
  412.             {
  413.              char *url=FileNameToURL(ent->d_name);
  414.              if(url)
  415.                {
  416.                 URL *Url=SplitURL(url);
  417.                 age=WhatPurgeAge(Url->proto,Url->host,Url->path,Url->args);
  418.                 FreeURL(Url);
  419.                 free(url);
  420.                }
  421.              else
  422.                 age=0;
  423.             }
  424.           else
  425.              age=def_age;
  426.           if(PurgeUseMTime)
  427.              t=buf.st_mtime;
  428.           else
  429.              t=buf.st_atime;
  430.          }
  431.        else
  432.          {
  433.           PrintMessage(Inform,"Cached file '%s/%s/%s' is not valid (U* or D* file); deleting it.",proto,host,ent->d_name);
  434.           age=0;
  435.          }
  436.        if(pass==2 && age>0)
  437.           age=(int)(age*age_scale+0.5);
  438.        if(age==-1 || t>(now-age*(24*3600)))
  439.          {
  440.           long size=2+(buf.st_size+buf2.st_size)/blocksize;
  441.           if(blocks_left==-1)
  442.              blocks_left=0;
  443.           blocks_left+=size;
  444.           if(age>0)
  445.             {
  446.              int days=(now-t)/(24*3600);
  447.              days=days*DefaultPurgeAge/age; /* scale the age to fit into 0 -> DefaultPurgeAge */
  448.              if(days>DefaultPurgeAge)
  449.                 days=DefaultPurgeAge;
  450.              blocks_by_age[days]+=size;
  451.             }
  452.           else
  453.              blocks_by_age[DefaultPurgeAge+1]+=size;
  454.          }
  455.        else
  456.          {
  457. #if DO_DELETE
  458.           if(unlink(ent->d_name))
  459.              PrintMessage(Warning,"Cannot unlink file '%s/%s/%s' [%!s].",proto,host,ent->d_name);
  460. #else
  461.           PrintMessage(Debug,"unlink(%s/%s/%s).",proto,host,ent->d_name);
  462. #endif
  463.           if(*ent->d_name=='U' || *ent->d_name=='D')
  464.             {
  465.              *ent->d_name^='U'^'D';
  466. #if DO_DELETE
  467.              if(unlink(ent->d_name))
  468.                 PrintMessage(Warning,"Cannot unlink file(2) '%s/%s/%s' [%!s].",proto,host,ent->d_name);
  469. #else
  470.              PrintMessage(Debug,"unlink(%s/%s/%s).",proto,host,ent->d_name);
  471. #endif
  472.             }
  473.          }
  474.       }
  475.    }
  476.  closedir(dir);
  477.  chdir("..");
  478.  return(blocks_left);
  479. }