Cover V02, I02
Article
Listing 1
Listing 2
Table 1

mar93.tar


Listing 1: cleantmp.c

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");
}