reap(8): expire news as space needed

Arnd Merlevede admerlev at immd4.informatik.uni-erlangen.de
Tue Apr 2 05:00:28 AEST 1991


dt at yenta.alb.nm.us (David B. Thomas) writes:

hallo andreas, vielleicht interessiert Dich
die follgende Msg falls Dus nicht selbst gelesen hast.

Uebrigns, liegt noch die Source von nn irgendwo ?
Und ist des schwer die zu installieren ? Ich wuerd sie gerne 
im ET-CipPool installieren (ala faui09)
?
Bis denn Arnd


>This message contains the README, followed by the source:

>README for reap version 1.1 by David B. Thomas (dt at yenta.alb.nm.us).

>A Problem:
>	News volume fluctuates wildly...sites with small disks must either
>expire aggresively, often deleting more than necessary, or worry that an
>unexpected huge dose of news might overfill the disk.


>A Solution:
>	Implement a tool that expires according to a user-defined scheme
>until sufficient freespace is reclaimed, then stops, leaving as much
>juicy news online as is feasible.  Reap does this.


>Expire Does Two Jobs:
>	Both Bnews and Cnews expires really do two jobs:
>		1. trim history files
>		2. delete outdated articles
>Thanks to some inspired jootsing (acronym for "jumping out of the system")
>by Mike Murphy (mrm at sceard.com) and others, it is more than possible to
>separate those two functions.  This is, of course, in keeping with the
>unix philosophy of one tool doing one job well!


>What Reap Does:
>	Reap only takes care of the second job: deleting old articles.
>It works by checking freespace, and processing one line at a time from a
>list of expire functions, until the desired freespace is attained.
>Each expire function consists of an age limit in days (decimals okay)
>and a list of newsgroups to process or not process, sys file style.  Ex:

>	.5	alt.sex.pictures,talk,!talk.bizarre,junk
>	1	rec,!rec.games,!rec.humor

>This example would check freespace, and if more space is needed, expire
>to .5 days everything in alt.sex.pictures, talk (except for talk.bizarre)
>and junk.  Then it would stop and check freespace again.  If still more
>space is needed, it would expire to 1 day everything in rec except
>rec.games.* and rec.humor.*.  It's that simple.


>Okay...So How Do I Arrange To Trim The History Files, Since Reap Can't:
>	Included in this distribution is a shell script (mostly written
>by Mike Murphy) to handle Cnews history files.  It shouldn't be too
>difficult to do something similar for Bnews, or you can give in and use
>the original expire utility with options that tell it to expire the
>history files only...but I think this will be slow.  It just comes down
>to removing lines from ordinary text files, based on their contents.
>Murphy and I used awk.


>But Is It Fast:
>	Yes, largely because it doesn't have to do much.  Even "find | rm"
>is slower because find is repeatedly exec-ing rm.  "rm -rf" has me beat,
>though, I'll bet!  :-)

>	Since the functions of deleting articles and trimming history
>are separate, I now run reap every six hours, but trim the history list
>just once a day.  That effectively keeps my disk space up to snuff, but
>only thrashes at the history file in the middle of the night.


>Credits:
>	I owe a lot to Mike Murphy for inspiring me with his "trasher"
>system.  I also owe a lot to all of your netters who will flood me with
>more suggestions and improvements in the coming weeks (hint, hint!).

>						little david
>						dt at yenta.alb.nm.us

>ps - reap is freeware.  No strings(1) attached.


