#!/usr/bin/perl -Tw
#
#  LMS version 1.11.7 Bastet
#
#  Copyright (C) 2001-2009 LMS Developers
#
#  Please, see the doc/AUTHORS for more information about authors!
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License Version 2 as
#  published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
#  USA.
#
#  $Id: lms-arping,v 1.22 2009/01/13 07:45:27 alec Exp $

use strict;
use DBI;
use Config::IniFiles;
use Getopt::Long;
use vars qw($configfile $quiet $help $version $iplist);

$ENV{'PATH'}='/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin';

my $_version = '1.11.7 Bastet';

my %options = (
	"--config-file|C=s"  	=>	\$configfile,
        "--quiet|q"          	=>     	\$quiet,
        "--help|h"		=>     	\$help,
        "--version|v"          	=>     	\$version,
	"--iplist|L=s"		=>	\$iplist
);

Getopt::Long::config("no_ignore_case");
GetOptions(%options);

sub mask2prefix($)
{
    my $mask = shift @_;
    my @tmp = split('\.',$mask,4);
    my $q = sprintf("%b%b%b%b",$tmp[0],$tmp[1],$tmp[2],$tmp[3]);
    $q =~ s/0*$//;
    if ($q =~ /0/) {
            print " You idiot. error in mask\n";
    }
    my $len = length($q) ;
    return $len;
}

if($help)
{
     print STDERR <<EOF;
lms-arping, version $_version
(C) 2001-2009 LMS Developers

-C, --config-file=/etc/lms/lms.ini     alternate config file (default: /etc/lms/lms.ini);
-h, --help			print this help and exit;
-v, --version               	print version info and exit;
-q, --quiet               	suppress any output, except errors;
-L, --iplist=/some/file		file that contains ip addresses of online computers; useful for external pingers ;-)
EOF
     exit 0;
}

if($version)
{
     print STDERR <<EOF;
lms-arping, version $_version
(C) 2001-2009 LMS Developers

EOF
     exit 0;
}

if(!$configfile)
{
     $configfile = "/etc/lms/lms.ini";
}

if(!$quiet)
{
     print STDOUT "lms-arping, version $_version\n";
     print STDOUT "(C) 2001-2009 LMS Developers\n";
     print STDOUT "Using file $configfile as config.\n";
}

if(! -r $configfile)
{
     print STDERR "Fatal error: Unable to read configuration file $configfile, exiting.\n";
     exit 1;
}

my $ini = new Config::IniFiles -file => $configfile;
print @Config::IniFiles::errors;

my $networks_list = $ini->val('arping','networks') || '';
my $excluded_networks_list = $ini->val('arping','excluded_networks') || '';
my $arping = $ini->val('arping','arping_binary') || '/sbin/arping';

my $dbtype = $ini->val('database', 'type') || 'mysql';
my $dbhost = $ini->val('database', 'host') || 'localhost';
my $dbuser = $ini->val('database', 'user') || 'root';
my $dbpasswd = $ini->val('database', 'password') || '';
my $dbname = $ini->val('database', 'database') || 'lms';

my $dbase;
my $utsfmt;

if($dbtype =~ /mysql/)
{
    $dbase = DBI->connect("DBI:mysql:database=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
    $utsfmt = "UNIX_TIMESTAMP()";
}
elsif($dbtype eq "postgres")
{
    $dbase = DBI->connect("DBI:Pg:dbname=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
    $utsfmt = "EXTRACT(EPOCH FROM CURRENT_TIMESTAMP(0))";
}
else
{
    print STDERR "Fatal error: unsupported database type: $dbtype, exiting.\n";
    exit 1;
}

if(!$networks_list)
{
    my $dbq = $dbase->prepare("SELECT name FROM networks");
    $dbq->execute();
    while (my $row = $dbq->fetchrow_hashref()) 
    {
	$networks_list = "$networks_list $row->{'name'}";
    }
    $dbq->finish();
}

my @networks = split ' ', $networks_list;

if($excluded_networks_list)
{
    my @excluded_networks = split ' ', $excluded_networks_list;
    foreach my $excluded_network (@excluded_networks)
    {
	@networks = grep(!/^$excluded_network$/, @networks);
    }
}

my @lines;

if ($iplist)
{
	if (! -r $iplist)
	{
		print STDERR "Fata error: Unable to read $iplist file, exiting.\n";
		exit 1;
	}
	open(IPLISTFILE, $iplist);
	@lines = <IPLISTFILE>;
	close(IPLISTFILE);
	foreach my $line (@lines)
	{
    		$line =~ s/^\s+//;
	        $line =~ s/\s+$//;

    		$dbase->do("UPDATE nodes SET lastonline=$utsfmt WHERE ipaddr=INET_ATON('$line')");
    		
		print "$line\n" if not $quiet;
	}
}
else
{
	foreach my $network (@networks)
	{
		my $dbq = $dbase->prepare("SELECT name, interface, INET_NTOA(address) AS address, mask FROM networks WHERE UPPER(name) = UPPER('$network')");
		$dbq->execute();

		if(my $row = $dbq->fetchrow_hashref())
		{
			my $netsize = 2**(32 - mask2prefix($row->{'mask'}));

			my $iface = $row->{'interface'} ? "-I $row->{'interface'}" : "";

			my $ndbq = $dbase->prepare("SELECT INET_NTOA(ipaddr) AS ipaddr, id FROM nodes WHERE ipaddr>INET_ATON('$row->{'address'}') AND ipaddr<INET_ATON('$row->{'address'}')+$netsize");
		        $ndbq->execute();

			while(my $nodrow = $ndbq->fetchrow_hashref())
		        {
				system("$arping -c1 -q $iface $nodrow->{'ipaddr'}");
				if("$?" == "0") 
				{ 
					$dbase->do("UPDATE nodes SET lastonline = $utsfmt WHERE id = $nodrow->{'id'}");
				
					print "$nodrow->{'ipaddr'}\n" if not $quiet;
				}
			}
			$ndbq->finish();
		}
		$dbq->finish();
	}
}

$dbase->disconnect();
