From: Andrew Tridgell Date: Thu, 28 Mar 2002 05:37:27 +0000 (+0100) Subject: *much* nicer cache_clean program X-Git-Tag: v1.0~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c79ad6ff5d63c19e2ef07c6b08d17344afef4daa;p=thirdparty%2Fccache.git *much* nicer cache_clean program --- diff --git a/README b/README index 97cb07128..a34f0062f 100644 --- a/README +++ b/README @@ -105,14 +105,15 @@ Cleaning the cache ccache tends to quickly fill up the cache directory. You may find the ccache_clean utility for removing old cache files. If called with no -arguments it will remove any cache files older than a week. You can -also pass a single argument specifying the number of days old files -need to be to be cleaned. For example: - - ccache_clean 3 - -will remove any files older than 3 days. You may wish to call -ccache_clean from a cron job to keep your disk space usage reasonable. +arguments it will trim the cache to be less than 1 Gigabyte. You can +also pass a single argument specifying the size limit on the cache, +for example: + ccache_clean 2G +would clear the oldest files to bring the cache below 2G in size. You +can use 'M' for megabytes, 'G' for gigabytes or 'K' for kilobytes. + +You may wish to call ccache_clean from a cron job to keep your disk +space usage reasonable. How it works ------------ diff --git a/ccache.h b/ccache.h index d3a111f5d..8f0b1b40f 100644 --- a/ccache.h +++ b/ccache.h @@ -41,7 +41,9 @@ int copy_file(const char *src, const char *dest); int create_dir(const char *dir); void x_asprintf(char **ptr, const char *format, ...); char *x_strdup(const char *s); -void traverse(const char *dir, void (*fn)(const char *)); +void *x_realloc(void *ptr, size_t size); +void *x_malloc(size_t size); +void traverse(const char *dir, void (*fn)(const char *, struct stat *)); int execute(char **argv, const char *path_stdout, diff --git a/ccache_clean.c b/ccache_clean.c index 1cb88b44b..9ed3aed38 100644 --- a/ccache_clean.c +++ b/ccache_clean.c @@ -22,29 +22,72 @@ #include "ccache.h" -static time_t threshold; +#define BLOCK_SIZE 1024 + char *cache_logfile = NULL; -static void clean_fn(const char *fname) +static struct files { + char *fname; + time_t mtime; + size_t size; +} **files; +static unsigned allocated; +static unsigned num_files; +static size_t total_size; +static size_t size_threshold = 1024*1024; /* 1G default */ + +static int files_compare(struct files *f1, struct files *f2) { - struct stat st; - if (stat(fname, &st) != 0 || !S_ISREG(st.st_mode)) return; + return f2->mtime - f1->mtime; +} - if (st.st_mtime >= threshold) return; +/* this builds the list of files in the cache */ +static void traverse_fn(const char *fname, struct stat *st) +{ + if (!S_ISREG(st->st_mode)) return; - if (unlink(fname) != 0) { - cc_log("unlink %s - %s\n", fname, strerror(errno)); - return; + if (num_files == allocated) { + allocated = 10000 + num_files*2; + files = x_realloc(files, sizeof(struct files *)*allocated); } - cc_log("cleaned %s\n", fname); + + files[num_files] = x_malloc(sizeof(struct files *)); + files[num_files]->fname = x_strdup(fname); + files[num_files]->mtime = st->st_mtime; + /* we deliberately overestimate by up to 1 block */ + files[num_files]->size = 1 + (st->st_size/BLOCK_SIZE); + total_size += files[num_files]->size; + num_files++; +} + +static void sort_and_clean(void) +{ + unsigned i; + + if (num_files > 1) { + /* sort in ascending data order */ + qsort(files, num_files, sizeof(struct files *), files_compare); + } + + /* delete enough files to bring us below the threshold */ + for (i=0;i= size_threshold;i++) { + if (unlink(files[i]->fname) != 0) { + fprintf(stderr, "unlink %s - %s\n", + files[i]->fname, strerror(errno)); + continue; + } + total_size -= files[i]->size; + } + + printf("cleaned %d of %d files (cache is now %.1f MByte)\n", + i, num_files, total_size/1024.0); } int main(int argc, char *argv[]) { char *cache_dir; - int num_days = 7; - + cache_dir = getenv("CCACHE_DIR"); if (!cache_dir) { x_asprintf(&cache_dir, "%s/.ccache", getenv("HOME")); @@ -59,13 +102,33 @@ int main(int argc, char *argv[]) exit(1); } + /* work out what size cache they want */ if (argc > 1) { - num_days = atoi(argv[1]); + char *s = argv[1]; + char m; + size_threshold = atoi(s); + m = s[strlen(s)-1]; + switch (m) { + case 'G': + case 'g': + size_threshold *= 1024*1024; + break; + case 'M': + case 'm': + size_threshold *= 1024; + break; + case 'K': + case 'k': + size_threshold *= 1; + break; + } } - threshold = time(NULL) - num_days*24*60*60; + /* build a list of files */ + traverse(cache_dir, traverse_fn); - traverse(cache_dir, clean_fn); + /* clean the cache */ + sort_and_clean(); return 0; } diff --git a/util.c b/util.c index 39d1bfe74..1d5d35de6 100644 --- a/util.c +++ b/util.c @@ -142,12 +142,38 @@ char *x_strdup(const char *s) return ret; } +/* + this is like malloc() but dies if the malloc fails +*/ +void *x_malloc(size_t size) +{ + void *ret; + ret = malloc(size); + if (!ret) { + fatal("out of memory in malloc\n"); + } + return ret; +} + +/* + this is like strdup() but dies if the malloc fails +*/ +void *x_realloc(void *ptr, size_t size) +{ + if (!ptr) return x_malloc(size); + ptr = realloc(ptr, size); + if (!ptr) { + fatal("out of memory in x_realloc"); + } + return ptr; +} + /* revsusive directory traversal - used for cleanup fn() is called on all files/dirs in the tree */ -void traverse(const char *dir, void (*fn)(const char *)) +void traverse(const char *dir, void (*fn)(const char *, struct stat *)) { DIR *d; struct dirent *de; @@ -173,7 +199,7 @@ void traverse(const char *dir, void (*fn)(const char *)) traverse(fname, fn); } - fn(fname); + fn(fname, &st); free(fname); }