Path: utzoo!utgpu!cs.utexas.edu!usc!snorkelwacker.mit.edu!ira.uka.de!fauern!faui43.informatik.uni-erlangen.de!admerlev From: admerlev@immd4.informatik.uni-erlangen.de (Arnd Merlevede) Newsgroups: alt.sources Subject: Re: reap(8): expire news as space needed Keywords: reap expire news cnews Message-ID: <1991Apr1.190028.14519@informatik.uni-erlangen.de> Date: 1 Apr 91 19:00:28 GMT References: <1991Mar28.025957.1976@yenta.alb.nm.us> Reply-To: admerlev@faui43.informatik.uni-erlangen.de Organization: CSD., University of Erlangen, Germany Lines: 1163 dt@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@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@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@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@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@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@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@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-/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@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@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 >#include >#include >#include >#include >#include >#include >#include >#include >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@faui43.informatik.uni-erlangen.de