purge.c
上传用户:seven77cht
上传日期:2007-01-04
资源大小:486k
文件大小:17k
- /***************************************
- $Header: /home/amb/wwwoffle/RCS/purge.c 2.26 2000/01/12 22:37:38 amb Exp $
- WWWOFFLE - World Wide Web Offline Explorer - Version 2.5d.
- Purge old files from the cache.
- ******************/ /******************
- Written by Andrew M. Bishop
- This file Copyright 1996,97,98,99,2000 Andrew M. Bishop
- It may be distributed under the GNU Public License, version 2, or
- any higher version. See section COPYING of the GNU Public license
- for conditions under which this file may be redistributed.
- ***************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <utime.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <time.h>
- #include <dirent.h>
- #include <unistd.h>
- #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
- #include <sys/param.h>
- #include <sys/mount.h>
- #elif (defined(__sun__) && defined(__svr4__)) || defined(__irix__)
- #include <sys/statvfs.h>
- #elif defined(__osf__)
- #include <sys/mount.h>
- #else
- #include <sys/vfs.h>
- #endif
- #if (defined(__sun__) && defined(__svr4__)) || defined(__irix__)
- #define STATFS statvfs
- #else
- #define STATFS statfs
- #endif
- #include "wwwoffle.h"
- #include "misc.h"
- #include "proto.h"
- #include "config.h"
- #include "errors.h"
- /* Local functions */
- static long PurgeFiles(char *proto,char *host);
- /*+ Set this to 0 for debugging so that nothing is deleted. +*/
- #define DO_DELETE 1
- /*+ The current time. +*/
- static time_t now;
- /*+ The number of blocks left of each age. +*/
- static int *blocks_by_age;
- /*+ The scaling factor for the ages in the second pass. +*/
- static double age_scale;
- /*+ The pass in the purge (1 or 2). +*/
- static int pass;
- /*+ The blocksize. +*/
- static long int blocksize;
- /*
- Note: Since some UNIX versions (Solaris) have st_size/st_blocks different for
- different files I have reverted to using 1+(st_size/blocksze) instead
- of st_blocks for counting the number of blocks in a file and using
- st_size/blocksize for directories (since they are multiples of blocks).
- This gives a small error in some cases but is fast to calculate compared
- to the more accurate value of st_size/blocksize+!!(st_size%blocksize).
- */
- /*++++++++++++++++++++++++++++++++++++++
- Purge files from the cache that meet the age criteria.
- int fd the file descriptor of the wwwoffle client.
- ++++++++++++++++++++++++++++++++++++++*/
- void PurgeCache(int fd)
- {
- int i,p;
- long diskfree;
- struct STATFS sbuf;
- struct stat buf;
- now=time(NULL)+600;
- age_scale=-1;
- blocks_by_age=(int*)malloc((DefaultPurgeAge+2)*sizeof(int));
- for(i=0;i<=DefaultPurgeAge+1;i++)
- blocks_by_age[i]=0;
- if(stat(".",&buf) || buf.st_size==-1 || buf.st_blocks==-1)
- {
- PrintMessage(Warning,"Cannot determine the disk block size [%!s]; using 1024 instead.");
- blocksize=1024;
- }
- else if(buf.st_blocks==0)
- {
- PrintMessage(Warning,"The number of blocks (0) looks wrong; using 1024 for blocksize.");
- blocksize=1024;
- }
- else
- {
- blocksize=buf.st_size/buf.st_blocks;
- if(blocksize!=512 && blocksize!=1024 && blocksize!=2048)
- {
- PrintMessage(Warning,"The blocksize (%d) looks wrong; using 1024 instead.",blocksize);
- blocksize=1024;
- }
- }
- /* Note: blocksize can be only 512, 1024 or 2048. */
- /* Handle this carefully to avoid overflows from doing blocks*blocksize/1024
- or any errors from doing (blocks/1024)*blocksize */
- #define Blocks_to_kB(blocks)
- (long)((blocksize==512)?((blocks)/2):
- ((blocksize==1024)?(blocks):
- (blocks)*2))
- if(STATFS(".",&sbuf) || sbuf.f_bsize==-1 || sbuf.f_bavail==-1)
- {
- PrintMessage(Warning,"Cannot determine the disk free space [%!s]; assuming 0.");
- diskfree=0;
- }
- else
- {
- int bs=blocksize,dbs=sbuf.f_bsize;
- /* Do this carefully to stop overflow and reduce errors. */
- while(!(dbs&1) && !(bs&1)) /* remove powers of 2. */
- {dbs>>=1;bs>>=1;}
- /* If both were powers of 2 then there should be no problem (either dbs or bs is now 1).
- If not then I am assuming that sbuf.f_bavail is larger than dbs so the error is smaller. */
- diskfree=dbs*(sbuf.f_bavail/bs);
- }
- for(pass=1;pass<=2;pass++)
- {
- int total_blocks=0,total_dirs=0,dir_blocks;
- write_string(fd,"n");
- if(PurgeCacheSize)
- if(pass==1)
- write_string(fd,"Pass 1: Checking dates and sizes of files:n");
- else
- write_string(fd,"Pass 2: Purging files down to specified size:n");
- else
- write_string(fd,"Checking dates of files:n");
- if(pass==1)
- {
- if(PurgeUseMTime)
- write_string(fd," (Using modification time.)n");
- else
- write_string(fd," (Using last access time.)n");
- if(PurgeUseURL)
- write_string(fd," (Using the full URL.)n");
- else
- write_string(fd," (Using the hostname and protocol only.)n");
- }
- write_string(fd,"n");
- for(p=0;p<NProtocols;p++)
- {
- DIR *dir;
- struct dirent* ent;
- struct stat buf;
- char *proto=Protocols[p].name;
- /* Open the spool directory. */
- if(stat(proto,&buf))
- {PrintMessage(Inform,"Cannot stat directory '%s' [%!s]; not purged",proto);continue;}
- dir_blocks=buf.st_size/blocksize;
- total_blocks+=dir_blocks;
- blocks_by_age[DefaultPurgeAge+1]+=dir_blocks;
- if(chdir(proto))
- {PrintMessage(Warning,"Cannot change to directory '%s' [%!s]; not purged.",proto);continue;}
- dir=opendir(".");
- if(!dir)
- {PrintMessage(Warning,"Cannot open directory '%s' [%!s]; not purged.",proto);chdir("..");continue;}
- ent=readdir(dir); /* skip . */
- if(!ent)
- {PrintMessage(Warning,"Cannot read directory '%s' [%!s]; not purged.",proto);closedir(dir);chdir("..");continue;}
- ent=readdir(dir); /* skip .. */
- /* Search through all of the sub directories. */
- while((ent=readdir(dir)))
- {
- struct stat buf;
- if(lstat(ent->d_name,&buf))
- PrintMessage(Inform,"Cannot stat directory '%s/%s' [%!s]; race condition?",proto,ent->d_name);
- else if(S_ISDIR(buf.st_mode))
- {
- long file_blocks=PurgeFiles(proto,ent->d_name);
- dir_blocks=buf.st_size/blocksize;
- if(file_blocks==-1)
- {
- #if DO_DELETE
- if(rmdir(ent->d_name))
- PrintMessage(Warning,"Cannot delete what should be an empty directory '%s/%s' [%!s].",proto,ent->d_name);
- #else
- PrintMessage(Debug,"rmdir(%s/%s).",proto,ent->d_name);
- #endif
- }
- else
- {
- struct utimbuf utbuf;
- utbuf.actime=buf.st_atime;
- utbuf.modtime=buf.st_mtime;
- utime(ent->d_name,&utbuf);
- blocks_by_age[DefaultPurgeAge+1]+=dir_blocks;
- total_blocks+=file_blocks+dir_blocks;
- total_dirs++;
- }
- if(PurgeUseURL)
- {
- if(file_blocks==-1)
- write_formatted(fd,"Purged %6s://%-32s ; (empty) - deletedn",proto,ent->d_name);
- else
- write_formatted(fd,"Purged %6s://%-32s ; %5ld kBn",proto,ent->d_name,Blocks_to_kB(file_blocks+dir_blocks));
- }
- else
- {
- int age=WhatPurgeAge(proto,ent->d_name,"/",NULL);
- if(pass==2 && age>0)
- age=(int)(age*age_scale+0.5);
- if(age<0)
- write_formatted(fd,"Not Purged %6s://%-32s ; %5ld kBn",proto,ent->d_name,Blocks_to_kB(file_blocks+dir_blocks));
- else if(file_blocks==-1)
- write_formatted(fd,"Purged (%2d days) %6s://%-32s ; (empty) - deletedn",age,proto,ent->d_name);
- else
- write_formatted(fd,"Purged (%2d days) %6s://%-32s ; %5ld kBn",age,proto,ent->d_name,Blocks_to_kB(file_blocks+dir_blocks));
- }
- }
- else
- {
- PrintMessage(Warning,"Found an unexpected file instead of a directory '%s/%s' [%!s]; deleting it.",proto,ent->d_name);
- #if DO_DELETE
- if(unlink(ent->d_name))
- PrintMessage(Warning,"Cannot delete the non-directory '%s/%s' [%!s].",proto,ent->d_name);
- #else
- PrintMessage(Debug,"unlink(%s/%s).",proto,ent->d_name);
- #endif
- }
- }
- closedir(dir);
- chdir("..");
- }
- write_string(fd,"n");
- write_formatted(fd,"Total of %d directories ; %ld kBn",total_dirs,Blocks_to_kB(total_blocks));
- if(pass==1)
- {
- int age_for_size=-1,age_for_free=-1;
- int age_blocks_used=blocks_by_age[DefaultPurgeAge+1];
- int age_blocks_free=diskfree+total_blocks-blocks_by_age[DefaultPurgeAge+1];
- write_string(fd,"n");
- write_string(fd,"Age Profile of cached pages:n");
- write_string(fd," (All ages scaled to the range 0 -> default age.)n");
- write_string(fd,"n");
- write_formatted(fd,"Total not purged ; %5ld kB (%6ld kB free)n",
- Blocks_to_kB(age_blocks_used),Blocks_to_kB(age_blocks_free));
- write_string(fd,"n");
- for(i=0;i<=DefaultPurgeAge;i++)
- {
- age_blocks_used+=blocks_by_age[i];
- age_blocks_free-=blocks_by_age[i];
- if(PurgeCacheSize && age_for_size<0 && Blocks_to_kB(age_blocks_used)>(1024*PurgeCacheSize))
- {
- age_for_size=i;
- write_formatted(fd,"Cutoff Age is %2d days for %3d MB cache sizen",age_for_size,PurgeCacheSize);
- }
- if(PurgeDiskFree && diskfree && age_for_free<0 && Blocks_to_kB(age_blocks_free)<(1024*PurgeDiskFree))
- {
- age_for_free=i;
- write_formatted(fd,"Cutoff Age is %2d days for %3d MB disk freen",age_for_free,PurgeDiskFree);
- }
- if(i==DefaultPurgeAge)
- write_formatted(fd,"Total all ages ; %5ld kB (%6ld kB free)n",
- Blocks_to_kB(age_blocks_used-blocks_by_age[DefaultPurgeAge+1]),
- diskfree?Blocks_to_kB(age_blocks_free):0);
- else
- write_formatted(fd,"Newer than %2d day%c ; %5ld kB (%6ld kB free)n",i+1,i?'s':' ',
- Blocks_to_kB(age_blocks_used-blocks_by_age[DefaultPurgeAge+1]),
- diskfree?Blocks_to_kB(age_blocks_free):0);
- }
- if(DefaultPurgeAge)
- {
- if(age_for_size!=-1 && (age_for_size<=age_for_free || age_for_free==-1))
- age_scale=(double)age_for_size/(double)DefaultPurgeAge;
- else if(age_for_free!=-1 && (age_for_free<age_for_size || age_for_size==-1))
- age_scale=(double)age_for_free/(double)DefaultPurgeAge;
- }
- else if(age_for_size!=-1 || age_for_free!=-1)
- age_scale=0;
- }
- if(age_scale==-1)
- break;
- }
- write_string(fd,"n");
- free(blocks_by_age);
- /* Purge the tmp.* files in outgoing. */
- if(chdir("outgoing"))
- PrintMessage(Warning,"Cannot change to directory 'outgoing' [%!s]; not purged.");
- else
- {
- DIR *dir;
- struct dirent* ent;
- dir=opendir(".");
- if(!dir)
- PrintMessage(Warning,"Cannot open directory 'outgoing' [%!s]; not purged.");
- else
- {
- ent=readdir(dir); /* skip . */
- if(!ent)
- PrintMessage(Warning,"Cannot read directory 'outgoing' [%!s]; not purged.");
- else
- {
- ent=readdir(dir); /* skip .. */
- while((ent=readdir(dir)))
- if(!strncmp(ent->d_name,"tmp.",4))
- {
- struct stat buf;
- if(!stat(ent->d_name,&buf) && buf.st_mtime<(now-60))
- {
- #if DO_DELETE
- if(unlink(ent->d_name))
- PrintMessage(Warning,"Cannot unlink file 'outgoing/%s' [%!s].",ent->d_name);
- #else
- PrintMessage(Debug,"unlink(outgoing/%s).",ent->d_name);
- #endif
- }
- }
- }
- closedir(dir);
- }
- }
- chdir("..");
- /* Purge the tmp.* files in temp. */
- if(chdir("temp"))
- PrintMessage(Warning,"Cannot change to directory 'temp' [%!s]; not purged.");
- else
- {
- DIR *dir;
- struct dirent* ent;
- dir=opendir(".");
- if(!dir)
- PrintMessage(Warning,"Cannot open directory 'temp' [%!s]; not purged.");
- else
- {
- ent=readdir(dir); /* skip . */
- if(!ent)
- PrintMessage(Warning,"Cannot read directory 'temp' [%!s]; not purged.");
- else
- {
- ent=readdir(dir); /* skip .. */
- while((ent=readdir(dir)))
- if(!strncmp(ent->d_name,"tmp.",4))
- {
- struct stat buf;
- if(!stat(ent->d_name,&buf) && buf.st_mtime<(now-60))
- {
- #if DO_DELETE
- if(unlink(ent->d_name))
- PrintMessage(Warning,"Cannot unlink file 'temp/%s' [%!s].",ent->d_name);
- #else
- PrintMessage(Debug,"unlink(temp/%s).",ent->d_name);
- #endif
- }
- }
- }
- closedir(dir);
- }
- }
- chdir("..");
- }
- /*++++++++++++++++++++++++++++++++++++++
- Delete the file in the current directory that are older than the specified age.
- long PurgeFiles Returns the number of blocks in files that are left.
- char *proto The name of the protocol directory to purge.
- char *host The name of the host directory to purge.
- ++++++++++++++++++++++++++++++++++++++*/
- static long PurgeFiles(char *proto,char *host)
- {
- long blocks_left=-1;
- DIR *dir;
- struct dirent* ent;
- int def_age=WhatPurgeAge(proto,host,"/",NULL);
- /* Open the spool subdirectory. */
- if(chdir(host))
- {PrintMessage(Warning,"Cannot change to directory '%s/%s' [%!s]; not purged.",proto,host);return(1);}
- dir=opendir(".");
- if(!dir)
- {PrintMessage(Warning,"Cannot open directory '%s/%s' [%!s]; not purged.",proto,host);chdir("..");return(1);}
- ent=readdir(dir); /* skip . */
- if(!ent)
- {PrintMessage(Warning,"Cannot read directory '%s/%s' [%!s]; not purged.",proto,host);closedir(dir);chdir("..");return(1);}
- ent=readdir(dir); /* skip .. */
- /* Check all of the files for age, and delete as needed. */
- while((ent=readdir(dir)))
- {
- struct stat buf,buf2;
- if(stat(ent->d_name,&buf))
- ;
- /*
- PrintMessage(Inform,"Cannot stat file '%s/%s/%s' [%!s]; race condition?",proto,host,ent->d_name);
- */
- else
- {
- int age=DefaultPurgeAge;
- time_t t=now;
- if(buf.st_mtime>now || buf.st_atime>now)
- {
- PrintMessage(Inform,"Cached file '%s/%s/%s' has a future timestamp; changing timestamp.",proto,host,ent->d_name);
- #if DO_DELETE
- utime(ent->d_name,NULL);
- #else
- PrintMessage(Debug,"utime(%s/%s/%s).",proto,host,ent->d_name);
- #endif
- }
- if(*ent->d_name=='U' || *ent->d_name=='D')
- {
- int s;
- *ent->d_name^='U'^'D';
- s=stat(ent->d_name,&buf2);
- *ent->d_name^='U'^'D';
- if(s)
- {
- PrintMessage(Inform,"Cached file '%s/%s/%s' is not complete (U* and D* files); deleting it.",proto,host,ent->d_name);
- age=0;
- }
- else if(*ent->d_name=='U')
- continue;
- else if(PurgeUseURL)
- {
- char *url=FileNameToURL(ent->d_name);
- if(url)
- {
- URL *Url=SplitURL(url);
- age=WhatPurgeAge(Url->proto,Url->host,Url->path,Url->args);
- FreeURL(Url);
- free(url);
- }
- else
- age=0;
- }
- else
- age=def_age;
- if(PurgeUseMTime)
- t=buf.st_mtime;
- else
- t=buf.st_atime;
- }
- else
- {
- PrintMessage(Inform,"Cached file '%s/%s/%s' is not valid (U* or D* file); deleting it.",proto,host,ent->d_name);
- age=0;
- }
- if(pass==2 && age>0)
- age=(int)(age*age_scale+0.5);
- if(age==-1 || t>(now-age*(24*3600)))
- {
- long size=2+(buf.st_size+buf2.st_size)/blocksize;
- if(blocks_left==-1)
- blocks_left=0;
- blocks_left+=size;
- if(age>0)
- {
- int days=(now-t)/(24*3600);
- days=days*DefaultPurgeAge/age; /* scale the age to fit into 0 -> DefaultPurgeAge */
- if(days>DefaultPurgeAge)
- days=DefaultPurgeAge;
- blocks_by_age[days]+=size;
- }
- else
- blocks_by_age[DefaultPurgeAge+1]+=size;
- }
- else
- {
- #if DO_DELETE
- if(unlink(ent->d_name))
- PrintMessage(Warning,"Cannot unlink file '%s/%s/%s' [%!s].",proto,host,ent->d_name);
- #else
- PrintMessage(Debug,"unlink(%s/%s/%s).",proto,host,ent->d_name);
- #endif
- if(*ent->d_name=='U' || *ent->d_name=='D')
- {
- *ent->d_name^='U'^'D';
- #if DO_DELETE
- if(unlink(ent->d_name))
- PrintMessage(Warning,"Cannot unlink file(2) '%s/%s/%s' [%!s].",proto,host,ent->d_name);
- #else
- PrintMessage(Debug,"unlink(%s/%s/%s).",proto,host,ent->d_name);
- #endif
- }
- }
- }
- }
- closedir(dir);
- chdir("..");
- return(blocks_left);
- }