#!/usr/local/bin/perl
# raquick - create a quick summary of RADIUS Accounting detail file,
#		if -f flag is given preserves first login time in that file
#
# 94/11/28	Author: Carl Rigney; cdr@livingston.com
# 95/01/22	modified
# 96/09/27	Updated to work with RADIUS 2.0
# 98/07/30	Now reports bandwidth usage by user in bits per second
#
# input files = /usr/adm/radacct/*/detail
#
# output format is username followed by hours:minutes:seconds, number of uses,
# calculated charge, input bps, output bps first login seen
# dave		1:36:48	2	0	  400   4200  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
$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;
		}
	}
}

while (<>) {
	next if /Acct-Session-Id = "00000000"/;
	if (/Acct-Status-Type = Stop/) {
		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;
		}
		if (/User-Name = "([^"]+)"/) {
			$user = $1;
			if (/Acct-Session-Time = (\d+)/) {
				$elapsed = $1;
				if ($elapsed > 0) {
					$uses{$user}++;
					$used{$user} += $elapsed;
				}
			}
			if (/Acct-Input-Octets = (\d+)/) {
				$input{$user} += $1;
			}
			if (/Acct-Output-Octets = (\d+)/) {
				$output{$user} += $1;
			}
			if ($first{$user} eq '' && 
			    /^([^\n]+)\n/) {
				$first{$user} = $1;
			}
		}
		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'};

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;
	}
	if ($used{$user} > 0) {
		$in = int(8*$input{$user}/$used{$user});
		$out = int(8*$output{$user}/$used{$user});
	} else {
		$in = $out = 0;
	}
	printf "%-16s  %s %4d %6d %5d %5d %s\n",$user,&hms($used{$user}),
		$uses{$user},$charge,$in,$out,$first{$user};
}

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);
}
