#!/usr/local/bin/perl -w
#
# Date: Mon, 23 Feb 1998 14:27:55 -0600 (CST)
# From: Chris Adams <cadams@ro.com>
#
########################################################################
# pmstat.pl
#
# Chris Adams <cadams@ro.com>
# http://ro.com/~cadams/files/pmstat.pl
#
########################################################################
# This program may be distributed under the terms of either the GNU
# General Public License or the Artistic License, as specified in the
# Perl README file.
#
########################################################################
#
# READ ALL OF THIS TO MAKE THIS PROGRAM WORK!!
# --------------------------------------------
#
# Requires the Perl SNMP module version 1.7 or higher (see
# http://www.perl.com/CPAN/modules/by-module/SNMP/) and UCD SNMP
# (ftp://ftp.ece.ucdavis.edu:/pub/snmp/).
#
# Queries one (or more) PortMasters via SNMP to get connection information.
# Usage:
#   pmstat.pl [-f] [-u <username>] <host> [<host> ...]
#       -f  run a little faster (no header or hostname lookups)
#       -u  only show info for logins of this username
#
# You can specify as many PortMaster hostnames as you want.
#
# Configuration:
# --------------
# Change $MAXNAMELEN to the longest username you will be showing.
# Longer usernames take away from hostname display.
#
# Change $MYDOMAIN below if you don't want to see your own domain name
# in hostname lookups.
#
# Change $LIV_MIB to point to the Livingston MIB file.
#
########################################################################
# VERSION HISTORY - you don't have to read this
#
# Version 1.0 - 12 Dec 1997
# - initial version
#
# Version 1.1 - 27 Jan 1998
# - modified to use SNMP 1.7 and did some general cleanup
#
# Version 1.1.1 - 30 Jan 1998
# - some more cleanup
#
# Version 1.2 - 13 Feb 1998
# - rearranged and reorganized the main loop to speed things up a bit
#
# Version 1.2.1 - 23 Feb 1998
# - put more better documentation in
#

use integer;
use SNMP 1.7;
use Socket;
use Getopt::Std;

########################################################################
# User settable variables
#
$MAXNAMELEN = 15;
#$MYDOMAIN = "ro.com";
$MYDOMAIN = "";
$LIV_MIB = "/usr/local/share/snmp/mibs/livingston.mib";

########################################################################
# Parse the options
#
$Getopt::Std::opt_u = "";
$Getopt::Std::opt_f = 0;
getopts ('fu:');
$user = $Getopt::Std::opt_u;
$fastrun = $Getopt::Std::opt_f;

########################################################################
# Figure the column sizes
#
$hostlen = 29 - $MAXNAMELEN;
$MAXNAMELEN *= -1;
$hostlen *= -1;

########################################################################
# Print the header and quote the metacharacters in the domain name
#
if (! $fastrun) {
    printf "%${MAXNAMELEN}s %${hostlen}s %11s %5s %-11s %-10s %-5s\n", "",
"",
            "Connect  ", "Idle", "   Speed", "Protocol/", " Reneg/";
    printf "%${MAXNAMELEN}s %${hostlen}s %11s %5s %-11s %-10s %-5s\n",
            "Username", "Hostname", "Time/Date ", "Time", "   In/Out  ",
            "Compress", " Retrn";
    print "---------------------------------------------------------------",
            "----------------";
    print "\n";

    # Escape domain name
    $MYDOMAIN =~ s/^([^\.])/.$1/;
    $MYDOMAIN = quotemeta ($MYDOMAIN);
}

########################################################################
# A few defines and the SNMP initialization
#
@comps = ("-", "None", "v42bis", "MNP5", "STAC");
@protos = ("-", "None", "LAPM", "MNP");

$SNMP::use_long_names = 0;
SNMP::initMib ();
SNMP::addMibFiles ($LIV_MIB);

########################################################################
# For each host on the command line, get the stats.
#
while ($ARGV[0])    {
    pmstat ($ARGV[0], $user, $MYDOMAIN, $MAXNAMELEN, $hostlen);
    shift @ARGV;
}

