source: trunk/minix/commands/simple/cleantmp.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 7.0 KB
RevLine 
[9]1/* cleantmp 1.6 - clean out a tmp dir. Author: Kees J. Bot
2 * 11 Apr 1991
3 */
4#define nil 0
5#include <sys/types.h>
6#include <stdio.h>
7#include <stddef.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <sys/stat.h>
11#include <string.h>
12#include <time.h>
13#include <dirent.h>
14#include <errno.h>
15
16#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
17#define arraylimit(a) ((a) + arraysize(a))
18
19#ifndef S_ISLNK
20/* There were no symlinks in medieval times. */
21#define lstat stat
22#endif
23
24#ifndef DEBUG
25#define NDEBUG
26#endif
27#include <assert.h>
28
29#define SEC_DAY (24 * 3600L) /* A full day in seconds */
30#define DOTDAYS 14 /* Don't remove tmp/.* in at least 14 days. */
31
32void report(const char *label)
33{
34 fprintf(stderr, "cleantmp: %s: %s\n", label, strerror(errno));
35}
36
37void fatal(const char *label)
38{
39 report(label);
40 exit(1);
41}
42
43void *alloc(size_t s)
44{
45 void *mem;
46
47 if ((mem= (void *) malloc(s)) == nil) fatal("");
48 return mem;
49}
50
51int force= 0; /* Force remove all. */
52int debug= 0; /* Debug level. */
53
54void days2time(unsigned long days, time_t *retired, time_t *dotretired)
55{
56 struct tm *tm;
57 time_t t;
58
59 time(&t);
60
61 tm= localtime(&t);
62 tm->tm_hour= 0;
63 tm->tm_min= 0;
64 tm->tm_sec= 0; /* Step back to midnight of this day. */
65 t= mktime(tm);
66
67 if (t < (days - 1) * SEC_DAY) {
68 *retired= *dotretired= 0;
69 } else {
70 *retired= t - (days - 1) * SEC_DAY;
71 *dotretired= t - (DOTDAYS - 1) * SEC_DAY;
72 if (*dotretired > *retired) *dotretired= *retired;
73 }
74 if (debug >= 2) fprintf(stderr, "Retired: %s", ctime(retired));
75 if (debug >= 2) fprintf(stderr, "Dotretired: %s", ctime(dotretired));
76}
77
78/* Path name construction, addpath adds a component, delpath removes it.
79 * The string 'path' is used throughout the program as the file under
80 * examination.
81 */
82
83char *path; /* Path name constructed in path[]. */
84int plen= 0, pidx= 0; /* Lenght/index for path[]. */
85
86void addpath(int *didx, char *name)
87/* Add a component to path. (name may also be a full path at the first call)
88 * The index where the current path ends is stored in *pdi.
89 */
90{
91 if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0]));
92
93 *didx= pidx; /* Record point to go back to for delpath. */
94
95 if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
96
97 do {
98 if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
99 if (pidx == plen &&
100 (path= (char *) realloc((void *) path,
101 (plen*= 2) * sizeof(path[0]))) == nil
102 ) fatal("");
103 path[pidx++]= *name;
104 }
105 } while (*name++ != 0);
106
107 --pidx; /* Put pidx back at the null. The path[pidx++]= '/'
108 * statement will overwrite it at the next call.
109 */
110 assert(pidx < plen);
111}
112
113void delpath(int didx)
114{
115 assert(0 <= didx);
116 assert(didx <= pidx);
117 path[pidx= didx]= 0;
118}
119
120struct file {
121 struct file *next;
122 char *name;
123};
124
125struct file *listdir(void)
126{
127 DIR *dp;
128 struct dirent *entry;
129 struct file *first, **last= &first;
130
131 if ((dp= opendir(path)) == nil) {
132 report(path);
133 return nil;
134 }
135
136 while ((entry= readdir(dp)) != nil) {
137 struct file *new;
138
139 if (strcmp(entry->d_name, ".") == 0
140 || strcmp(entry->d_name, "..") == 0) continue;
141
142 new= (struct file *) alloc(sizeof(*new));
143 new->name= (char *) alloc((size_t) strlen(entry->d_name) + 1);
144 strcpy(new->name, entry->d_name);
145
146 *last= new;
147 last= &new->next;
148 }
149 closedir(dp);
150 *last= nil;
151
152 return first;
153}
154
155struct file *shorten(struct file *list)
156{
157 struct file *junk;
158
159 assert(list != nil);
160
161 junk= list;
162 list= list->next;
163
164 free((void *) junk->name);
165 free((void *) junk);
166
167 return list;
168}
169
170/* Hash list of files to ignore. */
171struct file *ignore_list[1024];
172size_t n_ignored= 0;
173
174unsigned ihash(char *name)
175/* A simple hashing function on a file name. */
176{
177 unsigned h= 0;
178
179 while (*name != 0) h= (h * 0x1111) + *name++;
180
181 return h & (arraysize(ignore_list) - 1);
182}
183
184void do_ignore(int add, char *name)
185/* Add or remove a file to/from the list of files to ignore. */
186{
187 struct file **ipp, *ip;
188
189 ipp= &ignore_list[ihash(name)];
190 while ((ip= *ipp) != nil) {
191 if (strcmp(name, ip->name) <= 0) break;
192 ipp= &ip->next;
193 }
194
195 if (add) {
196 ip= alloc(sizeof(*ip));
197 ip->name= alloc((strlen(name) + 1) * sizeof(ip->name[0]));
198 strcpy(ip->name, name);
199 ip->next= *ipp;
200 *ipp= ip;
201 n_ignored++;
202 } else {
203 assert(ip != nil);
204 *ipp= ip->next;
205 free(ip->name);
206 free(ip);
207 n_ignored--;
208 }
209}
210
211int is_ignored(char *name)
212/* Is a file in the list of ignored files? */
213{
214 struct file *ip;
215 int r;
216
217 ip= ignore_list[ihash(name)];
218 while (ip != nil) {
219 if ((r = strcmp(name, ip->name)) <= 0) return (r == 0);
220 ip= ip->next;
221 }
222 return 0;
223}
224
225#define is_ignored(name) (n_ignored > 0 && (is_ignored)(name))
226
227time_t retired, dotretired;
228
229enum level { TOP, DOWN };
230
231void cleandir(enum level level, time_t retired)
232{
233 struct file *list;
234 struct stat st;
235 time_t ret;
236
237 if (debug >= 2) fprintf(stderr, "Cleaning %s\n", path);
238
239 list= listdir();
240
241 while (list != nil) {
242 int didx;
243
244 ret= (level == TOP && list->name[0] == '.') ?
245 dotretired : retired;
246 /* don't rm tmp/.* too soon. */
247
248 addpath(&didx, list->name);
249
250 if (is_ignored(path)) {
251 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
252 do_ignore(0, path);
253 } else
254 if (is_ignored(list->name)) {
255 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
256 } else
257 if (lstat(path, &st) < 0) {
258 report(path);
259 } else
260 if (S_ISDIR(st.st_mode)) {
261 cleandir(DOWN, ret);
262 if (force || st.st_mtime < ret) {
263 if (debug < 3 && rmdir(path) < 0) {
264 if (errno != ENOTEMPTY
265 && errno != EEXIST) {
266 report(path);
267 }
268 } else {
269 if (debug >= 1) {
270 fprintf(stderr,
271 "rmdir %s\n", path);
272 }
273 }
274 }
275 } else {
276 if (force || (st.st_atime < ret
277 && st.st_mtime < ret
278 && st.st_ctime < ret)
279 ) {
280 if (debug < 3 && unlink(path) < 0) {
281 if (errno != ENOENT) {
282 report(path);
283 }
284 } else {
285 if (debug >= 1) {
286 fprintf(stderr,
287 "rm %s\n", path);
288 }
289 }
290 }
291 }
292 delpath(didx);
293 list= shorten(list);
294 }
295}
296
297void usage(void)
298{
299 fprintf(stderr,
300 "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
301 exit(1);
302}
303
304int main(int argc, char **argv)
305{
306 int i;
307 unsigned long days;
308
309 i= 1;
310 while (i < argc && argv[i][0] == '-') {
311 char *opt= argv[i++] + 1;
312
313 if (opt[0] == '-' && opt[1] == 0) break;
314
315 if (opt[0] == 'd') {
316 debug= 1;
317 if (opt[1] != 0) debug= atoi(opt + 1);
318 } else
319 if (opt[0] == 'i') {
320 if (*++opt == 0) {
321 if (i == argc) usage();
322 opt= argv[i++];
323 }
324 do_ignore(1, opt);
325 } else
326 if (opt[0] == 'f' && opt[1] == 0) {
327 force= 1;
328 days= 1;
329 } else {
330 char *end;
331 days= strtoul(opt, &end, 10);
332 if (*opt == 0 || *end != 0
333 || days == 0
334 || ((time_t) (days * SEC_DAY)) / SEC_DAY != days
335 ) {
336 fprintf(stderr,
337 "cleantmp: %s is not a valid number of days\n",
338 opt);
339 exit(1);
340 }
341 }
342 }
343 if (days == 0) usage();
344
345 days2time(days, &retired, &dotretired);
346
347 while (i < argc) {
348 int didx;
349
350 if (argv[i][0] == 0) {
351 fprintf(stderr, "cleantmp: empty pathname!\n");
352 exit(1);
353 }
354 addpath(&didx, argv[i]);
355 cleandir(TOP, retired);
356 delpath(didx);
357 assert(path[0] == 0);
358 i++;
359 }
360 exit(0);
361}
Note: See TracBrowser for help on using the repository browser.