Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37510004
en ru br
Репозитории ALT
S:0.3-alt5
5.1: 0.3-alt2
4.1: 0.3-alt2
4.0: 0.3-alt2
3.0: 0.3-alt1
www.altlinux.org/Changes

Группа :: Работа с файлами
Пакет: stmpclean

 Главная   Изменения   Спек   Патчи   Исходники   Загрузить   Gear   Bugs and FR  Repocop 

stmpclean-0.3/000075500000000000000000000000001227421170200133365ustar00rootroot00000000000000stmpclean-0.3/FAQ000064400000000000000000000026321227421170200136730ustar00rootroot00000000000000This is the list of frequently asked questions about stmpclean.
Before you ask your question, please consult this.

$Id: FAQ,v 1.3 2000/10/17 17:53:58 shalunov Exp $

------------------------------------------------------------

> I get this error when I run ./stmpclean -t 3w /var/spool/mail

> stmpclean[24030]: RACE?: unlink("tcal") in /var/spool/mail:
> Permission denied, exiting

Stmpclean has not been designed to be run over mail spools or home
directories!

You should *not* run stmpclean on /var/spool/mail. You are risking
deleting precious mail files. You're going to delete *all* mail in
the system mailbox of all users who haven't gotten new mail (or
changed their mailbox by reading it and, say, deleting a message)
in three weeks.

If you insist on doing exactly that, you can use stmpclean, but you
must set permissions on /var/spool/mail as they are normally set on
/tmp. You better do a backup before you delete people's mail.

* Do "ls -ld /var/spool/mail"; notice the permissions on the
directory.

* Change the permissions: "chmod 1777 /var/spool/mail".

* Run stmpclean.

* Set permissions back to what they were. E.g., for "drwxrwxr-x"
you'd say "chmod 775 /var/spool/mail".

* Respond to angry users' questions as to where their mail is now
gone.


> Will stmpclean run on <insert your favorite system here>?

See list of supported platforms in the README file, which accompanies
the distribution.
stmpclean-0.3/Makefile000064400000000000000000000014131227421170200147750ustar00rootroot00000000000000# Makefile for stmpclean.
# 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

CFLAGS += -O6 -Wall -W -pedantic

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)/

stmpclean.o: stmpclean.c

stmpclean: stmpclean.o
$(CC) -o stmpclean stmpclean.o && strip stmpclean

stmpclean.0: stmpclean.8
nroff -mandoc stmpclean.8 > stmpclean.0

indent:
indent stmpclean.c

clean:
-rm -f *.o stmpclean a.out core *.core ktrace.out *.orig *~ *.BAK \
junk stmpclean.0
stmpclean-0.3/README000064400000000000000000000030031227421170200142120ustar00rootroot00000000000000 Supported Platforms

At this time, stmpclean works on BSD and Linux. It has been reported
to work on Solaris, IRIX, and SCO, but I have no way of verifying this.


Installation

To build, say ``make''. To install, execute the command ``make
install'' and then read the manual page. You can also read the
pre-formatted manual page before installation in the file stmpclean.0.


Rationale

People do stuff like

find /tmp -type f -atime +3 -ctime +3 ! -name '.X*-lock' -exec rm -f -- {} \;
find -d /tmp ! -name . -type d -mtime +1 -exec rmdir -- {} \; >/dev/null 2>&1

as root in /etc/crontab (or in daily maintenance scripts).

Don't ever do this. This can be easily tricked into deleting
arbitrary files on your system!

People also run nifty Perl scripts that overcome this problem. Well,
in case there is an attack, they won't delete your precious
/etc/ftpusers or whatever you guard most. They'll just fork 10000
children each trying to allocate 2MB of memory. Good luck.

This program solves these problems.


Further Information

The distribution is accompanied by a FAQ file. It should answer
most common questions. If your question isn't answered, or if you
have a problem with stmpclean, or a suggestion, or a bug report,
or a patch, send it to me (see address below).


Author

The stmpclean utility is written by Stanislav Shalunov. You can send
comments, bug reports, suggestions, and criticism to shalunov@internet2.edu.
http://www.internet2.edu/~shalunov/

