364 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*	cleantmp 1.6 - clean out a tmp dir.		Author: Kees J. Bot
 | 
						|
 *								11 Apr 1991
 | 
						|
 */
 | 
						|
#define nil 0
 | 
						|
#include <sys/types.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <string.h>
 | 
						|
#include <time.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
#define arraysize(a)	(sizeof(a) / sizeof((a)[0]))
 | 
						|
#define arraylimit(a)	((a) + arraysize(a))
 | 
						|
 | 
						|
#ifndef S_ISLNK
 | 
						|
/* There were no symlinks in medieval times. */
 | 
						|
#define lstat stat
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef DEBUG
 | 
						|
#ifndef NDEBUG
 | 
						|
#define NDEBUG
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
#define SEC_DAY	(24 * 3600L)	/* A full day in seconds */
 | 
						|
#define DOTDAYS	14		/* Don't remove tmp/.* in at least 14 days. */
 | 
						|
 | 
						|
void report(const char *label)
 | 
						|
{
 | 
						|
	fprintf(stderr, "cleantmp: %s: %s\n", label, strerror(errno));
 | 
						|
}
 | 
						|
 | 
						|
void fatal(const char *label)
 | 
						|
{
 | 
						|
	report(label);
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
void *alloc(size_t s)
 | 
						|
{
 | 
						|
	void *mem;
 | 
						|
 | 
						|
	if ((mem= (void *) malloc(s)) == nil) fatal("");
 | 
						|
	return mem;
 | 
						|
}
 | 
						|
 | 
						|
int force= 0;			/* Force remove all. */
 | 
						|
int debug= 0;			/* Debug level. */
 | 
						|
 | 
						|
void days2time(unsigned long days, time_t *retired, time_t *dotretired)
 | 
						|
{
 | 
						|
	struct tm *tm;
 | 
						|
	time_t t;
 | 
						|
 | 
						|
	time(&t);
 | 
						|
 | 
						|
	tm= localtime(&t);
 | 
						|
	tm->tm_hour= 0;
 | 
						|
	tm->tm_min= 0;
 | 
						|
	tm->tm_sec= 0;	/* Step back to midnight of this day. */
 | 
						|
	t= mktime(tm);
 | 
						|
 | 
						|
	if (t < (days - 1) * SEC_DAY) {
 | 
						|
		*retired= *dotretired= 0;
 | 
						|
	} else {
 | 
						|
		*retired= t - (days - 1) * SEC_DAY;
 | 
						|
		*dotretired= t - (DOTDAYS - 1) * SEC_DAY;
 | 
						|
		if (*dotretired > *retired) *dotretired= *retired;
 | 
						|
	}
 | 
						|
	if (debug >= 2) fprintf(stderr, "Retired:    %s", ctime(retired));
 | 
						|
	if (debug >= 2) fprintf(stderr, "Dotretired: %s", ctime(dotretired));
 | 
						|
}
 | 
						|
 | 
						|
/* Path name construction, addpath adds a component, delpath removes it.
 | 
						|
 * The string 'path' is used throughout the program as the file under
 | 
						|
 * examination.
 | 
						|
 */
 | 
						|
 | 
						|
char *path;	/* Path name constructed in path[]. */
 | 
						|
int plen= 0, pidx= 0;	/* Lenght/index for path[]. */
 | 
						|
 | 
						|
void addpath(int *didx, const char *name)
 | 
						|
/* Add a component to path. (name may also be a full path at the first call)
 | 
						|
 * The index where the current path ends is stored in *pdi.
 | 
						|
 */
 | 
						|
{
 | 
						|
	if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0]));
 | 
						|
 | 
						|
	*didx= pidx;	/* Record point to go back to for delpath. */
 | 
						|
 | 
						|
	if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
 | 
						|
 | 
						|
	do {
 | 
						|
		if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
 | 
						|
			if (pidx == plen &&
 | 
						|
				(path= (char *) realloc((void *) path,
 | 
						|
					(plen*= 2) * sizeof(path[0]))) == nil
 | 
						|
			) fatal("");
 | 
						|
			path[pidx++]= *name;
 | 
						|
		}
 | 
						|
	} while (*name++ != 0);
 | 
						|
 | 
						|
	--pidx;		/* Put pidx back at the null.  The path[pidx++]= '/'
 | 
						|
			 * statement will overwrite it at the next call.
 | 
						|
			 */
 | 
						|
	assert(pidx < plen);
 | 
						|
}
 | 
						|
 | 
						|
