#!/usr/local/bin/perl -w

#######################################################################
# Processus d'interrogation de l'ensemble des equipements Osiris
# - Interrogation SNMP + generation de graphes rrdtools
# - Mise a jour de la base d'association Postgres pour les AP WiFi
# - Mise a jour des fichiers d'etat du spanning tree pour l'ensemble
#   des commutateurs du parc Osiris
#
# Ce processus travaille avec des sondes qui interrogent les
# equipements.
#
# parametres :
# option :  indique si le processus travaille sur l'ensemble des sondes, sur
#           une liste de type de sonde executer ou a ignorer
#
# Les 2 parametres suivants determinent la maniere dont les processus
# vont se partager l'execution des sondes
# num_process   : donne le numero du process
# nb_process    : donne le nombre total de processus

use strict;
use warnings;
use Sys::Syslog;                          # Misses setlogsock.
use Sys::Syslog qw(:DEFAULT setlogsock);  # Also gets setlogsock
use Net::SNMP;
use SNMP_util;
use Socket;
use RRDs;
use DBI;
use POSIX;

our $group = $ARGV[0];
our $num_process = $ARGV[1];
our $nb_process = $ARGV[2];

require "/usr/local/lib/netmagis/libmetro.pl";

# lecture du fichier de configuration general
our $conf_file = "/usr/local/etc/netmagis.conf";
our %global_conf = read_global_conf_file($conf_file);

# check parameters, if one of them emty, set it to default value
check_params();

our %config = ();

$config{dir_lock}		= $global_conf{metrodatadir} . "/lock" ;
$config{defaultdomain} 		= $global_conf{defaultdomain};
$config{syslog_facility} 	= $global_conf{"pollerlogfacility"};
$config{path_probes} 		= "/usr/local/lib/netmagis/probes";
$config{path_poll} 		= $global_conf{metrodatadir} . "/poller";
$config{path_rrd_db} 		= $global_conf{metrodatadir} . "/db";
$config{path_cache_probes} 	= $global_conf{metrodatadir} . "/cache";
$config{dir_plugins}		= "/usr/local/lib/netmagis/plugins";
$config{dir_report}		= $global_conf{metrodatadir} . "/report";
# recuperation du groupe de pollers
$config{options} 		= $global_conf{"gpopt_$group"};
# arguments de connexion a la base PSQL
$config{PGHOST} 		= $global_conf{macdbhost};
$config{PGDATABASE} 		= $global_conf{macdbname};
$config{PGUSER} 		= $global_conf{macdbuser};
$config{PGPASSWORD} 		= $global_conf{macdbpassword};
$config{fichier_etat} 		= $global_conf{metrodatadir} . "/wifi/ap_state.txt";

# timeout snmp
$config{snmp_timeout} 		= $global_conf{snmptimeout};

# liste des sondes
our %function_probes = (
		'ifNom-ap'		=> \&ifNom_ap,
                'ifNom-snmp64'		=> \&ifNom_counter64,
		'ifNom-broadcast64'	=> \&ifNom_broadcast64,
		'broadcast'     	=> \&ifNom_broadcast64,
		'ifNom-multicast64'	=> \&ifNom_multicast64,
		'multicast'     	=> \&ifNom_multicast64,
		'ifNom_error'		=> \&ifNom_error,
                'ifNom-snmp32'		=> \&ifNom_counter32,
                'ifIP-snmp'		=> \&get_if_by_ip,
		'get_value_generic'	=> \&get_generic,
		'plugin'		=> \&get_plugins,
		'ipmac'			=> \&get_arp_table,
		'portmac.cisco'		=> \&get_portmac_cisco,
		'portmac.hp'		=> \&get_portmac_hp,
		'portmac.juniper'	=> \&get_portmac_juniper
);

# load all probes
require $config{path_probes} . "/sensor-if-snmp-ap.pl";
require $config{path_probes} . "/sensor-if-snmp64.pl";
require $config{path_probes} . "/sensor-if-snmp32.pl";
require $config{path_probes} . "/sensor-if-by-ip.pl";
require $config{path_probes} . "/sensor-generic-gauge.pl";
require $config{path_probes} . "/sensor-plugins.pl";
require $config{path_probes} . "/sensor-if-broadcast.pl";
require $config{path_probes} . "/sensor-if-multicast.pl";
require $config{path_probes} . "/sensor-if-error.pl";
require $config{path_probes} . "/sensor-arp-table.pl";
require $config{path_probes} . "/sensor-portmac.pl";

our $lock_cache_file = 0;
our $maj_cache_file = 0;

