#! /usr/bin/perl -w
################################################################################
# Copyright 2005-2011 MERETHIS
# Centreon is developped by : Julien Mathis and Romain Le Merlus under
# GPL Licence 2.0.
# 
# This program is free software; you can redistribute it and/or modify it under 
# the terms of the GNU General Public License as published by the Free Software 
# Foundation ; either version 2 of the License.
# 
# 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, see <http://www.gnu.org/licenses>.
# 
# Linking this program statically or dynamically with other modules is making a 
# combined work based on this program. Thus, the terms and conditions of the GNU 
# General Public License cover the whole combination.
# 
# As a special exception, the copyright holders of this program give MERETHIS 
# permission to link this program with independent modules to produce an executable, 
# regardless of the license terms of these independent modules, and to copy and 
# distribute the resulting executable under terms of MERETHIS choice, provided that 
# MERETHIS also meet, for each linked independent module, the terms  and conditions 
# of the license of that module. An independent module is a module which is not 
# derived from this program. If you modify this program, you may extend this 
# exception to your version of the program, but you are not obliged to do so. If you
# do not wish to do so, delete this exception statement from your version.
# 
# For more information : contact@centreon.com
# 
# SVN : $URL$
# SVN : $Id$
#
####################################################################################
#
# Script init
#

use strict;
use warnings;
use DBI;
use POSIX ":sys_wait_h";
use Fcntl qw(:flock SEEK_END); 
 
use RRDs;
use File::Copy;

# Init Globals
use vars qw($debug $LOG %status $generalcounter @rrd_dst);
use vars qw($mysql_user $mysql_passwd $mysql_host $mysql_database_oreon $mysql_database_ods $mysql_database_ndo);
use vars qw($con_oreon $con_ods);

# Init value
my ($file, $line, @line_tab, @data_service, $hostname, $service_desc, $metric_id, $configuration);

# Init Proc table
my %tabProc;
$tabProc{'parser'} = 0;
$tabProc{'synchro'} = 0;
$tabProc{'rebuild'} = 0;
my $centstorage_fh;

my $timeout_stop = 50;

my %tabProcChld;

# Init status tab
%status = ('OK' => '0', 'WARNING' => '1', 'CRITICAL' => '2', 'UNKNOWN' => '3', 'PENDING' => '4');

# Init RRD:DST -- Data Source Type
@rrd_dst = ("GAUGE","COUNTER","DERIVE","ABSOLUTE");

$debug = 0;
our $stop = 1;
my $error_value;
my $error_str;

my $LOG = "/DBA/centreon/2.4.5/log/centstorage.log";

my $installedPath = "/DBA/centreon/2.4.5/";

# Include Configuration Data
require "/DBA/centreon/2.4.5/etc/conf.pm";

#
# Debug Function 
# Create log line in $LOG file.
#
sub writeLogFile($){
    my ($log) = @_;

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());
    open (LOG, ">> ".$LOG) || print "can't write $LOG: $!";
    printf LOG "%04d-%02d-%02d %02d:%02d:%02d - %s\n", $year+1900, $mon+1, $mday, $hour, $min, $sec, $log;
    close LOG or warn $!;
}

sub init_config {
    my $file = $_[0];
    my $type = $_[1];

    unless (my $return = do $file) {
        writeLogFile("couldn't parse $file: $@") if $@;
        writeLogFile("couldn't do $file: $!") unless defined $return;
        writeLogFile("couldn't run $file") unless $return;
        if ($type == 1) {
            writeLogFile("Quit program");
            exit(1);
        }
    }
}

sub catch_zap_int {
    writeLogFile("$$ Receiving order to stop...");
    
    if ($tabProc{'parser'}) {
        writeLogFile("Send INT signal to parser sub process...");
        kill('INT', $tabProc{'parser'});
    }
    
    if ($tabProc{'rebuild'}) {
        writeLogFile("Send INT signal to rebuild sub process...");
        kill('INT', $tabProc{'rebuild'});
    }
}

sub catch_zap_term {
    $stop = 0;

    writeLogFile("$$ Receiving order to stop...");
    if ($tabProc{'parser'}) {
        writeLogFile("Send TERM signal to parser sub process...");
        kill('TERM', $tabProc{'parser'});	
    }

    if ($tabProc{'rebuild'}) {
        writeLogFile("Send TERM signal to rebuild sub process...");
        kill('TERM', $tabProc{'rebuild'});
    }
}

sub catch_reload {
    writeLogFile("Receiving order to reload...");
    init_config("/DBA/centreon/2.4.5/etc/conf.pm", 0);
    open $centstorage_fh, '>>', $LOG;
    open STDOUT, '>&', $centstorage_fh;
    open STDERR, '>&', $centstorage_fh;
}