$Id: README,v 1.7 2003/03/21 21:37:43 shalunov Exp $
stmpclean-0.3/stmpclean.8000064400000000000000000000061721227421170200154230ustar00rootroot00000000000000.\" Copyright (C) 1999 Stanislav Shalunov.
.\" http://www.internet2.edu/~shalunov/
.\" See stmpclean.c for copyright notice and legal conditions.
.\"
.\" $Id: stmpclean.8,v 1.6 2003/03/21 21:44:09 shalunov Exp $
.\"
.Dd August 1999
.Dt STMPCLEAN 8
.Os
.Sh NAME
.Nm stmpclean
.Nd remove old files from a world-writable directory
.Sh SYNOPSIS
.Nm stmpclean
.Op Fl "t<timespec>"
.Op Fl "v"
.Ar dir1
.Op "dir2 ..."
.Sh DESCRIPTION
The
.Nm
utility removes old files (and old empty directories) from the
specified directory. It'll be typically used to clean directories
such as ``/tmp'' where old files tend to accumulate.
.Pp
The
.Nm
utility never removes files or directories owned by root. It is a
feature, not a bug.
Great care is taken while descending into the directory, and the
operation is secure.
Anything that's not a directory, regular file, or symbolic link is
also left alone (because programs like
.Xr screen 1
create sockets and FIFOs under /tmp and expect them to be long-lived;
we accomodate this practice).
Unlike floating around Perl scripts that do the same task
.Nm
never forks and consumes limited amount of memory (these Perl scripts
easily turn into forking bombs when someone creates a lot a
directories under ``/tmp''). If your system is attacked and the
attacker creates an extremely deep file hierarchy,
.Nm
won't add to the problem by crashing your system trying to remove it.
But it won't help you in fighting the attack, either, because it
descends only to a limited depth (currently, 30 levels). If
.Nm
determines a race condition it'll log the situation (you can look for
the word ``RACE'' in log files) and exit with a failure.
.Pp
So,
.Nm
will clean temporary directories for you fine when there are no
attacks, and, when there is an attack,
.Nm
won't make the situation worse (in particular, it cannot be tricked
into removing files outside specified directories or consume unlimited
amount of resources).
.Pp
The following option is available:
.Bl -tag -width flag
.It Fl "t<timespec>"
The time specification that follows the
.Fl t
flag specifies how old a file or a directory has to be before it will
be removed. It can be a string like `1w' (one week) or `4d5h' (four
days plus five hours) or `2m3s' (two minutes plus three seconds). The
default is `3d' (three days).
.It Fl "v"
Be verbose: list each file deleted.
.El
.Pp
The
.Nm
utility exits 0 on success, and >0 if an error occurs.
.Sh EXAMPLES
The
.Nm
utility will typically be run nightly from
.Xr cron 8
as
.Bl -tag -width example
.It Li "stmpclean /tmp /var/tmp"
.El
.Pp
In FreeBSD
.Nm
invokation 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 .
.Sh SEE ALSO
.Xr cron 8
.Sh BUGS
When
.Nm
removes a file from a directory, modification time of the directory
changes and it looks new to
.Nm
when it examines it later (if the directory became empty). Thus,
removing a deep hierarchy can take some time. Notice that this only
delays removal of some empty directories.
stmpclean-0.3/stmpclean.c000064400000000000000000000257051227421170200155010ustar00rootroot00000000000000/*
* 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:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of Stanislav Shalunov nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without explicit prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
* NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY
* QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
* EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
* DISTRIBUTION OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/

#ifndef lint
static const char rcsid[] =
"$Id: stmpclean.c,v 1.13 2003/06/10 19:07:45 shalunov Exp $";
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>

/*
* How deep to descend into directories? Won't go any deeper than MAX_DEPTH
* levels.
*/
#define MAX_DEPTH (30)

#define SECONDS_IN_A_MINUTE (60)
#define SECONDS_IN_AN_HOUR (SECONDS_IN_A_MINUTE * 60)
#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)\
strcpy(cwd, "/FULL/PATH/TOO/LONG");}

/* 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;
/* Current working directory is used for logging purposes only. */
static char cwd[MAXPATHLEN];
/* Flag: be verbose? */
static int verbose = 0;

/* Print usage message, exit with a failure. */
void
usage()
{
fprintf(stderr,
"Usage: stmpclean [-t <timespec>] dir1 [dir2 [dir3]...]]\n\n"
"Where time specification <timespec> is a string like 1w\n"
"(one week) or 4d5h (four days plus five hours) or 2m3s\n"
"(two minutes plus three seconds). The default is 3d.\n\n"
"Arguments specify which directories are to be cleaned.\n\n"
"Typical usage: stmpclean /tmp /var/tmp\n");
exit(1);
}

