cnfsstat cleanup
Kjetil Torgrim Homme
kjetilho at ifi.uio.no
Thu Apr 13 02:19:41 UTC 2000
(I'm not on inn-patches)
This patch does:
* Rewrite "-a" mode to use sysread, so cnfsstat should never gobble
memory anymore.
* Add &safe_run to do backticks safely. (very unlikely exploitable)
* Changed output into a more intutitive and human readable format
(syslog output has not been modified)
* Removes support for storage.ctl
* Uses Math::BigInt rather than bigint.pl
It also shuffles the code around, so the patch is strictly larger than
necessary. But it was a mess, with subroutines splitting main() in
two etc., so I felt compelled to do it.
Kjetil T.
--- cnfsstat.in.old Wed Apr 5 12:01:32 2000
+++ cnfsstat.in Thu Apr 13 04:04:21 2000
@@ -6,7 +6,7 @@
# Copyright Andreas Lamrecht 1998
# <Andreas.Lamprect at siemens.at>
#
-# Modified by Kjetil T. Homme 1998
+# Modified by Kjetil T. Homme 1998, 2000
# <kjetilho at ifi.uio.no>
#
# Modified by Robert R. Collier 1998
@@ -16,55 +16,12 @@
use vars qw($opt_l $opt_h $opt_a $opt_s);
use Getopt::Long;
-
-# required for >32bit ints
-require 'bigint.pl';
+use Math::BigInt;
+use English;
my($conffile) = "$inn::pathetc/cycbuff.conf";
-my($storagectl) = "$inn::pathetc/storage.ctl";
my($storageconf) = "$inn::pathetc/storage.conf";
-# Hex to bigint conversion routine
-# bhex(HEXSTRING) returns BIGINT (with leading + chopped off)
-#
-# In most langauge, unlimited size integers are done using string math
-# libraries usually called bigint. (Java, Perl, etc...)
-
-# Bigint's are really just strings.
-
-# Mathematics routines for bigint's:
-
-# bneg(BINT) return BINT negation
-# babs(BINT) return BINT absolute value
-# bcmp(BINT,BINT) return CODE compare numbers (undef,<0,=0,>0)
-# badd(BINT,BINT) return BINT addition
-# bsub(BINT,BINT) return BINT subtraction
-# bmul(BINT,BINT) return BINT multiplication
-# bdiv(BINT,BINT) return (BINT,BINT) division (quo,rem) just quo if scalar
-# bmod(BINT,BINT) return BINT modulus
-# bgcd(BINT,BINT) return BINT greatest common divisor
-# bnorm(BINT) return BINT normalization
-
-sub bhex {
- my $hexValue = shift;
- $hexValue =~ s/^0x//;
-
- my $integerValue = '0';
- for (my $i = 0; $i < length($hexValue); $i+=2) {
- # Could be more efficient going at larger increments, but byte
- # by byte is safer for the case of 9 byte values, 11 bytes, etc..
-
- my $byte = substr($hexValue,$i,2);
- my $byteIntValue = hex($byte);
-
- $integerValue = bmul($integerValue,'256');
- $integerValue = badd($integerValue,"$byteIntValue");
- }
-
- $integerValue =~ s/^\+//;
- return $integerValue;
- }
-
sub usage {
print <<_end_;
Summary tool for CNFS
@@ -99,11 +56,11 @@
eval { require Sys::Syslog; import Sys::Syslog; $use_syslog = 1 };
if ($use_syslog) {
if (defined &Sys::Syslog::setlogsock && $] >= 5.00403) {
- # we really need a common module to work all this junk out
- if ($^O eq "dec_osf") {
- sub Sys::Syslog::_PATH_LOG { "/dev/log" }
- }
- Sys::Syslog::setlogsock('unix') if $^O =~ /linux|dec_osf/;
+ # we really need a common module to work all this junk out
+ if ($OSNAME eq "dec_osf") {
+ sub Sys::Syslog::_PATH_LOG { "/dev/log" }
+ }
+ Sys::Syslog::setlogsock('unix') if $OSNAME =~ /linux|dec_osf/;
}
openlog ('cnfsstat', 'pid', $inn::syslog_facility);
} else {
@@ -125,119 +82,11 @@
exit (1);
}
-unless (&read_storageconf || &read_storagectl) {
- print STDERR "No valid $storageconf or $storagectl.\n";
+unless (&read_storageconf) {
+ print STDERR "No valid $storageconf.\n";
exit (1);
}
-sub read_cycbuffconf {
- return 0 unless open (CONFFILE, $conffile);
-
- while(<CONFFILE>) {
- $_ =~ s/^\s*(.*?)\s*$/$1/;
- # Here we handle continuation lines
- while (m/\\$/) {
- $contline = <CONFFILE>;
- $contline =~ s/^\s*(.*?)\s*$/$1/;
- chop;
- $_ .= $contline;
- }
- # \x23 below is #. Emacs perl-mode gets confused by the "comment"
- next if($_ =~ /^\s*$/ || $_ =~ /^\x23/);
- next if($_ =~ /^cycbuffupdate:/ || $_ =~ /^refreshinterval:/);
-
- if($_ =~ /^metacycbuff:/) {
- @line = split(/:/, $_);
- if($class{$line[1]}) {
- print STDERR "Class $line[1] more than one time in CycBuff Conffile $conffile ...\n";
- return 0;
- }
-
- $class{$line[1]} = $line[2];
- next;
- }
-
- if ($_ =~ /^cycbuff/) {
- @line = split(/:/, $_);
- if($buff{$line[1]}) {
- print STDERR "Buff $line[1] more than one time in CycBuff Conffile $conffile ...\n";
- return 1;
- }
- $buff{$line[1]} = $line[2];
- next;
- }
-
- print STDERR "Unknown config line \"$_\" in CycBuff Conffile $conffile ...\n";
- }
- close(CONFFILE);
- return 1;
-}
-
-sub read_storagectl {
- return 0 unless open (STOR, $storagectl);
-
- while (<STOR>) {
- $_ =~ s/^\s*(.*?)\s*$/$1/;
- next if $_ =~ /^\s*$/ || $_ =~ /^#/;
-
- if ($_ =~ /^cnfs:/) {
- @line = split(/:/, $_);
- if($#line != 5) {
- print STDERR "Wrong Storage Control Line \"$_\" in $storagectl ...\n";
- return 0;
- }
-
- if($stor{$line[5]}) {
- print STDERR "CNFS Storage Line \"$_\" more than one time in $storagectl ...\n";
- return 0;
- }
- $stor{$line[5]} = join(":", @line[1 .. 4]);
- push(@storsort, $line[5]);
- }
- }
- close(STOR);
- return 1;
-}
-
-sub read_storageconf {
- my $line = 0;
- return 0 unless open (STOR, $storageconf);
-
- while (<STOR>) {
- ++$line;
- next if /^\s*#/;
-
- # defaults
- %key = ("NEWSGROUPS" => "*",
- "SIZE" => "0,0");
-
- if (/method\s+cnfs\s+\{/) {
- while (<STOR>) {
- ++$line;
- next if /^\s*#/;
- last if /\}/;
- if (/(\w+):\s+(\S+)/i) {
- $key{uc($1)} = $2;
- }
- }
- unless (defined $key{'CLASS'} && defined $key{'OPTIONS'}) {
- print STDERR "storage.conf:$line: ".
- "Missing 'class' or 'options'\n";
- return 0;
- }
-
- $key{'SIZE'} .= ",0" unless $key{'SIZE'} =~ /,/;
- $key{'SIZE'} =~ s/,/:/;
-
- if (!defined $stor{$key{'OPTIONS'}}) {
- $stor{$key{'OPTIONS'}} = "$key{'NEWSGROUPS'}:$key{'CLASS'}:" .
- "$key{'SIZE'}:$key{'OPTIONS'}";
- push(@storsort, $key{'OPTIONS'});
- }
- }
- }
- return 1;
-}
&mrtg($obuffer) if $obuffer;
&mrtg_config if $opt_p;
@@ -335,44 +184,114 @@
goto START;
}
+sub read_cycbuffconf {
+ return 0 unless open (CONFFILE, $conffile);
+
+ while(<CONFFILE>) {
+ $_ =~ s/^\s*(.*?)\s*$/$1/;
+ # Here we handle continuation lines
+ while (m/\\$/) {
+ $contline = <CONFFILE>;
+ $contline =~ s/^\s*(.*?)\s*$/$1/;
+ chop;
+ $_ .= $contline;
+ }
+ # \x23 below is #. Emacs perl-mode gets confused by the "comment"
+ next if($_ =~ /^\s*$/ || $_ =~ /^\x23/);
+ next if($_ =~ /^cycbuffupdate:/ || $_ =~ /^refreshinterval:/);
+
+ if($_ =~ /^metacycbuff:/) {
+ @line = split(/:/, $_);
+ if($class{$line[1]}) {
+ print STDERR "Class $line[1] more than one time in CycBuff Conffile $conffile ...\n";
+ return 0;
+ }
+
+ $class{$line[1]} = $line[2];
+ next;
+ }
+
+ if ($_ =~ /^cycbuff/) {
+ @line = split(/:/, $_);
+ if($buff{$line[1]}) {
+ print STDERR "Buff $line[1] more than one time in CycBuff Conffile $conffile ...\n";
+ return 1;
+ }
+ $buff{$line[1]} = $line[2];
+ next;
+ }
+
+ print STDERR "Unknown config line \"$_\" in CycBuff Conffile $conffile ...\n";
+ }
+ close(CONFFILE);
+ return 1;
+}
+
+sub read_storageconf {
+ my $line = 0;
+ return 0 unless open (STOR, $storageconf);
+
+ while (<STOR>) {
+ ++$line;
+ next if /^\s*#/;
+
+ # defaults
+ %key = ("NEWSGROUPS" => "*",
+ "SIZE" => "0,0");
+
+ if (/method\s+cnfs\s+\{/) {
+ while (<STOR>) {
+ ++$line;
+ next if /^\s*#/;
+ last if /\}/;
+ if (/(\w+):\s+(\S+)/i) {
+ $key{uc($1)} = $2;
+ }
+ }
+ unless (defined $key{'CLASS'} && defined $key{'OPTIONS'}) {
+ print STDERR "storage.conf:$line: ".
+ "Missing 'class' or 'options'\n";
+ return 0;
+ }
+
+ $key{'SIZE'} .= ",0" unless $key{'SIZE'} =~ /,/;
+ $key{'SIZE'} =~ s/,/:/;
+
+ if (!defined $stor{$key{'OPTIONS'}}) {
+ $stor{$key{'OPTIONS'}} = "$key{'NEWSGROUPS'}:$key{'CLASS'}:" .
+ "$key{'SIZE'}:$key{'OPTIONS'}";
+ push(@storsort, $key{'OPTIONS'});
+ }
+ }
+ }
+ return 1;
+}
+
sub print_cycbuff_head {
- my($buffpath) = $_[0];
- my($name,$len,$free,$update,$cyclenum,$nupdate_str,$nago_str,$oupdate_str,$oago_str) = &get_cycbuff_info($buffpath);
+ my ($buffpath) = $_[0];
+ my ($name, $len, $free, $update, $cyclenum, $oldart) =
+ &get_cycbuff_info($buffpath);
if ($use_syslog) {
($name) = split(/\b/, $name);
$name =~ s/\0//g;
- syslog ('notice', '%s Buffer %s, len: %d Mbytes, used: %.2f Mbytes (%4.1f%%) %3d cycles', $logline, $name, $len / (1024 * 1024), $free / (1024 * 1024), 100 * $free/$len, $cyclenum);
+ syslog ('notice', '%s Buffer %s, len: %d Mbytes, used: %.2f Mbytes (%4.1f%%) %3d cycles',
+ $logline, $name, $len / (1024 * 1024), $free / (1024 * 1024),
+ 100 * $free/$len, $cyclenum);
return 0;
- } else {
- $name =~ s/\0//g;
- print " Buffer $name, len: ";
- printf("%.2f", $len / (1024 * 1024));
- print " Mbytes, used: ";
- printf("%.2f Mbytes", $free / (1024 * 1024));
- printf(" (%4.1f%%) %3d cycles", 100 * $free/$len, $cyclenum);
}
-
- print "\n Newest: $nupdate_str, $nago_str ago\n";
-
- if ($opt_a) {
- print " Oldest: $oupdate_str, $oago_str ago\n";
- }
-}
-sub lookup_age {
- my ($msgid) = @_;
-
- # This isn't really sufficient; we should use a safe fork.
- $msgid =~ s/\\/\\\\/;
- $msgid =~ s/\'/\'\\\'\'/;
+ $name =~ s/\0//g;
+ print " Buffer $name, size: ", &human_readable($len, 4);
+ print ", current: ", &human_readable($free, 4);
+ printf(" %.2f cycles\n", $cyclenum + $free/$len);
+ my ($when, $ago) = &make_time($update);
+ print " Newest: $when, $ago ago\n";
- my $history = `grephistory -l '$msgid' 2>&1`;
- if ($history =~ /\t(\d+)~/) {
- return $1;
+ if ($opt_a) {
+ my ($when, $ago) = &make_time($oldart);
+ print " Oldest: $when, $ago ago\n";
}
- print " (Missing $msgid)\n";
- return 0;
}
sub make_time {
@@ -394,6 +313,27 @@
return @ret;
}
+sub human_readable {
+ my ($val, $digits) = @_;
+
+ my @name = ("kBytes", "MBytes", "GBytes", "TBytes");
+ my $base = 1024;
+ my $factor = 1024;
+
+ my $unit = $#name;
+ my $scaled = $val / $base / $factor**$unit;
+ while ($scaled < 1 && $unit) {
+ $scaled *= $factor;
+ $unit--;
+ }
+ my $predigits = int(log($scaled) / log(10));
+ $predigits = 0 if $predigits < 0;
+ my $postdigits = $digits - $predigits - 1;
+ ++$digits;
+
+ return sprintf ("%${digits}.${postdigits}f %s", $scaled, $name[$unit]);
+}
+
sub mrtg {
my $buffer = shift;
# print "Buffer = $buff{$buffer}\n";
@@ -456,7 +396,8 @@
exit(1);
}
- ($magic, $name, $path, $lena, $freea, $updatea, $cyclenuma) = unpack("a8 a16 a64 a16 a16 a16 a16", $buff);
+ ($magic, $name, $path, $lena, $freea, $updatea, $cyclenuma) =
+ unpack("a8 a16 a64 a16 a16 a16 a16", $buff);
if(!$magic) {
print STDERR "Error while unpacking header ...\n";
@@ -469,53 +410,104 @@
my($cyclenum) = hex($cyclenuma) - 1;
if ($opt_a) {
+
+ my $pagesize = 16384;
+ my $minartoffset = int($len / (512 * 8)) + 512;
+ # Align upwards:
+ $minartoffset = ($minartoffset + $pagesize) & ~($pagesize - 1);
- # The 16384 is a fuzz factor. New articles are actually
- # written a little past free.
-
- $offset = 0;
- $minartoffset = int($len / (512 * 8)) + 512;
- $tonextblock = 16384 - ($minartoffset & (16384 - 1));
- $minartoffset += $tonextblock;
- do_seek:
- while (!($cyclenum == 0 && $free == $minartoffset)) {
- $offset += 16384;
- seek (BUFF, $cyclenum ? $free + $offset : 0, 0);
-
- $num_tries = 0;
- while (<BUFF>) {
- next unless /^message-id:\s+(<.*>)/i;
- $num_tries++;
-
- # We give up if the article is missing in history, or else
- # we stand a high risk of checking the whole cycbuff...
-
- $time = &lookup_age ($1);
- if (!$time) {
- if ($num_tries > 10) {
- $oupdate_str = "beats me";
- $oago_str = "who knows how long";
- last do_seek; # give up
- } else {
- next; # try again
- }
- }
+ if ($cyclenum == 0 && $free == $minartoffset) {
+ # The cycbuff has no articles yet.
+ goto done;
+ }
+ # Don't loop endlessly, set rough upper bound
+ my $sentinel = $cyclenum == 0 ? $free : $len;
+ my $offset = $cyclenum == 0 ? $minartoffset : $free + $pagesize;
+
+ seek (BUFF, $offset, 0) || die "seek: $!\n";
+ sysread (BUFF, $buff, $pagesize)
+ || die "read: $!\n";
+ do {
+ sysread (BUFF, $chunk, $pagesize) || die "read: $!\n";
+
+ $buff .= $chunk;
+ while ($buff =~ /^message-id:\s+(<.*?>)/mi) {
+ $buff = $POSTMATCH;
+ $oldart = &lookup_age ($1);
+ next unless $oldart;
+
# Is the article newer than the last update?
- if ($time >= $update) {
- $update = $time;
- next do_seek;
+ if ($oldart >= $update) {
+ $update = $oldart;
+ } elsif ($oldart < $update - 60) {
+ goto done;
}
-
- ($oupdate_str, $oago_str) = &make_time ($time);
- last do_seek;
}
- }
+ # Just in case we chopped Message-ID in two, use the end
+ # at the front in next iteration.
+ $buff = substr ($buff, -512);
+
+ } while ($sentinel -= $pagesize > 0);
}
-
- my ($nupdate_str, $nago_str) = &make_time ($update);
+done:
close(BUFF);
- return($name,$len,$free,$update,$cyclenum,$nupdate_str,$nago_str,$oupdate_str,$oago_str);
+ return($name,$len,$free,$update,$cyclenum,$oldart);
+}
+
+sub lookup_age {
+ my ($msgid) = @_;
+
+ my $history = &safe_run("grephistory", "-l", $msgid);
+ if ($history =~ /\t(\d+)~/) {
+ return $1;
+ }
+ print " (Missing $msgid)\n";
+ return 0;
}
+sub safe_run {
+ my $output = "";
+
+ my $pid = open(KID_TO_READ, "-|");
+ die "fork: $!\n" unless defined $pid;
+ if ($pid) {
+ while (<KID_TO_READ>) {
+ $output .= $_;
+ }
+ close(KID_TO_READ);
+ } else {
+ exec(@_) || die "can't exec $_[0]: $!";
+ # NOTREACHED
+ }
+ return $output;
+}
+
+# Hex to bigint conversion routine
+# bhex(HEXSTRING) returns BIGINT (with leading + chopped off)
+#
+# In most languages, unlimited size integers are done using string math
+# libraries usually called bigint. (Java, Perl, etc...)
+
+# Bigint's are really just strings.
+
+sub bhex {
+ my $hexValue = shift;
+ $hexValue =~ s/^0x//;
+
+ my $integerValue = new Math::BigInt '0';
+ for (my $i = 0; $i < length($hexValue); $i += 2) {
+ # Could be more efficient going at larger increments, but byte
+ # by byte is safer for the case of 9 byte values, 11 bytes, etc..
+
+ my $byte = substr($hexValue, $i, 2);
+ my $byteIntValue = hex($byte);
+
+ $integerValue = $integerValue * "256";
+ $integerValue = $integerValue + "$byteIntValue";
+ }
+
+ $integerValue =~ s/^\+//;
+ return $integerValue;
+}
More information about the inn-patches
mailing list