Scott M. Mcdermott

UNIX Systems & Network Administrator
available for contract or salaried positions

dialin.pl

#!/usr/local/bin/perl

#
# - Takes NT RAS dialin reports, outputs summary for each user
# - VPN, dialin
# - data sent, data received, connection minutes
# - overall and individual totals
#

use POSIX;      # just for strftime()

use strict;
use diagnostics;

# Interesting fields from NT RAS logs
# (what an ugly format for a machine log)
my $logregex =
        "^([^\t]+)\t" .
        "([^\t]+).*" .
        "The user CORPDOMAIN\\\\([^ ]+).*" .
        "connected on port ([^ ]+).*" .
        "on[ ]+([^ ]+).*" .
        "at ([^ ]+).*" .
        "and disconnected on[ ]+([^ ]+).*" .
        "at ([^ \.]+)\.[ ]+" .
        "The user was active for ([^ ]+) minutes[ ]+" .
        "([^ ]+) seconds\.[ ]+" .
        "([^ ]+) bytes[ ]+were sent and." .
        "([^ ]+) bytes were received\.." .
        "The port speed was ([^ \.]+)\.[ ]+" .
        "The[ ]+reason for disconnecting was ([^\.]+).*\$";

# field width of columns in the output
my $fw = 15;

# each users' totals.
my %users;

# we use this to store combined totals for all users
my %totals;

# input from RAS log
while ($_ = <>) {

        my (
                $date,
                $time,
                $user,
                $port,
                $sdate,
                $stime,
                $edate,
                $etime,
                $minutes,
                $seconds,
                $bsent,
                $breceived,
                $speed,
                $disconnect
        ) = m/$logregex/;

        my $type;

        # PPP services
        if ($port =~ /COM/) {
                $type = "dialin";

        # PPTP services
        } elsif ($port =~ /VPN/) {
                $type = "vpn";
        }

        # the connection port should always the string `COM' or `VPN', or
        # something is very wrong...
        else {
                print "impossible condition: no dialin source match, exiting";
        }

        my $totalsecs = $minutes * 60 + $seconds;

        $totals{$type}{bsent} += $bsent;
        $totals{$type}{brcvd} += $breceived;
        $totals{$type}{mins} += $totalsecs / 60;
        $users{$user}{$type}{bsent} += $bsent;
        $users{$user}{$type}{brcvd} += $breceived;
        $users{$user}{$type}{mins} += $totalsecs / 60;
}

# master header for whole report

print "=" x 80 . "\n";
print "= VPN/dialup reports, run ";
print POSIX::strftime ("%Y%m%d", localtime()) . ", ";
print "data is for previous month, days 1-end\n";
print "=" x 80 . "\n";
print "\n4 reports follow\n";

# first we display totals accumulated while parsing, before breaking down into
# individual usage reports
print_header ("1: TOTALS FOR ALL USERS");

foreach my $which_total (keys (%totals)) {

        print "$which_total:\t";

        my $mins = $totals{$which_total}{mins};
        my $hours = $mins / 60;
        my $days = $hours / 24;
        printf ("connected | %7.2fh (%5.2fd) | ", $hours, $days);

        my $mbytes = $totals{$which_total}{bsent} / 1024 / 1024;
        printf ("sent: %8.2fMB | ", $mbytes);

        $mbytes = $totals{$which_total}{brcvd} / 1024 / 1024;
        printf ("received: %8.2fMB\n", $mbytes);
}

print_header ("2: CONNECTION TIME FOR INDIVIDUALS (HOURS, < 0.00 omitted)");
print_tablehead();
print_usage_lines ("mins", 60); # stored in minutes

print_header ("3: SENT DATA FOR INDIVIDUALS (Megabytes, < 0.00 omitted)");
print_tablehead();
print_usage_lines ("bsent", 1024 * 1024); # stored in bytes

print_header ("4: RECEIVED DATA FOR INDIVIDUALS (Megabytes, < 0.00 omitted)");
print_tablehead();
print_usage_lines ("brcvd", 1024 * 1024); # stored in bytes

print "\nEND REPORT\n";

### end script

sub
print_header
{
        my $arg = pop (@_);
        print "\n\n$arg:\n\n";
        return;
}

sub
print_tablehead
{
        my $fmt = "%-$fw" . "s%$fw" . "s%$fw" . "s%$fw" . "s%$fw" . "s\n";
        printf ($fmt, "USER", "DIALIN", "VPN", "TOTAL");
        print "-" x 80;
        print "\n";
        return;
}

sub
usage_sort
{
        my $sort_regex = "([^ ]+\$)";
        my @a_value = ($a =~ /$sort_regex/g);
        my @b_value = ($b =~ /$sort_regex/g);
        $a_value[0] <=> $b_value[0];
}

sub
print_arr_as_lines
{
        while (@_) {
                print pop (@_);
                print "\n";
        }
};

sub
print_usage_lines
{
        my $getwhat = shift (@_);
        my $divideby = shift (@_);

        my @usagelines;

        foreach my $user (keys (%users)) {

                my %u;
                my $total;
                my $usage;

                foreach my $t ("dialin", "vpn") {
                        if (defined ($users{$user}{$t})) {
                                $u{$t} = $users{$user}{$t}{$getwhat};
                        } else {
                                $u{$t} = 0;
                        }
                        $total+= $u{$t};
                }
                $u{total} = $total;

                # skip outputting this line if bytes were zero
                if ($total == 0) {
                        next;
                }

                $usage .= sprintf ("%-$fw" . "s", $user);
                foreach my $t ("dialin", "vpn", "total") {
                        $usage .= sprintf ("%$fw.2f", $u{$t} / $divideby);
                }
                push (@usagelines, $usage);

        }

        print_arr_as_lines (sort usage_sort @usagelines);
}