/*
* Parse time specification (a la sendmail queue time), return its value in
* seconds, or -1 if the spec is invalid.
*
* Side effects: Modifies contents of timespec.
*/
int
parse_time(timespec)
char *timespec;
{
char *p, *q;
char symbol;
int result, num, multiple;

result = 0;
p = timespec;
while (*p) {
if (!isdigit(*p))
return -1;
for (q = p; isdigit(*q); q++);
symbol = *q;
*q = 0;
num = atoi(p);
/*
* Put it back after atoi() in case someone doesn't read the
* comments and decides to use the value again anyway. I
* didn't want to have strdup()s here all around, did you?
*/
*q = symbol;
switch (symbol) {
case 'w':
multiple = SECONDS_IN_A_WEEK;
break;
case 'd':
multiple = SECONDS_IN_A_DAY;
break;
case 'h':
multiple = SECONDS_IN_AN_HOUR;
break;
case 'm':
multiple = SECONDS_IN_A_MINUTE;
break;
case 's':
multiple = 1;
break;
default:
return -1;
}
result += num * multiple;
if (result < 0)
return -1;
p = q + 1;
}
return result;
}

/* Set euid to UID, egid to GID. Exit unsuccessfully on error. */
void
setecreds(uid, gid)
uid_t uid;
gid_t gid;
{
if (geteuid()) {
if ((seteuid(uid) == -1) || (setegid(gid) == -1)) {
syslog(LOG_ERR, "cannot set EUID/EGID to %d/%d, exiting",
uid, gid);
exit(1);
}
} else {
if ((setegid(gid) == -1) || (seteuid(uid) == -1)) {
syslog(LOG_ERR, "cannot set EUID/EGID to %d/%d, exiting",
uid, gid);
exit(1);
}
}
return;
}

/*
* Return 1 if DIR is an empty directory, 0 otherwise. Assumes nothing
* changes while we are looking. Exit unsuccessfully on error.
*/
int
isemptydir(dir)
char *dir;
{
DIR *dirp;
struct dirent *dp;
int result = 1;

if ((dirp = opendir(dir)) == NULL) {
GETCWD;
syslog(LOG_ERR, "RACE?: isemptydir(): opendir(\"%s\") in %s: "
"%m, exiting", dir, cwd);
exit(1);
}
while ((dp = readdir(dirp)) != NULL)
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
result = 0;
break;
}
closedir(dirp);
return result;
}