# Set signals
$SIG{INT}   = \&catch_zap;
$SIG{TERM}  = \&catch_zap_term;
$SIG{HUP}   = \&catch_reload;

init_config("/DBA/centreon/2.4.5/etc/conf.pm", 1);

# require different files
require $installedPath."lib/misc.pm";
require $installedPath."lib/purge.pm";
require $installedPath."lib/getObjectInformations.pm";
require $installedPath."lib/identifyService.pm";
require $installedPath."lib/verifyHostServiceIdName.pm";
require $installedPath."lib/identifyMetric.pm";
require $installedPath."lib/updateFunctions.pm";

#########################################
# Database connection management
#########################################
sub CheckMySQLConnexion() {
    while ((!defined($con_oreon) || !$con_oreon->ping()) && (!defined($con_ods) || !$con_ods->ping())){
        if (!defined($con_oreon) || !$con_oreon->ping()) {
            $con_oreon = DBI->connect("DBI:mysql:database=".$mysql_database_oreon.";host=".$mysql_host, $mysql_user, $mysql_passwd, {'RaiseError' => 0, 'PrintError' => 0, 'AutoCommit' => 1});
            if (!defined($con_oreon)) {
                writeLogFile("Error when connecting to database : " . $DBI::errstr . "");
                sleep(2);
            }
        } else {
            sleep(2);
            undef($con_oreon);
            $con_oreon = DBI->connect("DBI:mysql:database=".$mysql_database_oreon.";host=".$mysql_host, $mysql_user, $mysql_passwd, {'RaiseError' => 0, 'PrintError' => 0, 'AutoCommit' => 1});
        }
        if (!defined($con_ods) || !$con_ods->ping()) {
            $con_ods = DBI->connect("DBI:mysql:database=".$mysql_database_ods.";host=".$mysql_host, $mysql_user, $mysql_passwd, {'RaiseError' => 0, 'PrintError' => 0, 'AutoCommit' => 1});
            if (!defined($con_ods)) {
                writeLogFile("Error when connecting to database : " . $DBI::errstr . "");
                sleep(2);
            }
        } else {
            sleep(2);
            undef($con_ods);
            $con_ods = DBI->connect("DBI:mysql:database=".$mysql_database_ods.";host=".$mysql_host, $mysql_user, $mysql_passwd, {'RaiseError' => 0, 'PrintError' => 0, 'AutoCommit' => 1});
        }
    }
}

sub CreateConnexionForCentstorage() {
   while (!defined($con_ods) || !$con_ods->ping()) {
        if (!defined($con_ods) || !$con_ods->ping()) {
            $con_ods = DBI->connect("DBI:mysql:database=".$mysql_database_ods.";host=".$mysql_host, $mysql_user, $mysql_passwd, {'RaiseError' => 0, 'PrintError' => 0, 'AutoCommit' => 1});
            if (!defined($con_ods)) {
                writeLogFile("Error when connecting to database : ".$DBI::errstr."");
                sleep(2);
            }
        }
    }
}

sub CreateConnexionForOreon() {
    while (!defined($con_oreon) || !$con_oreon->ping()){
        if (!defined($con_oreon) || !$con_oreon->ping()) {
            $con_oreon = DBI->connect("DBI:mysql:database=".$mysql_database_oreon.";host=".$mysql_host, $mysql_user, $mysql_passwd, {'RaiseError' => 0, 'PrintError' => 0, 'AutoCommit' => 1});
            if (!defined($con_oreon)) {
                writeLogFile("Error when connecting to database : ".$DBI::errstr."");
                sleep(2);
            }
        }
    }
}

sub error_thrown($$) {
    $error_value = shift;
    $error_str = shift;
    return undef;
}

########################################
# return perfdata file path
########################################

sub getPerfDataFile() {
    my ($filename, $sth2, $data, $con);
    
    $sth2 = $con_oreon->prepare("SELECT `nagios_perfdata` FROM `nagios_server` WHERE `localhost` = '1'");
    if (!$sth2->execute()){
	writeLogFile("Error when getting perfdata file : " . $sth2->errstr . "");
	return "";
    }
    $data = $sth2->fetchrow_hashref();
    $filename = $data->{'nagios_perfdata'};
    undef($sth2);
    undef($data);
    return $filename;
}

########################################
# Move perfdata file to tmp file 
########################################

