From 3e6fc0fd75607f41ead1aea88c798c139ebb8eea Mon Sep 17 00:00:00 2001 From: pbrown Date: Apr 08 1999 17:08:40 +0000 Subject: fixed directory removal problem -- directories were never removed because their time was always updated. --- diff --git a/tmpwatch.8 b/tmpwatch.8 index b821206..13ce7ad 100644 --- a/tmpwatch.8 +++ b/tmpwatch.8 @@ -1,9 +1,10 @@ -.TH TMPWATCH 8 "Mon Mar 24 1997" +.TH TMPWATCH 8 "Thu Apr 08 1999" .UC 4 .SH NAME tmpwatch \- removes files which haven't been accessed for a period of time .SH SYNOPSIS -\fBtmpwatch\fR [-fav] [--verbose] [--force] [--all] [--test] +\fBtmpwatch\fR [-favq] [--verbose] [--force] [--all] [--test] + [--quiet] .SH DESCRIPTION \fBtmpwatch\fR recursively removes files which haven't been accessed @@ -46,12 +47,13 @@ Print a verbose display. Two levels of verboseness are available -- use this option twice to get the most verbose output. .SH SEE ALSO -.IR cron (1) -.IR ls (1) -.IR rm (1) +.IR cron (1), +.IR ls (1), +.IR rm (1), .IR stat (1) -.SH AUTHOR +.SH AUTHORS .nf Erik Troan +Preston Brown .fi diff --git a/tmpwatch.c b/tmpwatch.c index a6f117e..b9cc6de 100644 --- a/tmpwatch.c +++ b/tmpwatch.c @@ -1,3 +1,9 @@ +/* + * tmpwatch.c -- remove files in a directory, but do it carefully. + * Copyright (c) 1997, 1999 Red Hat Software, Inc. + * Licensed under terms of the GPL. + */ + #include #include #include @@ -8,10 +14,9 @@ #include #include #include +#include #include -/* tmpwatch.c -- remove files in a directory, but do it carefully */ - #define LOG_REALDEBUG 1 #define LOG_DEBUG 2 #define LOG_VERBOSE 3 @@ -28,259 +33,269 @@ int logLevel = LOG_NORMAL; void message(int level, char * format, ...) { - va_list args; - FILE * where = stdout; + va_list args; + FILE * where = stdout; - if (level >= logLevel) { - va_start(args, format); + if (level >= logLevel) { + va_start(args, format); - if (level > LOG_NORMAL) { - where = stderr; - fprintf(stderr, "error: "); - } + if (level > LOG_NORMAL) { + where = stderr; + fprintf(stderr, "error: "); + } - vfprintf(stdout, format, args); + vfprintf(stdout, format, args); - if (level == LOG_FATAL) exit(1); - } + if (level == LOG_FATAL) exit(1); + } } int safe_chdir(char * dirname) { - struct stat sb1, sb2; - - if (lstat(dirname, &sb1)) { - message(LOG_ERROR, "lstat() of directory %s failed: %s\n", - dirname, strerror(errno)); - return 1; - } + struct stat sb1, sb2; + + if (lstat(dirname, &sb1)) { + message(LOG_ERROR, "lstat() of directory %s failed: %s\n", + dirname, strerror(errno)); + return 1; + } + + if (chdir(dirname)) { + message(LOG_ERROR, "chdir to directory %s failed: %s\n", + dirname, strerror(errno)); + return 1; + } + + if (lstat(".", &sb2)) { + message(LOG_ERROR, "second lstat() of directory %s failed: %s\n", + dirname, strerror(errno)); + return 1; + } + + if (sb1.st_ino != sb2.st_ino) { + message(LOG_ERROR, "inode information changed for %s!!!", + strerror(errno)); + message(LOG_FATAL, "this indicates a possible intrusion attempt\n"); + } else if (sb1.st_dev != sb2.st_dev) { + message(LOG_ERROR, "device information changed for %s!!!", + strerror(errno)); + message(LOG_FATAL, "this indicates a possible intrusion attempt\n"); + } + + return 0; +} - if (chdir(dirname)) { - message(LOG_ERROR, "chdir to directory %s failed: %s\n", - dirname, strerror(errno)); - return 1; +int cleanupDirectory(char * dirname, unsigned int killTime, int flags) { + DIR * dir; + struct dirent * ent; + struct stat sb; + int status, pid; + struct stat here; + struct utimbuf utb; + + message(LOG_DEBUG, "cleaning up directory %s\n", dirname); + + /* Do everything in a child process so we don't have to chdir(".."), + which would lead to a race condition. fork() on Linux is very efficient + so this shouldn't be a big deal (probably just a exception on one page + of stack, not bad). I should probably just keep a directory stack + and fchdir() back up it, but it's not worth changing now. */ + + if (!(pid = fork())) { + if (safe_chdir(dirname)) return 1; + dir = opendir("."); + + if (lstat(".", &here)) { + message(LOG_ERROR, "error statting current directory %s: %s", + dirname, strerror(errno)); + exit(1); } - if (lstat(".", &sb2)) { - message(LOG_ERROR, "second lstat() of directory %s failed: %s\n", - dirname, strerror(errno)); - return 1; + if (!dir) { + message(LOG_ERROR, "error opening directory %s: %s\n", dirname, + strerror(errno)); + exit(1); } - if (sb1.st_ino != sb2.st_ino) { - message(LOG_ERROR, "inode information changed for %s!!!", + do { + errno = 0; + ent = readdir(dir); + if (errno) { + message(LOG_ERROR, "error reading directory entry: %s\n", strerror(errno)); - message(LOG_FATAL, "this indicates a possible intrusion attempt\n"); - } else if (sb1.st_dev != sb2.st_dev) { - message(LOG_ERROR, "device information changed for %s!!!", - strerror(errno)); - message(LOG_FATAL, "this indicates a possible intrusion attempt\n"); - } - - return 0; -} - -int cleanupDirectory(char * dirname, unsigned int killTime, int flags) { - DIR * dir; - struct dirent * ent; - struct stat sb; - int status, pid; - struct stat here; - - message(LOG_DEBUG, "cleaning up directory %s\n", dirname); - - /* Do everything in a child process so we don't have to chdir(".."), - which would lead to a race condition. fork() on Linux is very efficient - so this shouldn't be a big deal (probably just a exception on one page - of stack, not bad). I should probably just keep a directory stack - and fchdir() back up it, but it's not worth changing now. */ - - if (!(pid = fork())) { - if (safe_chdir(dirname)) return 1; - dir = opendir("."); - - if (lstat(".", &here)) { - message(LOG_ERROR, "error statting current directory %s: %s", - dirname, strerror(errno)); - exit(1); + exit(1); + } + if (!ent) break; + + if ((ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || + ((ent->d_name[1] == '.') && (ent->d_name[2] == '\0'))))) + continue; + + message(LOG_REALDEBUG, "found directory entry %s\n", ent->d_name); + + if (lstat(ent->d_name, &sb)) { + message(LOG_ERROR, "failed to lstat %s/%s: %s\n", dirname, + ent->d_name, strerror(errno)); + continue; + } + + if (!sb.st_uid && !(flags & FLAGS_FORCE) && + !(sb.st_mode & S_IWUSR)) { + message(LOG_DEBUG, "non-writeable file owned by root " + "skipped: %s\n", ent->d_name);; + continue; + } else if (sb.st_dev != here.st_dev) { + message(LOG_VERBOSE, "file on different device skipped: %s\n", + ent->d_name); + continue; + } else if (S_ISDIR(sb.st_mode)) { + + cleanupDirectory(ent->d_name, killTime, flags); + + /* restore access time on the directory to original time */ + utb.actime = sb.st_atime; /* atime */ + utb.modtime = sb.st_mtime; /* mtime */ + utime(ent->d_name, &utb); + + if (sb.st_atime >= killTime) continue; + + message(LOG_VERBOSE, "removing directory %s\n", ent->d_name); + + if (!(flags & FLAGS_TEST)) { + if (flags & FLAGS_ALLFILES) { + if (rmdir(ent->d_name)) { + message(LOG_ERROR, "failed to rmdir %s: %s\n", + dirname, ent->d_name); + } + } else { + rmdir(ent->d_name); + } } - - if (!dir) { - message(LOG_ERROR, "error opening directory %s: %s\n", dirname, - strerror(errno)); - exit(1); + } else { + if (sb.st_atime >= killTime) continue; + + if ((flags & FLAGS_ALLFILES) || S_ISREG(sb.st_mode)) { + message(LOG_VERBOSE, "removing file %s/%s\n", + dirname, ent->d_name); + + if (!(flags & FLAGS_TEST)) { + if (unlink(ent->d_name)) + message(LOG_ERROR, "failed to unlink %s: %s\n", + dirname, ent->d_name); + } } + } - do { - errno = 0; - ent = readdir(dir); - if (errno) { - message(LOG_ERROR, "error reading directory entry: %s\n", - strerror(errno)); - exit(1); - } - if (!ent) break; - - if ((ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || - ((ent->d_name[1] == '.') && (ent->d_name[2] == '\0'))))) - continue; + } while (ent); - message(LOG_REALDEBUG, "found directory entry %s\n", ent->d_name); + closedir(dir); - if (lstat(ent->d_name, &sb)) { - message(LOG_ERROR, "failed to lstat %s/%s: %s\n", dirname, - ent->d_name, strerror(errno)); - continue; - } - - if (!sb.st_uid && !(flags & FLAGS_FORCE) && - !(sb.st_mode & S_IWUSR)) { - message(LOG_DEBUG, "non-writeable file owned by root " - "skipped: %s\n", ent->d_name);; - continue; - } else if (sb.st_dev != here.st_dev) { - message(LOG_VERBOSE, "file on different device skipped: %s\n", - ent->d_name); - continue; - } else if (S_ISDIR(sb.st_mode)) { - cleanupDirectory(ent->d_name, killTime, flags); - - if (sb.st_atime >= killTime) continue; - - message(LOG_VERBOSE, "removing directory %s\n", ent->d_name); - - if (!(flags & FLAGS_TEST)) { - if (flags & FLAGS_ALLFILES) { - if (rmdir(ent->d_name)) { - message(LOG_ERROR, "failed to rmdir %s: %s\n", - dirname, ent->d_name); - } - } else { - rmdir(ent->d_name); - } - } - } else { - if (sb.st_atime >= killTime) continue; - - if ((flags & FLAGS_ALLFILES) || S_ISREG(sb.st_mode)) { - message(LOG_VERBOSE, "removing file %s/%s\n", - dirname, ent->d_name); - - if (!(flags & FLAGS_TEST)) { - if (unlink(ent->d_name)) - message(LOG_ERROR, "failed to unlink %s: %s\n", - dirname, ent->d_name); - } - } - } + exit(0); + } - } while (ent); + waitpid(pid, &status, 0); - closedir(dir); + if (WIFEXITED(status)) + return WEXITSTATUS(status); - exit(0); - } - - waitpid(pid, &status, 0); - - if (WIFEXITED(status)) - return WEXITSTATUS(status); - - return 0; + return 0; } void printCopyright(void) { - fprintf(stderr, "tmpwatch " VERSION " - (c) 1997 Red Hat Software\n"); - fprintf(stderr, "This may be freely redistributed under the terms of " - "the GNU Public License.\n"); + fprintf(stderr, "tmpwatch " VERSION " - (c) 1997, 1999 Red Hat Software\n"); + fprintf(stderr, "This may be freely redistributed under the terms of " + "the GNU Public License.\n"); } void usage(void) { - printCopyright(); - fprintf(stderr, "\n"); - fprintf(stderr, "tmpwatch [-fav] [--verbose] [--force] [--all] [--test] \n"); - exit(1); + printCopyright(); + fprintf(stderr, "\n"); + fprintf(stderr, "tmpwatch [-favq] [--verbose] [--force] [--all] [--test] [--quiet] \n"); + exit(1); } int main(int argc, char ** argv) { - unsigned int grace; - unsigned int killTime, long_index; - int flags = 0, arg; - struct stat sb; - struct option options[] = { - { "all", 0, 0, 'a' }, - { "force", 0, 0, 'f' }, - { "test", 0, 0, GETOPT_TEST }, - { "verbose", 0, 0, 'v' }, - }; - - if (argc == 1) usage(); - - while (1) { - long_index = 0; - - arg = getopt_long(argc, argv, "afv", options, - &long_index); - if (arg == -1) break; - - switch (arg) { - case 'a': - flags |= FLAGS_ALLFILES; - break; - - case 'f': - flags |= FLAGS_FORCE; - break; - - case GETOPT_TEST: - flags |= FLAGS_TEST; - /* fallthrough */ - case 'v': - logLevel ? logLevel -= 1 : 0; - break; - - case '?': - exit(1); - } + unsigned int grace; + unsigned int killTime, long_index; + int flags = 0, arg; + struct stat sb; + struct option options[] = { + { "all", 0, 0, 'a' }, + { "force", 0, 0, 'f' }, + { "test", 0, 0, GETOPT_TEST }, + { "verbose", 0, 0, 'v' }, + { "quiet", 0, 0, 'q' }, + }; + + if (argc == 1) usage(); + + while (1) { + long_index = 0; + + arg = getopt_long(argc, argv, "afv", options, + &long_index); + if (arg == -1) break; + + switch (arg) { + case 'a': + flags |= FLAGS_ALLFILES; + break; + + case 'f': + flags |= FLAGS_FORCE; + break; + + case GETOPT_TEST: + flags |= FLAGS_TEST; + /* fallthrough */ + case 'v': + logLevel ? logLevel -= 1 : 0; + break; + case 'q': + logLevel = LOG_FATAL; + break; + case '?': + exit(1); } + } - if (optind == argc) { - fprintf(stderr, "error: time (in hours) must be given\n"); - exit(1); - } - - if ((sscanf(argv[optind], "%d", &grace) != 1) || (grace < 0)) { - fprintf(stderr, "error: bad time argument %s\n", argv[optind]); - exit(1); - } + if (optind == argc) { + fprintf(stderr, "error: time (in hours) must be given\n"); + exit(1); + } - optind++; - if (optind == argc) { - fprintf(stderr, "error: directory name(s) expected\n"); - exit(1); - } + if ((sscanf(argv[optind], "%d", &grace) != 1) || (grace < 0)) { + fprintf(stderr, "error: bad time argument %s\n", argv[optind]); + exit(1); + } - grace = grace * 3600; /* to seconds from hours */ + optind++; + if (optind == argc) { + fprintf(stderr, "error: directory name(s) expected\n"); + exit(1); + } - message(LOG_DEBUG, "grace period is %u\n", grace); + grace = grace * 3600; /* to seconds from hours */ - killTime = time(NULL) - grace; + message(LOG_DEBUG, "grace period is %u\n", grace); - while (optind < argc) { - if (lstat(argv[optind], &sb)) { - message(LOG_ERROR, "lstat() of directory %s failed: %s\n", - argv[optind], strerror(errno)); - exit(1); - } + killTime = time(NULL) - grace; - if (S_ISLNK(sb.st_mode)) { - message(LOG_DEBUG, "initial directory %s is a symlink -- " - "skipping\n", argv[optind]); - } else { - cleanupDirectory(argv[optind], killTime, flags); - } + while (optind < argc) { + if (lstat(argv[optind], &sb)) { + message(LOG_ERROR, "lstat() of directory %s failed: %s\n", + argv[optind], strerror(errno)); + exit(1); + } - optind++; + if (S_ISLNK(sb.st_mode)) { + message(LOG_DEBUG, "initial directory %s is a symlink -- " + "skipping\n", argv[optind]); + } else { + cleanupDirectory(argv[optind], killTime, flags); } - return 0; + optind++; + } + + return 0; } diff --git a/tmpwatch.spec b/tmpwatch.spec index 3b16b29..03e9f61 100644 --- a/tmpwatch.spec +++ b/tmpwatch.spec @@ -1,6 +1,6 @@ Summary: Cleans up files in directories based on their age Name: tmpwatch -%define version 1.5.1 +%define version 1.6 Version: %{version} Release: 1 Source: tmpwatch-%{version}.tar.gz @@ -14,33 +14,33 @@ recursively searches the directory (ignoring symlinks) and removes files that haven't been accessed in a user-specified amount of time. %changelog +* Thu Apr 08 1999 Preston Brown +- I am the new maintainer +- fixed cleanup of directories +- added --quiet flag +- freshen manpage * Wed Jun 10 1998 Erik Troan - - make /etc/cron.daily/tmpwatch executable * Tue Jan 13 1998 Erik Troan - - version 1.5 - fixed flags passing - cleaned up message() * Wed Oct 22 1997 Erik Troan - - added man page to package - uses a buildroot and %attr - fixed error message generation for directories - fixed flag propagation * Mon Mar 24 1997 Erik Troan - - Don't follow symlinks which are specified on the command line - Added a man page * Sun Mar 09 1997 Erik Troan - -Rebuilt to get right permissions on the Alpha (though I have no idea -how they ended up wrong). +- Rebuilt to get right permissions on the Alpha (though I have no idea +- how they ended up wrong). %prep %setup