>#!/bin/sh
># This is a shell archive (shar 3.44)
># made 03/28/1991 02:51 UTC by dt at yenta
># Source directory /u/dt/src/reap
>#
># existing files will NOT be overwritten unless -c is specified
>#
># This shar contains:
># length  mode       name
># ------ ---------- ------------------------------------------
>#    934 -rw-rw-r-- Install
>#    339 -rw-rw-r-- MANIFEST
>#    900 -rw-rw-r-- Makefile
>#   2918 -rw-rw-r-- README
>#    969 -rwxrwxr-x exphist
>#   3567 -rw-rw-r-- lib.c
>#   1611 -rw-rw-r-- list.sample
>#   2497 -rw-rw-r-- main.c
>#   6129 -rw-r--r-- reap.8
>#   2845 -rw-rw-r-- reap.c
>#   1195 -rw-rw-r-- reap.h
>#
># ============= Install ==============
>if test -f 'Install' -a X"$1" != X"-c"; then
>	echo 'x - skipping Install (File already exists)'
>else
>echo 'x - extracting Install (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'Install' &&
>X
>To install:
>X
>1. Edit tops of Makefile and reap.h to fit your system.
>X   [If you are installing this on a non-system-V machine, you will need
>X   to rewrite the freeblox() function at the top of lib.c.  This should
>X   be very easy.  Please email your working mods to me.]
>2. make
>3. Compose a sample function list file or use the sample provided to
>X	test reap (use the -n option to avoid really removing files).
>4. su and make install.
>5. Arrange to trim your news system's history list regularly.  If you have
>X	Cnews, try the "exphist" script provided, run from cron.  That
>X	script requires that a tiny file, /usr/lib/news/histdays, containing
>X	the number of days of history data to keep, or else you can hardcode
>X	in your favorite number.  If you have Bnews, I don't have a nifty
>X	suggestion, other than using expire(8) with a ridiculously long
>X	article expire time, but a sane history expire time.
>6. Arrange to run reap from cron.
>SHAR_EOF
>chmod 0664 Install ||
>echo 'restore of Install failed'
>Wc_c="`wc -c < 'Install'`"
>test 934 -eq "$Wc_c" ||
>	echo 'Install: original size 934, current size' "$Wc_c"
>fi
># ============= MANIFEST ==============
>if test -f 'MANIFEST' -a X"$1" != X"-c"; then
>	echo 'x - skipping MANIFEST (File already exists)'
>else
>echo 'x - extracting MANIFEST (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'MANIFEST' &&
>Included with this distribution are:
>X
>README		- general description and credits
>Install		- hints on how to install reap and get it going
>Xexphist		- history trimmer utility for Cnews
>list.sample	- sample function script file for reap
>reap.8		- man page
>lib.c		- source
>main.c		- source
>reap.c		- source
>reap.h		- source
>Makefile	- makefile
>SHAR_EOF
>chmod 0664 MANIFEST ||
>echo 'restore of MANIFEST failed'
>Wc_c="`wc -c < 'MANIFEST'`"
>test 339 -eq "$Wc_c" ||
>	echo 'MANIFEST: original size 339, current size' "$Wc_c"
>fi
># ============= Makefile ==============
>if test -f 'Makefile' -a X"$1" != X"-c"; then
>	echo 'x - skipping Makefile (File already exists)'
>else
>echo 'x - extracting Makefile (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
>#----------------------------
># parameters for installation
>#----------------------------
>X
># directories where manual and executable are to be installed
>BINDIR = /usr/lib/newsbin/expire
>MANDIR = /usr/man/man8
>X
># owner, group and file mode for manual and executable
>EXEOWNER = bin
>EXEGROUP = bin
>EXEMODE = 775
>MANOWNER = bin
>MANGROUP = bin
>MANMODE = 664
>X
>X
># your favorite C compiler
>#CC = cc
>CC = shcc
>X
># directory access functions library, if not in standard C library.
>LIB = -ldirent
>X
>X
>#-------------
># boring stuff
>#-------------
>X
>OBJ = main.o lib.o reap.o
>X
>reap: $(OBJ)
>X	$(CC) -s -o reap $(SHLIB) $(OBJ) $(LIB)
>X
>$(OBJ): reap.h
>X
>install: reap reap.8
>X	cp reap $(BINDIR)
>X	chown $(EXEOWNER) $(BINDIR)/reap
>X	chgrp $(EXEGROUP) $(BINDIR)/reap
>X	chmod $(EXEMODE) $(BINDIR)/reap
>X	cp reap.8 $(MANDIR)
>X	chmod $(MANMODE) $(MANDIR)/reap.8
>X	chown $(MANOWNER) $(MANDIR)/reap.8
>X	chgrp $(MANGROUP) $(MANDIR)/reap.8
>SHAR_EOF
>chmod 0664 Makefile ||
>echo 'restore of Makefile failed'
>Wc_c="`wc -c < 'Makefile'`"
>test 900 -eq "$Wc_c" ||
>	echo 'Makefile: original size 900, current size' "$Wc_c"
>fi
># ============= README ==============
>if test -f 'README' -a X"$1" != X"-c"; then
>	echo 'x - skipping README (File already exists)'
>else
>echo 'x - extracting README (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'README' &&
>X
>README for reap version 1.1 by David B. Thomas (dt at yenta.alb.nm.us).
>X
>A Problem:
>X	News volume fluctuates wildly...sites with small disks must either
>Xexpire aggresively, often deleting more than necessary, or worry that an
>unexpected huge dose of news might overfill the disk.
>X
>X
>A Solution:
>X	Implement a tool that expires according to a user-defined scheme
>until sufficient freespace is reclaimed, then stops, leaving as much
>juicy news online as is feasible.  Reap does this.
>X
>X
>Expire Does Two Jobs:
>X	Both Bnews and Cnews expires really do two jobs:
>X		1. trim history files
>X		2. delete outdated articles
>Thanks to some inspired jootsing (acronym for "jumping out of the system")
>by Mike Murphy (mrm at sceard.com) and others, it is more than possible to
>separate those two functions.  This is, of course, in keeping with the
>unix philosophy of one tool doing one job well!
>X
>X
>What Reap Does:
>X	Reap only takes care of the second job: deleting old articles.
>It works by checking freespace, and processing one line at a time from a
>list of expire functions, until the desired freespace is attained.
>Each expire function consists of an age limit in days (decimals okay)
>and a list of newsgroups to process or not process, sys file style.  Ex:
>X
>X	.5	alt.sex.pictures,talk,!talk.bizarre,junk
>X	1	rec,!rec.games,!rec.humor
>X
>This example would check freespace, and if more space is needed, expire
>to .5 days everything in alt.sex.pictures, talk (except for talk.bizarre)
>and junk.  Then it would stop and check freespace again.  If still more
>space is needed, it would expire to 1 day everything in rec except
>rec.games.* and rec.humor.*.  It's that simple.
>X
>X
>Okay...So How Do I Arrange To Trim The History Files, Since Reap Can't:
>X	Included in this distribution is a shell script (mostly written
>by Mike Murphy) to handle Cnews history files.  It shouldn't be too
>difficult to do something similar for Bnews, or you can give in and use
>the original expire utility with options that tell it to expire the
>history files only...but I think this will be slow.  It just comes down
>to removing lines from ordinary text files, based on their contents.
>Murphy and I used awk.
>X
>X
>But Is It Fast:
>X	Yes, largely because it doesn't have to do much.  Even "find | rm"
>is slower because find is repeatedly exec-ing rm.  "rm -rf" has me beat,
>though, I'll bet!  :-)
>X
>X	Since the functions of deleting articles and trimming history
>are separate, I now run reap every six hours, but trim the history list
>just once a day.  That effectively keeps my disk space up to snuff, but
>only thrashes at the history file in the middle of the night.
>X
>X
>Credits:
>X	I owe a lot to Mike Murphy for inspiring me with his "trasher"
>system.  I also owe a lot to all of your netters who will flood me with
>more suggestions and improvements in the coming weeks (hint, hint!).
>X
>X						little david
>X						dt at yenta.alb.nm.us
>X
>ps - reap is freeware.  No strings(1) attached.
>SHAR_EOF
>chmod 0664 README ||
>echo 'restore of README failed'
>Wc_c="`wc -c < 'README'`"
>test 2918 -eq "$Wc_c" ||
>	echo 'README: original size 2918, current size' "$Wc_c"
>fi
># ============= exphist ==============
>if test -f 'exphist' -a X"$1" != X"-c"; then
>	echo 'x - skipping exphist (File already exists)'
>else
>echo 'x - extracting exphist (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'exphist' &&
>#! /bin/sh
># exphist - expire history file
>X
># =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
>. ${NEWSCONFIG-/usr/lib/news/bin/config}
>X
>PATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH
>umask $NEWSUMASK
>X
>days=`cat /usr/lib/news/histdays`
>X
>lock="$NEWSCTL/LOCKexpire"
>ltemp="$NEWSCTL/L.$$"
>Xecho $$ >$ltemp
>trap "rm -f $ltemp ; exit 0" 0 1 2 15
>if newslock $ltemp $lock
>then
>X	trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15
>Xelse
>X	echo "$0: expire apparently already running" | mail "$NEWSMASTER"
>X	exit 1
>fi
>X
>cd $NEWSCTL
>rm -f history.n history.n.pag history.n.dir
>now=`getdate now`
>age=`expr 86400 \* $days`
>ago=`expr $now - $age`
>awk "{split(\$2,dates,\"~\");if(dates[1]>$ago)print \$0}" history >history.n
>mkdbm history.n
>[ -s history.n ] &&
>mv history history.o &&    # install new ASCII history file
>mv history.n history &&
>rm -f history.pag &&       # and related dbm files
>rm -f history.dir &&
>mv history.n.pag history.pag &&
>mv history.n.dir history.dir
>Xexit 0
>SHAR_EOF
>chmod 0775 exphist ||
>echo 'restore of exphist failed'
>Wc_c="`wc -c < 'exphist'`"
>test 969 -eq "$Wc_c" ||
>	echo 'exphist: original size 969, current size' "$Wc_c"
>fi
># ============= lib.c ==============
>if test -f 'lib.c' -a X"$1" != X"-c"; then
>	echo 'x - skipping lib.c (File already exists)'
>else
>echo 'x - extracting lib.c (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'lib.c' &&
>X
>/*
>X * lib.c
>X *	subroutines needed by reap utility
>X */
>X
>#include "reap.h"
>X
>/*
>X * freeblox(devno)
>X *
>X * THIS WILL REQUIRE MODIFICATION TO WORK UNDER NON-SYSTEM-V UNIX.
>X * IT SHOULD BE EASY.  PLEASE EMAIL WORKING MODS TO THE AUTHOR:
>X *			dt at yenta.alb.nm.us
>X *
>X *	assuming devno is the device number of a file system,
>X *	this function returns the free space on that file system,
>X *	in blocks (whatever that means for that filesystem) as an int.
>X *
>X */
>X
>freeblox(devno)
>int	devno;
>{
>X	static struct ustat	ust;
>X
>X	if (ustat(devno, &ust))
>X		ouch ("%s: ustat() failed\n");
>X
>X	return (ust.f_tfree);
>X
>} /* freeblox() */
>X
>X
>X
>X
>/*
>X * parse(cmd)
>X *	execute a line from the script file
>X *
>X * valid command lines:
>X *	a blank line is ignored
>X *	# comment (ignored)
>X *	days filespecs
>X *
>X * filespecs is a comma separated list of any combination of the following:
>X * 1. a directory (newsgroup) to include, without recursion.
>X *	ex:  alt.sources.
>X * 2. a directory to include, recursively.
>X *	ex:  alt.sources
>X * 3. a directory to exclude, without recursion.
>X *	ex:  !alt.sex.
>X * 4. a directory to exclude, recursively.
>X *	ex:  !alt.sex
>X */
>X
>parse(cmd)
>char	*cmd;
>{
>X	register int	l;
>X	int		recurseflag;
>X	double		days;
>X	char		*t,
>X			*p,
>X			*q,
>X			*seps = ",\n ";
>X
>X
>/* skip initial whitespace */
>X	p = cmd;
>X	while (isspace(*p))
>X		++p;
>X
>/* ignore blank lines or comments */
>X	if (*p == '\0' || *p == '#')
>X		return(1);
>X
>/* read the expire time from the line as a floating point number,
>X * then figure out what the timestamp on a file that old would be */
>X	age = now - (time_t) (atof(p) * SECINDAY);
>X
>/* skip ahead to the filespec list */
>X	while (!isspace(*p))
>X		++p;
>X	while (isspace(*p))
>X		++p;
>X
>/* initialize exclusion list to null list */
>X	preclude();
>X
>/* for each filespec in list */
>X	for (t = strtok(p, seps); t; t = strtok(NULL, seps)) {
>X
>X	/* forbid starting with a slash */
>X		if (*t == '/')
>X			*t = '.';
>X
>X	/* change dots to slashes except in column 1 */
>X		while ( (q = strchr(t+1,'.')) != NULL)
>X			*q = '/';
>X
>X	/* for final dot or slash, remove it, and shut off recursion */
>X		recurseflag = 1;
>X		if (t[l = (strlen(t) - 1)] == '/') {
>X			recurseflag = 0;
>X			t[l] = '\0';
>X		}
>X
>X		if (*t == '!')
>X			exclude (t+1, recurseflag);
>X		else
>X			include (t, recurseflag);
>X	}
>X
>X	reap();
>X
>X	return (0);
>X
>} /* parse() */
>X
>X
>X
>/*
>X * newnode,exclude, include, preclude
>X *
>X *	functions to maintain global linked lists:
>X *		incl	include this path in list of dirs to reap
>X *		excl	leave out this directory
>X			(each entry can be recursive or not)
>X *
>X *	the functions are:
>X *		include (text,rflag)	add to incl list
>X *		exclude (text,rflag)	add to excl list
>X *		preclude()		clear both lists
>X */
>struct filspec *
>newnode(text)
>char	*text;
>{
>X	struct filspec	*f;
>X
>X	if ( (f = (struct filspec *) malloc (sizeof(struct filspec))) == NULL ||
>X	    (f->name = malloc (strlen(text)+1)) == NULL)
>X		ouch ("%s: out of memory\n");
>X
>X	strcpy (f->name, text);
>X	return (f);
>}
>Xexclude(text, rflag)
>char	*text;
>int	rflag;
>{
>X	struct filspec	*f;
>X
>X	f = newnode(text);
>X	f->recurse = rflag;
>X	f->next = excl;
>X	excl = f;
>}
>include(text, rflag)
>char	*text;
>int	rflag;
>{
>X	struct filspec	*f;
>X
>X	f = newnode(text);
>X	f->recurse = rflag;
>X	f->next = incl;
>X	incl = f;
>}
>preclude()
>{
>X	struct filspec	*p;
>X
>X	while (incl != NULL) {
>X		p = incl;
>X		incl = incl->next;
>X		free (p->name);
>X		free (p);
>X	}
>X	while (excl != NULL) {
>X		p = excl;
>X		excl = excl->next;
>X		free (p->name);
>X		free (p);
>X	}
>}
>X
>X
>/*
>X * ouch(fmt, arg)
>X *	outputs an error message and exits with error status
>X */
>X
>ouch (fmt,arg)
>char	*fmt, *arg;
>{
>X	fprintf (stderr, fmt, progname, arg);
>X	exit (-1);
>}
>SHAR_EOF
>chmod 0664 lib.c ||
>echo 'restore of lib.c failed'
>Wc_c="`wc -c < 'lib.c'`"
>test 3567 -eq "$Wc_c" ||
>	echo 'lib.c: original size 3567, current size' "$Wc_c"
>fi
># ============= list.sample ==============
>if test -f 'list.sample' -a X"$1" != X"-c"; then
>	echo 'x - skipping list.sample (File already exists)'
>else
>echo 'x - extracting list.sample (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'list.sample' &&
># yenta's /usr/lib/news/reaplist function script file.
>.1 junk,alt.flame,control,comp.binaries,alt.sex.pictures.,alt.fractals.pictures
>.1 alt.desert-storm,alt.desert-shield,soc.motss,soc.culture
>1 soc.singles,alt.activism,alt.romance.chat
>.5 talk
>1 sci,!sci.skeptic
>2 sci.skeptic
>.5 comp.text,comp.sys.atari,comp.windows,comp.os.vms
>.5 comp.unix.sysv386
>.5 misc.jobs,misc.handicap
>2 soc,!soc.singles,!soc.motss,!soc.culture
>.5 rec,!rec.arts.anime,!rec.arts.animation,!rec.arts.movies,!rec.games.hack,!rec.radio.amateur,!rec.autos,!rec.arts.tv,!rec.audio,!rec.video,!rec.music,!rec.skydiving
>1 rec.music,!rec.music.synth
>3 rec.music.synth,rec.skydiving
>2 alt.tv
>2 rec.arts.anime,rec.arts.animation,rec.arts.movies,rec.radio.amateur,rec.autos,rec.arts.tv,rec.audio,rec.video
>3 rec.games.hack
>1 misc.headlines,misc.consumers,misc.kids,misc.legal
>1 alt.sex,!alt.sex.bondage,!alt.sex.pictures.
>1.5 comp,!comp.dcom.telecom,!comp.binaries,!comp.text,!comp.sys.atari,!comp.windows,!comp.os.vms,!comp.sys.att,!comp.sys.3b1,!comp.sources.3b1
>3 comp.dcom.telecom
>1 comp.binaries
>1 alt.sources
>1 news,!news.announce.newusers
>3 biz
>3 misc
>3 alt,!alt.sources,!alt.tv,!alt.desert-shield,!alt.activism,!alt.fractals.pictures,!alt.desert-storm,!alt.romance.chat
>3 comp.sys.att
>5 comp.sys.3b1
>5 comp.sources.3b1
>7 general
># up to here is "normal expire"... now let's get desparate
>.1 alt.sex.pictures
>.1 rec.arts.anime,rec.arts.animation,rec.arts.movies,rec.ham-radio,rec.autos,rec.arts.tv,rec.audio,rec.video
>.1 biz
>.1 talk
>.1 news
>.1 soc
>.1 sci
>.1 comp
>.1 alt
>.1 misc
># if we got here, we're in big trouble -- wipe it all!
>0 .
>SHAR_EOF
>chmod 0664 list.sample ||
>echo 'restore of list.sample failed'
>Wc_c="`wc -c < 'list.sample'`"
>test 1611 -eq "$Wc_c" ||
>	echo 'list.sample: original size 1611, current size' "$Wc_c"
>fi
># ============= main.c ==============
>if test -f 'main.c' -a X"$1" != X"-c"; then
>	echo 'x - skipping main.c (File already exists)'
>else
>echo 'x - extracting main.c (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
>X
>/*
>X * main.c
>X *	main routine and globals for reap utility, version 1.1
>X */
>X
>X
>#include "reap.h"
>X
>X
>/* linked lists for included and excluded file specs */
>struct filspec
>X		*incl = NULL,
>X		*excl = NULL
>;
>char
>X		*scriptfile = SCRIPT,	/* path of function script	*/
>X		*progname,		/* argv[0] for errors		*/
>X		*newsdir = NEWSDIR	/* news root directory		*/
>;
>int
>X		verbose = 0,		/* verbosity (-v) flag		*/
>X		dryrun = 0		/* do-not-unlink (-n) flag	*/
>;
>time_t
>X		age,			/* unlink files older than this	*/
>X		now			/* current time			*/
>;
>X
>X
>/************************
>X *	MAIN FUNCTION	*
>X ************************/
>X
>main(argc, argv)
>int	argc;
>char	*argv[];
>{
>X	int		device,		/* device # containing newsdir	*/
>X			c,
>X			lineno = 0,
>X			summary = 0,
>X			thenfree,
>X			errflag = 0;
>X	long		wantblox;	/* desired free space		*/
>X	char		*p;
>X	static char	line[MAXLINE];
>X	FILE		*script;	/* file ptr for function script	*/
>X	struct stat	st;
>X
>X
>/* save argv[0] for error spewings */
>X	progname = argv[0];
>X
>/* parse args */
>X	while ( (c=getopt(argc,argv,"vsnf:")) != EOF)
>X		switch(c) {
>X		case 'n':
>X			++dryrun;
>X			break;
>X		case 'v':
>X			++verbose;
>X			break;
>X		case 'f':
>X			scriptfile = optarg;
>X			break;
>X		case 's':
>X			++summary;
>X			break;
>X		default:
>X			++errflag;
>X			break;
>X		}
>X	
>X	if (argc - optind != 1 ||
>X	    sscanf(argv[optind], "%ld", &wantblox) != 1 )
>X		++errflag;
>X
>X	if (errflag) {
>X		fprintf (stderr,
>X		    "usage: %s [-v] [-s] [-n] [-f funclist] freeblocks\n",
>X		    progname);
>X		exit (-1);
>X	}
>X
>X
>/* stat newsdir to find the number of the device it's on */
>X	if (stat(newsdir, &st))
>X		ouch ("%s: can't stat %s\n", newsdir);
>X	device = st.st_dev;
>X
>/* open function script file for reading */
>X	if ( (script = fopen (scriptfile, "r")) == NULL)
>X		ouch ("%s: can't read %s\n", scriptfile);
>X
>/* Record the current time, for deciding what gets the axe. */
>X	time (&now);
>X
>/* chdir to newsdir */
>X	chdir (newsdir);
>X
>/* record initial freespace if summary is desired */
>X	if (summary)
>X		thenfree = freeblox(device);
>X
>/* main loop ... process until goal reached or end of script */
>X
>X	while (freeblox(device) < wantblox &&
>X	    (p = fgets (line, MAXLINE, script)) != NULL )
>X		parse (line), ++lineno;
>X
>X	fclose (script);
>X
>X	if (summary) {
>X		c = freeblox(device);
>X		printf ("Freespace was %d, now %d.  Cleared %d.\n",
>X		    thenfree, c, c - thenfree);
>X		printf ("Stopped after line %d in %s\n", lineno,
>X		    scriptfile);
>X	}
>X
>/* return 0 if freespace goal was met at exit, 1 if not */
>X	exit (freeblox(device) >= wantblox ? 0 : 1);
>X
>} /* main() */
>X
>SHAR_EOF
>chmod 0664 main.c ||
>echo 'restore of main.c failed'
>Wc_c="`wc -c < 'main.c'`"
>test 2497 -eq "$Wc_c" ||
>	echo 'main.c: original size 2497, current size' "$Wc_c"
>fi
># ============= reap.8 ==============
>if test -f 'reap.8' -a X"$1" != X"-c"; then
>	echo 'x - skipping reap.8 (File already exists)'
>else
>echo 'x - extracting reap.8 (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'reap.8' &&
>.TH REAP 8 LOCAL
>.SH NAME
>reap - remove news articles as space needed
>.SH SYNOPSIS
>.B reap
>[-v] [-s] [-n] [-f scriptfile] freeblocks
>.SH DESCRIPTION
>.I Reap
>checks disk freespace and deletes netnews articles
>according to a flexible user-specified
>scheme until
>.I freeblocks
>minimum freespace is available.
>It does this by sequentially reading commands (expire functions) from a
>text file (function script), and executing them one at a time.
>.PP
>Each expire function consists of an age limit and a list of newsgroups
>to expire to that limit.
>Before executing
>Xeach new expire function, reap checks the freespace on the news spool
>device.  Reap will exit when either the desired freespace is attained, or
>the end of the function script file is reached (the latter case is considered
>an unsuccessful exit).
>By carefully designing the function script, a news administrator can
>automate a wide variety of expiring policies.
>.PP
>The
>.B -n
>causes a dry run (as in make(1)).  When this
>option is specified, reap merely reports which articles it would delete if
>the option were left off.  This is useful for debugging function scripts.
>However, since no space is actually freed in this mode,
>it will either do nothing
>(there was enough space to begin with) or proceed through the entire function
>script file, indicating that reap would delete all applicable articles.
>.PP
>With the
>.B -v
>option, reap prints a verbose commentary on its progress to the standard
>output.
>.PP
>The
>.B -s
>option causes reap to print a brief summary of blocks freed to the standard
>output just before exiting.  It also indicates how much of the script file
>was processed.  This is independent of -v.
>.PP
>By default, reap looks in /usr/lib/news/reaplist for its list of functions.
>The
>.B -f
>option is used to specify an alternate function script file.
>.SH FUNCTION SCRIPT FILE FORMAT
>A function script file consists of a series of expire functions, one per line.
>Each expire function contains an age limit (in days, decimals okay), followed
>by a comma-separated list of newsgroup specifications, similar to the
>sys file format.
>.PP
>There are four types of valid newsgroup specifications that can go in the list:
>.PP
>(1) An ordinary newsgroup name (alt.sex) indicates that any articles in that
>newsgroup and any of its descendants (such as alt.sex.pictures), which are
>older than the age limit, should be expired.
>.PP
>(2) A newsgroup name with a trailing dot (alt.sex.) indicates that any articles
>in that newsgroup, which are older than the age limit, should be expired.
>However, recursion is not implied.  Articles in descendants of that group
>(such as alt.sex.pictures) are not affected.
>.PP
>(3) A newsgroup name with a preceding exclamation point (!alt.sex.pictures)
>indicates that that newsgroup and all of its descendants should be excluded
>from the list.
>For example, "1 alt.sex,!alt.sex.pictures" means to expire everything subsumed
>under alt.sex to 1 day, but do not touch anything subsumed under
>alt.sex.pictures.
>.PP
>(4) A newsgroup name with both a preceding exclamation point and a trailing
>dot (!alt.sex.pictures.) indicates that that newsgroup should be excluded
>from the list, but its descendants should remain in the list.  For example,
>"1 alt.sex,!alt.sex.pictures." means to expire everything subsumed under
>alt.sex to 1 day, including the contents of any subgroups of alt.sex.pictures
>(like alt.sex.pictures.d), but do not touch the articles in the newsgroup
>alt.sex.pictures.
>.PP
>Blank lines and lines beginning with a "#" are ignored.
>A sample function script file:
>.PP
>.nf
>X	# my first function script file
>X	0.5	talk,junk,alt.sex.pictures.
>X	2	rec,!rec.games,!rec.humor
>X	2	misc
>.fi
>.PP
>In the example,
>if space is needed, reap
>Xexpires to .5 days the entire talk and junk
>hierarchies, and alt.sex.pictures (not touching any subgroups of
>alt.sex.pictures, such as alt.sex.pictures.d).
>If still more space is needed, the
>rec hierarchy (excepting all of rec.games.* and rec.humor.*) is expired
>to 2 days.  If freespace is still short, reap then expires all of misc
>to 2 days.
>.PP
>Note that, while it would be possible to consolidate the 2-day
>lines, leaving them separate makes it possible for reap to stop
>in between them if sufficient space is cleared.
>.PP
>A practical function script file would directly or indirectly
>include every group carried on the system, with some age limit.  Otherwise,
>the spool directory will inevitably overflow without operator intervention.
>.PP
>It is valid to specify a dot all by itself (.) in the newsgroup list field.
>This is taken to mean the entire spool directory hierarchy, so "14 ." means
>to expire all news to 14 days.  Be warned that reap must search the entire
>news hierarchy when this feature is used.  Still, it is probably a good
>idea to end all function script files with a "0 .", so that, in absolute
>desperation, all news would be removed.
>.SH FILES
>.TP 25
>/usr/spool/news
>News spool directory
>.TP 25
>/usr/lib/news/reaplist
>Default function list file
>.SH DIAGNOSTICS
>Returns zero if sufficient space was free at exit, 1 if the entire function
>script was executed without freeing enough space, and -1 on an error
>condition.
>.SH CAVEATS
>What constitutes a disk "block" is implementation-dependent.
>.PP
>History files are not updated.  That must be performed as a separate operation.
>.PP
>For best performance, exclude recursively (without the dot) whenever
>possible.  Otherwise, reap will have to search the excluded directory
>for any subdirectories which might not have been excluded, which is slow.
>.SH BUGS
>The use of the ustat() system call is not very portable, and your humble
>author isn't aware of its non-system-V equivalents.
>.PP
>Lines in the function file are limited to 1024 characters.
>.PP
>The "cleared" figure in the summary merely indicates the difference in
>freespace before and after the run, not necessarily the actual number of
>blocks liberated by reap.
>.SH AUTHOR
>Reap is freeware by David B. Thomas (dt at yenta.alb.nm.us).  You are free
>to copy, distribute, staple, bend, fold or mutilate this package to your
>heart's content.  Please email any enhancements or ideas for enhancements.
>SHAR_EOF
>chmod 0644 reap.8 ||
>echo 'restore of reap.8 failed'
>Wc_c="`wc -c < 'reap.8'`"
>test 6129 -eq "$Wc_c" ||
>	echo 'reap.8: original size 6129, current size' "$Wc_c"
>fi
># ============= reap.c ==============
>if test -f 'reap.c' -a X"$1" != X"-c"; then
>	echo 'x - skipping reap.c (File already exists)'
>else
>echo 'x - extracting reap.c (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'reap.c' &&
>X
>/*
>X * reap.c
>X *	contains the reap() function.
>X *
>X * Once the global linked lists incl and excl have been stuffed,
>X * reap() actually scans the filesystem for files that meet the specs
>X * and unlinks them if they are older than the age global variable.
>X */
>X
>#include "reap.h"
>X
>X
>reap()
>{
>X	struct filspec	*f;
>X
>X	for (f = incl; f != NULL; f = f->next) {
>X		if (verbose)
>X			printf ("scanning %s ...\n", f->name);
>X		dodir (f->name, f->recurse);
>X	}
>X
>} /* reap() */
>X
>X
>X
>dodir(name, rflag)
>char	*name;
>int	rflag;
>{
>X	/* the following can be overwritten safely during recursion */
>X	static struct filspec	*e;
>X	static struct stat	st;
>X	static struct dirent	*dp;
>X	static char		thisname[MAXFILENAME+1];
>X	/* the following must be preserved through recursion */
>X	char			*fullpath;
>X	int			eflag = 0;
>X	DIR			*dirp;
>X
>X
>/* open directory for reading */
>X	if ( (dirp = opendir(name)) == NULL)
>X		ouch ("%s: can't read directory %s\n", name);
>X
>X
>/* see if this directory is excluded.
>X * If it's excluded recursively, quit here.
>X * If it's excluded non-recursively, set a flag, so we won't consider
>X *	deleting any files in it, but we'll still explore subdirectories.
>X */
>X	for (e = excl; e != NULL; e = e->next)
>X		if (!strcmp (name, e->name))
>X			break;
>X	if (e != NULL) {
>X		if (e->recurse)
>X			return;
>X		else
>X			++eflag;
>X	}
>X
>X
>/* loop for each directory entry */
>X	while ( (dp = readdir(dirp)) != NULL) {
>X
>X	/* name might be exactly MAXFILENAME characters long, and thus
>X	 * might not be null-terminated.  Some insurance:
>X	 */
>X		strncpy (thisname, dp->d_name, MAXFILENAME);
>X		thisname[MAXFILENAME] = '\0';
>X
>X	/* skip dot and dotdot */
>X		if (!strcmp(thisname, ".") || !strcmp(thisname, ".."))
>X			continue;
>X
>X	/* build the full pathname of current object */
>X		if ( (fullpath =
>X		    malloc(strlen(name)+strlen(thisname)+2)) == NULL)
>X			ouch ("%s: out of memory\n");
>X
>X		sprintf (fullpath, "%s/%s", name, thisname);
>X
>X	/* try to stat the object */
>X		if (stat(fullpath,&st)) {
>X			fprintf (stderr, "%s: can't stat %s\n",
>X			    progname, fullpath);
>X			free (fullpath);
>X			continue;
>X		}
>X
>X	/* maybe recurse if it's a directory */
>X		if ( st.st_mode & S_IFDIR ) {
>X			if (rflag)
>X				dodir (fullpath, 1);
>X			free (fullpath);
>X			continue;
>X		}
>X
>X	/* it's a file ...  is this a non-recursively excluded directory?
>X	 * if so, there's nothing to do to this file
>X	 */
>X		if (eflag)
>X			continue;
>X
>X	/* leave it alone if this directory is excluded, or
>X	 * if it's new enough.
>X	 */
>X		if (eflag || st.st_mtime > age) {
>X			free (fullpath);
>X			continue;
>X		}
>X
>X	/* reap this file! */
>X		if (dryrun) {
>X			printf ("Would unlink %s\n", fullpath);
>X			free (fullpath);
>X			continue;
>X		}
>X		if (verbose)
>X			printf ("Unlinking %s\n", fullpath);
>X
>X		if (unlink (fullpath) == -1)
>X			fprintf (stderr,
>X			    "%s: cannot unlink %s\n", progname, fullpath);
>X
>X		free (fullpath);
>X
>X	} /* while */
>X
>X	closedir (dirp);
>X
>} /* dodir() */
>SHAR_EOF
>chmod 0664 reap.c ||
>echo 'restore of reap.c failed'
>Wc_c="`wc -c < 'reap.c'`"
>test 2845 -eq "$Wc_c" ||
>	echo 'reap.c: original size 2845, current size' "$Wc_c"
>fi
># ============= reap.h ==============
>if test -f 'reap.h' -a X"$1" != X"-c"; then
>	echo 'x - skipping reap.h (File already exists)'
>else
>echo 'x - extracting reap.h (Text)'
>sed 's/^X//' << 'SHAR_EOF' > 'reap.h' &&
>X
>/*
>X * reap.h
>X *	header file for reap utility
>X */
>X
>/* set MAXFILENAME to the maximum number of characters in a filename for
>X * your system.  Typically 14 or infinity, where infinity equals
>X * 256 characters. :-)
>X */
>#define MAXFILENAME	14
>X
>/* set NEWSDIR to the directory containing news on your system.
>X * Very commonly /usr/spool/news
>X */
>#define NEWSDIR		"/usr/spool/news"
>X
>/* set SCRIPT to the path of the default function script file
>X * Usually this is /usr/lib/news/reaplist
>X */
>#define SCRIPT		"/usr/lib/news/reaplist"
>X
>X
>X
>X
>#include <stdio.h>
>#include <string.h>
>#include <ctype.h>
>#include <time.h>
>#include <malloc.h>
>#include <sys/types.h>
>#include <sys/stat.h>
>#include <ustat.h>
>#include <dirent.h>
>X
>#define MAXLINE		1024		/* max len of line in script	*/
>#define SECINDAY	(3600 * 24)	/* seconds in a day		*/
>X
>/* structure for linked lists of included and excluded file specs */
>struct filspec {
>X	char		*name;
>X	int		recurse;
>X	struct filspec	*next;
>};
>X
>Xextern struct filspec
>X		*incl,
>X		*excl
>;
>Xextern char
>X		*progname,
>X		*scriptfile,
>X		*newsdir
>;
>Xextern int
>X		verbose,
>X		dryrun,
>X		optind
>;
>Xextern char	*optarg;
>Xextern double	atof();
>Xextern long	freeblox();
>Xextern time_t
>X		age,
>X		now
>;
>SHAR_EOF
>chmod 0664 reap.h ||
>echo 'restore of reap.h failed'
>Wc_c="`wc -c < 'reap.h'`"
>test 1195 -eq "$Wc_c" ||
>	echo 'reap.h: original size 1195, current size' "$Wc_c"
>fi
>exit 0
>-- 
>Bottom of stack = 0x40000
>Stack pointer   = 0x3fffe
>Don't push it!
---
 reply to : admerlev at faui43.informatik.uni-erlangen.de



More information about the Alt.sources mailing list