#!/usr/bin/perl
#!/bin/perl -d
#=====================================================================
#
#      maillog_stats - sendmail statistics summary
#
#=====================================================================
#
#      Print a summary of email activity.  
#      Original written by Paul Vixie, DEC
#
#      Modified by Rich Bjorkund, CSC
#
#      Last modified:  Fri Jul  1 16:25:21 EDT 1994
#
#=====================================================================
#
# maillog_stats
#
#      -a       input ~postman/log/maillog.?????? 
#               (includes a soft link to /var/log/maillog)
#
#      -z       input ~postman/log/v8_sendmail/maillog.??????.gz
#
#      -s       summarizes all inputs as if from a single file
#               (will take input from ARGV or stdin)
#
# todays_stats  uses /var/log/maillog* as input
#
#=====================================================================
#
#      -t       test mode
#
#=====================================================================
#
#         SMTP "from:" lines          SMTP "to:" lines
#         mon                         mon
#         dd                          dd
#         time                        time
#         host                        host
#         client                      client
# rest    qid                 rest    qid
# [0]     from                [0]     to
# [1]     size                [1]     ctladdr (optional)
# [2]     class               [1/2]   delay
# [3]     pri                 [2/3]   mailer
# [4]     nrcpts              [3/4]   relay (optional)
# [5]     msgid (optional)    [4/3/5] stat
# [6/5]   proto (optional)
# [5/7/6] relay
#
#=====================================================================
#=====================================================================
#
# House keeping
#
#=====================================================================
# format
#
format q_top =
Syslog   Input:                   Output:             90th     Msgs  User Host
File     Msgs Kbytes AvgSz Rcips  Sent  Avg Delay  Percentile  Dferd Unkn Unkn
.
format q_line =
@<<<<<< @>>>> @>>>>> @>>>> @>>>> @>>>> @>>>>>>>>>> @>>>>>>>>>> @>>>> @>>> @>>> 
$logfn,$msgs,$kbytes,$avgsiz,$m11rcip,$sent,$avgdly,$nintyth,$dferd,$user_unknown,$host_unknown
.

# constants
#
$^          = "q_top";
$~          = "q_line";
$secperday  = 24 * 60 * 60;
$shortdelay = $secperday;
$K          = 1024;
$total_msgs = 0;

# command line arguments
#
($program = $0) =~ s%.*/%%;

while ($ARGV[0] =~ /^-/) {
    $ARGV[0] =~ s/^-//;
    foreach $flag ( split (//,$ARGV[0]) ) {
        if ( 'astz' !~ /$flag/ ) {
            printf stderr "unknown flag: %s\n", $flag;
            die "usage: $program [-astz] [maillog_file ...]\n";
        } 
        die "$0: '$flag' flag already set\n" if ($flags{$flag}++);
    } 
    shift;
}

#=====================================================================
#=====================================================================
#
# Main driver loop
#
#=====================================================================
if ($program =~ /todays_stats/o && $flags{'s'}) {
    $logdir = "/var/log";
    chdir($logdir) || die "can't chdir to $logdir: $!";
    open(stdin, "cat maillog* |") ||
        die "can't open log files";
    &mailstats();
    close(stdin);
    $logfn = "summary";
    write();

} elsif ($program =~ /todays_stats/o) {
    $logdir = "/var/log";
    chdir($logdir) || die "can't chdir to $logdir: $!";
    foreach $logfn (<maillog*>) {
        open(stdin, "cat $logfn |") ||
            die "can't open log files";
        &mailstats();
        close(stdin);
        $logfn =~ s/^.*\.//;
        write();
    }

} elsif ($flags{'s'} && $flags{'z'} && $flags{'a'}) {

    open(stdin, "(cat ./maillog.?????? ; gzcat ./maillog.??????.gz) |") ||
#    open(stdin, "(cat /posthome/postman/log/maillog.?????? ; gzcat /posthome/postman/log/v8_sendmail/maillog.??????.gz) |") ||
        die "can't open log files";
    &mailstats();
    close(stdin);
    $logfn = "summary";
    write();

} elsif ($flags{'s'} && $flags{'z'}) {
    $logdir = "/home/birch/rich/perl/maillog";
#    $logdir = "/posthome/postman/log/v8_sendmail";
    chdir($logdir) || die "can't chdir to $logdir: $!";
    open(stdin, "gzcat maillog.??????.gz |") ||
        die "can't open log files";
    &mailstats();
    close(stdin);
    $logfn = "summary";
    write();
#
} elsif ($flags{'s'} && $flags{'a'}) {
    $logdir = "/home/birch/rich/perl/maillog";
#    $logdir = "/posthome/postman/log";
    chdir($logdir) || die "can't chdir to $logdir: $!";
    open(stdin, "cat maillog.?????? |") ||
        die "can't open log files";
    &mailstats();
    close(stdin);
    $logfn = "summary";
    write();

} elsif ($flags{'z'} || $flags{'a'}) {
    if ($flags{'z'}) {
        $logdir = "/home/birch/rich/perl/maillog";
#        $logdir = "/posthome/postman/log/v8_sendmail";
        chdir($logdir) || die "can't chdir to $logdir: $!";
        foreach $logfn (<maillog.??????.gz>) {
            open(stdin, "gzcat $logfn |") ||
                die "can't open log files";
            &mailstats();
            $logfn =~ s/^maillog\.(\d+)\.gz/$1/;
            write();
        }
    }
    if ($flags{'a'}) {
        $logdir = "/home/birch/rich/perl/maillog";
#        $logdir = "/posthome/postman/log";
        chdir($logdir) || die "can't chdir to $logdir: $!";
        foreach $logfn (<maillog.??????>) {
            open(stdin, "cat $logfn |") ||
                die "can't open log files";
            &mailstats();
            $logfn =~ s/^.*\.//;
            write();
        }
    }
    close(stdin);
    
} elsif ($flags{'s'}) {
    &mailstats();
    $logfn = "summary";
    write();
#
} else {
#    print "\n";
#    print "maillog_stats\n";
#    print "\n";
#    print "	-a	input ~postman/log/maillog.?????? \n";
#    print "		(includes a soft link to /var/log/maillog)\n";
#    print "\n";
#    print "	-z	input ~postman/log/v8_sendmail/maillog.??????.gz\n";
#    print "\n";
#    print "	-s	sumerizes all inputs as if from a single file\n";
#    print "		(will take input from ARGV or stdin)\n";
#    print "\n";
#    print "	-t	test mode\n";
#    print "\n";
#    print "todays_stats	uses /var/log/maillog* as input\n";
#    print "\n";
#    print "\n";
#    print "	usage:	$program [-astz]\n";
#    print "\n";
#    print "	or:	$program [-st] [maillog_file ...]\n";
#    print "\n";
#    print "	or:	| $program [-st]\n";
#    print "\n";
#    print "	or:	todays_stats [-st]\n";
#    print "\n";
#    foreach $logfn (@ARGV) {
#        open(stdin, "cat $logfn |") ||
#            die "can't open log files";
#        &mailstats();
#        $logfn =~ s/^.*\.//;
#        write();
#    }
#
    foreach $logfn (<maillog.??????>) {
        open(stdin, "cat $logfn |") ||
            die "can't open log files";
        &mailstats();
        close(stdin);
        $logfn =~ s/^.*\.//;
        write();
    }
#    while (@ARGV) {
#        $logfn = shift @ARGV;
#        open(stdin, "cat $logfn |") ||
#            die "can't open log files";
#        &mailstats();
#        $logfn =~ s/^.*\.//;
#        write();
#    }
#    close(stdin);
} 
exit(0);

#=====================================================================
#
#  Time format function & sort by numeric sequence function
#
#=====================================================================
#
sub fmt_time {
    local($t) = @_;
    local($f) = int(($t - int($t)) * 100);
    local($s) = int($t);
    local($h) = int($s / 3600);  $s -= $h*3600;
    local($m) = int($s / 60);    $s -= $m*60;
#   local($d) = int($h / 24);    $h -= $d*24;         # handle days
    local($x) = "";
    $x = sprintf("%02d",  $f) .$x;
    $x = sprintf("%02d.", $s) .$x;
    $x = sprintf("%02d:", $m) .$x;
    $x = sprintf("%02d:", $h) .$x;
#   $x = sprintf("%01d+", $d) .$x;                    # handle days
    return $x;
}

sub bynumber { $a <=> $b; }

#=====================================================================
#=====================================================================
#
# The guts
#
#=====================================================================
sub mailstats {
    ($msgs,$bytes,$delay,$m11rcip,$sent,$dferd,$md,$d,$user_unknown,
     $host_unknown) = (0,0,0,0,0,0,0,0,0,0);
    while (<>) {
        ($mon,$dd,$time,$host,$client,$qid,@rest) = split;
        @rest=split(/, /,join(' ',@rest));
        if ($client = /sendmail\[[0-9]+\]:/) {
            if ($rest[0] =~ /^from=/ && $rest[1] =~ /^size=(\d+)/) {
                $bytes += $1;
                $msgs++;
                $total_msgs++;
                $rest[4] =~ /^nrcpts=(\d+)/;
                $m11rcip += $1;
            } elsif ($rest[0] =~ /^to=/io) {
                if ($rest[4] =~ /^stat=/io) {
                    $stat_value = $rest[4];
                } elsif ($rest[3] =~ /^stat=/io) {
                    $stat_value = $rest[3];
                } elsif ($rest[5] =~ /^stat=/io) {
                    $stat_value = $rest[5];
                } else {
                    $stat_value = "";
                }
                if ($rest[1] =~ /^delay=/io) {
                    $delay_value = $rest[1];
                } elsif ($rest[2] =~ /^delay=/io) {
                    $delay_value = $rest[2];
                } else {
                    $delay_value = "";
                }
#
                if (stat_value ne "") {
                    $stat++;
                    $delay_value =~ /^delay=([^,]+)/i;
                    $md = $1;
                    $d = 0;
                    if ($md =~ /(\d+)\+(.+)/i) {
                        $d += $md * $secperday;
                        $md =~ s/\d+\+//;
                    }
                    $md =~ /^(\d+):(\d+):(\d+)/i;
                    $d += ($1 * 3600 + $2 * 60 + $3);

                    if ($stat_value =~ /stat=Sent/io) {
                        push(@sent,$d);
                        $deferred_delays{$qid} = -1;
                        $delay += $d if ($d < $shortdelay) ||
                                        ($flags{'a'})      ||
                                        ($flags{'s'})      ||
                                        ($flags{'t'})      ||
                                        ($flags{'z'});
                        $sent++      if ($d < $shortdelay) ||
                                        ($flags{'a'})      ||
                                        ($flags{'s'})      ||
                                        ($flags{'t'})      ||
                                        ($flags{'z'});
                        next;
                    }
                    if ($stat_value =~ /stat=User unknown/io) {
                        $user_unknown++;
                        next;
                    }
                    if ($stat_value =~ /stat= Host unknown/io) {
                        $host_unknown++;
                        next;
                    }
                    if ($stat_value =~ /stat=Defer/io) {
                        $deferred_delays{$qid} = $d;
                        if (!$deferred{$qid}) {
                            $deferred{$qid}++;
                            $dferd++;
                        }
                    }
                }
            }
        }
    }
#
    if ($msgs == 0) {
        $avgdly = &fmt_time(0);
    } else {
        $avgdly = &fmt_time($delay / $sent);
    }
    $kbytes = int(0.5+$bytes/$K);
    if ($msgs == 0) {
        $avgsiz = 0;
    } else {
        $avgsiz = int(0.5+$bytes/$msgs);
    }

    while (($key,$del_val) = each(%deferred_delays)) {
        if ($del_val != -1) {
            push(@sent,$del_val);
        }
    }
    @sorted_sent = sort bynumber @sent;

    $nth_index        = 0.9 * $#sorted_sent;
    $nth_ind_int      = int($nth_index);
    if (($sorted_sent[$nth_ind_int] == $sorted_sent[$nth_ind_int + 1]) ||
        ($nth_index == $nth_ind_int)) {
        $nth_seconds  = $sorted_sent[$nth_ind_int];
    } else {
        $nth_ind_frac = $nth_index - $nth_ind_int;
        $nth_seconds  = ($nth_ind_frac * $sorted_sent[$nth_ind_int + 1])
                        + ((1 - $nth_ind_frac) * $sorted_sent[$nth_ind_int]);
    }
    $nintyth = &fmt_time($nth_seconds);

    return;
}
#=====================================================================