sub copyTruncatePerfDataFile($$) {
    my $PERFDATA = $_[0];
    my $flag_bckp = $_[1];
    my $PERFDATA_src = $PERFDATA;
    
    if ($flag_bckp == 1) {
        $PERFDATA_src = $PERFDATA . ".bckp";
        if (move($PERFDATA_src, $PERFDATA."_read")) {
            return 1;
        }
        writeLogFile("Cannot copy $PERFDATA file : $!");
        return 0;
    }
    if (!open(FD_SRC, "+<" . $PERFDATA_src)) {
    	writeLogFile("Cannot read/write the $PERFDATA file : $!");
    	undef($PERFDATA);
    	return(0);
    }
    if (!open(FD_DEST, ">" . $PERFDATA . "_read")) {
    	close(FD_SRC);
    	writeLogFile("Cannot write the " . $PERFDATA . "_read file : $!");
    	undef($PERFDATA);
    	return(0);
    }
    while (<FD_SRC>) {
    	if (/\n$/) {
	    print FD_DEST $_;
	}
    }
    close(FD_DEST);
    truncate(FD_SRC, 0);
    close(FD_SRC);
    return(1);
}

########################################
# Get ODS config data  
########################################

# Get Centstorage Configuration
sub getConfig($){
    my ($sth2, $data, $con);
    $con = $_[0];
    
    # Get information in config table of centstorage
    $sth2 = $con->prepare("SELECT auto_drop,drop_file,perfdata_file FROM config");
    if (!$sth2->execute) {
	writeLogFile("Error when getting drop and perfdata properties : ".$sth2->errstr."");
    }
    $data = $sth2->fetchrow_hashref();	
    undef($sth2);
    undef($con);
    return($data);
}

sub prepardPerfdataParsed($) {
    my $str = $_[0];
    my @line_tab;
    
    @line_tab = split('\t', $str);
    undef($str);

    return @line_tab;
}

sub getGeneralConfig(){   
    my $sth1 = $con_ods->prepare("SELECT * FROM `config`");
    if (!$sth1->execute) {
	writeLogFile("Error:" . $sth1->errstr . "\n");
    }
    my $config = $sth1->fetchrow_hashref();
    $sth1->finish();
    return $config;
}

sub GetPerfData() {
    # Init Var
    my ($line_tab, $sth2, $data, $flag_drop, $sleeptime, $configOptions, $now, $flag_bckp);
    use vars qw($con_oreon $con_ods %host %service $dataBinInfo $counterData);

    # Init Buffer var
    $counterData = 0;
    $flag_bckp = 0;
    $dataBinInfo = "";
    $now = time() + 86400;

    # Connect MySQL DB
    CheckMySQLConnexion();

    # Get path of perfdata file
    my $PFDT = getPerfDataFile();

    if (!defined($PFDT)) {
        writeLogFile("Could not find perf data file!");
    } 
    if ((defined($PFDT)) && (-r $PFDT || -r $PFDT.".bckp")) {
        # Check backup perfdata file 
        if (-r $PFDT.".bckp") {
            $flag_bckp = 1;
        }

        # Move perfdata File befor reading		
        if (copyTruncatePerfDataFile($PFDT, $flag_bckp) && open(PFDT, "< $PFDT"."_read")) {
            # Get Properties
            $configOptions = getGeneralConfig();

            $flag_drop = 1;
            if ($configOptions->{'auto_drop'} == 1 && defined($configOptions->{'drop_file'})){
                if (!open(DROP, ">> ".$configOptions->{'drop_file'})){
                    $flag_drop = 0;
                    writeLogFile("can't write in ".$configOptions->{'drop_file'}." : $!");
                }
            } else {
                $flag_drop = 0;
            }
            undef($data);

            # Read File
            while (<PFDT>) {
                if (!m/^\#.*/ && !m/^\[.*/) {
                    if ($stop == 0) {
                        if ($flag_drop != 2) {
			    if (open(DROP, ">> ".$PFDT.".bckp")){
				writeLogFile("Save reading data into ".$PFDT.".bckp");
				$flag_drop = 2;
			    } else {
				writeLogFile("Cannot save reading data into ".$PFDT.".bckp : $!");
			    }
                        }
                        print DROP $_;
                    } else {
                        if ($flag_drop == 1) {
                            print DROP $_  ;
                        }

                        $_ =~ s/\n//g;
			my $save_line = $_;
                        @line_tab = prepardPerfdataParsed($_);

                        if ($line_tab[0] !~ m/^[0-9]*$/g || $line_tab[0] > $now) {
                            writeLogFile("Error Timestamp... Unknown format : " . $line_tab[0]);
                        } else {
                            if (defined($line_tab[5]) && ($line_tab[5] ne '' && $line_tab[5] ne "" && $line_tab[0] < $now)) {
                                if ($#line_tab != 5) {
                                    writeLogFile("Line not well formed: " . $_);
                                    next;
                                }
                                if (!defined(checkAndUpdate(@line_tab, $configOptions))) {
				    if ($error_value == 1 || $error_value == 2) {
					# Error in "identify_*_service" functions. We save data
					# For 2, not succeed rrd or data_bin construct. We have to save
					if (open(DROP, ">> ".$PFDT.".bckp")){
					    writeLogFile("Save reading data into ".$PFDT.".bckp");
					    print DROP $save_line; 
					    close DROP;
					} else {
					    writeLogFile("Cannot save reading data into ".$PFDT.".bckp : $!");
					}
				    }
				    writeLogFile("error_value = " . $error_value . ": " . $error_str);
				} else {
				    $counterData++;
				}
                            }
                            undef($line_tab);
                        }

                        # flush buffer
                        if ($counterData >= 400) {
                            if ($dataBinInfo ne "") {
                                my $sth1 = $con_ods->prepare("INSERT INTO `data_bin` (`id_metric`, `ctime`, `value`, `status`) VALUES $dataBinInfo");
                                if (!$sth1->execute()) {
                                    writeLogFile("Error:" . $sth1->errstr . "");
                                }
                                undef($sth1);
                                $dataBinInfo = "";
                                $counterData = 0;
                            }
                        }
                    }
                }
            }

            # Close read file
            if (!close(PFDT)) {
                writeLogFile("Cannot close filehandle PFDT : ".$!);
            }

            # Insert last Data
            if ($dataBinInfo ne "") {
                my $sth1 = $con_ods->prepare("INSERT INTO `data_bin` (`id_metric`, `ctime`, `value`, `status`) VALUES $dataBinInfo");
                if (!$sth1->execute) {
                    writeLogFile("Error:" . $sth1->errstr . "");
                }
                undef($sth1);
                $dataBinInfo = "";
            }

            # Remove Read File
            if (-r $PFDT."_read" && !unlink($PFDT."_read")) {
                writeLogFile("Error When removing service-perfdata file : ".$!);
            }

            # Flush buffer
            if ($flag_drop > 0) {
                if (!close(DROP)) {
                    writeLogFile("Cannot close filehandle DROP : ".$!);
                }
            }	
            undef($line_tab);
            undef($flag_drop);
        } else {
            writeLogFile("Error When writing data in tmp read file : ".$!);
        }
    }
    undef($dataBinInfo);
    undef($counterData);
    $con_oreon->disconnect();
    $con_ods->disconnect();
} 

