#! /bin/perl --    # -*-Perl-*-
#
# inflow-stat - display summaries of inflow.sum data files as provided
#               by the inflow-collect script  
#
# History: The inflow-package is based on the ideas of counter.pl, a perl
#          script written by Juan Garcia, Rediris, 1995-96.
#
# 960806 V1.0 released
# 960826 V1.0.1 fixed altgroups-table length (Juan Garcia)
# 960917 V1.0.2 added debugging stuff to track down sorting problems
#               which show up on certain servers only. 
# 960918 V1.0.3 more debugging, system perl info included
# 960919 V1.0.4 sorting problem fixed (Gerhard Winkler)
# 960930 V1.0.5 fixed bug concerning output to STDOUT
# 961216 V1.0.6 fixed bug in resultfile header parsing
# 970113 V1.2   modified some default paths, renumbered version
# 970129 V1.2.1 easier portability: made system commands configurable
#               fixed unproper file closings
#
$Copy="(c) 1996 Felix.Kugler\@switch.ch";

$RELDATE = "Tue Jan 29 10:08:10 MET 1997";
$RELEASE = "V1.2.1";
#
#
# local settings ----------------------------------------------------
#
$SUMDIR    = "/home/news/inflow";        # source data (summaryfile)
$SUMFILE   = "inflow.sum";
$HTMLDIR   = "/home/news/status";        # where to put HTML output 
$TEXTDIR   = "/home/news/status";        # where to put ASCII output
$NODETABLE = "pernodetraf";	         # These names will be completed with  
$HIERTABLE = "perhiertraf";	         #  local hostname + ".html" extension.
$ALTHTABLE = "peralthtraf";	         #  Effective only if -F is selected.
#
$LOCALTZ   = "MET DST";
#
$MINLEN    = 20;		# default length of node name field
$MAXLEN    = 32;		# max length of node name field
$TABLESIZE = 20;		# default table length
#
# local HTML stuff: you can put custom header & footer information into 
#                   local files; optional...
#
$HTMLHEADER = "/home/news/config/inflowhtmlheader";
$HTMLFOOTER = "/home/news/config/inflowhtmlfooter";
# 
$PLOTSCRIPT = "inflow-plot -t"; # script to plot data
#
$GREP      = "/opt/gnu/bin/grep";
# 
# end local settings ------------------------------------------------

require "getopts.pl";

($path,$0) = ($0 =~ /^(.*)\/([^\/]+)$/);                # strip path...

$Usage="$0    -  $Copy

Release $RELEASE  of $RELDATE

Process information from inflow summary file $SUMDIR/$SUMFILE
and show articles received by node and hierarchy in either ASCII or
HTML format.

Usage: 	$0 [-dhpwAFGLNV] 
        [-a<tablelen>][-g<tablelen>][-n<tablelen>] [<sum_file>]

Parameters:

   -d:            turn on verbosity; for debugging only
   -h:            This help.
   -p:            plot collected data.
   -w:            WWW support: show results in HTML table format
   -a<tablelen>:  Length of alt hierarchy table. Default is $TABLESIZE,
                  tablelen = 0 means unlimited.
   -A:            Do not create the alt hierarchy table, even if the required
                  data is present in <sum_file>.
   -F:            Write to files defined in script header. Default is STDOUT. 
   -g<tablelen>:  Length of hierarchy (group) table. Default is $TABLESIZE,
                  tablelen = 0 means unlimited.
   -G:            Do not create hierarchy (group) table.
   -L:            Show all available hierarchy information. Make sure that
                  the required data is present in <sum_file>.
                  Default is to show toplevel hierarchies only.
   -N:            Do not create node table.
   -V:            Sort tables on traffic volume. Default is sorting based  
                  on number of successfully transmitted articles.
   -n<tablelen>:  Length of node table. Default is $TABLESIZE.
                  tablelen = 0 means unlimited.
   <sum_file>:    Read data file <sum_file>.
                  Default is $SUMDIR/$SUMFILE

Optional HTML-stuff:

header file: $HTMLHEADER
footer file: $HTMLFOOTER

\n";

#
# initialisation ----------------------------------------------------
#

umask 022;			# file mask

&Getopts('dhpwa:g:n:AFGLNV');

&gethostandfqdn;		# construct fully qualified domain name...

