cleantmp.c

Go to the documentation of this file.
00001 /*      cleantmp 1.6 - clean out a tmp dir.             Author: Kees J. Bot
00002  *                                                              11 Apr 1991
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 /* There were no symlinks in medieval times. */
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)    /* A full day in seconds */
00030 #define DOTDAYS 14              /* Don't remove tmp/.* in at least 14 days. */
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;                   /* Force remove all. */
00052 int debug= 0;                   /* Debug level. */
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;  /* Step back to midnight of this day. */
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 /* Path name construction, addpath adds a component, delpath removes it.
00079  * The string 'path' is used throughout the program as the file under
00080  * examination.
00081  */
00082 
00083 char *path;     /* Path name constructed in path[]. */
00084 int plen= 0, pidx= 0;   /* Lenght/index for path[]. */
00085 
00086 void addpath(int *didx, char *name)
00087 /* Add a component to path. (name may also be a full path at the first call)
00088  * The index where the current path ends is stored in *pdi.
00089  */
00090 {
00091         if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0]));
00092 
00093         *didx= pidx;    /* Record point to go back to for delpath. */
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;         /* Put pidx back at the null.  The path[pidx++]= '/'
00108                          * statement will overwrite it at the next call.
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 /* Hash list of files to ignore. */
00171 struct file *ignore_list[1024];
00172 size_t n_ignored= 0;
00173 
00174 unsigned ihash(char *name)
00175 /* A simple hashing function on a file name. */
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 /* Add or remove a file to/from the list of files to ignore. */
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 /* Is a file in the list of ignored files? */
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                         /* don't rm tmp/.* too soon. */
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 }

Generated on Fri Apr 14 22:57:04 2006 for minix by  doxygen 1.4.6