########################################
# Check if nagios restart and if we 
# must to check configuration and 
# launch purge process  
# -> Thread
########################################

sub CheckRestart(){
    my ($last_restart_stt, $last_restart, $sth2, $data, $y);
    use vars qw($con_oreon $con_ods);

    $last_restart = getLastRestart();
    $last_restart_stt = getLastRestartInMemory();
    if (!$last_restart_stt || $last_restart ne $last_restart_stt){
	check_HostServiceID();
	if (getPurgeConfig()){
	    CheckMySQLDrain();
	    deleteStatusDatabase();
	    deleteMetricsDatabase();
	}
	saveLastRestartInMemory($last_restart);
    }
}

sub checkAndUpdate($$){
    my $config = $_[6];

    if ($_[5]) {
	if ($_[1] =~ /_Module_[a-zA-Z]*/){
	    # Get Service infos
	    @data_service = identify_hidden_service($_[1], $_[2]); # return index_id and storage
	    return undef if (!@data_service);	    

	    if (defined($data_service[0]) && $data_service[0] ne 0) {
		# perfdata index status time type counter rebuild
		my $status = identify_hidden_metric($_[5], $data_service[0], $_[4], $_[0], $config->{'storage_type'}, $data_service[2], $config); 
	        return undef if (!defined($status));
	    }
	} else {
	    # Get Service infos
	    # writeLogFile(" --> ".$_[1]." / ".$_[2]);
	    @data_service = identify_service($_[1], $_[2]); # return index_id and storage
	    return undef if (!@data_service);
	    
	    if (defined($data_service[0]) && $data_service[0] ne 0) {
		# perfdata index status time type counter rebuild configuration
		my $status = identify_metric($_[5], $data_service[0], $_[4], $_[0], $config->{'storage_type'}, $data_service[2], $config); 
	        return undef if (!defined($status));
	    }
	    
	    # Update status 
	    updateServiceState($line_tab[1], $line_tab[2], $line_tab[0], $data_service[0],  $line_tab[4], $config->{'RRDdatabase_status_path'});
	}
    }
    return 0;
}

