| Listing 1: actgroups
 
#!/usr/bin/perl
#
# Active groups -- detecting actively accessed news groups and adjusting
#         -- expire.ctl so that active groups stay longer
#
# Program Name:     actgroups
#         for INN 1.4, 1.5, 1.7, 1.8, 2.0
# Author: Yufan Hu ( yufan@recmail.com )
# Version:     1.0
# Release:     Jan 28 1998
#         This is a total rewrite of actgroups version 0.3 and its
#         modified version activexp.pl floating around the net since 1994
# Copyright:   by Yufan Hu. Freely redistribution, use and modification
#         by anybody as long as the author is acknowledged and this
#         copywrite notice preserved.
# Disclaim:    Use at your own risk. No Warrantee.
# Fee:         No. But appreciate an email acknowledge so I know
#         who is using it.
#
#========================= Installation ========================
# 1. Copy this file to a directory of your choice. I put it in
#    /usr/local/news/local
# 2. Change the values of $LastRead, $expire, $newslog according to
#    your own news configuration
# 3. pass the path to actgroups to news.daily in your cron job entry,
#    such as:
#    30 3 * * * /usr/local/news/bin/news.daily delayrm
#    /usr/local/news/local/actgroups
# 4. Setting rules in expire.ctl file
#
#========================= expire.ctl configuration =================
# The expire.ctl file remains almost the same except that the original rules
# definition act as the default expiration rules for inactive groups.
# A new set of rules, beginning with "#% " define the adjustment
# for groups that are actively read.
# i.e.
#    *:A:0:1:7
#    #% *:A:10:30:45
# means that if a group is not actively read, its expire rule will be
#    *:A:0:1:7
# and if a group is being read within 45 days, its expire time will be adjusted
# to:
#    *:A:10:30:45
# The above two lines can be used as the simplest configuration. If you want
# finer control you can add more, such as:
#    *:A:0:1:7
#    #% *:A:10:30:45
#    comp.*:A:7:7:10
#    #% comp.*:A:30:60:90
#    #% alt.binaries*:A:2:2:2
# You should include at least line "#% *:A:...". If no "#% " rule
# matches an actively read group it is treat the same as an inactive group.
# The "active period" of a group is defined by the <purge> field
# of the last matching "#% " rule. If no read happens with this period
# of time, the group is removed from the LastRead database
#
# actgroups will modify expire.ctl file for adding the adjustment. It must
# has read/write access to the directory expire.ctl resides. Original
# expire.ctl will be backuped to expire.ctl.day, where day is the day of
# the week the actgroups runs.
#
# WARNING: It may take a few days to collect the readership information.
#       it is adviced that you keep the original "*:A:..." line with
#       larger expire time for a period of time until the readership
#       information is accumulated in LastRead.log database, if you
#       use it at the first time.
#
# For actgroups version 0.3 and 0.4 users:
#    The LastRead.log database should be compatible with this version.
#
###############################################################
#
# Configuration part
#
#    $LastRead the DBM database to record the time a group was last
#              seen read
#    $expire        the full path to expire.ctl file on your system
#    $newslog  the full path to news.notice file on your system
#              this is the file where syslogd log all news.notice
#              level message
#
##############################################################
$LastRead      = "/usr/local/news/etc/LastRead.log";
$expire   = "/usr/local/news/etc/expire.ctl";
$newslog  = "/usr/local/news/log/news.notice";
# delimiters in expire.ctl
$start = "### Actively Accessed Groups Start ###";
$end = "### Actively Accessed Groups End ###";
# End of Configuration part
##################### No change needed ###########################
require 'getopts.pl';
# default options
$debug = 0;
# Get the command line options
Getopts('l:d');
$newslog = $opt_l if ($opt_l);
$debug = 1 if ($opt_d);
$warn .= "Logfile: $newslog\n" if $debug;
$expire_new = "$expire.new";
# open the database recording the time the group was last read
# access this database as an associated array
dbmopen(%LASTREAD, $LastRead, 0600);
# open the tempory expire.ctl.new file
open(EXP_NEW, ">$expire.new") || die "Cannot create $expire.new: $!\n";
# Seconds in a day
$day = 3600 * 24;
$now = time;
$total = $thrown = 0;
$date = qx{date};
chop $date;
scan_log();
scan_expire_ctl();
# now we have detected active group in %LASTREAD database
# we check the group agains the %rules to find any match.
# if a match is found, then the corresponding rule part is used
# for that group. If no match is found, the group is not adjusted
adjust_expire_ctl();
# close the database and the new expire.ctl file
close(EXP_NEW);
dbmclose(%LASTREAD);
$debug && ( $warn .= "##  Total: $total, thrown: $thrown\n");
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
if( $debug )
{
$warn .= "Collecting group information only, $expire intacted.\n" .
"Expire list is in $expire.new\n";
}
else
{
system "mv $expire $expire.$wday; mv $expire_new $expire";
}
$debug && $warn &&  print "\nMessage from actgroups:\n$warn";
exit;
# scan the news.notice file and record any detected read group
# in LastRead DBMS
sub scan_log {
# open the news log file -- usually the news.notice
open(LOG, $newslog);
# scan through the log file, detecting any accessed groups within current
# day and store the current time in the database
while(<LOG>) {
if(/^(.*\d\d:\d\d:\d\d).*nnrpd.*:\s+(.*) \\s+group\s+([^\s]+)\s(\d+)/){
$group = $3;
$arts = $4;
($warn .= "Adding:  $group\n")
if( $debug && (! 				        $LASTREAD{$group}) );
# if $arts == 0 then we will add \
 $group in only
# if it is already there \
 ($LASTREAD{$group} = $now) if( $arts \
 || $LASTREAD{$group});
}
}
close(LOG);
}
# scan the expire.ctl, looking for adjustment ctl
# line in the format of
# "#% pat:?:(\d+)|never:(\d+)|never:(\d+)|never"
sub scan_expire_ctl {
open(EXP, $expire);
%rules = ();
@patterns = ();
while(<EXP>)
{
/$start/ && last;
print EXP_NEW;
chop;
# try to find the #% rules
/^#%\s*([^:]+)\s*:\s*([^:]+)\s*:\s*([^:]+) \
 \s*:\s*([^:]+)\s*:\s*([^:]+)\s*/
|| next;
$rule = ":$2:$3:$4:$5";
$pat = $1;
$pat =~ s/\./\\./g;
$pat =~ s/([\*\+\?])/.$1/g;
$rules{$pat} = $rule;
push(@patterns, $pat);
}
close EXP;
}
sub adjust_expire_ctl {
print EXP_NEW "$start
###############################################
# Automatically generated by actgroups version
 #   1.0
# Author: Yufan Hu ( yufan\@rightiming.com )
# Date:        $date
#
 # Do not change anything below this line. Any
 #   changes will be overwritten
# the next time actgroups runs
################################################
\n";
for $group ( sort (keys %LASTREAD) )
{
$total++;
# does the group match anything in $rules
$rule = "";
# find the last matching rule
for $pat ( @patterns )
{
($group =~ /$pat/) && ($rule = $rules{$pat});
}
if( $rule )
{
# we found a match, do the adjustment
($purge) = $rule =~ /:([^:]+)$/;
if( int($purge) != 0
&& ($now - $LASTREAD{$group}) > ($purge * $day) )
{
$debug && ($warn .= "$group inactive for $purge days\n");
delete $LASTREAD{$group};
$thrown++;
}
else
{
print EXP_NEW "$group" . $rule . "\n";
}
}
else
{
$debug && ( $warn .= "No adjustment rule for $group\n" );
delete $LASTREAD{ $group };
$thrown++;
}
}
print EXP_NEW "##  Total: $total, thrown: $thrown\n";
print EXP_NEW "\n$end\n## Do not put anything after this line ###\n";
}
# End of File
 
 
 |