void delpath(int didx)
 | 
						|
{
 | 
						|
	assert(0 <= didx);
 | 
						|
	assert(didx <= pidx);
 | 
						|
	path[pidx= didx]= 0;
 | 
						|
}
 | 
						|
 | 
						|
struct file {
 | 
						|
	struct file	*next;
 | 
						|
	char		*name;
 | 
						|
};
 | 
						|
 | 
						|
struct file *listdir(void)
 | 
						|
{
 | 
						|
	DIR *dp;
 | 
						|
	struct dirent *entry;
 | 
						|
	struct file *first, **last= &first;
 | 
						|
 | 
						|
	if ((dp= opendir(path)) == nil) {
 | 
						|
		report(path);
 | 
						|
		return nil;
 | 
						|
	}
 | 
						|
 | 
						|
	while ((entry= readdir(dp)) != nil) {
 | 
						|
		struct file *new;
 | 
						|
 | 
						|
		if (strcmp(entry->d_name, ".") == 0
 | 
						|
			|| strcmp(entry->d_name, "..") == 0) continue;
 | 
						|
 | 
						|
		new= (struct file *) alloc(sizeof(*new));
 | 
						|
		new->name= (char *) alloc((size_t) strlen(entry->d_name) + 1);
 | 
						|
		strcpy(new->name, entry->d_name);
 | 
						|
 | 
						|
		*last= new;
 | 
						|
		last= &new->next;
 | 
						|
	}
 | 
						|
	closedir(dp);
 | 
						|
	*last= nil;
 | 
						|
 | 
						|
	return first;
 | 
						|
}
 | 
						|
 | 
						|
struct file *shorten(struct file *list)
 | 
						|
{
 | 
						|
	struct file *junk;
 | 
						|
 | 
						|
	assert(list != nil);
 | 
						|
 | 
						|
	junk= list;
 | 
						|
	list= list->next;
 | 
						|
 | 
						|
	free((void *) junk->name);
 | 
						|
	free((void *) junk);
 | 
						|
 | 
						|
	return list;
 | 
						|
}
 | 
						|
 | 
						|
/* Hash list of files to ignore. */
 | 
						|
struct file *ignore_list[1024];
 | 
						|
size_t n_ignored= 0;
 | 
						|
 | 
						|
unsigned ihash(const char *name)
 | 
						|
/* A simple hashing function on a file name. */
 | 
						|
{
 | 
						|
	unsigned h= 0;
 | 
						|
 | 
						|
	while (*name != 0) h= (h * 0x1111) + *name++;
 | 
						|
 | 
						|
	return h & (arraysize(ignore_list) - 1);
 | 
						|
}
 | 
						|
 | 
						|
void do_ignore(int add, const char *name)
 | 
						|