# process command line
$summaryfile = $#ARGV>=0 ? $ARGV[0] : "$SUMDIR/$SUMFILE";
$nodetablelen = defined($opt_n) ? $opt_n : $TABLESIZE;
$grouptablelen = defined($opt_g) ? $opt_g : $TABLESIZE;
$altgrouptablelen = defined($opt_a) ? $opt_a : $TABLESIZE;
die "silly command line options !\n\n$usage\n" if ($opt_G && $opt_N && $opt_A);

if ($opt_F) {
    if ($opt_w) {
	$nodetable = "$HTMLDIR/$NODETABLE-$hostname.html";
	$hiertable = "$HTMLDIR/$HIERTABLE-$hostname.html";
	$althtable = "$HTMLDIR/$ALTHTABLE-$hostname.html";
    } else {
	$nodetable = "$TEXTDIR/$NODETABLE-$hostname";
	$hiertable = "$TEXTDIR/$HIERTABLE-$hostname";
	$althtable = "$TEXTDIR/$ALTHTABLE-$hostname";
    }
}

if ($opt_h) { print "$Usage"; exit 0; }

## Current date. Needed for HTML headers.
&Getdate();

#  check for custom header & footer files
if (open(IN,$HTMLHEADER)) {  while (<IN>) { $htmlheader .= $_; } close(IN); }
if (open(IN,$HTMLFOOTER)) {  while (<IN>) { $htmlfooter .= $_; } close(IN); }

# check for non-empty summaryfile...
unless (-f "$summaryfile") {
    die "couldn't open $summaryfile - aborting...\n";
}

&readresults;

#
# prepare data
#
$begin = &Convdate($firstart);
$last = &Convdate($lastart);
$duration = $lastart - $firstart;
$total = $Tvolume = $Tarticles = $articles = 0;	

# per node
while (($rsite,$articles) = each(%arts)) {
    push(@persite,"$rsite:$articles:$bytes{$rsite}");
    $Tvolume += $bytes{$rsite};
    $Tarticles += $articles;
    $rsitekeylen = &getstrlength($rsitestrlen,$rsite);
}

# per hierarchy/subhierarchy
if ($opt_L && defined(%artspsh)) {
    while (($group,$articles)=each(%artspsh)) {
	push(@perhier,"$group:$articles:$bytespsh{$group}");
	$groupkeylen = &getstrlength($groupkeylen,$group);
    }
} else {
    # per toplevel hierarchy
    while (($group,$articles)=each(%artsph)) {
	push(@perhier,"$group:$articles:$bytesph{$group}");
	$groupkeylen = &getstrlength($groupkeylen,$group);
    }
}
# special table for alt.* groups
if (!$opt_A && defined(%artspah)) {   
    while (($group,$articles)=each(%artspah)) {
	push(@peralthier,"$group:$articles:$bytespah{$group}");
	$altgroupkeylen = &getstrlength($altgroupkeylen,$group);
    }
}

# produce output...

unless ($opt_F) {
    open(OUT, ">-") || die "unable to open STDOUT: $!\n";
    &initpage;
}
&printnodetable unless $opt_N;
&printgrouptable unless $opt_G;
&printaltgrouptable unless ($opt_A || ! defined(%artspah));

unless ($opt_F) {
    &closepage;
    close(OUT) || die "unable to close STDOUT: $!\n";
}

if ($opt_p && $PLOTSCRIPT ne "") { exec("$path/$PLOTSCRIPT"); }


# initpage
# -----------------------------------------------------------------------------
# Print Page Headers 
#
sub initpage {
    if ($opt_w) { 
	print OUT "<html><head><!--Content-type: text/html\nRefresh: 60-->\n\n";
#        print OUT "<title>NetNews received on $fqdn</title><!--idx--></head>
	print OUT "<title>NetNews received on $fqdn</title></head>
           <body>
           $htmlheader
           <h1>NetNews received on $fqdn</h1>
           <p><b>From:</b> $begin<br>\n<b>To:</b>   $last</p>\n";
    } else {
	print OUT "NetNews received on $fqdn\n\nFrom: $begin\nTo:   $last\n\n";
    }
}


# closepage
# -----------------------------------------------------------------------------
# close page, write footers...
#
sub closepage {
    if ($opt_w) {
	if ($htmlfooter) { print OUT "$htmlfooter\n"; } 
	else { print OUT "<hr> &copy; $cyear $fqdn \n"; }
	print OUT "</body></html>\n";
    } else { print OUT "\n\n"; }
}


