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