/* Add or remove a file to/from the list of files to ignore. */
 | 
						|
{
 | 
						|
	struct file **ipp, *ip;
 | 
						|
 | 
						|
	ipp= &ignore_list[ihash(name)];
 | 
						|
	while ((ip= *ipp) != nil) {
 | 
						|
		if (strcmp(name, ip->name) <= 0) break;
 | 
						|
		ipp= &ip->next;
 | 
						|
	}
 | 
						|
 | 
						|
	if (add) {
 | 
						|
		ip= alloc(sizeof(*ip));
 | 
						|
		ip->name= alloc((strlen(name) + 1) * sizeof(ip->name[0]));
 | 
						|
		strcpy(ip->name, name);
 | 
						|
		ip->next= *ipp;
 | 
						|
		*ipp= ip;
 | 
						|
		n_ignored++;
 | 
						|
	} else {
 | 
						|
		assert(ip != nil);
 | 
						|
		*ipp= ip->next;
 | 
						|
		free(ip->name);
 | 
						|
		free(ip);
 | 
						|
		n_ignored--;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int is_ignored(const char *name)
 | 
						|
/* Is a file in the list of ignored files? */
 | 
						|
{
 | 
						|
	struct file *ip;
 | 
						|
	int r;
 | 
						|
 | 
						|
	ip= ignore_list[ihash(name)];
 | 
						|
	while (ip != nil) {
 | 
						|
		if ((r = strcmp(name, ip->name)) <= 0) return (r == 0);
 | 
						|
		ip= ip->next;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#define is_ignored(name) (n_ignored > 0 && (is_ignored)(name))
 | 
						|
 | 
						|
time_t retired, dotretired;
 | 
						|
 | 
						|
enum level { TOP, DOWN };
 | 
						|
 | 
						|
void cleandir(enum level level, time_t retired)
 | 
						|
{
 | 
						|
	struct file *list;
 | 
						|
	struct stat st;
 | 
						|
	time_t ret;
 | 
						|
 | 
						|
	if (debug >= 2) fprintf(stderr, "Cleaning %s\n", path);
 | 
						|
 | 
						|
	list= listdir();
 | 
						|
 | 
						|
	while (list != nil) {
 | 
						|
		int didx;
 | 
						|
 | 
						|
		ret= (level == TOP && list->name[0] == '.') ?
 | 
						|
			dotretired : retired;
 | 
						|
			/* don't rm tmp/.* too soon. */
 | 
						|
 | 
						|
		addpath(&didx, list->name);
 | 
						|
 | 
						|
		if (is_ignored(path)) {
 | 
						|
			if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
 | 
						|
			do_ignore(0, path);
 | 
						|
		} else
 | 
						|
		if (is_ignored(list->name)) {
 | 
						|
			if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
 | 
						|
		} else
 | 
						|
		if (lstat(path, &st) < 0) {
 | 
						|
			report(path);
 | 
						|
		} else
 | 
						|
		if (S_ISDIR(st.st_mode)) {
 | 
						|
			cleandir(DOWN, ret);
 | 
						|
			if (force || st.st_mtime < ret) {
 | 
						|
				if (debug < 3 && rmdir(path) < 0) {
 | 
						|
					if (errno != ENOTEMPTY
 | 
						|
							&& errno != EEXIST) {
 | 
						|
						report(path);
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					if (debug >= 1) {
 | 
						|
						fprintf(stderr,
 | 
						|
							"rmdir %s\n", path);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			if (force || (st.st_atime < ret
 | 
						|
					&& st.st_mtime < ret
 | 
						|
					&& st.st_ctime < ret)
 | 
						|
			) {
 | 
						|
				if (debug < 3 && unlink(path) < 0) {
 | 
						|
					if (errno != ENOENT) {
 | 
						|
						report(path);
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					if (debug >= 1) {
 | 
						|
						fprintf(stderr,
 | 
						|
							"rm %s\n", path);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		delpath(didx);
 | 
						|
		list= shorten(list);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void usage(void)
 | 
						|
{
 | 
						|
	fprintf(stderr,
 | 
						|
	"Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	unsigned long days;
 | 
						|
 | 
						|
	i= 1;
 | 
						|
	while (i < argc && argv[i][0] == '-') {
 | 
						|
		char *opt= argv[i++] + 1;
 | 
						|
 | 
						|
		if (opt[0] == '-' && opt[1] == 0) break;
 | 
						|
 | 
						|
		if (opt[0] == 'd') {
 | 
						|
			debug= 1;
 | 
						|
			if (opt[1] != 0) debug= atoi(opt + 1);
 | 
						|
		} else
 | 
						|
		if (opt[0] == 'i') {
 | 
						|
			if (*++opt == 0) {
 | 
						|
				if (i == argc) usage();
 | 
						|
				opt= argv[i++];
 | 
						|
			}
 | 
						|
			do_ignore(1, opt);
 | 
						|
		} else
 | 
						|
		if (opt[0] == 'f' && opt[1] == 0) {
 | 
						|
			force= 1;
 | 
						|
			days= 1;
 | 
						|
		} else {
 | 
						|
			char *end;
 | 
						|
			days= strtoul(opt, &end, 10);
 | 
						|
			if (*opt == 0 || *end != 0
 | 
						|
				|| days == 0
 | 
						|
				|| ((time_t) (days * SEC_DAY)) / SEC_DAY != days
 | 
						|
			) {
 | 
						|
				fprintf(stderr,
 | 
						|
				"cleantmp: %s is not a valid number of days\n",
 | 
						|
					opt);
 | 
						|
				exit(1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (days == 0) usage();
 | 
						|
 | 
						|
	days2time(days, &retired, &dotretired);
 | 
						|
 | 
						|
	while (i < argc) {
 | 
						|
		int didx;
 | 
						|
 | 
						|
		if (argv[i][0] == 0) {
 | 
						|
			fprintf(stderr, "cleantmp: empty pathname!\n");
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
		addpath(&didx, argv[i]);
 | 
						|
		cleandir(TOP, retired);
 | 
						|
		delpath(didx);
 | 
						|
		assert(path[0] == 0);
 | 
						|
		i++;
 | 
						|
	}
 | 
						|
	exit(0);
 | 
						|
}
 |