# setup_table
# -----------------------------------------------------------------------
# set table header, data row format and TOTAL string
#
sub setup_table {
    local($title,$firstcol,$keylen) = @_;
    
    if ($opt_w ) {
	print OUT "<center><TABLE BORDER>
            <caption><h3>$title</h3></caption>
        <TR><TD>
            <TD align=center colspan=2><b>Articles</b>
            <td align=center colspan=3><b>Volume</b>
            <td align=right><b>avg_size</b>
        <TR><TD><b>$firstcol</b>
            <TD align=right><b>count</b>
            <td align=right><b>%</b>
            <td align=right><b>Mbytes</b>
            <td align=right><b>%</b>
            <td align=right><b>kbps</b>
            <td align=right><b>kB</b>\n";
	$out_form="<tr><b>
            <td align=left>%${keylen}s
            <td align=right>%7d
            <td align=right>%6.2f
            <td align=right>%8.3f
            <td align=right>%6.2f
            <td align=right>%6.2f
            <td align=right>%7.3f</b>";
	$diff_out_form="<tr><tr>
            <td align=left>%${keylen}s
            <td align=right>%7d
            <td align=right>%6s
            <td align=right>%8.3f
            <td align=right>%6s
            <td align=right>%6.2f
            <td align=right>%7.3f";
	$bold_out_form="<tr><tr>
            <td align=left><b>%${keylen}s</b>
            <td align=right><b>%7d</b>
            <td align=right><b>%6.2f</b>
            <td align=right><b>%8.3f</b>
            <td align=right><b>%6.2f</b>
            <td align=right><b>%6.2f</b>
            <td align=right><b>%7.3f</b>";
        $TotalStr="TOTALS";
    } else {
	$dashes = 78 - length($title);
	printf(OUT "%s %s\n\n", $title, "-" x $dashes);
	printf(OUT "%${keylen}s %16s %23s %7s %9s\n\n",
	       " ", "received_art's", "received_volume", "kbps", "avg_size");
	$out_form = $bold_out_form = $diff_out_form = 
	    "%${keylen}s %7d (%5.1f%%) %11.6f MB (%5.1f%%)  %6.2f %6.2f kB";
	$TotalStr = "TOTALS";
    }
}

# size
# --------------------------------------------------------------------------
# sort an array of strings of format "field1:field2:field3" according to 
# the (numeric) field2 or field3.
#
sub size {
	local($x1,$x2,$x3,$y1,$y2,$y3,$res);
	($x1,$x2,$x3)=split(/:/,$a);
	($y1,$y2,$y3)=split(/:/,$b);
	$res = $opt_V ? $y3<=>$x3 : $y2<=>$x2; # by volume : by no of articles
}

# Getdate
# --------------------------------------------------------------------------
# get date from localtime
#
sub Getdate {    
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);

    $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
    $day=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$wday];
    $cyear="19".$year;
    $cmin=sprintf("%02d",$min);
    $date="$day $month $mday $hour:$min:$sec $LOCALTZ $cyear";
}


# Convdate
# --------------------------------------------------------------------------
# get date & time from ctime style data string
#
sub Convdate {    
    local($ctime) = @_;
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($ctime);

    $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
    $day=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$wday];
    $cyear="19".$year;
    $cmin=sprintf("%02d",$min);
    $date=sprintf("%s %s %2d %02d:%02d:%02d $LOCALTZ %s",
	 $day, $month, $mday, $hour, $min, $sec, $cyear);
    return $date;
}