sub getModulesInterval($$){
    my $service_description = $_[0];
    $con_oreon = $_[1];
    my $interval;
    $service_description =~ /([a-zA-Z0-9]*)_([0-9]*)/;
    if ($1 eq "meta"){
	my $sth_interval = $con_oreon->prepare("SELECT normal_check_interval FROM meta_service WHERE meta_id = '".$2."'");
	if (!$sth_interval->execute) {
	    writeLogFile("Error when getting metrics interval for Meta : " . $sth_interval->errstr . "");
	}
	my $meta_conf = $sth_interval->fetchrow_hashref();
	if (defined($meta_conf->{'normal_check_interval'}) && $meta_conf->{'normal_check_interval'}){
	    $interval = $meta_conf->{'normal_check_interval'} * getIntervalLenght();
	} else {
	    $interval = 5 * getIntervalLenght();
	}
	undef($meta_conf);
	undef($sth_interval);
    } elsif ($1 eq "ba") {
	my $sth_interval = $con_oreon->prepare("SELECT normal_check_interval FROM mod_bam WHERE ba_id = '".$2."'");
	if (!$sth_interval->execute) {
	    writeLogFile("Error when getting metrics interval for BAM : " . $sth_interval->errstr . "");
	}
	my $bamConf = $sth_interval->fetchrow_hashref();
	if (defined($bamConf->{'normal_check_interval'}) && $bamConf->{'normal_check_interval'}){
	    $interval = $bamConf->{'normal_check_interval'} * getIntervalLenght();
	} else {
	    $interval = 2 * getIntervalLenght();
	}
	undef($bamConf);
	undef($sth_interval);
    } else {
	$interval = 90;
    }
    return $interval;
}

# Host name, sercvice desc, ctime, index, status, $rrdPath
sub updateServiceState($$$$$$) { 
    my $interval = 4000;
    my $nb_value;
    my $interval_length;
    my $rrdtoolDBPath = $_[5];

    # Set Index
    my $index = $_[3];

    # Set Hard State
    my $status = $_[4];

    if (defined($status)){
	if ($status =~ /OK/){
	    $status = 100;
	} elsif ($status =~ /WARNING/){
	    $status = 75;
	} elsif ($status =~ /CRITICAL/){
	    $status = 0;
	} elsif ($status =~ /UNKNOWN/){
	    undef($status);
	} else {
	    ;
	}
    }

    # Check if RRD DB Directory is available.
    if (checkDBDirectory($rrdtoolDBPath) == 0) {
	writeLogFile("Data droped....");
	return 0;
    }

    # call function to check if DB exist and else create it
    if (defined($index) && defined($status) && $_[2]){
	if (-e $rrdtoolDBPath."/".$index.".rrd"){
	    updateRRDDatabase($rrdtoolDBPath, $index, "status", $_[2], $status, 0);
	} else {
	    my $begin = time() - 200000;
	    $interval = getServiceCheckIntervalWithSVCid($index) * getIntervalLenght();
	    my $interval_hb = $interval * 10;

	    $nb_value = getLenStorageDB() / $interval;
	    
	    createRRDDatabase($rrdtoolDBPath, $index, $begin, $interval, "status", $nb_value, 0);
	    tuneRRDDatabase($rrdtoolDBPath, $index, "status", $interval_hb);
	    updateRRDDatabase($rrdtoolDBPath, $index, "status", $_[2], $status, 0);
	    
	    undef($begin);
	}
    }
}

#
# Remove Special char in metrics name 
# for RRDTool compatibility
#
sub replaceMetricSpecialChar($){
    my $metric_name = $_[0];
    $metric_name =~ s/\//slash\_/g;
    $metric_name =~ s/\\/bslash\_/g;
    $metric_name =~ s/\%/pct\_/g;
    $metric_name =~ s/\#S\#/slash\_/g;
    $metric_name =~ s/\#BS\#/bslash\_/g;
    $metric_name =~ s/\#P\#/pct\_/g;
    $metric_name =~ s/[^0-9_\-a-zA-Z]/-/g;
    return $metric_name;
}

#
# Create RRDTool Databases
#
sub createRRDDatabase($$$$$$$){
    my ($RRDdatabase_path, $metric_id, $begin, $interval, $metric_name, $my_len_storage_rrd, $data_source_type) = @_;
    
    RRDs::create($RRDdatabase_path.$metric_id.".rrd", "-b ".$begin, "-s ".$interval, "DS:".substr(replaceMetricSpecialChar($metric_name), 0, 19).":".$rrd_dst[$data_source_type].":".$interval.":U:U", "RRA:AVERAGE:0.5:1:".$my_len_storage_rrd, "RRA:AVERAGE:0.5:12:".($my_len_storage_rrd / 12));
    my $ERR = RRDs::error;
    if( $ERR) {
	writeLogFile("ERROR while creating ".$RRDdatabase_path.$metric_id.".rrd : $ERR");
    } else {
	writeLogFile("New Database creation successful : ".$RRDdatabase_path.$metric_id.".rrd");
        my $chCmd = `chmod 664 ${RRDdatabase_path}${metric_id}.rrd`;
    }
    undef($ERR);
}

