/*
* DEXPIRECACHE.C - dreaderd cache expire.
*
* (c)Copyright 2002, Francois Petillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*
*/
#include "defs.h"
#include <sys/param.h>
#ifndef _AIX
#include <sys/mount.h>
#endif
#ifdef _AIX
#include <sys/statfs.h>
#endif
#if USE_SYSV_STATFS
#include <sys/statfs.h>
#define f_bavail f_bfree
#endif
/*
* The solaris statvfs is rather limited, but we suffer with reduced
* capability (and hence take a possible performance hit).
*/
#if USE_SUN_STATVFS
#include <sys/statvfs.h> /* god knows if this hack will work */
#define f_bsize f_frsize /* god knows if this hack will work */
#define fsid_t u_long
#define statfs statvfs
#endif
#if USE_SYS_VFS /* this is mainly for linux */
#include <sys/vfs.h>
#endif
char *basepath=NULL;
int freeblocks=0, freeinodes=0, treeblocks=0;
time_t oldest,oldestremoved,newestremoved=0, now=0;
int unread=0,reread=0,unreadremoved=0,readremoved=0,filled=0,lazy=0,lazyremoved=0,cleaned=0;
int verbose=0, trnct=0;
struct FileQueue {
struct FileQueue *h,*l;
char *name;
long size;
time_t atime;
time_t mtime;
} FileQueue;
struct InodeQueue {
struct InodeQueue *next;
char *name;
time_t ctime;
} InodeQueue;
struct FileQueue *fileBeg=NULL, *fileFree=NULL;
struct InodeQueue *inodeBeg=NULL, *inodeEnd=NULL;
time_t fileEnd;
int
removeFiles(struct FileQueue *node, int val) {
if (!node) return val;
val = removeFiles(node->l, val);
if (val<freeblocks) {
if (trnct && node->size) {
truncate(node->name,0);
} else {
unlink(node->name);
}
cleaned++;
if(node->size) {
if (node->atime==node->mtime) {
unreadremoved++;
} else {
readremoved++;
}
if (node->mtime<oldestremoved) oldestremoved=node->mtime;
if (node->mtime>newestremoved) newestremoved=node->mtime;
} else {
lazyremoved++;
}
return removeFiles(node->h, val+node->size);
} else {
return val;
}
}
void
removeLinks(void) {
struct InodeQueue *i=inodeBeg;
while(i) {
if (verbose) printf("Removing %s\n", i->name);
unlink(i->name);
i = i->next;
}
}
void
dropTree (struct FileQueue **pnode) {
struct FileQueue *node = *pnode;
if (!node) return;
dropTree(&node->l);
dropTree(&node->h);
free(node->name);
node->h = fileFree;
fileFree = node;
*pnode = NULL;
}
int
cleanTree (struct FileQueue **pnode, int *val) {
struct FileQueue *node = *pnode;
int hd=0, ld=0;
if (!node) return 0;
ld = cleanTree(&node->l, val);
if (*val>freeblocks) {
/* I am out */
if (node->atime < fileEnd) {
fileEnd = node->atime;
}
dropTree(&node->h);
free(node->name);
node->h = fileFree;
fileFree = node;
*pnode = node->l;
return ld;
}
/* I am in */
*val += node->size;
if (*val>freeblocks) {
/* high tree is out */
dropTree(&node->h);
hd = 0;
} else {
hd = cleanTree(&node->h, val);
}
/* rebalance the tree */
if (ld>hd+1) {
*pnode = node->l;
node->l = (*pnode)->h;
(*pnode)->h = node;
ld--; hd++;
} else if (hd>ld+1) {
*pnode = node->h;
node->h = (*pnode)->l;
(*pnode)->l = node;
hd--; ld++;
}
if (hd>ld)
return hd+1;
return ld+1;
}
int
statTree(struct FileQueue *node, int *size, int *el) {
int hd, ld;
if (!node) return 0;
*el += 1;
*size += node->size;
ld = statTree(node->l, size, el)+1;
hd = statTree(node->h, size, el)+1;
if (ld>hd) return ld;
return hd;
}
void
addFile(char *name, long size, time_t atime, time_t mtime) {
struct FileQueue *ftmp;
int s;
/* to manage lazy cache */
if (atime < mtime) atime = mtime;
if (size) {
filled++;
if (atime==mtime) {
unread++;
} else {
reread++;
}
if (mtime<oldest) oldest=mtime;
} else {
lazy++;
}
if (!freeblocks || (atime > fileEnd))
return;
treeblocks += size;
s = strlen(name)+1;
if (!fileFree) {
struct FileQueue *fq;
int f;
int i;
f = (int) malloc(1024*sizeof(struct FileQueue));
fileFree = (struct FileQueue*)f;
for (i=0; i<1024 ; i++) {
fq = (struct FileQueue*) f;
f += sizeof(struct FileQueue);
fq->h = (struct FileQueue*) f;
}
fq->h = NULL;
}
ftmp = fileFree;
fileFree = ftmp->h;
ftmp->name = (char*) malloc(s);
strncpy(ftmp->name, name, s - 1);
ftmp->name[s - 1] = '\0';
ftmp->size = size;
ftmp->atime = atime;
ftmp->mtime = mtime;
ftmp->h = NULL;
ftmp->l = NULL;
if (!fileBeg) {
fileBeg = ftmp;
return;
} else {
struct FileQueue *i=fileBeg;
while(i) {
if (atime < i->atime) {
if (i->l) {
i = i->l;
} else {
i->l = ftmp;
i = NULL;
}
} else {
if (i->h) {
i = i->h;
} else {
i->h = ftmp;
i = NULL;
}
}
}
}
if (treeblocks>2*freeblocks) {
treeblocks = 0;
if(verbose>1) {
int size=0, el=0, depth;
depth = statTree(fileBeg, &size, &el);
printf("\nResize : %i elements %i octets (%i/%i#%i)\n", el, size, filled, lazy, depth);
}
cleanTree(&fileBeg, &treeblocks);
if(verbose>1) {
int size=0, el=0, depth;
depth = statTree(fileBeg, &size, &el);
printf("Resized : %i elements %i octets (%is#%i)\n", el, size, (int)fileEnd, depth);
}
}
}
void
addSLink(char *name, time_t ctime) {
struct InodeQueue *itmp, *i;
int s;
long inodes=0;
if (ctime < time(NULL)-2*24*3600)
unlink(name);
if (!freeinodes || (inodeEnd && (ctime > inodeEnd->ctime)))
return;
s = strlen(name)+1;
itmp = (struct InodeQueue*) malloc(sizeof(struct InodeQueue));
itmp->name = (char*) malloc(s);
strncpy(itmp->name, name, s - 1);
itmp->name[s - 1] = '\0';
itmp->ctime = ctime;
if (!inodeBeg) {
inodeBeg = itmp;
inodeEnd = itmp;
itmp->next = NULL;
return;
} else if (ctime<inodeBeg->ctime) {
itmp->next = inodeBeg;
inodeBeg = itmp;
i = itmp;
} else {
i = inodeBeg;
while (i->next) {
inodes++;
if(ctime<i->next->ctime) {
itmp->next = i->next;
i->next = itmp;
i = i->next;
break;
}
i = i->next;
}
}
while (i) {
inodes++;
if (inodes>freeinodes) break;
i = i->next;
}
if (i) {
inodeEnd = i;
i = i->next;
inodeEnd->next=NULL;
while (i) {
struct InodeQueue *j;
j = i; i = i->next;
free(j->name);
free(j);
}
}
}
void
parseDir(char *path) {
DIR *dir = NULL;
struct dirent *entry;
if ((dir = opendir(path)) == NULL) {
fprintf(stderr, "Can not parse %s\n", path);
return;
}
while((entry=readdir(dir)) != NULL) {
char name[PATH_MAX];
struct stat st;
if (!strncmp(entry->d_name, ".", 2)
|| !strncmp(entry->d_name, "..", 3)) {
continue;
}
snprintf(name, sizeof(name),"%s/%s", path, entry->d_name);
if (lstat(name, &st)) {
fprintf(stderr, "Can not stat %s\n", name);
continue;
}
if (S_ISDIR(st.st_mode)) {
parseDir(name);
} else if (S_ISLNK(st.st_mode)) {
addSLink(name, st.st_ctime);
} else if (S_ISREG(st.st_mode)) {
if (st.st_blocks) {
addFile(name, st.st_blocks/2, st.st_atime, st.st_mtime);
} else {
if (strncmp(name+(strlen(name)-4),".tmp",4)) {
addSLink(name, st.st_ctime);
} else {
addFile(name, 0, st.st_ctime, st.st_ctime);
}
}
}
}
closedir(dir);
}
int
main(int ac, char **av)
{
int i;
now=oldest=oldestremoved=fileEnd = time(NULL);
for (i = 1; i < ac; ++i) {
char *ptr = av[i];
int v;
if (*ptr != '-') {
fprintf(stderr, "Unexpected option: %s\n", ptr);
exit(1);
}
ptr += 2;
v = (*ptr) ? strtol(ptr, NULL, 0) : 1;
switch(ptr[-1]) {
case 'd':
basepath = av[++i];
break;
case 'f':
freeblocks = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
break;
case 'i':
freeinodes = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
break;
case 't' :
trnct = 1;
break;
case 'v' :
verbose++;
break;
default:
fprintf(stderr, "unknown option: %s\n", ptr - 2);
}
}
if (chdir(basepath) == -1) {
fprintf(stderr, "Unable to change to `%s' (%s).\n",basepath,strerror(errno));
exit(1);
}
{ /* stat fs */
struct statfs fs;
int fb;
if (statfs(".", &fs)) {
fprintf(stderr, "Can not stat fs (%s).\n",strerror(errno));
exit(1);
}
fb = fs.f_bavail*(fs.f_bsize/1024);
if (fb<freeblocks) {
freeblocks -= fb;
} else {
freeblocks = 0;
}
if (fs.f_ffree<freeinodes) {
freeinodes -= fs.f_ffree;
} else {
freeinodes = 0;
}
}
if (freeblocks || freeinodes) {
if (verbose) printf("Base path %s\nFree Space needed : %i\nFree inodes needed : %i\n", basepath, freeblocks, freeinodes);
parseDir(".");
removeFiles(fileBeg,0);
removeLinks();
printf("\nStats :\n* %7i cache files found\n\t%7i reread\n\t%7i unread\n* %7i pre-cache files found\n* %7i files have been cleaned\n\t%7i reread\n\t%7i unread\n\t%7i pre-cache\n", filled, reread, unread, lazy, cleaned, readremoved, unreadremoved, lazyremoved);
printf("Oldest removed file was created %s ago\n", dtlenstr(now-oldestremoved));
printf("Newest removed file was created %s ago\n", dtlenstr(now-newestremoved));
printf("Oldest file found was created %s ago\n", dtlenstr(now-oldest));
printf("DexpireCache has run for %is\n",(int)(time(NULL)-now));
} else {
if (verbose) printf("No need to expire cache\n");
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1