our %collsess;
our %liste_ap;
our %liste_ap_state;
our %ApSnmpHashref = ();
our %APSupSSID = ();
our @total_activesess = ();
our @total_authsess = ();
our %mac_auth;

our %ok_sondes = ();
our %nok_sondes = ();

init_test_sondes();

# recuperation de la date et heure de lancement du script
our %time = get_time("NOW");

# lectures des options passees en parametres
$ok_sondes{'all'} = 1;

my @liste_options = split(/,/,$config{'options'});
foreach my $elem (@liste_options)
{
    if($elem=~m/^!(.*)/)
    {
	$nok_sondes{$1} = 1;
    }
    else
    {
	$ok_sondes{$elem} = 1;
	$ok_sondes{'all'} = 0;
    }
}

# multiprocessus calcul du resultat du modulo pour matcher une sonde
our $modulo_match = $num_process % $nb_process;

# Creation d'une socket syslog unix
setlogsock("unix");

# Message des logs demarrage du dmon ####################
writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
	"\t -> START. Demarrage du polling ########");


##################################################################
# gestion specifique pour les AP WiFi
# recuperation de la liste des authentifies sur le WiFi
if($ok_sondes{'assoc_ap'} == 1 && check_periodicity("*/5 * * * *"))
{
    writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
        "\t -> INFO : Recuperation des authentifies du reseau sans fil ...");

    get_authaccess();
}

#################################################################
# Traitement des sondes
#
opendir(MAJDIR, $config{'path_poll'});
our @FICHIERS=grep(/(\.sensor|\.plugin)$/, readdir MAJDIR);
closedir(MAJDIR);

# lecture des fichiers en cache
our %idxcache;

if(open(CACHE,"$config{path_cache_probes}/idx.cache"))
{
	while(my $l=<CACHE>)
	{
		chomp $l;
		if($l =~ /(.+);(.+);(.+)/)
		{
			$idxcache{$1}{$2} = $3;
		}
	}
	close(CACHE);
}
else
{
	writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
        	"\t -> ERROR : fichier de cache : $!");
}

read_tab_asso(%idxcache);


my $elem;
my $compteur = 0;
foreach $elem (@FICHIERS)
{
	$compteur += ouvre_fichier_conf($elem);
}

writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
        "\t -> $compteur sondes");

Net::SNMP->snmp_dispatcher();

# ecriture des fichiers qui stockent les index snmp des interfaces
maj_if_files();

########

##################################################################
# gestion specifique pour les AP WiFi
if($ok_sondes{'assoc_ap'} == 1 && check_periodicity("*/5 * * * *"))
{
    ###################################
    # cration du tableau d'tat des AP
    writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
        "\t -> Ecriture du fichier de supervision des AP");

    maj_liste_ap_state();

    ################################################################
    ## mise  jour de la base des associations WiFi
    writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
        "\t -> MAJ de la base des associations WIFI");

    set_assoc_ap_base();
}
########


writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
                "\t -> STOP. Fin du polling ###############");
########



##################################################################
# FONCTIONS
##################################################################

##################################################################
# mise  jour des fichiers contenant les index SNMP des interfaces
# des quipements connus
sub maj_if_files
{
	my $i;
	my @fichier;

	if($maj_cache_file == 1)
	{
		if(check_lock_file($config{'dir_lock'},"metropollercache.lock","metropoller") == 0)
		{
        		# on locke tant que l'appli tourne
        		create_lock_file($config{'dir_lock'},"metropollercache.lock","metropoller");

			writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
                        "\t -> INFO : Mise a jour du fichier de cache");

			open(BD,">$config{path_cache_probes}/idx.cache");

			foreach my $key (keys %idxcache)
        		{
                		foreach my $kkey (keys %{$idxcache{$key}})
                		{
					my $string = "$key;$kkey;$idxcache{$key}{$kkey}";
					if($string =~ /(.+);(.+);(.+)/)
					{
						print BD "$string\n";
					}
                		}
        		}

			close(BD);

			# suppression du verrou
        		delete_lock_file("$config{'dir_lock'}/metropollercache.lock");
        	}
		else
		{
			writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
                                "\t WARNING fichier de cache locke");
		}
	}
}