#
# Tune RRDTool Databases
#
sub tuneRRDDatabase($$$$){
    my ($RRDdatabase_path, $metric_id ,$metric_name, $interval_hb) = @_;

    RRDs::tune($RRDdatabase_path.$metric_id.".rrd", "-h", substr(replaceMetricSpecialChar($metric_name), 0, 19).":".$interval_hb);
    my $ERR = RRDs::error;
    if ($ERR) {
	writeLogFile("ERROR while tunning operation on ".$RRDdatabase_path.$metric_id.".rrd : $ERR");
    } else {
	writeLogFile("DB Tuning : ".$RRDdatabase_path.$metric_id.".rrd");
    }
    undef($ERR);
}

sub removeOldRRDDatabase($$$$) {
    my ($RRDdatabase_path, $metric_id, $interval, $my_len_storage_rrd) = @_;
    if (!unlink($RRDdatabase_path.$metric_id.".rrd")) {
	writeLogFile("Cannot remove ".$RRDdatabase_path.$metric_id.".rrd");
    }
    writeLogFile("Rebuild database : ".$RRDdatabase_path.$metric_id.".rrd (interval : $interval - Len : $my_len_storage_rrd)");		
}

sub updateRRDDatabase($$$$$$){
    my ($RRDdatabase_path, $metric_id, $metric_name, $ctime, $value, $data_source_type) = @_;
    
    $value =~ s/\,/\./g;
    if ( $data_source_type == 0 ) {
        RRDs::update ($RRDdatabase_path.$metric_id.".rrd" , "--template", substr(replaceMetricSpecialChar($metric_name), 0, 19), $ctime.":".sprintf("%e", $value));
    } else {
        # 'Data Source Type' : COUNTER need long integer
        RRDs::update ($RRDdatabase_path.$metric_id.".rrd" , "--template", substr(replaceMetricSpecialChar($metric_name), 0, 19), $ctime.":".sprintf("%.0f", $value));
      }
    my $ERR = RRDs::error;
    if ($ERR){
	writeLogFile("ERROR while updating ".$RRDdatabase_path.$metric_id.".rrd at ".$ctime." -> ".$value." : $ERR");
    } else {
	if ($debug) {
	    writeLogFile("DB updating : ".$RRDdatabase_path.$metric_id.".rrd");
	}
    }
    undef($ERR);
}

sub updateRRDDatabaseForRebuild($$$$$$){
    my ($RRDdatabase_path, $metric_id, $metric_name, $ctime, $value, $data_source_type) = @_;
        
    $value =~ s/\,/\./g;
    if ( $data_source_type == 0 ) {
        RRDs::update ($RRDdatabase_path.$metric_id.".rrd" , "--template", substr(replaceMetricSpecialChar($metric_name), 0, 19), $ctime.":".sprintf("%e", $value));
    } else {
	# 'Data Source Type' : COUNTER need long integer
	RRDs::update ($RRDdatabase_path.$metric_id.".rrd" , "--template", substr(replaceMetricSpecialChar($metric_name), 0, 19), $ctime.":".sprintf("%.0f", $value));
    }
    my $ERR = RRDs::error;
    if ($ERR){
	if ($ERR =~ /([0-9]*) when last update time is ([0-9]*)/) {
	    if ($1 != $2) {
		writeLogFile("ERROR while updating during rebuild ".$RRDdatabase_path.$metric_id.".rrd at ".$ctime." -> ".$value." : $ERR");
	    }
	}
    } else {
	if ($debug) {
	    writeLogFile("DB updating : ".$RRDdatabase_path.$metric_id.".rrd");
	}
    }
    undef($ERR);
}