# readresults
# ----------------------------------------------------------------------
# read existing resultfile
#
sub readresults {
    local($par,$val,$startday,$thisday,$ho,$mi);
    unless (open(SUM,"$summaryfile")) {
	warn "missing $summaryfile\n";
	return;
    }
    warn "reading results from $summaryfile...\n" if $opt_d;
    $firstart = $lastart = '';
    while(<SUM>) {		#  read header
	chop;
	unless (/^\#/) { seek(SUM,0,0); last; }
	next if (/^\#\s*$/);
        ($par,$val)  = /^\# ([^:]+): +(\S*)/;
        warn "resultfile header: par=$par  val=$val\n" if $opt_d;
        if ($par eq 'host') { $fqdn = $val; }
        elsif ($par eq 'firstart') { $firstart = $val; }
        elsif ($par eq 'lastart') { $lastart = $val; }
        elsif ($par eq 'lastperiod') { $lastperiod = $val; }
        elsif ($par eq 'lastarts') { $lastarts = $val; }
        elsif ($par eq 'lastbytes') { $lastbytes = $val; }
    }
    while (<SUM>) {		# read summary data
	($par,$rsite,$cnt,$vol) = split;
	if ($par eq 'pn') { $arts{$rsite} += $cnt; $bytes{$rsite} += $vol; }
        elsif ($par eq 'ph') {
            $artsph{$rsite} += $cnt; $bytesph{$rsite} += $vol;
        }
        elsif ($par eq 'psh') {
            $artspsh{$rsite} += $cnt; $bytespsh{$rsite} += $vol;
        }
        elsif ($par eq 'pah') {
            $artspah{$rsite} += $cnt; $bytespah{$rsite} += $vol;
        }
    }
    close(SUM);
    # align command line options with read data
    $opt_L = 0 unless (defined(%artspsh));
}

# getstrlength
# ----------------------------------------------------------------------
# return actual lenght of a string field within $MINSIZE and $MAXSIZE 
# boundaries
#
sub getstrlength {
    local($oldlen,$str) = @_;
    local($newlen) = length($str);
    if ($len > $oldlen) { 
	$len = $oldlen;		# 
	if ($len > $MAXLEN) { $len = $MAXLEN; }	
    }
    if ($len < $MINLEN) { $len = $MINLEN; }	
    return $len;
}


# printnodetable
# --------------------------------------------------------------------------
# Print counters by node
sub printnodetable {
    if ($opt_F) {
	open(OUT,">$nodetable") || die "unable to open $nodetable: $!\n";
	&initpage;
    }

    $addtotitle = $opt_V ? ", sorted by volume" : "";
    $addtotitle .= $nodetablelen>0 ? " - Top $nodetablelen" : "";
    &setup_table("NetNews received by node$addtotitle","Node",$rsitekeylen);

    $i=0;

    if ($opt_d) {		# print @persite
	local($item,$x1,$x2,$x3,$sysinfo);
	$sysinfo = `uname -a`; 
	print STDERR "Debugging output from $0 on $fqdn\n\n";
	print STDERR "platform info: $sysinfo\n"; 
	print STDERR "perl info: $]";
	print STDERR "\n\@persite unsorted:\n\n";
	printf(STDERR "%8s %12s  %s\n", "articles", "Bytes", "site");
	foreach $item (@persite) {
	    ($x1,$x2,$x3)=split(/:/,$item);
	    printf(STDERR "%8d %12d  %s\n", $x2, $x3, $x1);
	}
	if ($opt_V) {
	    print STDERR "\n\@persite sorted by volume:\n\n";
	} else {
	    print STDERR "\n\@persite sorted by articles:\n\n";
	}
	printf(STDERR "%8s %12s  %s\n", "articles", "Bytes", "site");
    }

    foreach (sort size @persite) {
       	($rsite,$articles,$bytes)=split(/:/);
	if ($opt_d) {		# print @persite sorted
	    printf(STDERR "%8d %12d  %s\n", $articles, $bytes, $rsite);
	}
	$perc_bytes = $Tvolume > 0 ? $bytes/$Tvolume*100 : 0;
	$perc_art = $Tarticles > 0 ? $articles/$Tarticles*100 : 0;
	$avg_size = $articles > 0 ? $bytes/($articles*1000) : "";
	$rate = $duration > 0 ? $bytes/(125*$duration) : 0;
	printf(OUT "$out_form\n",
	       $rsite,$articles,$perc_art,$bytes/1000000,
	       $perc_bytes,$rate,$avg_size);
	last if ($nodetablelen>0 && ++$i >= $nodetablelen);
    }
    # summary line...
    $avg_size = $Tarticles > 0 ? $Tvolume/($Tarticles*1000) : "";
    $rate = $duration > 0 ? $Tvolume/(125*$duration) : 0;
    printf(OUT "\n$bold_out_form\n\n",
	   $TotalStr,$Tarticles,100,$Tvolume/1000000,100,$rate,$avg_size);

    $avg_size = $lastarts > 0 ? $lastbytes/($lastarts*1000) : "";
    $rate = $lastperiod > 0 ? $lastbytes/(125*$lastperiod) : 0;
    printf(OUT "\n$diff_out_form\n\n",
	   "Last $lastperiod seconds",$lastarts,"",$lastbytes/1000000,
	   "",$rate,$avg_size);

    if ($opt_w) { print OUT "</table></center><P><P>\n"; }
    if ($opt_F) { &closepage; close(OUT); }
}


# printgrouptable
# --------------------------------------------------------------------------
# Print counters by hierarchy
sub printgrouptable {
    if ($opt_F) {
	open(OUT,">$hiertable") || die "unable to open $hiertable: $!\n";
	&initpage;
    }

    $addtotitle = $opt_V ? ", sorted by volume" : "";
    $addtotitle .= $grouptablelen>0 ? " - Top $grouptablelen" : "";
    &setup_table("NetNews received by hierarchy$addtotitle",
		 "Hierarchy",$groupkeylen);
    
    $i=0;

    if ($opt_d) {		# print @perhier
	local($item,$x1,$x2,$x3);
	print STDERR "\n\@perhier unsorted:\n\n";
	printf(STDERR "%8s %12s  %s\n", "articles", "Bytes", "site");
	foreach $item (@perhier) {
	    ($x1,$x2,$x3)=split(/:/,$item);
	    printf(STDERR "%8d %12d  %s\n", $x2, $x3, $x1);
	}
	if ($opt_V) {
	    print STDERR "\n\@perhier sorted by volume:\n\n";
	} else {
	    print STDERR "\n\@perhier sorted by articles:\n\n";
	}
	printf(STDERR "%8s %12s  %s\n", "articles", "Bytes", "newsgroups");
    }

    foreach (sort size @perhier) {
	($group,$articles,$bytes) = split(/:/);
	if ($opt_d) {		# print @persite sorted
	    printf(STDERR "%8d %12d  %s\n", $articles, $bytes, $group);
	}
	$perc_bytes = $Tvolume > 0 ? $bytes/$Tvolume*100 : 0;
	$perc_art = $Tarticles > 0 ? $articles/$Tarticles*100 : 0;
	$avg_size = $articles > 0 ? $bytes/($articles*1000) : "";
	$rate = $duration > 0 ? $bytes/(125*$duration) : 0;
	printf(OUT "$out_form\n",$group,$articles,$perc_art,
	       $bytes/1000000,$perc_bytes,$rate,$avg_size);
	last if ($grouptablelen>0 && ++$i >= $grouptablelen);
    }
    # close table
    if ($opt_w) { print OUT "</table></center><P><P>\n"; }
    if ($opt_F) { &closepage; close(OUT); }
}


# printaltgrouptable
# --------------------------------------------------------------------------
# Print counters for alt.* hierarchy
sub printaltgrouptable {
    if ($opt_F) {
	open(OUT,">$althtable") || die "unable to open $althtable: $!\n";
	&initpage;
    }

    $addtotitle = $opt_V ? ", sorted by volume" : "";
    $addtotitle .= $altgrouptablelen>0 ? " - Top $altgrouptablelen" : "";
    &setup_table("Details for alt.* hierarchy$addtotitle",
		 "Hierarchy",$altgroupkeylen);
    
    $i=0;
    foreach (sort size @peralthier) {
	($group,$articles,$bytes) = split(/:/);
	$perc_bytes = $Tvolume > 0 ? $bytes/$Tvolume*100 : 0;
	$perc_art = $Tarticles > 0 ? $articles/$Tarticles*100 : 0;
	$avg_size = $articles > 0 ? $bytes/($articles*1000) : "";
	$rate = $duration > 0 ? $bytes/(125*$duration) : 0;
	printf(OUT "$out_form\n",$group,$articles,$perc_art,
	       $bytes/1000000,$perc_bytes,$rate,$avg_size);
	last if ($altgrouptablelen>0 && ++$i >= $altgrouptablelen);
    }
    # close table
    if ($opt_w) { print OUT "</table></center>\n"; }
    if ($opt_F) { &closepage; close(OUT); }
}


# gethostandfqdn
#---------------------------------------------------------------------- 
# construct fully qualified domain name...
sub gethostandfqdn {
    chop($str=`uname -n`);
    if ($str =~ /\./) {             # str is fqdn
	$fqdn = $str;
	($hostname) = ($str =~ /^([^.]+)\./);
    } else {                        # str is simple hostname
	$hostname = $str;
	$str = `$GREP domain /etc/resolv.conf`;
	$str =~ /domain\s*(\S+)$/;
	$fqdn = $hostname . "." . $1;
    }
}