/*
* Recursively clean directory DIR, descending no deeper than MAX_DEPTH.
* Exit with a failure if a race condition is detected.
*/
void
clean_dir(dir, depth)
char *dir;
int depth;
{
struct stat st, st_after;
int dir_fd, dot_dot_fd;
DIR *dirp;
struct dirent *dp;

if (depth < 0) {
/*
* We do getcwd() inside error handling blocks for
* efficiency.
*/
GETCWD;
syslog(LOG_WARNING, "won't descend to %s from %s: "
"reached maximum depth (%d)", dir, cwd, MAX_DEPTH);
return;
}
if (lstat(dir, &st) == -1) {
GETCWD;
syslog(LOG_ERR, "RACE?: lstat(\"%s\") in %s failed: %m, "
"exiting", dir, cwd);
exit(1);
}
if ((st.st_mode & S_IFMT) != S_IFDIR) {
GETCWD;
syslog(LOG_ERR, "RACE?: %s in %s is not a directory, exiting",
dir, cwd);
exit(1);
}
dir_fd = open(dir, O_RDONLY);
if (dir_fd == -1) {
GETCWD;
syslog(LOG_ERR,
"RACE?: cannot open(\"%s\"): %m (lstat was OK), "
"exiting", dir);
exit(1);
}
if (fstat(dir_fd, &st_after) == -1) {
GETCWD;
syslog(LOG_ERR, "cannot fstat(%d), pointing to %s in %s: %m, "
"exiting", dir_fd, dir, cwd);
exit(1);
}
if (st.st_dev != st_after.st_dev
|| st.st_ino != st_after.st_ino
|| st.st_rdev != st_after.st_rdev
|| st.st_uid != st_after.st_uid
|| st.st_gid != st_after.st_gid) {
GETCWD;
syslog(LOG_CRIT, "RACE: %s in %s changed between lstat and "
"open, exiting", dir, cwd);
exit(1);
}
/*
* We'll chdir up later once done with recursive descend. Hence the
* name.
*/
dot_dot_fd = open(".", O_RDONLY);
if (dot_dot_fd == -1) {
GETCWD;
syslog(LOG_ERR, "open(\".\") in %s: %m, exiting", cwd);
exit(1);
}
if (fchdir(dir_fd) == -1) {
GETCWD;
syslog(LOG_ERR, "fchdir(\"%d\") [fd to %s] failed in %s: %m, "
"exiting", dir_fd, dir, cwd);
exit(1);
}
if (close(dir_fd) == -1) {
GETCWD;
syslog(LOG_ERR, "close(\"%d\") [fd to ../%s] failed in %s: "
"%m, exiting", dir_fd, dir, cwd);
exit(1);
}
/* OK, we are now in the directory to clean. */
if ((dirp = opendir(".")) == NULL) {
GETCWD;
syslog(LOG_ERR, "RACE?: opendir(\".\") in %s: %m, "
"exiting", cwd);
exit(1);
}
while ((dp = readdir(dirp)) != NULL) {
/* Ignore "." and ".." entries. */
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
if (lstat(dp->d_name, &st) == -1) {
GETCWD;
syslog(LOG_ERR, "RACE?: lstat(\"%s\") in %s: %m, "
"exiting", dp->d_name, cwd);
exit(1);
}
if ((st.st_mode & S_IFMT) == S_IFDIR) {
/* Looking at a directory. */
if (isemptydir(dp->d_name)) {
/* Looking at an empty directory. */
if (now - st.st_mtime > 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) {
GETCWD;
syslog(LOG_ERR, "RACE?: rmdir"
"(\"%s\") in %s: %m, "
"exiting", dp->d_name,
cwd);
exit(1);
} else if (verbose) {
GETCWD;
syslog(LOG_INFO,
"removed dir %s/%s",
cwd, dp->d_name);
}
setecreds(0, 0);
}
} else {
/*
* Looking at a non-empty directory. Clean it
* recursively (call ourselves).
*/
clean_dir(dp->d_name, depth - 1);
}
} else {
/* Looking at a non-directory. */
if ((now - st.st_mtime > minage)
&& (now - st.st_ctime > minage)
&& st.st_uid
&& (st.st_nlink == 1)
&& (((st.st_mode & S_IFMT) == S_IFREG)
|| ((st.st_mode & S_IFMT) == S_IFLNK))) {
/*
* Old non-root owned regular file or
* symlink.
*/
setecreds(st.st_uid, st.st_gid);
if (unlink(dp->d_name) == -1) {
GETCWD;
syslog(LOG_ERR, "RACE?: unlink(\"%s\")"
"in %s: %m, exiting",
dp->d_name, cwd);
/* It's actually safe to continue... */
exit(1);
} else if (verbose) {
GETCWD;
syslog(LOG_INFO, "removed file %s/%s",
cwd, dp->d_name);
}
setecreds(0, 0);
}
}
}
closedir(dirp);
if (fchdir(dot_dot_fd) == -1) {
GETCWD;
syslog(LOG_ERR, "fchdir(%d) [fd to \"..\"] in %s: %m, exiting",
dot_dot_fd, cwd);
exit(1);
}
close(dot_dot_fd);
return;
}

int
main(argc, argv)
int argc;
char *argv[];
{
/* By default, delete files older than three days. */
extern char *optarg;
extern int optind;
int c, i;
struct rlimit rlp;

if (argc <= 0)
usage();
openlog("stmpclean", LOG_PID | LOG_CONS | LOG_PERROR, LOG_DAEMON);
minage = SECONDS_IN_A_DAY * 3;
while ((c = getopt(argc, argv, "vt:")) != -1)
switch (c) {
case 't':
minage = parse_time(optarg);
if (minage == -1)
usage();
break;
case 'v':
verbose++;
break;
default:
usage();
}
argc -= optind;
argv += optind;
if (argc <= 0)
usage();
/*
* For logging niceties in case one of the directories on the command
* line is bad.
*/
chdir("/");
now = time(NULL);
rlp.rlim_max = 0;
rlp.rlim_cur = 0;
if (setrlimit(RLIMIT_CORE, &rlp) == -1) {
syslog(LOG_ERR,
"cannot disable core dumps: setrlimit: %m, exiting");
exit(1);
}
for (i = 0; i < argc; i++)
if (argv[i][0] != '/') {
syslog(LOG_ERR, "directories to clean must be"
" absolute pathnames, `%s' is not, exiting",
argv[i]);
exit(1);
}
for (i = 0; i < argc; i++)
clean_dir(argv[i], MAX_DEPTH);
exit(0);
/* NOTREACHED */
}
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin