#!/usr/bin/perl ## # smlogstats.pl - sendmail logfile analyzer ## # # Ver 1.1 by Ted George (ted@kcnet.com) # Oct 24, 2001 # # smlogstats.pl analyzes sendmail 8.12 logfile and produces html output # # # usage: smlogstats.pl [ -d ] [ -l logfile ] [ -t n ] # # -d print detail # -l full path name to logfile (default /var/log/maillog) # -t print top n addresses (default 10) # require 'getopts.pl'; &Getopts('dl:t:'); # location of the html output file here $output = '/home/www/usmil/html/sendmail/stats.html'; # pattern to match for message id. this works for 8.12 $pattern = '\[\d+\]: (\w{14,14}): '; # location of date command $date = '/bin/date'; $printdetail = $opt_d || 0; $top = $opt_t || 10; $logfile = $opt_l || "/var/log/maillog"; $report_date = `$date`; &main(); sub main { &read_logfile(); open(OUTPUT, "> $output") or die "Can't open $output: $!\n"; &print_header(); &summary(); if ($printdetail) { &count_mail(); $title = "Top $top senders by number of recipients sent to"; &pr_detail($title, "Address", $cnt_sent_to, \%rcpts_to_addr) if $cnt_sent_to; $title = "Top $top senders summarized by domain"; &pr_detail($title, "Domain", $cnt_sent_to, \%rcpts_to_domain) if $cnt_sent_to; $title = "Top $top relays by number of recipients sent to"; &pr_detail($title, "Relay_IP", $cnt_sent_to, \%rcpts_to_ip) if $cnt_sent_to; $title = "Top $top senders to local unknown users"; &pr_detail($title, "Address", $cnt_unknown, \%rcpts_to_unknown) if $cnt_unknown; $title = "Top $top senders triggering SMTP RCPT flood throttle"; &pr_detail($title, "Address", $flooding, \%rcpt_flood) if $flooding; } print OUTPUT ""; close(OUTPUT); } sub print_header { print OUTPUT "\n"; print OUTPUT "Sendmail Logfile Stats\n"; print OUTPUT "\n"; print OUTPUT "
\n"; print OUTPUT "\n"; } sub read_logfile { local($id); open(INPUT, "$logfile") or die "Can't open $logfile: $!\n"; while () { $entries++; $datestr = substr($_, 0, 15); $date_from = $datestr unless $date_from; $rejecting++ if /rejecting connections on daemon MTA/; $deferring++ if /deferring connections on daemon MTA/; $delaying++ if /delaying connections on daemon MTA/; $rulesetreject++ if /: ruleset=check_/; $id = $1 if /$pattern/; next unless $id; &proc_to($_, $id) if /: to=\S+, /; &proc_from($_, $id) if /: from=\S+, /; if (/Possible SMTP RCPT flood/) { $flooding++; $flooding{$id}++; } if (/\.\.\. User unknown$/) { $sent_to{$id}++; $cnt_sent_to++; $cnt_unknown++; $sent_to_unknown{$id}++; } } close(INPUT); } sub proc_to { local($_) = shift(); local($id) = shift(); $sent_to{$id}++; $cnt_sent_to++; if (/, stat=Sent/) { $to_internal++ if / mailer=local, /; $to_external++ if / mailer=esmtp, /; # site specific information, probably don't need this } elsif (/, mailer=local, .*, stat=User unknown/) { $to_unknown++ unless /: to=bounce,/; } else { $to_failed++; } } sub proc_from { local($_) = shift(); local($id) = shift(); /: from=(\S+), /; $from{$id} = &fix_angles($1); /, relay=.*\[([^\]]+)/; $from_relay{$id} = $1; $cnt_from++; } sub summary { local(@_); print OUTPUT "\n"; } sub pr_detail { local($title) = shift(); local($heading) = shift(); local($count) = shift(); local($hashref) = @_[$[]; local(@table); print OUTPUT "\n"; } sub count_mail { local($id); while ($id = each %from) { $rcpts_to_addr{$from{$id}} += $sent_to{$id}; $rcpts_to_unknown{$from{$id}} += $sent_to_unknown{$id}; $rcpt_flood{$from{$id}} += $flooding{$id}; $domain = $1 if $from{$id} =~ /\@([^\>]+)/; $rcpts_to_domain{$domain} += $sent_to{$id}; } while ($id = each %from_relay) { $rcpts_to_ip{$from_relay{$id}} += $sent_to{$id}; } } sub fix_angles { local($_) = shift(); s/^/\/ unless /$\>/; $_; } sub commas { local($_) = shift() || 0; 1 while s/(.*\d)(\d\d\d)/$1,$2/; $_; } sub fixhtml { local($_) = shift(); s/ /_/g; s/\n"; foreach (@_) { @row = split(); print OUTPUT "\n"; for ($i = 0; $i < $cols; $i++) { @row[$i] =~ s/_/ /g; print OUTPUT "@row[$i]" if substr($align, $i, 1) eq 'l'; print OUTPUT " align=\"right\">@row[$i]" if substr($align, $i, 1) eq 'r'; print OUTPUT " align=\"center\">@row[$i]" if substr($align, $i, 1) eq 'c'; print OUTPUT "" if $firstrow and $headings; print OUTPUT "" unless $firstrow and $headings; } $firstrow = 0; print OUTPUT "\n"; } print OUTPUT "

Sendmail Logfile Stats
"; print OUTPUT "for $date_from - $datestr

"; print OUTPUT "

Report generated by smlogstats.pl on $report_date"; print OUTPUT "


Logfile Summary

"; $_ = &commas($entries); push(@_, "Total_logfile_entries_processed $_"); $_ = &commas($cnt_from); push(@_, "Total_envelopes_sent $_"); $_ = &commas($cnt_sent_to); push(@_, "Total_recipients_sent_to $_"); $_ = &commas($to_internal); push(@_, "Internal_deliveries $_"); $_ = &commas($to_external); push(@_, "External_deliveries $_"); $_ = &commas($cnt_unknown); push(@_, "Sent_to_local_unknown_users $_"); $_ = &commas($to_unknown); push(@_, "Procmail_filter_rejections $_") if $to_unknown; $_ = &commas($to_failed); push(@_, "Other_failed_deliveries $_"); $_ = &commas($rejecting); push(@_, "Throttling_-_rejecting_connections_on_daemon_MTA $_") if $rejecting; $_ = &commas($deferring); push(@_, "Throttling_-_deferring_connections_on_daemon_MTA $_") if $deferring; $_ = &commas($delaying); push(@_, "Throttling_-_delaying_connections_on_daemon_MTA $_") if $delaying; $_ = &commas($flooding); push(@_, "Throttling_-_possible_SMTP_RCPT_flood $_") if $flooding; $_ = &commas($rulesetreject); push(@_, "Rejections_based_on_check_*_rulesets $_") if $rulesetreject; print_table(1, 80, 0, 2, 'lr', @_); print OUTPUT "

$title

"; push(@table, "$heading Count %Total"); sub hashvalues { $hashref->{$b} <=> $hashref->{$a}; } $i = 0; foreach $key (sort hashvalues (keys(%$hashref))) { $i++; $percent = sprintf("%6.2f%s", $hashref->{$key} / $count * 100, "%"); $skey = fixhtml($key); $_ = &commas($hashref->{$key}); push(@table, "$skey $_ $percent"); last if $i >= $top; } print_table(1, 80, 1, 3, 'lrr', @table); print OUTPUT "

\n"; }