Listing 1  cleantmp -- C source

/*
 *  cleantmp.c --
 *	delete old files and empty directories from /tmp 
 *	and /usr/tmp in accord with Official Policy 
 */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#define TOPFILELIM 86400	/* top level: one day 		*/
#define SUBFILELIM 3 * 86400	/* subdirectories: 3 days	*/

typedef struct listnode *ListPtr;
typedef struct listnode {	/* list entry for filenames	*/
    char *name;			/* pointer to name		*/
    ListPtr next;		/* pointer to next entry	*/
} ListNode;

/* prototypes */
unsigned clean(const char *dirname, int level);
ListPtr make_filelist(const char *dirname);
void free_filelist(ListPtr p);
void *emalloc(size_t nbytes);
char *savestr(char *s);
void die(int syserr, const char *fmt, ...);
void warn(int syserr, const char *fmt, ...);
void errmesg(int syserr, const char *fmt, va_list ap);

/* globals */
time_t now;			/* current time		*/
int verbose;			/* verbose if nonzero   */
int nonuke;			/* no delete if nonzero	*/

int main(int argc, char **argv)
{
    int c;			/* option 		*/
    extern int optind;		/* index after getopt() */
    int errflag;		/* error flag		*/

    errflag = 0;
    verbose = 0; 		/* default: be quiet	*/
    nonuke = 0; 		/* default: delete	*/
    while ((c = getopt(argc, argv, "vn")) != EOF) {
	switch (c) {
	    case 'v': verbose = 1; break;
	    case 'n': nonuke = 1; break;
	    case '?': errflag = 1; break;
	}
    }
    if (errflag != 0 || optind < argc) 
        die(0, "Usage: %s [-v] [-n]", argv[0]);
    now = time((time_t *) NULL);
    (void) clean("/tmp", 0);
    (void) clean("/usr/tmp", 0);
    exit(0);
}

unsigned clean(const char *dirname, int level)
/*
 *  clean directory "dirname", "level" levels below base 
 *  directory; return number of entries AFTER cleaning
 */
{
    double age;		/* file age      	*/
    unsigned entries;	/* entries in this dir	*/
    struct stat statbuf;/* stat for entry	*/
    unsigned subent;	/* entries in subdir	*/
    ListPtr filelist;	/* file list		*/
    ListPtr p;		/* list traversal ptr	*/
    int deleteflag;	/* deletion flag	*/

    if (verbose)
	printf("%*sCleaning directory '%s'\n", 
	    4 * level, "", dirname);
    filelist = make_filelist(dirname);
    if (chdir(dirname) == -1) {
	warn(1, "chdir to %s failed", dirname);
	free_filelist(filelist);
	return 0;
    }
    entries = 0;
    for (p = filelist; p != NULL; p = p->next) {
	if (strcmp(p->name, ".") == 0 || 
	    strcmp(p->name, "..") == 0)
	    continue;
	if (lstat(p->name, &statbuf) == -1) {
	    warn(1, "lstat on %s failed", p->name);
	    continue;
	}
	if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
	    /* it's a directory ... */
	    subent = clean(p->name, level + 1);
	    deleteflag = (subent == 0 && statbuf.st_uid != 0);
	    if (deleteflag && nonuke == 0) {
		if (rmdir(p->name) == -1)
		    warn(1, "rmdir on %s failed", p->name);
	    }
	    else 
		entries++;
	    if (verbose) 
		printf("%*sdirectory '%s' has %u entries: %s deleted\n", 
		    4 * level + 4, "", p->name, subent,
		    deleteflag ? (nonuke ? "would be" : "") :
			         (nonuke ? "would not be" : "not"));
	}
	else { 
	    /* it's a file or something else */
	    age = difftime(now, statbuf.st_ctime);
	    deleteflag = (level == 0 && age > TOPFILELIM) ||
		         (level > 0  && age > SUBFILELIM);
	    if (deleteflag && nonuke == 0) {
		if (unlink(p->name) == -1)
		    warn(1, "unlink on %s failed", p->name);
	    }
	    else
		entries++;
	    if (verbose) 
		printf("%*sfile '%s' is %g days old: %s deleted\n", 
		    4 * level + 4, "", p->name, age/86400.0,
		    deleteflag ? (nonuke ? "would be" : "") :
			         (nonuke ? "would not be" : "not"));
	}
    }
    free_filelist(filelist);
    if (chdir("..") == -1) 
	die(1, "chdir to %s/.. failed", dirname); 
    return entries;
}

ListPtr make_filelist(const char *dirname)
/*
 *  make list of entries in specified directory
 */
{
    DIR *dirp;		/* directory file	*/
    struct direct *dp;	/* directory entry	*/
    ListPtr p;		/* pointer to list	*/
    ListPtr newent;	/* pointer to new entry */

    p = (ListPtr) NULL;
    if ((dirp = opendir(dirname)) == NULL) {
	warn(1, "opendir on %s failed", dirname);
	return p;
    }
    errno = 0;
    for (dp = readdir(dirp); dp; dp = readdir(dirp)) {
	newent = (ListPtr) emalloc(sizeof(ListNode));
	newent->name = savestr(dp->d_name);
	newent->next = p;
	p = newent;
    }
    if (errno != 0) 	/* readdir error */
	warn(1, "readdir on %s failed", dirname);
    if (closedir(dirp) == -1)
	warn(1, "closedir on %s failed", dirname);
    return p;
}

void free_filelist(ListPtr p)
/*
 *  free name list
 */
{
    ListPtr q;

    while (p != NULL) {
	q = p->next;
	free(p->name);
	free(p);
	p = q;
    }
}

void *emalloc(size_t nbytes)
/*
 *  allocate specified amount of memory; die on failure
 */
{
    void *p;

    if ((p = malloc(nbytes)) == NULL)
	die(0, "malloc failed to allocate %lu bytes\n", nbytes);
    return p;
}

char *savestr(char *s)
/*
 *  store string s somewhere
 */
{
    char *p;

    p = (char *) emalloc(strlen(s) + 1);
    strcpy(p, s);
    return p;
}

void die(int syserr, const char *fmt, ...)
/*
 *  output error message to stderr and die
 */
{
    va_list ap;

    va_start(ap, fmt);
    errmesg(syserr, fmt, ap);
    va_end(ap);
    exit(1);
}

void warn(int syserr, const char *fmt, ...)
/*
 *  output warning message
 */
{
    va_list ap;

    va_start(ap, fmt);
    errmesg(syserr, fmt, ap);
    va_end(ap);
}

void errmesg(int syserr, const char *fmt, va_list ap)
/*
 *  output error message to stderr and return
 */
{
    int save;

    save = errno;
    vfprintf(stderr, fmt, ap);
    if (syserr != 0 && save != 0)
        fprintf(stderr, ": %s", strerror(save));
    fprintf(stderr, "\n");
}