########################################################################
# The main function
#
sub pmstat  {
    my ($pm, $muser, $mydomain, $namelen, $hostlen) = @_;
    my ($sess, $vars, $sess_err);
    my ($started, $idletime, $protocol);
    my ($stat, $user, $hostname, $start, $idle, $type, $in, $out, $comp);
    my ($proto, $reneg, $retr, $con_info);

	####################################################################
	# Open a session and initialize the variable list
	#
    $sess = new SNMP::Session (DestHost => $pm, Community => 'public')
            or die "can't open SNMP session: $!";
    $sess_err = \$sess->{ErrorStr};

    $vars = new SNMP::VarList (['livingstonSerialUser',1],
        ['livingstonSerialIpAddress',1],
        ['livingstonSerialPortStatus',1],
        ['livingstonSerialStarted',1],
        ['livingstonSerialIdle',1],
        ['livingstonSerialInSpeed',1],
        ['livingstonSerialOutSpeed',1],
        ['livingstonSerialModemCompression',1],
        ['livingstonSerialModemProtocol',1],
        ['livingstonSerialModemRenegotiates',1],
        ['livingstonSerialModemRetrains',1],
        ['livingstonSerialPhysType',1]
    );

	####################################################################
	# Get the first set of vars
	#
    ($user, $ip, $stat, $started, $idletime, $in, $out, $compress,
            $protocol, $reneg, $retr, $type) = $sess->getnext ($vars);

	####################################################################
	# As long as there is not an error, process the vars and get more
	#
    while ($$sess_err eq "")    {
		################################################################
		# Skip this entry if there is not a user logged in or if we are
		# looking for a particular user and this is not the right one
		#
        next if ($stat != 3);
        next if (($muser ne "") && ($user ne $muser));

		################################################################
		# Convert the info for printing
		#
        $hostname = host_lookup ($ip, $hostlen, $mydomain);
        $start = convert_date ($started);
        $idle = convert_time ($idletime);
        $comp = $comps[$compress];
        $proto = $protos[$protocol];

		################################################################
		# Print everything out
		#
        if ($type != 5) {
            $con_info = sprintf "   %5s     %6s", $in, $comp;
        }
        else    {
            if ((($out % 1000) == 0) && ($out > 31200)) {
                $out /= 1000;
                $out .= "K";
            }
            $con_info = sprintf "%5s/%-5s %4s/%-6s %2s/%-2s", $in, $out,
                    $proto, $comp, $reneg, $retr;
        }

        printf "%${MAXNAMELEN}s %${hostlen}s %11s %5s %s\n", $user,
$hostname,
                $start, $idle, $con_info;

    }
	####################################################################
	# Continue block - get the next set of variables
    continue    {
        ($user, $ip, $stat, $started, $idletime, $in, $out, $compress,
                $protocol, $reneg, $retr, $type)
                = $sess->getnext ($vars);
    }
}

########################################################################
# convert_time - Take the number of seconds and make it into a short
# readable field
#
sub convert_time    {
    my ($time) = @_;
    my ($seconds, $minutes, $hours, $days);

    $seconds = $time / 100;
    $minutes = $seconds / 60;
    $hours = $minutes / 60;
    $days = $hours / 24;
    $seconds %= 60;
    $minutes %=60;
    $hours %=24;

    if ($minutes == 0)  {
        return ($seconds);
    }
    elsif ($hours == 0) {
        return (sprintf "%d:%02d", $minutes, $seconds);
    }
    elsif ($hours < 10) {
        return (sprintf "%dh:%02d", $hours, $minutes);
    }
    elsif ($days == 0)  {
        return (sprintf "%dh", $hours);
    }
    elsif ($days < 10)  {
        return (sprintf "%dd:%02d", $days, $hours);
    }
    else    {
        return (sprintf "%dd", $days);
    }
}

########################################################################
# convert_date - Print the time in a nice format
#
sub convert_date    {
    my ($time) = @_;
    my ($start, $sec, $min, $hour, $mday, $mon);

    $start = time () - $time / 100;
    ($sec, $min, $hour, $mday, $mon) = localtime ($start);
    return (sprintf "%2d/%-2d %2d:%02d", $mon + 1, $mday, $hour, $min);
}

########################################################################
# host_lookup - Lookup a hostname, either in the builtin cache or from
# DNS (and then cache it)
#
sub host_lookup {
    my ($addr, $hostlen, $mydomain) = @_;
    my ($name, $ret);

	####################################################################
	# Initialize the cache to empty except for a telnet login
	#
	BEGIN	{
		%host_hash = ();
		$host_hash{0} = "(telnet)";
	}

    if (defined ($host_hash{$addr}))    {
        $ret = $host_hash{$addr};
    }
    else    {
        $name = gethostbyaddr (inet_aton ($addr), AF_INET) if (! $fastrun);
        if ($name)  {
            $ret = $name;
        }
        else    {
            $ret = $addr;
        }
        $host_hash{$addr} = $ret;
    }

    $ret =~ s/$mydomain$//i;
    $ret = substr ($ret, 0, (-1 * $hostlen));
    return ($ret);
}
