dem@meaddata.com (David Myers) (03/11/91)
Here's a perl script I cooked up to do accounting from sendmail
logs.
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# syslog.perl
#
if test -f 'syslog.perl'
then
echo shar: will not over-write existing file "'syslog.perl'"
else
echo x - 'syslog.perl'
sed 's/^X//' >'syslog.perl' << 'SHAR_EOF'
X#!/usr/local/bin/perl
X#
X# Extracts mail accounting information from sendmail logs.
X#
X# usage: <whatever you call this> <sendmail log file>
X#
X# The idea here is to look at each message logged by sendmail, and to
X# try to determine which local user is responsible for it. That user
X# is then "charged" for it. Only sucessfully delivered mail is
X# accounted for.
X#
X# If your sendmail produces logging information in a different
X# format than that of the sendmail shipped with SunOS 4.[01], you're
X# SOL.
X#
X# The basic algorithm is to first look at the sender, and if the
X# address is local, charge to that user. If not, look at the
X# recipient, and if local, charge. If neither the sender nor
X# recipient are local, charge to the fictional user "external".
X#
X# The actual routines that try to determine whether or not a user is
X# local are very simplistic, and likely not to be accurate for every
X# situation. Also, you might want to change the field widths of the
X# printed fields to be more appropriate for your site. I run this
X# script once a week, and for a small- to medium-sized UUCP site like
X# us, the output is just what I want.
X#
X# Like many, I'm a perl novice, and the Camel book has not made it to
X# the local bookstore yet. So I'm sure this isn't the most efficient
X# way to do things. But since this is run in the early morning hours
X# from cron, what do I care how slow it is?
X#
X# "Just another perl hacker" (where have I heard that before? :-)
X# dem@meaddata.com (David Myers)
X
X
X# The local domain name - CHANGE THIS.
X$domain = "meaddata.com";
X
X# The name of the sendmail log - CHANGE THE DEFAULT.
X$syslog = (shift || "/var/log/syslog");
X
X
X# OK, boys - lets do it.
X
X# Open sesame.
Xopen(syslog) || die "Just where is this file $syslog?\n";
X
X# Read in all user names for use in the report.
Xwhile (($login, $p, $u, $g, $q, $c, $name, $h, $s) = getpwent) {
X $name{$login} = $name;
X}
Xendpwent;
X
X# Read in all host names and *assume they are all local*.
X# This is a big leap of faith, but it works for me.
Xwhile (($host, @misc) = gethostent) {
X $localhost{$host} = 1;
X}
Xendhostent;
X
X# Scan the sendmail log.
Xwhile (<syslog>) {
X ($1, $2, $3, $4, $5, $id, $tofrom, $size, $stat) = split;
X chop ($id, $tofrom, $size);
X if ($tofrom =~ /from=/) { # Found a sender.
X $from{$id} = substr($tofrom, 5);
X $size{$id} = substr($size, 5);
X }
X elsif (($tofrom) =~ /to=/ && ($stat =~ /Sent/)) { # Found a recipient.
X if ($from{$id}) {
X
X if ($user = &islocal($from{$id})) {}
X elsif ($user = &islocal(substr($tofrom, 3))) {}
X else {
X $user = "external";
X }
X
X $mesgs{$user}++;
X $bytes{$user} += $size{$id};
X
X $mesgstotal++;
X $bytestotal += $size{$id};
X }
X }
X}
Xclose(syslog);
X
X# Sort and print in one shot.
Xforeach $user (sort compare keys %bytes) {
X $bytespercent = $bytes{$user} / $bytestotal * 100;
X $mesgspercent = $mesgs{$user} / $mesgstotal * 100;
X write;
X}
X
X$~ = "total";
Xwrite;
X
X
X# Done. Subroutines follow.
X
X# Compares the total bytes attributed to two users.
Xsub compare {
X $bytes{$b} - $bytes{$a};
X}
X
X# Extracts the user component of an address.
X# I suppose this and the next subroutine could have been implemented
X# with regexps. Would that have been a better idea?
Xsub extractuser {
X local($addr) = @_;
X $addr = substr($addr, rindex($addr, '!') + 1);
X if (($len = index($addr , '@')) > 0) {
X $addr = substr($addr , 0, $len);
X }
X $addr;
X}
X
X# Extracts the host component of an address.
Xsub extracthost {
X local($addr) = @_;
X $addr = substr($addr, rindex($addr, '@') + 1);
X if (($len = index($addr , '!')) > 0) {
X $addr = substr($addr , 0, $len);
X }
X $addr;
X}
X
X# Returns a "billable" user name if the address is local.
Xsub islocal {
X local($_) = @_;
X local($host, $user);
X s/[<>]//g;
X s/%/@/g;
X $host = &extracthost($_);
X $user = &extractuser($_);
X
X # If the host is not in our host table, and it's not in our domain,
X # and extracthost didn't simply return the user name, then the
X # host is not local.
X if (!($localhost{$host}) && !($host =~ $domain) && !($host eq $user)) {
X $user = 0;
X }
X $user;
X}
X
X
X
Xformat =
X@>>>>>>>> @##.##% @>>>> @##.##% @<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<
X$bytes{$user}, $bytespercent, $mesgs{$user}, $mesgspercent, $user, $name{$user}
X.
X
Xformat total =
X -------- ------- ----- ------- ------------- -------------------------
X@>>>>>>>> @##.##% @>>>> @##.##% @<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<
X$bytestotal, 100, $mesgstotal, 100, "TOTAL", ""
X.
Xformat top =
X
X
X
X
X Bytes Messages
X Total Percent Total Percent Login Full Name
X -------- ------- ----- ------- ------------- -------------------------
X.
SHAR_EOF
if test 4732 -ne "`wc -c < 'syslog.perl'`"
then
echo shar: error transmitting "'syslog.perl'" '(should have been 4732 characters)'
fi
fi
echo Done
exit 0
--
David Myers (513) 865-1343
Mead Data Central Friends don't Interactive Systems
P.O. Box 933 let friends dem@meaddata.com
Dayton, Ohio 45401 use DOS. ...!uunet!meaddata!dem