#!/opt/gnu/bin/perl -w
# ---------------------------------------------------------------------------
# NAME
# countmail - greps the sendmail log file and counts messages
# to and from a particular address
#
# SYNOPSIS
# countmail [-m]
#
# OPTIONS
# -m Send report to a mailing list
# (with no options, report is *only* sent to the test recipients)
#
# NOTE
# This script is intended to run as a batch job,
# It's good for getting a daily count of mail traffic
# for a particular address. It counts mail messages sent
# to a recipient on the day before.
#
# I tried to take into consideration as many error
# conditions as possible. It only counts successfully
# sent messages. Queued attempts are not counted.
# Also, mail from the mailer-daemon (i.e., bounces)
# are also ignored.
#
# Limitation: since this script basically ignores any day
# but yesterday, it probably will not count messages which stay
# in the queue from one day to the next.
#
# $Id: countmail,v 1.7 1999/12/24 06:29:35 john Exp john $
# ---------------------------------------------------------------------------
=head1 NAME
countmail - greps the sendmail log file and counts messages
to and from a particular address
=head1 SCRIPT CATEGORIES
UNIX/System_administration
Mail
=head1 SYNOPSIS
C<countmail [-m] >
=head1 README
This script scans the current mail log and several recent
mail log archives, and searches for messages sent to or from
a particular address. It counts them up and mails a report
with an inbound count and an outbound count.
=head1 DESCRIPTION
This script scans the current mail log and several recent
mail log archives, and searches for messages sent to or from
a particular address. It counts them up and mails a report
with an inbound count and an outbound count.
This script is intended to run as a batch job,
It's good for getting a daily count of mail traffic
for a particular address. It counts mail messages sent
to a recipient on the day before.
I tried to take into consideration as many error
conditions as possible. It only counts successfully
sent messages. Queued attempts are not counted.
Also, mail from the mailer-daemon (i.e., bounces)
are also ignored.
Limitation: since this script basically ignores any day
but yesterday, it probably will not count messages which stay
in the queue from one day to the next.
=head1 PREREQUISITES
This script requres C<Date::Manip>, as well as C<MIME::Entity>.
C<MIME::Entity> requires the C<MailTools> bundle, which itself requires
C<MIME::Base64>. (If I remember correctly... they're all nifty modules,
just install all of them.)
=head1 COPYRIGHT
Copyright (c) 1998,1999 John Nolan <
[email protected]>. All rights reserved.
This program is free software. You may modify and/or distribute it
under the same terms as Perl itself. This copyright notice
must remain attached to the file.
=head1 REVISION
$Id: countmail,v 1.7 1999/12/24 06:29:35 john Exp john $
=cut
# ---------------------------------------------------------------------------
# CONFIGURATION - adjust these values for your setup
# The log files we want to examine. $maillog will be read directly,
# but the files in @maillog_archives will be gunzipped before
# they are read.
#
my $logdir = '/var/log';
my $maillog = 'maillog';
my @maillog_archives = qw( maillog.2.gz maillog.1.gz maillog.0.gz );
# The target address. Make sure that the syntax here matches
# what your versions of sendmail & syslogd actually record.
#
my $target = '
[email protected]';
my $ingrep = "to=$target";
my $outgrep = "from=<$target>";
my $sendmail = '/usr/lib/sendmail';
my $gunzip = '/usr/local/bin/gunzip';
# These should be comma-delimited lists
#
my $err_recipient = '
[email protected]';
my $ok_recipients = '
[email protected]';
# ---------------------------------------------------------------------------
# You shouldn't need to modify anything below
# this line, unless you want to hack the script itself.
# ---------------------------------------------------------------------------
require 5;
use Date::Manip;
use MIME::Entity;
use Getopt::Std;
use strict;
use vars qw( $opt_m );
getopts('m');
# ---------------------------------------------------------------------------
# Initialize some global variables
my $daemongrep = 'from=<>';
my $yesterday = &UnixDate(&DateCalc("today", "- 1 day"), "%d");
my $inbound_count = 0;
my $outbound_count = 0;
my $datestring = &UnixDate(&ParseDate("yesterday"), "%m/%d %a");
my ($day,$msg,$address,$line);
my (%was_seen_outbound);
my (%was_seen_inbound);
my (%from_daemon);
# ---------------------------------------------------------------------------
# MAIN LOGIC
# ---------------------------------------------------------------------------
# --------------------------------
# Read in the log files
foreach my $archive (@maillog_archives) {
open (LOGFILE, "$gunzip < $logdir/$archive |") or
warn "Problem piping from $logdir/$archive : $!";
process_line($_) while defined ($_ = <LOGFILE>) ;
}
open (LOGFILE, "< $logdir/$maillog") or
warn "Problem opening file $logdir/$maillog for reading: $!";
process_line($_) while defined ($_ = <LOGFILE>) ;
close(LOGFILE);
# --------------------------------
# Format counts, subject header, recipients
$inbound_count = commify($inbound_count);
$outbound_count = commify($outbound_count);
my ($subject,$recipients);
$subject = "Mail count ($target) $datestring: $inbound_count in - $outbound_count out";
if ($opt_m) {
$subject = $subject;
$recipients = $ok_recipients;
} else {
$subject = "(Test mail) $subject";
$recipients = $err_recipient;
}
# --------------------------------
# Compose mail message, Attach HTML docs to message
#
my $mesgbody = sprintf <<EOM;
This message is generated automatically each day.
Please send any questions or complaints to $err_recipient
EOM
# --------------------------------
# Create mail object, attach messages, and send it off
#
my $mimedoc = build MIME::Entity
Type => "multipart/mixed",
-From => "Mail counter <$err_recipient>",
-To => "$recipients",
-Subject => "$subject";
attach $mimedoc Data=>$mesgbody;
open MAIL, "| $sendmail -t -i"
or die "Problem piping to $sendmail: $!";
$mimedoc->print(\*MAIL);
# ---------------------------------------------------------------------------
# END of main logic
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# This function is called for each line of the log files.
# It takes into account the problem of queued messages
# (i.e., it only counts successful attempts to send a message).
#
# This function probably will not count messages that spend
# one or more midnights in the queue.
#
# Each mail message can generate 4 or more lines in a log file.
#
# Use the hashes %was_seen_outbound, %was_seen_inbound,
# and %from_daemon (which are keyed on message serial number)
# to record various facts about a message and remember them
# when we process future lines. (For example, if a message
# was sent by a daemon, then we can't count it.)
#
#
sub process_line {
chomp($line = shift);
# Grab the 2nd, 6th and 7th field of each line.
# $msg contains the message serial number.
#
($day,$msg,$address) = (split /\s+/, $line)[1,5..6];
# Skip this line unless it was written yesterday
#
return unless $day == $yesterday;
# Note any messages which were sent by a daemon
#
if ($address =~ /$daemongrep/o) {
$from_daemon{ $msg } = 1 ;
}
# Note that a message was logged as inbound
#
if ($address =~ /$ingrep/o) {
$was_seen_inbound{ $msg } = 1 ;
}
# Now we look for a line indicating that
# that this particular message was actually sent.
# If we find one, count it.
#
if ($line =~ /stat=Sent/ and defined $was_seen_inbound{ $msg } ) {
# But DON'T count it if it was sent by a daemon
#
unless( defined $from_daemon{ $msg } ) {
$inbound_count++;
} else {
undef $from_daemon{ $msg };
}
undef $was_seen_inbound{ $msg };
}
# Note that a message was logged as outbound
#
if ($address =~ /$outgrep/o) {
$was_seen_outbound{ $msg } = 1 ;
}
# Now we actually count it if we find a line indicating
# that this particular message was actually sent.
# If we find one, count it.
#
if ($line =~ /stat=Sent/ and defined $was_seen_outbound{ $msg } ) {
$outbound_count++;
undef $was_seen_outbound{ $msg };
}
}
# ---------------------------------------------------------------------------
# Regex to comma-delimit numbers (Perl FAQ 5)
#
sub commify {
local $_ = shift;
1 while s/^(-?\d+)(\d{3})/$1,$2/;
return $_;
}