sub CheckRebuild(){
    use vars qw($con_oreon $con_ods);
    my ($RRDdatabase_path, $RRDdatabase_status_path, $len_storage_rrd, $svc_mst_be_rbld);
    my ($data, $ERR, $flag, $metric, $cpt);

    # ---------------------------------------------
    # enable db connexion
    CreateConnexionForCentstorage();
    CreateConnexionForOreon();

    # ---------------------------------------------
    # Rebuild database
    $RRDdatabase_path = getRRDdatabase_path();
    $RRDdatabase_status_path = getRRDdatabase_status_path();
    $len_storage_rrd = getLenStorageDB();

    my $sth2 = $con_ods->prepare("SELECT id, service_id, host_name, service_description FROM index_data WHERE `must_be_rebuild` = '1'");
    writeLogFile("Error when getting service id who must be rebuild : ".$sth2->errstr."") if (!$sth2->execute);
    while ($svc_mst_be_rbld = $sth2->fetchrow_hashref()){
    	if (!$stop) {
            last;
        }

	my $sth = $con_ods->prepare("UPDATE index_data SET `must_be_rebuild` = '2' WHERE id = '".$svc_mst_be_rbld->{'id'}."'");
	writeLogFile("Error when getting perfdata file : " . $sth->errstr . "") if (!$sth->execute);
	undef($sth);

	writeLogFile("Rebuild Graphs for Services : ".$svc_mst_be_rbld->{'id'}."");

	# -----------------------------------------------------
	# Get check interval for this service
	my $interval;
	my $sth_interval;
	if ($svc_mst_be_rbld->{'host_name'} =~ /_Module_([a-zA-Z0-9]*)/){
	    $interval = getModulesInterval($svc_mst_be_rbld->{'service_description'}, $con_oreon);
	} else {
	    $interval = getServiceCheckIntervalFromService($svc_mst_be_rbld->{'id'}) * getIntervalLenght();
	}
	$interval = 330 if (!defined($interval));

	my $interval_hb = $interval * 10;
	my $sth3 = $con_ods->prepare("SELECT metric_id, metric_name, data_source_type FROM metrics WHERE index_id = '".$svc_mst_be_rbld->{'id'}."'");
	if (!$sth3->execute) {writeLogFile("Error when getting metrics id who must be rebuild : " . $sth3->errstr . "");}

	writeLogFile("ERROR : can t rebuild... interval = 0 (time unit : ".getIntervalLenght().")") if ($interval == 0);
	my $sth4;
	my $my_len_storage_rrd;

	if ($interval != 0) {
	    while ($metric = $sth3->fetchrow_hashref()) {
		# Replace Special chars
		$metric->{'metric_name'} = replaceMetricSpecialChar($metric->{'metric_name'});
		
		# manage 'data_source_type' default value : NULL = '0'
		$metric->{'data_source_type'} = defined($metric->{'data_source_type'}) ? $metric->{'data_source_type'} : 0;
		writeLogFile("Get Data for rebuilding $RRDdatabase_path".$metric->{'metric_id'}.".rrd");					

		$my_len_storage_rrd = $len_storage_rrd / $interval;

		# Clean old Database
		removeOldRRDDatabase($RRDdatabase_path, $metric->{'metric_id'}, $interval, $my_len_storage_rrd);

		# Get All Data
		$sth4 = $con_ods->prepare("SELECT * FROM data_bin WHERE id_metric = '".$metric->{'metric_id'}."' ORDER BY ctime");
		writeLogFile("Error when getting perfdata file : " . $sth4->errstr . "") if (!$sth4->execute);

		for ($flag = 0, $cpt = 0;$data = $sth4->fetchrow_hashref();$cpt++) {
		    if (!$flag){    
			# Calculate first entry in database
			my $begin = $data->{'ctime'} - 200;
			
			# Create DB
			createRRDDatabase($RRDdatabase_path, $metric->{'metric_id'}, $begin, $interval, $metric->{'metric_name'}, $my_len_storage_rrd, $metric->{'data_source_type'});
			tuneRRDDatabase($RRDdatabase_path, $metric->{'metric_id'}, $metric->{'metric_name'}, $interval_hb);
			
			undef($begin);
			
			$flag++;
		    }
		    updateRRDDatabaseForRebuild($RRDdatabase_path, $metric->{'metric_id'}, $metric->{'metric_name'}, $data->{'ctime'}, $data->{'value'}, $metric->{'data_source_type'});
		}
		undef($sth4);
		undef($my_len_storage_rrd);
		# -----------------------------------------------------
	    }
	}
	undef($interval);
	undef($sth3);
	
	$sth = $con_ods->prepare("UPDATE index_data SET `must_be_rebuild` = '0' WHERE id = '".$svc_mst_be_rbld->{'id'}."'");
	if (!$sth->execute()) {
	    writeLogFile("Error when updating rebuild flag for service ". $svc_mst_be_rbld->{'id'} ." : " . $sth->errstr . "");
	}
	undef($sth);
	undef($cpt);
	undef($metric);
    }
    $sth2->finish();
    undef($flag);
    undef($svc_mst_be_rbld);
    undef($sth2);
    undef($data);
    if (defined($con_oreon)){
	$con_oreon->disconnect();
    }
    if (defined($con_ods)){
     	$con_ods->disconnect();
    }
}