###############################################################
# lecture des fichiers contenant les ordres de polling
sub ouvre_fichier_conf
{
	my ($file) = @_;
	my ($sonde,$base,$host,$community,$l_param,$periodicity);
	my $ok = 0;
	my $num_ligne = 0;

	open(FILE, "$config{'path_poll'}/$file");

	#print LOG "fichier : $config{'path_poll'}/$file\n";

	while(<FILE>)
	{
		if(! /^#/ && ! /^\s+/)
		{
		    chomp;
		    # detection d'un parametre de periodicite facon cron pour savoir si la sonde doit etre executee
		    if(/(.*);(.*)/)
		    {
			$periodicity = $1;
			($sonde,$base,$host,$community,$l_param) = (split(/\s+/,$2))[0,1,2,3,4];
		    }
		    # aucun parametre de periodicite, la sonde est execute par defaut
		    else
		    {
			$periodicity = "*/5 * * * *";
		     	($sonde,$base,$host,$community,$l_param) = (split(/\s+/,$_))[0,1,2,3,4];
		    }

		    $num_ligne ++;

        #print LOG "ligne : $periodicity;$sonde,$base,$host,$community,$l_param\n";

		    # multiprocessus : ce processus doit-il traiter cette ligne?
		    my $test_modulo = $num_ligne % $nb_process;

		    if($test_modulo == $modulo_match)
		    {
			if(check_periodicity($periodicity))
			{
				$ok ++;
				# nettoyage du nom de la sonde
				$sonde = clean_probe($sonde);

				# parametres a appliquer dans l'appel de chaque fonction
				# $base,$host,$community,$l_param,$sonde
				if(defined($function_probes{$sonde}))
				{
			    		if(($ok_sondes{'all'} == 1 || $ok_sondes{$sonde} == 1) && $nok_sondes{$sonde} != 1)
			    		{
						$function_probes{$sonde}->($base,$host,$community,$l_param,$sonde,$periodicity);
			    		}
			    		else
			    		{
						$ok --;
			    		}
				}
				else
				{
			    		if(($ok_sondes{'all'} == 1 || $ok_sondes{$sonde} == 1) && $nok_sondes{$sonde} != 1)
                            		{
						writelog("poller_$group$num_process",$config{'syslog_facility'},"info",
				    			"\t -> WARNING: Sonde $sonde inexistante");
						# aucune sonde trouvee, n'a rien fait
						$ok --;
			    		}
				}
			}
		    }
		}
	}
	close(FILE);

	return $ok;
}


######################################
# Nettoie le nom des sondes dans update_rrd qui disposent encore
# du chemin /local/obj999 ....
sub clean_probe
{
    my ($sonde) = @_;

    my @decomp_rep = split(/\//,$sonde);
    my $t_decomp_rep = @decomp_rep;
    ($sonde) = (split(/\.pl/,$decomp_rep[$t_decomp_rep - 1]))[0];

    return $sonde;
}


##########################################################
# fonction de mise  jour du fichier de supervision des AP
sub maj_liste_ap_state
{
    my $key;

    open(STATE,">$config{'fichier_etat'}");
    foreach $key (keys %liste_ap_state)
    {
	    print STATE "$key=$liste_ap_state{$key}\n";
    }
    close(STATE);
}


############################################################
# initialisation des booleens qui indiquent au programme
# les types de sondes a executer
sub init_test_sondes
{
    foreach my $key (keys %function_probes)
    {
        $ok_sondes{$key} = 0;
        $nok_sondes{$key} = 0;
    }
}

############################################################
# cree un tableau nominatif avec les index sql des tous les
# ssid du reseau wifi
sub init_list_ssid
{
    my ($l) = @_;

    my %l_ssid;

    chomp $l;
    my @ll = split(/,/,$l);

    foreach my $s (@ll)
    {
        $l_ssid{$s} = read_conf_file("$conf_file","ID_SQL_$s");
    }

    return %l_ssid;
}

############################################################
# controle le parametre de periodicite du lancement d'une sonde
# exemple :
#   */5 * * * * : toutes les 5 minutes
#   * */1 * * * : toute les heures
#   sinon renseigner a la maniere de cron :
#           minute hour    mday    month   wday (1 = lundi)
# exemple : *      *       *       *       1 : tous les lundi
#
# renvoie 1 si la sonde doit etre lancee
sub check_periodicity
{
        my($periodicity) = @_;

	my($mwday,$mmonth,$mmday);
        #print LOG "$periodicity, time = $time{WDAY}, $time{MON}, $time{MDAY}, $time{HOUR}:$time{MIN}\n";

	my($min,$hour,$mday,$month,$wday) = (split(/\s+/,$periodicity))[0,1,2,3,4];

        if($wday eq "*" || $wday !~/[1-7]/)
        {
		$mwday = $wday;
                $wday = "0,1,2,3,4,5,6";
        }
        if($month eq "*" || $month !~/[1-12]/)
        {
		$mmonth = $month;
                $month = "1,2,3,4,5,6,7,8,9,10,11,12";
        }
        if($mday eq "*" || $mday !~/[1-31]/)
        {
		$mmday = $mday;
                $mday = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31";
        }
        if($min eq "*")
        {
		if($mwday =~/[0-6]/ || $mmonth =~/[1-12]/ || $mmday =~/[1-31]/)
		{
			$min = 0;
		}
		else
		{
                	$min = "0-59";
		}
        }
        elsif($min =~/\*\/([0-9]+)/)
        {
                my $temp_min ="";

                for(my $i=0;$i<60;$i++)
                {
                        my $modulo = $i % $1;
                        if($modulo == 0)
                        {
                                $temp_min = "$temp_min" . "$i,";
                        }
                }
                $min = $temp_min;
        }
        if($hour eq "*")
        {
		if($mwday =~/[0-6]/ || $mmonth =~/[1-12]/ || $mmday =~/[1-31]/)
                {
                        $hour = 0;
                }
                else
                {
                        $hour = "0-23";
                }
        }
        elsif($hour  =~/\*\/([0-9]+)/)
        {
                my $temp = "";

                for(my $i=0;$i<23;$i++)
                {
                        my $modulo = $i % $1;
                        if($modulo == 0)
                        {
                                $temp = "$temp" . "$i,";
                        }
                }
                $hour = $temp;
        }


        my $etap_check = 0;

        my @l_wday = split(/,/,$wday);
        for (my $i=0;$i<@l_wday;$i++)
        {
        #	print LOG "WDAY : $l_wday[$i] == $time{WDAY}";

		if($l_wday[$i] == $time{WDAY})
                {
                # on est dans le bon jour de la semaine pour executer la sonde
                        $i = @l_wday;
                        $etap_check =1;
                }
        }
        if($etap_check == 1)
        {
                #print LOG "=> OK\n";

		my @l_month = split(/,/,$month);
                for (my $i=0;$i<@l_month;$i++)
                {
	#	 	print LOG "MON : $l_month[$i] == $time{MON}";

                        if($l_month[$i] == $time{MON})
                        {
                        # on est dans le bon mois pour executer la sonde
                                $i = @l_month;
                                $etap_check =2;
                        }
                }
        }
        if($etap_check == 2)
        {
		#print LOG "=> OK\n";

                my @l_mday = split(/,/,$mday);
                for (my $i=0;$i<@l_mday;$i++)
                {
			#print LOG "MDAY : $l_mday[$i] == $time{MDAY}";

                        if($l_mday[$i] == $time{MDAY})
                        {
                        # on est dans le bon mois pour executer la sonde
                                $i = @l_mday;
                                $etap_check =3;
                        }
                }
        }
        if($etap_check == 3)
        {
		#print LOG "=> OK\n";

		if($hour eq "0-23")
		{
			$etap_check =4;

			#print LOG "HOUR : $hour == $time{HOUR}";
		}
		else
		{
               	 	my @l_hour = split(/,/,$hour);
                	for (my $i=0;$i<@l_hour;$i++)
                	{
		#	print LOG "HOUR : $l_hour[$i] == $time{HOUR}";

                        	if($l_hour[$i] == $time{HOUR})
                        	{
                        	# on est dans le bon mois pour executer la sonde
                                	$i = @l_hour;
                                	$etap_check =4;
                        	}
                	}
		}
        }
        if($etap_check == 4)
        {
		if($min eq "0-59")
                {
			return 1;

			#print LOG "=> OK\n";
                }
                else
                {
                	my @l_min= split(/,/,$min);
                	for (my $i=0;$i<@l_min;$i++)
                	{
                        	if($l_min[$i] == $time{MIN})
                        	{
                        	# on est dans le bon mois pour executer la sonde
                                	$i = @l_min;
                                	return 1;

					#print LOG "=> OK\n";
                        	}
                	}
        	}
	}
        return 0;
}


# check parameters
sub check_params
{
	if(not defined $group)
	{
		$group = "default";
		print "WARNING : process group not defined, set to default\n";
	}
	if(defined $num_process and defined $nb_process and $num_process > $nb_process)
	{
		print "ERROR : $num_process > $nb_process. Exit.\n";
		usage();
		exit();
	}
	if(not defined $num_process)
	{
		print "WARNING : process number not defined, set to 1, set number of processes to 1\n";
		usage();
		$num_process = 1;
		$nb_process = 1;
	}
	if(not defined $nb_process)
	{
		print "WARNING : number of processes not defined, set to 1, set process number to 1\n";
		usage();
		$num_process = 1;
                $nb_process = 1;
	}
}

# print usage
sub usage
{
	print "Metropoller
	###
	./metropoller <group> <process number> <total number of processes>

	- group : group of sensors. Default value 'default',
	- process number : process number between 1 and <total number of processes>,
	- total number of processes : total number of processes.
	\n";
}
