00001
00002
00003
00004 #define nil 0
00005 #include <sys/types.h>
00006 #include <stdio.h>
00007 #include <stddef.h>
00008 #include <stdlib.h>
00009 #include <unistd.h>
00010 #include <sys/stat.h>
00011 #include <string.h>
00012 #include <time.h>
00013 #include <dirent.h>
00014 #include <errno.h>
00015
00016 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
00017 #define arraylimit(a) ((a) + arraysize(a))
00018
00019 #ifndef S_ISLNK
00020
00021 #define lstat stat
00022 #endif
00023
00024 #ifndef DEBUG
00025 #define NDEBUG
00026 #endif
00027 #include <assert.h>
00028
00029 #define SEC_DAY (24 * 3600L)
00030 #define DOTDAYS 14
00031
00032 void report(const char *label)
00033 {
00034 fprintf(stderr, "cleantmp: %s: %s\n", label, strerror(errno));
00035 }
00036
00037 void fatal(const char *label)
00038 {
00039 report(label);
00040 exit(1);
00041 }
00042
00043 void *alloc(size_t s)
00044 {
00045 void *mem;
00046
00047 if ((mem= (void *) malloc(s)) == nil) fatal("");
00048 return mem;
00049 }
00050
00051 int force= 0;
00052 int debug= 0;
00053
00054 void days2time(unsigned long days, time_t *retired, time_t *dotretired)
00055 {
00056 struct tm *tm;
00057 time_t t;
00058
00059 time(&t);
00060
00061 tm= localtime(&t);
00062 tm->tm_hour= 0;
00063 tm->tm_min= 0;
00064 tm->tm_sec= 0;
00065 t= mktime(tm);
00066
00067 if (t < (days - 1) * SEC_DAY) {
00068 *retired= *dotretired= 0;
00069 } else {
00070 *retired= t - (days - 1) * SEC_DAY;
00071 *dotretired= t - (DOTDAYS - 1) * SEC_DAY;
00072 if (*dotretired > *retired) *dotretired= *retired;
00073 }
00074 if (debug >= 2) fprintf(stderr, "Retired: %s", ctime(retired));
00075 if (debug >= 2) fprintf(stderr, "Dotretired: %s", ctime(dotretired));
00076 }
00077
00078
00079
00080
00081
00082
00083 char *path;
00084 int plen= 0, pidx= 0;
00085
00086 void addpath(int *didx, char *name)
00087
00088
00089
00090 {
00091 if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0]));
00092
00093 *didx= pidx;
00094
00095 if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
00096
00097 do {
00098 if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
00099 if (pidx == plen &&
00100 (path= (char *) realloc((void *) path,
00101 (plen*= 2) * sizeof(path[0]))) == nil
00102 ) fatal("");
00103 path[pidx++]= *name;
00104 }
00105 } while (*name++ != 0);
00106
00107 --pidx;
00108
00109
00110 assert(pidx < plen);
00111 }
00112
00113 void delpath(int didx)
00114 {
00115 assert(0 <= didx);
00116 assert(didx <= pidx);
00117 path[pidx= didx]= 0;
00118 }
00119
00120 struct file {
00121 struct file *next;
00122 char *name;
00123 };
00124
00125 struct file *listdir(void)
00126 {
00127 DIR *dp;
00128 struct dirent *entry;
00129 struct file *first, **last= &first;
00130
00131 if ((dp= opendir(path)) == nil) {
00132 report(path);
00133 return nil;
00134 }
00135
00136 while ((entry= readdir(dp)) != nil) {
00137 struct file *new;
00138
00139 if (strcmp(entry->d_name, ".") == 0
00140 || strcmp(entry->d_name, "..") == 0) continue;
00141
00142 new= (struct file *) alloc(sizeof(*new));
00143 new->name= (char *) alloc((size_t) strlen(entry->d_name) + 1);
00144 strcpy(new->name, entry->d_name);
00145
00146 *last= new;
00147 last= &new->next;
00148 }
00149 closedir(dp);
00150 *last= nil;
00151
00152 return first;
00153 }
00154
00155 struct file *shorten(struct file *list)
00156 {
00157 struct file *junk;
00158
00159 assert(list != nil);
00160
00161 junk= list;
00162 list= list->next;
00163
00164 free((void *) junk->name);
00165 free((void *) junk);
00166
00167 return list;
00168 }
00169
00170
00171 struct file *ignore_list[1024];
00172 size_t n_ignored= 0;
00173
00174 unsigned ihash(char *name)
00175
00176 {
00177 unsigned h= 0;
00178
00179 while (*name != 0) h= (h * 0x1111) + *name++;
00180
00181 return h & (arraysize(ignore_list) - 1);
00182 }
00183
00184 void do_ignore(int add, char *name)
00185
00186 {
00187 struct file **ipp, *ip;
00188
00189 ipp= &ignore_list[ihash(name)];
00190 while ((ip= *ipp) != nil) {
00191 if (strcmp(name, ip->name) <= 0) break;
00192 ipp= &ip->next;
00193 }
00194
00195 if (add) {
00196 ip= alloc(sizeof(*ip));
00197 ip->name= alloc((strlen(name) + 1) * sizeof(ip->name[0]));
00198 strcpy(ip->name, name);
00199 ip->next= *ipp;
00200 *ipp= ip;
00201 n_ignored++;
00202 } else {
00203 assert(ip != nil);
00204 *ipp= ip->next;
00205 free(ip->name);
00206 free(ip);
00207 n_ignored--;
00208 }
00209 }
00210
00211 int is_ignored(char *name)
00212
00213 {
00214 struct file *ip;
00215 int r;
00216
00217 ip= ignore_list[ihash(name)];
00218 while (ip != nil) {
00219 if ((r = strcmp(name, ip->name)) <= 0) return (r == 0);
00220 ip= ip->next;
00221 }
00222 return 0;
00223 }
00224
00225 #define is_ignored(name) (n_ignored > 0 && (is_ignored)(name))
00226
00227 time_t retired, dotretired;
00228
00229 enum level { TOP, DOWN };
00230
00231 void cleandir(enum level level, time_t retired)
00232 {
00233 struct file *list;
00234 struct stat st;
00235 time_t ret;
00236
00237 if (debug >= 2) fprintf(stderr, "Cleaning %s\n", path);
00238
00239 list= listdir();
00240
00241 while (list != nil) {
00242 int didx;
00243
00244 ret= (level == TOP && list->name[0] == '.') ?
00245 dotretired : retired;
00246
00247
00248 addpath(&didx, list->name);
00249
00250 if (is_ignored(path)) {
00251 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
00252 do_ignore(0, path);
00253 } else
00254 if (is_ignored(list->name)) {
00255 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
00256 } else
00257 if (lstat(path, &st) < 0) {
00258 report(path);
00259 } else
00260 if (S_ISDIR(st.st_mode)) {
00261 cleandir(DOWN, ret);
00262 if (force || st.st_mtime < ret) {
00263 if (debug < 3 && rmdir(path) < 0) {
00264 if (errno != ENOTEMPTY
00265 && errno != EEXIST) {
00266 report(path);
00267 }
00268 } else {
00269 if (debug >= 1) {
00270 fprintf(stderr,
00271 "rmdir %s\n", path);
00272 }
00273 }
00274 }
00275 } else {
00276 if (force || (st.st_atime < ret
00277 && st.st_mtime < ret
00278 && st.st_ctime < ret)
00279 ) {
00280 if (debug < 3 && unlink(path) < 0) {
00281 if (errno != ENOENT) {
00282 report(path);
00283 }
00284 } else {
00285 if (debug >= 1) {
00286 fprintf(stderr,
00287 "rm %s\n", path);
00288 }
00289 }
00290 }
00291 }
00292 delpath(didx);
00293 list= shorten(list);
00294 }
00295 }
00296
00297 void usage(void)
00298 {
00299 fprintf(stderr,
00300 "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
00301 exit(1);
00302 }
00303
00304 int main(int argc, char **argv)
00305 {
00306 int i;
00307 unsigned long days;
00308
00309 i= 1;
00310 while (i < argc && argv[i][0] == '-') {
00311 char *opt= argv[i++] + 1;
00312
00313 if (opt[0] == '-' && opt[1] == 0) break;
00314
00315 if (opt[0] == 'd') {
00316 debug= 1;
00317 if (opt[1] != 0) debug= atoi(opt + 1);
00318 } else
00319 if (opt[0] == 'i') {
00320 if (*++opt == 0) {
00321 if (i == argc) usage();
00322 opt= argv[i++];
00323 }
00324 do_ignore(1, opt);
00325 } else
00326 if (opt[0] == 'f' && opt[1] == 0) {
00327 force= 1;
00328 days= 1;
00329 } else {
00330 char *end;
00331 days= strtoul(opt, &end, 10);
00332 if (*opt == 0 || *end != 0
00333 || days == 0
00334 || ((time_t) (days * SEC_DAY)) / SEC_DAY != days
00335 ) {
00336 fprintf(stderr,
00337 "cleantmp: %s is not a valid number of days\n",
00338 opt);
00339 exit(1);
00340 }
00341 }
00342 }
00343 if (days == 0) usage();
00344
00345 days2time(days, &retired, &dotretired);
00346
00347 while (i < argc) {
00348 int didx;
00349
00350 if (argv[i][0] == 0) {
00351 fprintf(stderr, "cleantmp: empty pathname!\n");
00352 exit(1);
00353 }
00354 addpath(&didx, argv[i]);
00355 cleandir(TOP, retired);
00356 delpath(didx);
00357 assert(path[0] == 0);
00358 i++;
00359 }
00360 exit(0);
00361 }