# Check if Centstorage have to start
sub checkProgramStatus() {
    # Init Var
    use vars qw($con_oreon $con_ods);

    # Connect MySQL DB
    CheckMySQLConnexion();				

    my $sth = $con_oreon->prepare("SELECT * FROM options WHERE `key` = 'centstorage'");
    if (!$sth->execute()) {
	writeLogFile("Error when getting status of centstorage program : " . $sth->errstr . "");
    }
    my $status = 0;
    while (my $option = $sth->fetchrow_hashref()) {
	if ($option->{'value'} == 1) {
	    $status = 1;
	}
    }

    if ($status == 0) {
	writeLogFile("Centstorage is not enable into Centreon Config Panel");
	writeLogFile("Program cannot start");
	exit(2);
    }

    $con_oreon->disconnect();
    $con_ods->disconnect();
}


# Write in log file that program is starting
sub signalstartprogram() {
    # Starting centstrorage Engine
    writeLogFile("Starting centstorage engine...");
}

sub unset_protection {
    foreach my $pid (keys %tabProcChld) {
	if ($tabProc{'parser'} == $pid) {
	    delete $tabProcChld{$pid};
	    $tabProc{'parser'} = 0;
	}
	if ($tabProc{'synchro'} == $pid) {
	    delete $tabProcChld{$pid};
	    $tabProc{'synchro'} = 0;
	}
	if ($tabProc{'rebuild'} == $pid) {
	    delete $tabProcChld{$pid};
	    $tabProc{'rebuild'} = 0;
	}
    }
}

# Write in log file that program is stoping
sub signalstopprogram() {
    writeLogFile("Stopping centstorage engine...");
    # Waiting childs ended
    my $cpt = 1;
    while ($cpt < $timeout_stop) {
	if ($tabProc{'parser'} == 0 && $tabProc{'rebuild'} == 0 && $tabProc{'synchro'} == 0) {
	    last;
	}
	unset_protection();
	sleep(1);
	$cpt++;
    }

    if ($cpt >= $timeout_stop) {
	writeLogFile("Violent killing...Avoid double run...");
	if ($tabProc{'parser'}) {
	    writeLogFile("Send KILL signal to parser sub process...");
	    kill('KILL', $tabProc{'parser'})
	}
	if ($tabProc{'rebuild'}) {
	    writeLogFile("Send KILL signal to rebuild sub process...");
	    kill('KILL', $tabProc{'rebuild'})
	}
    }
}


sub REAPER {
    my $child_pid;
    while (($child_pid = waitpid(-1, &WNOHANG)) > 0) {
	$tabProcChld{$child_pid} = 1;
    }
    $SIG{CHLD} = \&REAPER;
}

# Check Program State
checkProgramStatus();

open $centstorage_fh, '>>', $LOG;
open STDOUT, '>&', $centstorage_fh;
open STDERR, '>&', $centstorage_fh;
$SIG{CHLD} = \&REAPER;

my $timeParser = time();
my $timeSynchro = time();
my $timeRebuild = time();

$stop = 1;

signalstartprogram();
while ($stop) {
    if (time() - $timeParser >= 7 && !$tabProc{'parser'} && !$tabProc{'rebuild'}) {
	$tabProc{'parser'} = fork();
	if (!defined($tabProc{'parser'})) {
	    writeLogFile("Parser cannot fork: $!");
	} elsif (!$tabProc{'parser'}){
	    open STDOUT, '>&', $centstorage_fh;
            open STDERR, '>&', $centstorage_fh;
	    GetPerfData();
	    exit(0);
	}
	$timeParser = time();
    }
    if (time() - $timeSynchro >= 60 && !$tabProc{'synchro'}) {
	$tabProc{'synchro'} = fork();
	if (!defined($tabProc{'synchro'})) {
	    writeLogFile("Synchro cannot fork: $!");
	} elsif (!$tabProc{'synchro'}){
	    open STDOUT, '>&', $centstorage_fh;
            open STDERR, '>&', $centstorage_fh;
	    CheckRestart();	
	    exit(0);
	}
	$timeSynchro = time();
    }
    if (time() - $timeRebuild >= 40 && !$tabProc{'rebuild'} && !$tabProc{'parser'}) {
	$tabProc{'rebuild'} = fork();
	if (!defined($tabProc{'rebuild'})) {
	    writeLogFile("rebuild cannot fork: $!");
	}
	if (!$tabProc{'rebuild'}){
	    open STDOUT, '>&', $centstorage_fh;
            open STDERR, '>&', $centstorage_fh;
	    CheckRebuild();
	    exit(0);
	}
	$timeRebuild = time();
    }
    unset_protection();
    sleep(1);
}

signalstopprogram();
exit(1);

__END__

