/* * 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 #ifndef _AIX #include #endif #ifdef _AIX #include #endif #if USE_SYSV_STATFS #include #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 /* 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 #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 (valsize) { truncate(node->name,0); } else { unlink(node->name); } cleaned++; if(node->size) { if (node->atime==node->mtime) { unreadremoved++; } else { readremoved++; } if (node->mtimemtime; 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 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 (ctimectime) { itmp->next = inodeBeg; inodeBeg = itmp; i = itmp; } else { i = inodeBeg; while (i->next) { inodes++; if(ctimenext->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