#!/usr/local/bin/perl
# raquick - create a quick summary of RADIUS Accounting detail file,
#
# 94/11/28	Author: Carl Rigney; cdr@livingston.com
# 95/01/22	modified
# 96/09/27	Updated to work with RADIUS 2.0
# 97/01/12	Comments added
# 97/03/18	truncates usernames after space, same as authentication does
#
# input files = /usr/adm/radacct/*/detail
#
# output format is username followed by hours:minutes:seconds, number of uses,
# calculated charge, first login seen
# dave		1:36:48	2	0	Wed Jun  7 10:54:54 1995

# calculate charge based on minutes of usage (round down)
# charging $firstrate per minute until $breakpoint, then $secondrate

$firstrate = 0;		# charge per minute until breakpoint minutes
$secondrate = 0;	# charge per minute after breakpoint minutes
$breakpoint = 600;	# breakpoint in minutes to change charging rates

$/ = '';		# read paragraph at a time

# The -f flag is dangerous since if you give the arguments in the wrong order
# it can overwrite the detail file; therefore it is disabled in this release.
# Use at your own risk.
# if -f flag is given preserves first login time in that file
# 
# $FIRST = '';
# 
# if ($ARGV[0] eq '-f') { # this feature currently only works for single file
# 	shift;
# 	$FIRST = shift;
# 	unlink($FIRST.".old");		# back up file
# 	link($FIRST,$FIRST.".old");
# 
# 	open(FIRST);
# 
# 	while (<FIRST>) {		# initialize first login
# 		next if /^#/;
# 		chop;
# 		if (/^(\S+)\s*(.*)/) {
# 			$first{$1} = $2;
# 		}
# 	}
# }

# Read through accounting detail file
while (<>) {
#	skip messages about PortMaster going down/up
	next if /Acct-Session-Id = "00000000"/;
#	only process stop messages, since they have all the info we need
	if (/Acct-Status-Type = Stop/) {
#	All valid user session accounting records have an Acct-Session-Id.
#	Get the ID and address of the PortMaster and combine them to create
#	a unique identifier for this session, in order to check for duplicates
		if (/Acct-Session-Id = "([^"]+)"/) {
			$id = $1;
                        if (/NAS-IP-Address = (\S+)/ ||
                            /Client-Id = (\S+)/) {
                                $nas = $1;
                                $id .= '@'.$nas;
                                if ($seen{$id}++) {
                                        $dup++;
                                        next;
                                }
                        }
		} else {
			$err{'No ID'}++;
			next;
		}
#	Get the Username and elapsed time (in seconds)
#	Increase the number of logins and total time used, for this user
		if (/User-Name = "([^"]+)"/) {
			$user = $1;
			$user =~ s/\s.*//;	# truncate name on space
			if (/Acct-Session-Time = (\d+)/) {
				$elapsed = $1;
				if ($elapsed > 0) {
					$uses{$user}++;
					$used{$user} += $elapsed;
				}
			}
#			if ($first{$user} eq '' && 
#			    /^([^\n]+)\n/) {
#				$first{$user} = $1;
#			}
		}
#	Record the number of uses and time used for the port, too.
#	Currently this doesn't get printed, but could be
		if (/NAS-IP-Address = (\S+)/ ||
		    /Client-Id = (\S+)/) {
                        $nas = $1;
                        if (/NAS-Port = (\d+)/ ||
                            /Client-Port-Id = (\d+)/) {
                                $port = $1;
                                $nasport = sprintf("%s\t%2d",$nas,$port);
                                if (/Acct-Session-Time = (\d+)/) {
                                        $elapsed = $1;
                                        if ($elapsed > 0) {
                                                $npuses{$nasport}++;
                                                $npused{$nasport} += $elapsed;
                                        }
                                }
                        }
                }

	}
}

# if ($FIRST ne '') {
# 	open(FIRST,">$FIRST") || warn "$0: unable to write to $FIRST; $!\n";
# 	for $user (sort keys %first) {
# 		printf FIRST "%-16s\t%s\n",$user,$first{$user};
# 	}
# 
# }
# close($FIRST);

print "# $dup duplicates\n" if $dup;
print "# $err{'No ID'} stop records without Acct-Session-ID\n" if $err{'No Id'};

# print usage by user
for $user (sort keys %used) {
	# calculate charge based on minutes of usage (round down)
	# charging $firstrate per minute until $breakpoint, then $secondrate
	$m = int($used{$user}/60);
	if ($m <= $breakpoint) {
		$charge = $m * $firstrate;
	} else {
		$charge = $breakpoint * $firstrate + ($m-$breakpoint) * $secondrate;
	}
	printf "%-16s\t%s  %4d  %6d  %s\n",$user,&hms($used{$user}),
		$uses{$user},$charge,$first{$user};
}

#
# Subroutine
#

# hms($seconds) returns time in hh:mm:ss format
sub hms {
	local($h,$m);
	local ($s) = shift(@_);
	$m = int($s / 60);
	$s = $s % 60; 
	$h = int($m / 60);
	$m = $m % 60;
	sprintf("%4d:%02d:%02d",$h,$m,$s);
}
