diff -urpN stmpclean-0.3.orig/Makefile stmpclean-0.3/Makefile --- stmpclean-0.3.orig/Makefile Fri Mar 21 21:58:00 2003 +++ stmpclean-0.3/Makefile Wed Jun 11 20:32:26 2003 @@ -2,24 +2,29 @@ # Written by Stanislav Shalunov. # $Id: Makefile,v 1.6 2003/03/21 21:58:00 shalunov Exp $ -PREFIX=/usr/local -BINDIR=$(PREFIX)/sbin -MANDIR=$(PREFIX)/man/man8 +CC = gcc +LD = gcc +CFLAGS = -O2 -fomit-frame-pointer -Wall -W -pedantic +LDFLAGS = -s -CFLAGS += -O6 -Wall -W -pedantic +DESTDIR = +PREFIX = /usr/local +SBINDIR = $(PREFIX)/sbin +MANDIR = $(PREFIX)/man all: stmpclean stmpclean.0 -install: all - if [ ! -d $(BINDIR) ] ; then mkdir -p -m 0755 $(BINDIR); fi - if [ ! -d $(MANDIR) ] ; then mkdir -p -m 0755 $(MANDIR); fi - install -c -o 0 -g 0 -m 0555 stmpclean $(BINDIR)/ - install -c -o 0 -g 0 -m 0444 stmpclean.8 $(MANDIR)/ +install: stmpclean + mkdir -p -m 755 $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man8 + install -m 700 stmpclean $(DESTDIR)$(SBINDIR)/ + ln -s stmpclean $(DESTDIR)$(SBINDIR)/tmpwatch + install -m 644 stmpclean.8 tmpwatch.8 $(DESTDIR)$(MANDIR)/man8/ stmpclean.o: stmpclean.c + $(CC) $(CFLAGS) -c stmpclean.c stmpclean: stmpclean.o - $(CC) -o stmpclean stmpclean.o && strip stmpclean + $(LD) $(LDFLAGS) -o stmpclean stmpclean.o stmpclean.0: stmpclean.8 nroff -mandoc stmpclean.8 > stmpclean.0 diff -urpN stmpclean-0.3.orig/stmpclean.8 stmpclean-0.3/stmpclean.8 --- stmpclean-0.3.orig/stmpclean.8 Fri Mar 21 21:44:09 2003 +++ stmpclean-0.3/stmpclean.8 Wed Jun 11 21:40:54 2003 @@ -4,7 +4,9 @@ .\" .\" $Id: stmpclean.8,v 1.6 2003/03/21 21:44:09 shalunov Exp $ .\" -.Dd August 1999 +.\" Last updated on 2003/06/11 by Solar Designer +.\" +.Dd June 2003 .Dt STMPCLEAN 8 .Os .Sh NAME @@ -85,21 +87,21 @@ as .Pp In FreeBSD .Nm -invokation should be placed into the file +invocation should be placed into the file .Pa /etc/periodic/daily/110.clean-tmps . In other versions of BSD it should go into the .Pa /etc/daily script. -In Linux, check if you have -.Pa /etc/periodic , -and if not, you can just run it from cron; usually you'd have to edit -.Pa /etc/crontab . +On LSB-compliant Linux distributions, the invocation of +.Nm +may be placed in a script under +.Pa /etc/cron.daily . .Sh SEE ALSO .Xr cron 8 .Sh BUGS When .Nm -removes a file from a directory, modification time of the directory +scans a directory, access time of the directory changes and it looks new to .Nm when it examines it later (if the directory became empty). Thus, diff -urpN stmpclean-0.3.orig/stmpclean.c stmpclean-0.3/stmpclean.c --- stmpclean-0.3.orig/stmpclean.c Tue Jun 10 19:07:45 2003 +++ stmpclean-0.3/stmpclean.c Wed Jun 11 21:36:53 2003 @@ -1,9 +1,9 @@ /* * stmpclean.c -- remove old files from a world-writable directory. * Written by Stanislav Shalunov, http://www.internet2.edu/~shalunov/ - * + * * Copyright (C) 1999, Stanislav Shalunov. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -42,6 +42,7 @@ static const char rcsid[] = "$Id: stmpclean.c,v 1.13 2003/06/10 19:07:45 shalunov Exp $"; #endif +#define _GNU_SOURCE #include #include #include @@ -56,7 +57,16 @@ static const char rcsid[] = #include #include #include +#include #include +#include + +#ifndef O_DIRECTORY +#define O_DIRECTORY 0 +#endif +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif /* * How deep to descend into directories? Won't go any deeper than MAX_DEPTH @@ -69,13 +79,15 @@ static const char rcsid[] = #define SECONDS_IN_A_DAY (SECONDS_IN_AN_HOUR * 24) #define SECONDS_IN_A_WEEK (SECONDS_IN_A_DAY * 7) -#define GETCWD {if (getcwd(cwd, MAXPATHLEN) == NULL)\ +#define GETCWD {if (getcwd(cwd, sizeof(cwd)) == NULL) \ strcpy(cwd, "/FULL/PATH/TOO/LONG");} +/* Are we emulating tmpwatch? */ +static int am_tmpwatch; /* Time at the start of the program, in seconds since beginning of epoch. */ static time_t now; -/* Minimum age (mtime) of a file or empty directory to be deleted. */ -int minage; +/* Minimum age (atime) of a file or empty directory to be deleted. */ +static int minage; /* Current working directory is used for logging purposes only. */ static char cwd[MAXPATHLEN]; /* Flag: be verbose? */ @@ -83,8 +95,13 @@ static int verbose = 0; /* Print usage message, exit with a failure. */ void -usage() +usage(void) { + if (am_tmpwatch) { + fprintf(stderr, "stmpclean: " + "Don't know how to emulate these tmpwatch options.\n"); + exit(1); + } fprintf(stderr, "Usage: stmpclean [-t ] dir1 [dir2 [dir3]...]]\n\n" "Where time specification is a string like 1w\n" @@ -96,9 +113,10 @@ usage() } /* - * Parse time specification (a la sendmail queue time), return its value in - * seconds, or -1 if the spec is invalid. - * + * Parse time specification (a la sendmail queue time or number of hours + * when doing tmpwatch emulation), return its value in seconds, or -1 if + * the spec is invalid. + * * Side effects: Modifies contents of timespec. */ int @@ -107,14 +125,16 @@ parse_time(timespec) { char *p, *q; char symbol; - int result, num, multiple; + int result, num, multiple, seconds; result = 0; p = timespec; while (*p) { - if (!isdigit(*p)) + if (!isdigit((int)(unsigned char)*p)) + return -1; + for (q = p; isdigit((int)(unsigned char)*q); q++); + if (q == p || q - p > 9) return -1; - for (q = p; isdigit(*q); q++); symbol = *q; *q = 0; num = atoi(p); @@ -124,6 +144,8 @@ parse_time(timespec) * didn't want to have strdup()s here all around, did you? */ *q = symbol; + if (am_tmpwatch && symbol) + return -1; switch (symbol) { case 'w': multiple = SECONDS_IN_A_WEEK; @@ -131,6 +153,11 @@ parse_time(timespec) case 'd': multiple = SECONDS_IN_A_DAY; break; + case '\0': + if (!am_tmpwatch) + return -1; + q--; + /* FALLTHROUGH */ case 'h': multiple = SECONDS_IN_AN_HOUR; break; @@ -143,8 +170,11 @@ parse_time(timespec) default: return -1; } - result += num * multiple; - if (result < 0) + seconds = num * multiple; + if (seconds < 0 || seconds / multiple != num) + return -1; + result += seconds; + if (result < seconds) return -1; p = q + 1; } @@ -157,14 +187,18 @@ setecreds(uid, gid) uid_t uid; gid_t gid; { + gid_t groups[2]; + + groups[0] = groups[1] = gid; if (geteuid()) { - if ((seteuid(uid) == -1) || (setegid(gid) == -1)) { + if (seteuid(uid) || setegid(gid) || + (uid == 0 && setgroups(1, groups))) { syslog(LOG_ERR, "cannot set EUID/EGID to %d/%d, exiting", uid, gid); exit(1); } } else { - if ((setegid(gid) == -1) || (seteuid(uid) == -1)) { + if (setgroups(1, groups) || setegid(gid) || seteuid(uid)) { syslog(LOG_ERR, "cannot set EUID/EGID to %d/%d, exiting", uid, gid); exit(1); @@ -201,13 +235,13 @@ isemptydir(dir) } /* - * Recursively clean directory DIR, descending no deeper than MAX_DEPTH. + * Recursively clean directory DIR, descending no deeper than DEPTH. * Exit with a failure if a race condition is detected. */ void -clean_dir(dir, depth) +clean_dir(dir, depth, specified) char *dir; - int depth; + int depth, specified; { struct stat st, st_after; int dir_fd, dot_dot_fd; @@ -224,7 +258,9 @@ clean_dir(dir, depth) "reached maximum depth (%d)", dir, cwd, MAX_DEPTH); return; } - if (lstat(dir, &st) == -1) { + if ((specified ? stat(dir, &st) : lstat(dir, &st)) == -1) { + if (errno == ENOENT && !specified) + return; GETCWD; syslog(LOG_ERR, "RACE?: lstat(\"%s\") in %s failed: %m, " "exiting", dir, cwd); @@ -236,8 +272,11 @@ clean_dir(dir, depth) dir, cwd); exit(1); } - dir_fd = open(dir, O_RDONLY); + dir_fd = open(dir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | + (specified ? 0 : O_NOFOLLOW)); if (dir_fd == -1) { + if (errno == ENOENT) + return; GETCWD; syslog(LOG_ERR, "RACE?: cannot open(\"%s\"): %m (lstat was OK), " @@ -264,7 +303,8 @@ clean_dir(dir, depth) * We'll chdir up later once done with recursive descend. Hence the * name. */ - dot_dot_fd = open(".", O_RDONLY); + dot_dot_fd = open(".", + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_NOFOLLOW); if (dot_dot_fd == -1) { GETCWD; syslog(LOG_ERR, "open(\".\") in %s: %m, exiting", cwd); @@ -303,11 +343,18 @@ clean_dir(dir, depth) /* Looking at a directory. */ if (isemptydir(dp->d_name)) { /* Looking at an empty directory. */ - if (now - st.st_mtime > minage && st.st_uid) { + if (now - st.st_atime > minage && st.st_uid) { /* An old non-root owned directory. */ setecreds(st.st_uid, st.st_gid); if (rmdir(dp->d_name) == -1 - && errno != EACCES) { + && errno != ENOENT + && errno != EACCES + && errno != EPERM) { + if (errno == ENOTEMPTY) { + setecreds(0, 0); + goto notempty; + } + setecreds(0, 0); GETCWD; syslog(LOG_ERR, "RACE?: rmdir" "(\"%s\") in %s: %m, " @@ -323,16 +370,16 @@ clean_dir(dir, depth) setecreds(0, 0); } } else { +notempty: /* * Looking at a non-empty directory. Clean it * recursively (call ourselves). */ - clean_dir(dp->d_name, depth - 1); + clean_dir(dp->d_name, depth - 1, 0); } } else { /* Looking at a non-directory. */ - if ((now - st.st_mtime > minage) - && (now - st.st_ctime > minage) + if ((now - st.st_atime > minage) && st.st_uid && (st.st_nlink == 1) && (((st.st_mode & S_IFMT) == S_IFREG) @@ -342,7 +389,11 @@ clean_dir(dir, depth) * symlink. */ setecreds(st.st_uid, st.st_gid); - if (unlink(dp->d_name) == -1) { + if (unlink(dp->d_name) == -1 + && errno != ENOENT + && errno != EACCES + && errno != EPERM) { + setecreds(0, 0); GETCWD; syslog(LOG_ERR, "RACE?: unlink(\"%s\")" "in %s: %m, exiting", @@ -377,15 +428,27 @@ main(argc, argv) /* By default, delete files older than three days. */ extern char *optarg; extern int optind; + char *me; int c, i; struct rlimit rlp; if (argc <= 0) usage(); + me = strdup(argv[0]); + if (!me) { + errno = ENOMEM; + perror("strdup"); + exit(1); + } + am_tmpwatch = !strcmp(basename(me), "tmpwatch"); openlog("stmpclean", LOG_PID | LOG_CONS | LOG_PERROR, LOG_DAEMON); minage = SECONDS_IN_A_DAY * 3; - while ((c = getopt(argc, argv, "vt:")) != -1) + while ((c = getopt(argc, argv, am_tmpwatch ? "uafv" : "vt:")) != -1) switch (c) { + case 'u': + case 'a': + case 'f': + break; case 't': minage = parse_time(optarg); if (minage == -1) @@ -399,8 +462,14 @@ main(argc, argv) } argc -= optind; argv += optind; - if (argc <= 0) + if (argc <= (am_tmpwatch ? 1 : 0)) usage(); + if (am_tmpwatch) { + argc--; + minage = parse_time(*argv++); + if (minage == -1) + usage(); + } /* * For logging niceties in case one of the directories on the command * line is bad. @@ -422,7 +491,7 @@ main(argc, argv) exit(1); } for (i = 0; i < argc; i++) - clean_dir(argv[i], MAX_DEPTH); + clean_dir(argv[i], MAX_DEPTH, 1); exit(0); /* NOTREACHED */ } diff -urpN stmpclean-0.3.orig/tmpwatch.8 stmpclean-0.3/tmpwatch.8 --- stmpclean-0.3.orig/tmpwatch.8 Thu Jan 1 00:00:00 1970 +++ stmpclean-0.3/tmpwatch.8 Sat Mar 30 00:24:51 2002 @@ -0,0 +1 @@ +.so man8/stmpclean.8