[fwgold-users] Re: can't manage to update database

  • From: "Gianluca Rotoni" <gianluca@xxxxxxxxxx>
  • To: fwgold-users@xxxxxxxxxxxxx
  • Date: Tue, 26 Nov 2002 15:24:45 +0100

Hi,

I've found something on the way the remote IP/name check is done which
could cause your problem. I've tested on my site and managed to
reproduce the fault and fix it.

Could you please test the attached fwgold script and tell me whether
it fixes your problem?

Regards,
Gianluca




-- Attached file included as plaintext by Ecartis --
-- File: fwgold
-- Desc: fwgold

#!/usr/bin/perl -w
#

use strict;
use Socket;
use Carp;
use Getopt::Long;
$Getopt::Long::autoabbrev=1;
use IO::Handle;
use Math::BigInt;
use File::Copy;
use File::Basename;
use Time::Local;

# Constant definitions
use constant VER => "2.0beta";
use constant OK => 0;
use constant ERROR => 1;
use constant FATAL => 2;

use constant MINUTE => 60;
use constant FIVEMINUTES => 300;
use constant HALFANHOUR => 1800;
use constant HOUR => 3600;
use constant DAY => 86400;
use constant WEEK => 604800;
use constant MONTH => 2419200;
use constant YEAR => 31536000;

use constant TOKEN => "ok";
use constant SEQUENCE => "or";

my %MONTHS = ('Jan' => 0,
              'Feb' => 1,
              'Mar' => 2,
              'Apr' => 3,
              'May' => 4,
              'Jun' => 5,
              'Jul' => 6,
              'Aug' => 7,
              'Sep' => 8,
              'Oct' => 9,
              'Nov' => 10,
              'Dec' => 11
             );

# MAIN
#

#
# Read the options
#
use vars qw($conf $init $overwrite $graph $html $mode $help $db $update $match 
            $datapid $version $checkconfig $offline_logfile $start_date 
@RRDsARGS);

&GetOptions("configuration:s" => \$conf,
            "version" => \$version,
            "mode:s" => \$mode,
            "offline:s" => \$offline_logfile,
            "checkconfig" => \$checkconfig,
            "initialize" => \$init,
            "start:s" => \$start_date,
            "overwrite" => \$overwrite,
            "updatedb" => \$update,
            "graphics" => \$graph,
            "makehtml" => \$html,
            "help" => \$help);

my $prog = basename($0);

#
# Initialize the fwgold main object
#
my $me = {status => OK,
          debug => 0};
bless $me;
$me->{Config} = ();
$me->{Db} = ();
$me->{prog} = $prog;

#
# Print the version and exit
#
if ($version) {
  print "$prog " , VER, " - FwGold - FW-1(r) Graphics Oriented Log (analysis) 
Database\nwritten by Gianluca Rotoni <gianluca\@rotoni.com>\n";
  exit
}

#
# Print the usage and exit
#
if ($help) {
  $me->Usage(msg => "");
  exit
}

#
# Check that the config file is specified and it's readable
#
unless ((defined $conf) &&
        (-r _) &&
        (-T _)) {
  $me->Usage(msg => "Cannot read configuration file") ;
  exit 1;
}

#
# Process the config file
#
$me->ReadConfig(file => "$conf");
die "$prog: $me->{msg}" if ($me->{status} ne OK);

#
# Process the UserDB file
#
$me->ReadUserDBConfig();
die "$prog: $me->{msg}" if ($me->{status} ne OK);

$me->{mode} = $mode if (defined $mode);

if ($checkconfig) {
  die "$prog :\n" , $me->{msg} if ($me->ErrorInConfig);
  print "Check syntax OK!\n";
  exit
}

unless (defined $me->{mode}) {
  $me->Usage(msg => "Unspecified running mode") ;
  exit 1;
}

if ($me->{mode} eq "server") {
  
  die "$prog: offline session not valid on server mode" if ($offline_logfile);

  #
  # Fork the process into one listener and one data collector
  #
  
  #
  # Open up an IO pipe between the two processes
  #
  pipe (DATACOLLECTOR, LISTENER);
  LISTENER->autoflush(1);
  
  if (! defined($datapid = fork)) {
    die "$prog: cannot fork: $!";
    return;
  } elsif (! $datapid) {
    
    #
    #  I'm the child -- start the data collection
    #
    close DATACOLLECTOR;
    &DataCollection;
    
  }
  
  #
  # I'm the listener, close the unrelevant file handle
  #
  close LISTENER;
  
  #
  # Define signal handlers for this process
  #
  use sigtrap qw(handler StopDataCollection KILL);
  use sigtrap qw(handler StopDataCollection TERM);
  
  #
  # Define the spawning subroutine
  #
  sub spawn;
  
  #
  # Set the port and protocol variables
  #
  my $port = $me->{Config}->{DATAPORT};
  my $proto = getprotobyname('tcp');
  
  #
  # Save the server's pid
  #
  if (-w $me->{Config}->{SERVERTMPDIR}) {
    if (-r "$me->{Config}->{SERVERTMPDIR}/$prog.pid") {
      unlink "$me->{Config}->{SERVERTMPDIR}/$prog.pid";
    }
    open (PID,">$me->{Config}->{SERVERTMPDIR}/$prog.pid");
    print PID $$,"\n";
    close PID;
  } else {
    print STDERR "Cannot write to SERVERTMPDIR! Check the configuration at 
$conf.\n";
    exit 1;
  }
  
  #
  # Open up the configured port and list the to it
  #
  socket(Server, PF_INET, SOCK_STREAM, $proto) or die "$prog: socket: $!";
  setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "$prog: 
setsockopt: $!";
  bind(Server, sockaddr_in($port, INADDR_ANY)) or die "$prog: bind: $!";
  listen(Server, SOMAXCONN) or die "$prog: listen: $!";
  
  my $waitedpid = 0;
  my $paddr;
  
  sub REAPER {
    $SIG{CHLD} = \&REAPER;
    $waitedpid = wait;
  }
  
  $SIG{CHLD} = \&REAPER;
  
  #
  # Listen forever
  #
  FOR : for ( $waitedpid = 0;
              ($paddr = accept(Client, Server)) || $waitedpid;
              $waitedpid = 0, close Client) {
    
    #
    # Fix an odd communication problem
    # thanks to Ron Sterenberg <sterenberg80@xxxxxxxxx>
    #
    next FOR if $waitedpid and not $paddr;

    my ($port,$iaddr) = sockaddr_in($paddr);
    my $name = gethostbyaddr($iaddr,AF_INET);
    my $remote_ip = inet_ntoa($iaddr);
    
    if ($me->{debug}) {
      print "Connected on port $port from $name ($remote_ip)\n";
    }
    
    if (($name ne $me->{Config}->{CLIENTHOST}) &&
        ($iaddr ne $me->{Config}->{CLIENTHOST})) {
      next FOR;
    }
    
    #
    # A connection has arrived! Read the DATA from the collecting process
    #
    kill 'USR1' => $datapid;
    my $DATA=<DATACOLLECTOR>;
    
    #
    # Duplicate I/O to and from the Client
    #
    
    open (OUT,">&Client");
    open (IN,"<&Client");
    
    OUT->autoflush(1);
    my $ReadyToReceive;
    
    # Wait Ready to Receive
    read IN, $ReadyToReceive, 2;
    
    # The client is ready to receive DATA, go on
    print OUT "$DATA\n" if (($ReadyToReceive eq "ok") && ($DATA));
    close IN;
    close OUT;
    $waitedpid=0;
    
    next FOR;
  }

} elsif ($me->{mode} eq "client") {
  
  $me->Usage() unless ($init ||
                       $graph ||
                       $html ||
                       $update);

  #
  # Set the path where to find the RRD module
  # if this hasn't been installed within the perl library path
  #
  if (defined $me->{Config}->{RRDPERLMODULEPATH}) {
    use lib qw( $me->{Config}->{RRDPERLMODULEPATH} );
  }
  
  #
  # Use require/import so that the presence of RRDs is only checked at runtime
  #
  require RRDs;
  RRDs->import();

  #
  # Create the RRD DB's if required
  #
  if ($init) {
    if (defined $start_date) {
      $me->MakeDB(overwrite => $overwrite,
                  start => $start_date)
    } else {
      $me->MakeDB(overwrite => $overwrite)
    }         
    die "$prog: $me->{msg}" if ($me->{status} ne OK);
    unlink "$me->{Config}->{CLIENTTMPDIR}/fwgoldlast.data";
    exit 0;
  }

  unless ($update) {

    die "$prog: must specify --update when running offline" if 
($offline_logfile);

  } else {
    
    #
    # Handle offline DB updates
    #
    if (defined $offline_logfile) {
      unless (-r $offline_logfile) {
        die "$prog: offline log file not found or not readable";
      }

      unless (-T $offline_logfile) {
        die "$prog: offline log file is not in ASCII format";
      }

      my $day;
      my $month;
      my $year;
      my $hours;
      my $minutes;
      my $seconds;

      unless (defined $start_date) {
        print "Offline log date not specified, assuming today\n";
        ($day,$month,$year) = (localtime)[3..5];
        $year += 1900;
        $month += 1;
        $start_date = "$year/$month/$day";
      }
      
      unless ($start_date =~ m/(\d{4})\/(\d{1,2})\/(\d{1,2})/) {
        die "$prog: wrong offline log date format ($start_date). Specify 
YYYY/MM/DD";
      } 
      
      $year = $1 - 1900;
      $month = $2 - 1;
      $day = $3;

      my $offline_start = timelocal(0,0,0,$day,$month,$year);
 
      if (! open OFFLINE, "<$offline_logfile") {
        die "$prog: cannot open $offline_logfile";
      }
      
      $|=1;

      my $offline_log;
      my $offline_time;
      my $offline_logdata;
      my $offline_cuncurrentlog;
      my $offline_previoustime;
      my $offline_firstupdate;
      my $match_name;

    OFFLINE: while (<OFFLINE>) {
        
        print "Offline logged data: $offline_log\n" if ($me->{debug});
        
        #
        # Verify whether the date has changed in the log
        #
        my $offline_log = $_;
        if (/^Date:\s+(\S+)\s+(\d{1,2})\,\s+(\d{4})/) {
          print "Date switched in log, new date is $1 $2, $3\n";
          $year = $3;
          $day = $2;
          $month = $MONTHS{$1};
          $offline_start = timelocal(0,0,0,$day,$month,$year);
        }
        next if ($offline_log !~ m/^\s*(\d{1,2}):(\d{1,2}):(\d{1,2})\s+/);
        $hours = $1;
        $minutes = $2;
        $seconds = $3;

        #
        # Calculate the exact log time
        #
        
        $offline_time = timelocal($seconds,$minutes,$hours,$day,$month,$year);
        if (defined $offline_previoustime) {
          $offline_cuncurrentlog = (($offline_time - $offline_previoustime) < 
$me->{Config}->{FREQUENCY});
        } else {
          $offline_previoustime = $offline_time;
        }
        
        #
        # Check the log matched against the user defined filters
        #
        $me->ProcessLogEntry(logEntry => "$offline_log");

        #
        # Create a 'server like' offline processed log data
        #
        $offline_logdata = $offline_time . ":";
        foreach $db (keys %{$me->{DB}}) {
          $offline_logdata .=  $db . ":";
          #
          # DB match level
          #
        MATCH: foreach $match_name (keys %{$me->{DB}->{$db}->{MATCH}}) {
            #
            # DB match filter level
            #
            if (! defined $me->{DB}->{$db}->{MATCH}->{$match_name}->{COUNTER}) {
              open (TMPLOG, ">>$me->{Config}->{SERVERTTMPDIR}/unusual.log") or 
die "$prog: Cannot write onto $me->{Config}->{SERVERTMPDIR}: $?";
              print TMPLOG "DB $db MATCH $match_name hasn't been correctly 
initialized\n";
              close TMPLOG;
              next MATCH;
            }
            
            $offline_logdata .=  ":" . $match_name . "=" . 
$me->{DB}->{$db}->{MATCH}->{$match_name}->{COUNTER};
          }
          $offline_logdata .=  "/";
        }
        
        #
        # Process the logged data
        #
        my $log_hash_ref = $me->ProcessData(data => "$offline_logdata");
        die "$prog: $me->{msg}" if ($me->{status} ne OK);

        #
        # Process all cuncurrent log entries
        #
        next OFFLINE if ($offline_cuncurrentlog);
        
        #
        # Update the fwgold DB if required
        #
        my %LOG = %{$log_hash_ref};
        foreach $db (keys %LOG) {
          foreach $match (keys %{$LOG{$db}}) {
            $me->{DB}->{$db}->{MATCH}->{$match}->{DATA} = 
$LOG{$db}->{$match}->{DATA};
            $me->{DB}->{$db}->{MATCH}->{$match}->{AVERAGE} = 
$LOG{$db}->{$match}->{AVERAGE};
          }
        }

        unless (defined $offline_firstupdate) {
          print "Upgrading DB's...";
          $offline_firstupdate = 1;
        } else {
          print "."
        }

        $offline_previoustime = $offline_time;
        $me->UpdateDB(logtime => "$offline_time");
        if ($me->{status} ne OK) {
          die "$prog: Cannot update RRD DB's\nMake sure that the DB starting 
date is older that the log entry" if ($me->{msg} =~ /second/);
          die "$prog: $me->{msg}";
        }
      }
      
      #
      # Create the graphics after the offline job
      #
      print "\nGenerating graphics...\n";
      $me->MakeGraphics(start => "$offline_start");
      die "$prog: $me->{msg}" if ($me->{status} ne OK);
      print "Finished.\n";
      
    } else {
      
      #
      # Connect to the remote server and take the logged data
      #
      my $log = $me->ReadDataFromServer();
      die "$prog: $me->{msg}" if ($me->{status} ne OK);
      die "$prog: server sent no data" if ((! defined $log) ||
                                           (! $log));
      
      print "Logged data: $log\n" if ($me->{debug});
      
      #
      # Process the logged data
      #
      my $log_hash_ref = $me->ProcessData(data => "$log");
      die "$prog: $me->{msg}" if ($me->{status} ne OK);
      
      #
      # Update the fwgold DB if required
      #
      my %LOG = %{$log_hash_ref};
      foreach $db (keys %LOG) {
        foreach $match (keys %{$LOG{$db}}) {
          $me->{DB}->{$db}->{MATCH}->{$match}->{DATA} = 
$LOG{$db}->{$match}->{DATA};
          $me->{DB}->{$db}->{MATCH}->{$match}->{AVERAGE} = 
$LOG{$db}->{$match}->{AVERAGE};
        }
      }
      my $logtime = time();
      $me->UpdateDB(logtime => "$logtime");
      die "$prog: $me->{msg}" if ($me->{status} ne OK);
    }
  }
  
  #
  # Update the RRD graphics if required
  #
  $me->MakeGraphics() if ($graph);
  die "$prog: $me->{msg}" if ($me->{status} ne OK);
  
  #
  # Generate the html pages
  #
  $me->GenerateHtml() if ($html);
  die "$prog: $me->{msg}" if ($me->{status} ne OK);
  
  exit 0
    
} else {
  
  #
  # Wrong running mode (must be server or client)
  #
  $me->Usage(msg => "Wrong running mode specified : $me->{mode}");;
  exit 1;
  
}

#
# End of MAIN
#

sub DataCollection {

  #
  # define the FW log command
  #
  my $CMD = $me->{Config}->{FWBINPATH} . "/fw log " . 
$me->{Config}->{FWLOGOPTS};

  #
  # Print the collected data into the pipe when awaked from the listener
  #
  use sigtrap qw(handler PrintData USR1);

  #
  # Read the FW log into a hash %LOG
  #
  
  open LOG, "$CMD |" or die "$prog: Cannot start $CMD : $!";

  if ($me->{debug}) {
    print "Started logging command $CMD\n";
  }      
  
  while (<LOG>) {
    if ($me->{debug}) {
      print "Got log entry : $_\n";
    }      
    $me->ProcessLogEntry(logEntry => "$_");
  }
}

sub ProcessLogEntry() {

  #
  # Process a FW log entry according to the user defined filter
  #
  my $self = shift;
  my $param = Params(@_);
  
  my $logEntry = $self->ThisParam('logEntry', $param);
  
  use vars qw (@LOGENTRY @lcLOGENTRY %LOG $key $value $db $match $result_ref 
$match_name $pid);
 
  return if (/^Date/);

  if (uc($self->{Config}->{CPVERSION}) eq 'NG') {
    s/product VPN-1 \& FireWall-1//;
  }
  
  return if (! m/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S)(\S+)\s+(.*)\s*$/);
  
  my $Time = $1;
  my $Action = $2;
  
  #
  # Skip control logs
  #
  return if ($Action eq "ctl");
  return if ($Action eq "keyinst");
  
  my $Origin = $3;
  my $Direction = ($4 eq '<') ? 'out':'in';
  my $Interface = $5;

  #
  # Erase multiple spaces from the rest of the log
  #
  my $rest_of_log = $6;
  $rest_of_log =~ s/\s+/ /g;

  #
  # Skip logs with the reason tag set
  #
  return if ($rest_of_log =~ /reason:/);

  #
  # store lat log entry onto a temporary file for debugging purposes. This is 
normally commented-out.
  #
  # open (DEBUGLOG, ">$me->{Config}->{SERVERTMPDIR}/lastentry.log") or die 
"$prog: Cannot write onto $me->{Config}->{SERVERTMPDIR}: $?";
  # print DEBUGLOG $rest_of_log;
  # close DEBUGLOG;
  
  #
  # Check whether the size of the rest of the log is even
  #
  @LOGENTRY = split /\s/, $rest_of_log;
  if ((2*int(scalar(@LOGENTRY)/2)) != scalar(@LOGENTRY)) {
    #
    # The size is not even, save this log entry and then ignore it
    #
    open (TMPLOG, ">>$me->{Config}->{SERVERTMPDIR}/unusual.log") or die "$prog: 
Cannot write onto $me->{Config}->{SERVERTMPDIR}: $?";
    print TMPLOG "$Time $Action $Origin $Direction $Interface";
    foreach (@LOGENTRY) {
      print TMPLOG " $_";
    }
    print TMPLOG "\n";
    close TMPLOG;
    return
  }
    
  #
  # From here on the log is made of key-value pairs
  #
  # Make all the log keys lower-case
  #
  my $i;
  for ($i=0; $i<scalar(@LOGENTRY); $i++) {
    splice (@LOGENTRY, $i, 1, lc($LOGENTRY[$i]));
    $i++;
  }
  
  @LOGENTRY = ('time',$Time,
               'action',$Action,
               'origin',$Origin,
               'direction',$Direction,
               'interface',$Interface,
               @LOGENTRY);
  %LOG = @LOGENTRY;
  
  #
  # Debug
  #
  if ($me->{debug}) {
    foreach $key (keys %LOG) {
      $value = $LOG{$key};
      print $key, " => ", $value, "\n";
    }      
  }
  
  #
  # Chech whether the log matches any filter
  #
  # foreach $db (keys %{$me->{DB}}) {
  $me->LogMatch(log => \%LOG);
  die "$prog: $me->{msg}" if ($me->{status} ne OK);
  return
}

sub StopDataCollection
  {
    kill 'KILL' =>      $datapid;
  }

# sub PrintNewData() {

#   my $xml_output;
#   my $xml = new XML::Writer(OUTPUT => *LISTENER,
#                           NEWLINES => 0);
  
#   $xml->startTag("fwgold",
#                "time" => time());
  
#   foreach $db (keys %{$me->{DB}}) {
    
#     $xml->startTag("db",
#                  "name" => $db);
    
#   MATCH: foreach $match_name (keys %{$me->{DB}->{$db}->{MATCH}}) {
#       #
#       # DB match filter level
#       #
#       if (! defined $me->{DB}->{$db}->{MATCH}->{$match_name}->{COUNTER}) {
#       open (TMPLOG, ">>$me->{Config}->{SERVERTTMPDIR}/unusual.log") or die 
"$prog: Cannot write onto $me->{Config}->{SERVERTMPDIR}: $?";
#       print TMPLOG "DB $db MATCH $match_name hasn't been correctly 
initialized\n";
#       close TMPLOG;
#       next MATCH;
#       }

#       $xml->emptyTag("match",
#                    "name" => "$match_name",
#                    "data" => 
"$me->{DB}->{$db}->{MATCH}->{$match_name}->{COUNTER}");
#     }
    
#     $xml->endTag("db");
#   }
#   $xml->endTag("fwgold");
#   $xml->end();
# }

sub PrintData() {

  use vars qw($value $key $db $match $match_name);
  #
  # Return the collected data for each UserDB
  #
  print LISTENER time(),":";
  foreach $db (keys %{$me->{DB}}) {
    print LISTENER $db, ":";
    #
    # DB match level
    #
  MATCH: foreach $match_name (keys %{$me->{DB}->{$db}->{MATCH}}) {
      #
      # DB match filter level
      #
      if (! defined $me->{DB}->{$db}->{MATCH}->{$match_name}->{COUNTER}) {
        open (TMPLOG, ">>$me->{Config}->{SERVERTTMPDIR}/unusual.log") or die 
"$prog: Cannot write onto $me->{Config}->{SERVERTMPDIR}: $?";
        print TMPLOG "DB $db MATCH $match_name hasn't been correctly 
initialized\n";
        close TMPLOG;
        next MATCH;
      }

      print LISTENER ":", $match_name, "=", 
$me->{DB}->{$db}->{MATCH}->{$match_name}->{COUNTER};
    }
    print LISTENER "/";
  }
  print LISTENER "\n";
}

sub Params {
  return defined $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
}

sub ThisParam {
  my $self = shift;
  my $key = shift;
  while (my $par = shift) {
    if (defined $par->{$key}) {
      return $par->{$key}
    }
  }
  if (defined $self->{param}->{$key}) {
    return $self->{param}->{$key}
  }
  return undef;
}

sub SetParam {
  my $self = shift;
  my $key = shift;
  my $value = shift;
  $self->{param}->{$key} = $value;
}

sub ReadConfig() {
  
  #
  # Read the User main config file into the config hash
  #
  my $self = shift;
  my $param = Params(@_);
  my $file = shift;
  unless ($file = $self->ThisParam('file', $param)) {
    $self->{msg} = "File not specified";
    $self->{status} = FATAL;
    return undef;;
  }

  $self->{CFG_file} = (-r $file) ? $file : "/etc/fwgold.conf";
  unless (-r $self->{CFG_file}) {
    $self->{status} = FATAL;
    $self->{msg} = "Cannot read configuration file"; 
    return undef;
  }
  
  if (! open CONF, "< $self->{CFG_file}") {
    $self->{status} = FATAL;
    $self->{msg} = "Cannot open config file : $!";
    return undef;
  }
    
  my $line=0;
  
 CFG: while (<CONF>) {
    
    #
    # Keep the line number
    #
    $line++;

    #
    # Skip comments or empty lines
    #
    next CFG if (/^\s*$/);
    next CFG if (/^\s*\#/);

    #
    # Read config entry - exit on syntax error
    #
    
    if (! m/^\s*(\S+)\s*\=\s*(.*)\s*$/) {
      $self->{status} = FATAL;
      $self->{msg} = "Invalid assign statement at line: $line" ;
      return undef;
    }
    
    #
    # Store the config entry into the hash
    #
    $self->{Config}->{$1} = $2;
  }
  
  #
  #Check that the DB configuration file exists and it's readable
  #
  if (defined $self->{Config}->{USERDBCONFIG}) {
    unless ((-r $self->{Config}->{USERDBCONFIG}) && 
            (-T _)) {
      $self->{status} = FATAL;
      $self->{msg} = "Cannot read user db config file 
$self->{Config}->{USERDBCONFIG}";
      return undef;
    }      
  } else {
    $self->{status} = FATAL;
    $self->{msg} = "USERDBCONFIG not defined in config file";
    return undef;
  }

  #
  # Keep compatibility with older version of config file
  #
  if (defined $self->{Config}->{FREQUENCY}) {
    $self->{Config}->{FREQUENCY} = 60 * $self->{Config}->{FREQUENCY};
  } else {
    $self->{Config}->{FREQUENCY} = 300;
  }
  $self->{Config}->{TIMEOUT} = ($self->{Config}->{FREQUENCY}*3);

  #
  #
  #
  if (defined $self->{Config}->{CPVERSION}) {
    if ((uc($self->{Config}->{CPVERSION}) ne '4.X') &&
        (uc($self->{Config}->{CPVERSION}) ne 'NG')) {
            $self->{status} = FATAL;
            $self->{msg} = "CPVERSION set to invalid value 
($self->{Config}->{CPVERSION}) at line $line";
            return undef;
    }
  } else {
    $self->{Config}->{CPVERSION} = 'FW-1';
  }

  return OK
}

sub ReadUserDBConfig() { 

  #
  # Read the User DB config into the DB hash
  #
  
  my $self = shift;
  use vars qw ($db $match $match_name $key $value $filter_key $filter_operator 
$filter_parameter);

  my $dbconf_file = $self->{Config}->{USERDBCONFIG};
  my $line=0;
  my $open_db=0;
  my $open_match=0;
  my $db_name;
  my $match_name;
  my $sequence = SEQUENCE;

  if (! open DBCONF,"$dbconf_file") {
    $self->{status} = FATAL;
    $self->{msg} = "Cannot open $dbconf_file : $!";
    return undef;
  }
    
 MAIN: while (<DBCONF>) {
    $line++;
    next if /^\s*\#/;
    next if /^\s*$/;
    if (! /^\s*DB\s+(\S+)\s*$/) {
      $self->{msg} = "Error parsing $dbconf_file : Unmatched DB/ENDDB at line 
$line";
      $self->{status} = FATAL;
      return undef;
    }
    $open_db=1;
    $db_name=$1;
    $self->{DB}->{$db_name}->{NAME}=$db_name;
    
    #
    # Default values
    #
    $self->{DB}->{$db_name}->{VLABEL}="connections / minute";

  DBENTRY: while (<DBCONF>) {
      $line++;
      next if /^\s*\#/;
      next if /^\s*$/;
      if (/^\s*DB\s*$/) {
        $self->{msg} = "Error parsing $dbconf_file : Unmatched DB/ENDDB at line 
$line";
        $self->{status} = FATAL;
        return undef;
      }
      if (/^\s*ENDDB\s*$/) {
        if ($open_match) {
          $self->{msg} = "Error parsing $dbconf_file : Unmatched MATCH/ENDMATCH 
at line $line";
          $self->{status} = FATAL;
          return undef;
        }
        $open_db=0;
        last DBENTRY;
      }
      if (/^\s*TITLE\s+(.*)\s*$/) {
        $self->{DB}->{$db_name}->{TITLE}=$1;
        next DBENTRY;
      }
      if (/^\s*VLABEL\s+(.*)\s*$/) {
        $self->{DB}->{$db_name}->{VLABEL}=$1;
        next DBENTRY;
      }
      if (/^\s*MATCH\s+(.*)\s*$/) {
        if ($open_match) {
          $self->{msg} = "Error parsing $dbconf_file : Unmatched MATCH/ENDMATCH 
at line $line";
          $self->{status} = FATAL;
          return undef;
        }
        $match_name=$1;
        $open_match = 1;

        #
        # Add this to the list of matches
        #
        push @{$self->{DB}->{$db_name}->{MATCHLIST}}, $match_name;
        #
        # Reset the counter for this MATCH
        #
        $self->{DB}->{$db_name}->{MATCH}->{$match_name}->{COUNTER} = 0;

        next DBENTRY;
      }
      if (/^\s*ENDMATCH\s*$/) {
        if (! $open_match) {
          $self->{msg} = "Error parsing $dbconf_file : Unmatched MATCH/ENDMATCH 
at line $line";
          $self->{status} = FATAL;
          return undef;
        }
        $open_match = 0;
        next DBENTRY;
      }
      if (/^\s*[Cc]olor\s+(\S+)\s*$/) {

        #
        # Handle old Color parameter (without 'eq' operator) for compatibility 
with old configs
        #
        $filter_parameter = Trim($1);
        unless ($open_match){
          $self->{msg} = "Error parsing $dbconf_file : Color parameter not 
allowed outside a MATCH at line $line";
          $self->{status} = FATAL;
          return undef;
        }
        if ($filter_parameter !~ /^\#[\d\w]{6}$/) {
          $self->{msg} = "Color definition ($filter_parameter) must be 
\'#abcdef\'! Error at line $line";
          $self->{status} = FATAL;
          return undef;
        }
        $self->{DB}->{$db_name}->{MATCH}->{$match_name}->{COLOR} = 
$filter_parameter;
        next DBENTRY;
      }
      if (/^\s*(\S+)\s+(\S+)\s+(.*)\s*$/) {
        if (! $open_match) {
          $self->{msg} = "Error parsing $dbconf_file : Unmatched MATCH/ENDMATCH 
at line $line";
          $self->{status} = FATAL;
          return undef;
        }
        
        # Check whether the third parameter of the filter contains
        # multiple values
        $filter_key = lc($1);
        $filter_operator = lc($2);
        $filter_parameter = Trim($3);

        #
        # Check whweter the operator is valid
        #
        unless (($filter_operator eq "eq") ||
                ($filter_operator eq "ne")) {
          $self->{msg} = "Invalid operator $filter_operator, must be either 
'eq' or 'ne' at line $line";
          $self->{status} = FATAL;
          return undef;
        }

        #
        # Check whether the key is Color (special case)
        #
        if ($filter_key eq 'color') {

          #
          # If an operator is specified within a Color entry, it must be 'eq' 
          #
          if ($filter_operator ne "eq") {
            $self->{msg} = "Color operator must be \'eq\'! Error at line $line";
            $self->{status} = FATAL;
            return undef;
          }
          if ($filter_parameter !~ /^\#[\d\w]{6}$/) {
            $self->{msg} = "Color definition ($filter_parameter) must be 
\'#abcdef\'! Error at line $line";
            $self->{status} = FATAL;
            return undef;
          }
          $self->{DB}->{$db_name}->{MATCH}->{$match_name}->{COLOR} = 
$filter_parameter;
          next DBENTRY;
        }
        if ($filter_parameter =~ m/\,/) {
          
          #
          # Split the filter into a sequence
          #
          my $first_entry = 1;
          foreach (split ('\,', $filter_parameter)) {
            if ($first_entry) {
              push 
@{$self->{DB}->{$db_name}->{MATCH}->{$match_name}->{FILTER}},"$filter_key,$filter_operator,$_";
              $first_entry = 0;
            } else {
              push 
@{$self->{DB}->{$db_name}->{MATCH}->{$match_name}->{FILTER}},"$sequence,$filter_operator,$_";
            }
          }
        } else {
          
          #
          # Insert the normal filter into the list
          #
          push 
@{$self->{DB}->{$db_name}->{MATCH}->{$match_name}->{FILTER}},"$filter_key,$filter_operator,$filter_parameter";
        }
      }
    }
  }
  
  if ($open_db) {
    $self->{msg} = "Error parsing $dbconf_file : Unmatched DB/ENDDB at line 
$line";
    $self->{status} = FATAL;
    return undef;
  }
  if ($open_match) {
    $self->{msg} = "Error parsing $dbconf_file : Unmatched MATCH/ENDMATCH at 
line $line";
    $self->{status} = FATAL;
    return undef;
  }

  if ($self->{debug}) {

    #
    # DB level
    # 
    foreach $db (keys %{$self->{DB}}) {
      print "DB => ", $db, "\n";
      
      #
      # DB match level
      #
      foreach $match (@{$self->{DB}->{$db}->{MATCHLIST}}) {
        print "\tMATCH => ", $match, "\n";
        foreach $key (keys %{$self->{DB}->{$db}->{MATCH}->{$match}}) {
          print "\t\tKEY => ", $key, "\n";
          $value = $self->{DB}->{$db}->{MATCH}->{$match}->{$key};
          if ($key ne "FILTER") {
            print "\t\t\tVALUE => ", $value, "\n";
          } else {
            foreach (@{$self->{DB}->{$db}->{MATCH}->{$match}->{FILTER}}) {
              print "\t\t\tFILTER => ", $_, "\n";
            }
          }
        }
      }
    }
  }

  return OK
}
    
sub LogMatch() {
  
  # 
  # Take a reference to a FW log entry and check it against the user defined 
criteria
  # incrementing the counter for the matches found
  #
  my $self = shift;
  my $param = Params(@_);

  my $log_reference;
  unless ($log_reference = $self->ThisParam('log', $param)) {
    $self->{msg} = "Log reference not specified";
    $self->{status} = FATAL;
    return undef
  }
  
  my $sequence = SEQUENCE;
  my %log = %{$log_reference};
  
  use vars qw ($db $match $match_entry $log_entry $keyword $previous_keyword 
$operator $value $log_value @result);

 DB: foreach $db (keys %{$self->{DB}}) {
  MATCH: foreach $match_entry (@{$self->{DB}->{$db}->{MATCHLIST}}) {
      $match = 1;
    FILTER: foreach (@{$self->{DB}->{$db}->{MATCH}->{$match_entry}->{FILTER}}) {
        ($keyword,$operator,$value) = split ',' , $_; 

        #
        # No reason to continue checking if the previous filter didn't match
        #
        next MATCH if ((! $match) && ($keyword ne $sequence));

        #
        # Make all the parameters lower-case
        #
        # $keyword=lc($keyword);
        # $operator=lc($operator);
        # $value=lc($value);

        next if ($keyword eq "color");
        if ($keyword eq $sequence) {
          #
          # Within a OR condition it is sufficient that one and just one
          # parameter matches to match the entire condition
          #
          next FILTER if ($match);
          $keyword = $previous_keyword;
        } else {
          $previous_keyword = $keyword;
        } 

        if (! defined ($log{$keyword})) {
          if (! open TMPLOG, ">>$self->{Config}->{SERVERTMPDIR}/unusual.log") {
            $self->{msg} = "Cannot write onto $self->{Config}->{SERVERTMPDIR}: 
$?";
            $self->{status} = FATAL;
            return undef;
          }
          print TMPLOG "Not defined keyword $keyword found in filter 
$match_entry.\nLog entry :\n";
          foreach (keys %log) {
            print TMPLOG $_,"\t=>\t",$log{$_},"\n";
          }
          close TMPLOG;
          next MATCH;
        }

        $log_value = Trim($log{$keyword});
      
        if ($value =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/) {
          #
          # Check range
          #
          $match = &InRange($operator, $log_value, $1, $2);
        
        } elsif ($value =~ /^\/.*\/$/) {
          #
          # Check similarities
          #
          $operator = ($operator eq "eq")? "=~":"!~";
          $match = eval "\"$log_value\" $operator $value";
        
        } else {
          #
          # Check exact matches
          #
          $match = eval "\"$log_value\" $operator \"$value\"";
        }
      }
      $self->{DB}->{$db}->{MATCH}->{$match_entry}->{COUNTER}++ if ($match);
    }
  }
  return
}

sub MakeGraphics {
  
  #
  # Generates the graphics for each User DB 
  #
  my $self = shift;
  my $max_label_size = 30;

  use vars qw ($db %tag $time $lcimgformat $match $match_name $match_in_minutes 
$average $param $start_time $start $stop_time);

  $param = Params(@_);
  $start_time = $self->ThisParam('start', $param);
  $stop_time = ($start_time) ? ($start_time+DAY):(time());

  my %tag = (
             '86400' => "day",
             '604800' => "week",
             '2419200' => "month",
             '31536000' => "year",
            );

  foreach $db (keys %{$self->{DB}}) {

    my $average="(n/a)";
    foreach $time ((DAY, WEEK, MONTH, YEAR)) {
      my $lcimgformat=lc($self->{Config}->{IMGFORMAT});
      @RRDsARGS = 
"$self->{Config}->{CLIENTIMAGEPATH}/$db\-$tag{$time}.$lcimgformat";
      push @RRDsARGS, "--start";
      if (defined $start_time) {
        $start = $start_time-$time;
        push @RRDsARGS, $start;
      } else {
        push @RRDsARGS, -$time;
      }
      push @RRDsARGS, ("--end", $stop_time);
      push @RRDsARGS, ("-t", $self->{DB}->{$db}->{TITLE}) if (defined 
$self->{DB}->{$db}->{TITLE});;
      push @RRDsARGS, ("-v", $self->{DB}->{$db}->{VLABEL}) if (defined 
$self->{DB}->{$db}->{VLABEL});;
      push @RRDsARGS, ("--imgformat", $self->{Config}->{IMGFORMAT});
      foreach $match_name (@{$self->{DB}->{$db}->{MATCHLIST}}) {
        $match_in_minutes = $match_name . "_in_minutes";
        
        push @RRDsARGS, 
"DEF:$match_name=$self->{Config}->{CLIENTDBPATH}/$db\.rrd:$match_name:AVERAGE";
        push @RRDsARGS, "CDEF:$match_in_minutes=$match_name,60,*";

        $average = ($time == DAY) ? ("(" . 
$self->{DB}->{$db}->{MATCH}->{$match_name}->{AVERAGE} . ")"):"" if (defined  
$self->{DB}->{$db}->{MATCH}->{$match_name}->{AVERAGE});

        push @RRDsARGS, "GPRINT:$match_in_minutes:AVERAGE:Avg %2.1lf";
        push @RRDsARGS, "GPRINT:$match_in_minutes:MAX:Max %2.1lf";
        push @RRDsARGS, "GPRINT:$match_in_minutes:LAST:Current %2.1lf";

        my $label = $match_name . (' ' x ($max_label_size - 
length($match_name)));
        push @RRDsARGS, 
"AREA:$match_in_minutes$self->{DB}->{$db}->{MATCH}->{$match_name}->{COLOR}:$label";
        push @RRDsARGS, "COMMENT:\\r";

      }

      RRDs::graph(@RRDsARGS);
      my $ERR=RRDs::error();
      if ($ERR) {
        $self->{msg} = "ERROR while creating graphic for $db: $ERR";
        $self->{status} = FATAL;
        return undef;
      }
    }
  }
  return OK
}

sub MakeDB {
  
  #
  # Generates the graphics for each User DB 
  #
  my $self = shift;
  my $param = Params(@_);
  
  my $overwrite = $self->ThisParam('overwrite', $param);
  my $start = $self->ThisParam('start', $param);
  
  #
  #  Handle the specifed DB start date
  #
  if (defined $start) {

    unless ($start =~ m/(\d{4})\/(\d{1,2})\/(\d{1,2})/) {
      die "$prog: wrong DB start date format ($start). Specify YYYY/MM/DD";
    } 
    
    my $year = $1 - 1900;
    my $month = $2 - 1;
    my $day = $3;
    
    $start = timelocal(0,0,0,$day,$month,$year);
    $start--
  }

  # Prevent data inconsistency by zeroing the counters
  unlink ("$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data") if (-r 
"$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data");

  #
  # RDD time settings
  # 
  my %archive_keep = (
                      halfanhour => int((30 * MINUTE) / 
$self->{Config}->{FREQUENCY}),
                      twohours => int((120 * MINUTE) / 
$self->{Config}->{FREQUENCY}),
                      oneday => int(DAY / $self->{Config}->{FREQUENCY}),
                      twodays => int((2 * DAY) / $self->{Config}->{FREQUENCY}),
                      twoweeks => int((14 * DAY) / 
$self->{Config}->{FREQUENCY}),
                      fiftydays => int((50 * DAY) / 
$self->{Config}->{FREQUENCY}),
                      twoyears => int((797 * DAY) / 
$self->{Config}->{FREQUENCY}),
                     );
  
 DB: foreach $db (keys %{$self->{DB}}) {
    
    if (-w "$self->{Config}->{CLIENTDBPATH}/$db.rrd") {
      if (! $overwrite) {
        print "Skipped existing database 
$self->{Config}->{CLIENTDBPATH}/$db.rrd, specify --overwrite to overwrite.\n";
        next DB;
      }
    }

    # Prepare the RRDs::create command
    @RRDsARGS = ("$self->{Config}->{CLIENTDBPATH}/$db.rrd", "--step", 
$self->{Config}->{FREQUENCY});
    push @RRDsARGS, ("--start", $start) if (defined $start);

    foreach $match (@{$self->{DB}->{$db}->{MATCHLIST}}) {
      push @RRDsARGS, "DS:$match:COUNTER:$self->{Config}->{TIMEOUT}:U:U";
    }
    push @RRDsARGS, "RRA:AVERAGE:0.5:1:$archive_keep{twodays}";
    push @RRDsARGS, 
"RRA:AVERAGE:0.5:$archive_keep{halfanhour}:$archive_keep{twoweeks}";
    push @RRDsARGS, 
"RRA:AVERAGE:0.5:$archive_keep{twohours}:$archive_keep{fiftydays}";
    push @RRDsARGS, 
"RRA:AVERAGE:0.5:$archive_keep{oneday}:$archive_keep{twoyears}";
    
    # Execute the RRDs command
    RRDs::create (@RRDsARGS);

    my $ERR=RRDs::error();
    if ($ERR) {
      $self->{msg} = "ERROR while creating DB $db: $ERR";
      $self->{status} = FATAL;
      return undef;
    }
  }
  return OK
}

sub UpdateDB {
  
  #
  # Update the counters for all the DB's
  #
  my $self = shift;
  my $param = Params(@_);
  
  my $logtime = $self->ThisParam('logtime', $param);

  use vars qw ($RRDsCMD $match);
  
 DB: foreach $db (keys %{$self->{DB}}) {

    if (! -w "$self->{Config}->{CLIENTDBPATH}/$db.rrd") {
      $self->{msg} = "Cannot write database 
$self->{Config}->{CLIENTDBPATH}/$db.rrd";
      $self->{status} = FATAL;
      return undef;
    }

    # Prepare the RRDs::update command
    @RRDsARGS = "$self->{Config}->{CLIENTDBPATH}/$db.rrd";
    $RRDsCMD = "$logtime";
    foreach $match (@{$self->{DB}->{$db}->{MATCHLIST}}) {
      $RRDsCMD .= ":" . $self->{DB}->{$db}->{MATCH}->{$match}->{DATA};
    }
    push @RRDsARGS, $RRDsCMD;
    
    # Execute the RRDs command
    
    RRDs::update (@RRDsARGS);

    my $ERR=RRDs::error();
    if ($ERR) {
      $self->{msg} = "ERROR while updating DB $db: $ERR";
      $self->{status} = FATAL;
      return undef;
    }
  }
}

sub GenerateHtml {

  my $self=shift;
  
  use vars qw($fwgoldlogo $IMGFORMAT $INDEXHTML $DBName $HTML $time);
  
  $fwgoldlogo="fwgold.jpg";
  $IMGFORMAT=lc($self->{Config}->{IMGFORMAT});
  
  # Build the index html header
  $INDEXHTML=<<EOV;
<HTML><HEAD><META HTTP-EQUIV="Refresh" CONTENT="300">
 <TITLE>Firewall-1 Statistics</TITLE></HEAD>
 <BODY BGCOLOR="#FFFFFF"><CENTER>
  <H1>Firewall-1 Statistics</H1>
EOV
            
 DB: foreach $DBName (keys %{$self->{DB}}) {
    
    # Create an HTML for each DATABASE
    $HTML=<<EOV;
<HTML><HEAD><META HTTP-EQUIV="Refresh" CONTENT="300">
 <TITLE>Firewall-1 Statistics of $DBName</TITLE></HEAD>
 <BODY BGCOLOR="#FFFFFF"><CENTER>
  <H1>Firewall-1 Statistics of $DBName</H1>
EOV
  
    foreach $time (qw(day week month year)) {
      $HTML .= "<P><IMG SRC=\"$DBName-$time.$IMGFORMAT\"></P>\n";
    }
    
    # Put a the fwgold log and close the HTML
    $HTML .= "<BR><BR><EM><FONT SIZE=-1>Firewall Log Statistics created 
by</FONT></EM><BR><A HREF=\"http://www.rotoni.com/FwGold\";><IMG 
SRC=\"$fwgoldlogo\"></A></BODY></HTML>";
    
    # Save the HTML file for the current database
    if (! open (HTML, ">$self->{Config}->{CLIENTIMAGEPATH}/$DBName.html")) {
      $self->{msg} = "Cannot write to 
$self->{Config}->{CLIENTIMAGEPATH}/$DBName.html";
      $self->{status} = FATAL;
      return undef;
    }
    print HTML $HTML;
    close HTML;
    
    # Add a pointer to the database HTML file into
    # the index file
    $INDEXHTML .= "<P><A HREF=\"$DBName.html\"><IMG 
SRC=\"$DBName-day.$IMGFORMAT\"></A></P>\n";
  }
  
  # Put a the fwgold log and close the HTML
  $INDEXHTML .= "<BR><BR><EM><FONT SIZE=-1>Firewall Log Statistics created 
by</FONT><BR></EM><A HREF=\"http://www.rotoni.com/FwGold\";><IMG 
SRC=\"$fwgoldlogo\"></A></BODY></HTML>";
  
  # Save the index HTML file
  if (! open (HTML, 
">$self->{Config}->{CLIENTIMAGEPATH}/$self->{Config}->{INDEXHTMLNAME}")) {
    $self->{msg} = "Cannot write to 
$self->{Config}->{CLIENTIMAGEPATH}/$self->{Config}->{INDEXHTMLNAME}";
    $self->{status} = FATAL;
    return undef;
  }
  print HTML $INDEXHTML;
  close HTML;
  return OK
}

sub ProcessData {

  #
  # Take a string of server's collected data and return the refernce
  # to the processed data hash
  #
  my $self = shift;
  my $param = Params(@_);

  my $data;
  unless ($data = $self->ThisParam('data', $param)) {
    $self->{msg} = "Log data not specified";
    $self->{status} = FATAL;
    return undef;;
  }
  
  use vars qw(@db_data $db $db_name $match $match_values %new_collected_data 
%reset_collected_data %prev_collected_data $match_name $value $new_time 
$previous_time $new_log $previous_log);
  
  #
  # Extract the time when the data have been produced
  #
  $new_log = $data;
  $data =~ m/(\d+):(.*)\s*$/;
  $new_time = $1;
  $data = $2;
  
  #
  # Split the string into a list of collected data for each DB
  #
  @db_data = split /\//, $data;
  
  #
  # Split each DB's data into a list of matches' data
  #
  foreach $db (@db_data) {
    next if !($db =~ m/([^:]+)::{0,1}(.*)$/);
    $db_name = $1;
    $match_values = $2;
    print "db : $db\ndbname : $db_name\nvalue : $match_values\n\n" if 
($self->{debug});
    
    #
    # Assign the collected data to the relative DB's match
    #
    foreach $match (split (/\:/, $match_values)) {
      ($match_name,$value) = split /\=/, $match;
      print "match name : $match_name\nvalue : $value\n\n" if ($self->{debug});
      
      $new_collected_data{$db_name}->{$match_name}->{DATA}=$value;
      $reset_collected_data{$db_name}->{$match_name}->{DATA}="U";
    }
  }
  
  if (-r "$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data") {
    open (PREV, "<$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data");
    $previous_log = <PREV>;
    close PREV;

    if (defined $previous_log) {
      
      #
      # Extract the time when the previous data have been produced
      # 
      $previous_log =~ m/(\d+):(.*)\/\s*$/;
      $previous_time = $1;
      $previous_log = $2;
      print "new time : $new_time\nprevious time : $previous_time\nprevious log 
: $previous_log\n\n" if ($self->{debug});
      
      #
      # Check that the timeout hasn't experied
      #
      if (($new_time - $previous_time) < $self->{Config}->{TIMEOUT}) {

        #
        # Split the string into a list of collected data for each DB
        #
        @db_data = split /\//, $previous_log;
        
        #
        # Split each DB's data into a list of matches' data
        #
        foreach $db (@db_data) {
          next if !($db =~ m/([^:]+)::{0,1}(.*)$/);
          $db_name = $1;
          $match_values = $2;
          print "previous db : $db\ndbname : $db_name\nvalue : 
$match_values\n\n" if ($self->{debug});

          #
          # Assign the collected data to the relative DB's match
          #
          foreach $match (split (/\:/, $match_values)) {
            ($match_name,$value) = split /\=/, $match;
            print "previous match name : $match_name\nvalue : $value\n\n" if 
($self->{debug});
            
            $prev_collected_data{$db_name}->{$match_name}->{DATA}=$value;
          }
        }
        
        #
        # Calculate the last time period values
        #
        foreach $db (keys %new_collected_data) {
          foreach $match (keys %{$new_collected_data{$db}}) {
            if ($new_collected_data{$db}->{$match}->{DATA} < 
$prev_collected_data{$db}->{$match}->{DATA}) {
              unlink "$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data";
              return \%reset_collected_data;
            }
            $new_collected_data{$db}->{$match}->{AVERAGE} = 
$new_collected_data{$db}->{$match}->{DATA} - 
$prev_collected_data{$db}->{$match}->{DATA};
          }
        }
      }
    }
  }
  
  if (! open PREV, ">$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data") {
    $self->{msg} = "Cannot write onto 
$self->{Config}->{CLIENTTMPDIR}/fwgoldlast.data: $!";
    $self->{status} = FATAL;
    return undef;
  }
  print PREV $new_log;
  close PREV;
  return \%new_collected_data;
}

sub Usage() {
  
  #
  # Display the usage with an optional a message
  #
  my $self = shift;
  my $param = Params(@_);
  my $msg = $self->ThisParam('msg', $param);
  
  print STDERR <<EOF;
FwGold - FW-1(r) Graphics Oriented Log (analysis) Database

$msg
Usage: fwgold --help (prints this message) 

       fwgold --version

       fwgold --configuration <path_to_fwgold.conf>
              --mode server
              [ --checkconfig (verifies the server configuration) ]

       fwgold --configuration <path_to_fwgold.conf>
              --mode client
              [ --checkconfig (verifies the client configuration) ]
              [ --checkconfig (verifies the client configuration) ]
              [ --init (initializes the rrd databases) [--start <YYYY/MM/DD>]]]
              [ --overwrite (overwrite existing rrd databases) ]
              [ --updatedb (updates the rrd databases) [ --offline 
<fwlog_ascii_file> [--start <YYYY/MM/DD>]]]
              [ --graphics (produces the graphics) ]
              [ --makehtml (generates the html index pages) ]
EOF
return OK
}

  sub start {
    my $self = shift;
    my $val = shift;
    if (@_) {
      if ($val eq "fwgold") {
        shift;
        my $new_time = shift;
      } 
      if ($val eq "db") {
        shift;
        my $current_db = shift;
      } 
      if ($val eq "match") {
        shift;
        my $match_name = shift;
        shift;
        my $value = shift;
        #$new_collected_data{$current_db}->{$match_name}->{DATA}=$value;
        #$reset_collected_data{$db_name}->{$match_name}->{DATA}="U";
      } 
    }
  }

sub end {
  my ($self, $val) = @_;
  #if ($self->current_element eq "config") {
  #  push @config, $self->xml_escape($val, '>', "\xD");
  #}
}

sub ParseData {
  my $log = shift;
  my $parser = new XML::Parser(ErrorContext => 2,
                               Handlers     => {
                                                Start => \&start,
                                                End   => \&end,
                                               }); 
    
  $parser->parse($log);
  
}

sub ReadDataFromServer {

  #
  # Read the data from the server and return the read string
  #
  my $self = shift;
  
  #
  # Open the connection to the server
  #
  my $remote = $self->{Config}->{SERVERHOST};
  my $port = $self->{Config}->{DATAPORT};
  unless ((defined $remote) &&
          (defined $port)) {
    $self->{msg} = ("Server host or port number undefined");
    $self->{status} = FATAL;
    return undef;
  }
    
  if ($self->{debug}) {
    print "Opening connection to $self->{Config}->{SERVERHOST} on port 
$self->{Config}->{DATAPORT}\n";
  }

  if ($port =~ /\D/) {
    $port = getservbyname($port, 'tcp');
  }
  unless ($port) {
    $self->{msg} = ("Not a valid TCP port");
    $self->{status} = FATAL;
    return undef;
  }
  my $iaddr = inet_aton($remote);
  my $paddr = sockaddr_in($port, $iaddr);

  my $proto = getprotobyname('tcp');
  if (! socket(SOCK, PF_INET, SOCK_STREAM, $proto)) {
    $self->{msg} = "socket: $!";
    $self->{status} = FATAL;
    return undef;
  }
  if (! connect (SOCK, $paddr)) { 
    $self->{msg} = "No service running on host $remote port $port";
    $self->{status} = FATAL;
    return undef;
  }
  
  #
  # Send Ready to Receive
  #
  syswrite SOCK, TOKEN, 2;
 
  #
  # return the read the DATA
  #
  return <SOCK>;

}

sub ErrorInConfig {
  
  my $self = shift;
  $self->{msg} = "";
  
  #
  # Check Common options
  #
  $self->{msg} .= "DATAPORT must be assigned\n" if (! 
$self->{Config}->{DATAPORT});
  
  #
  # Check Client's only config
  #
  if ((! defined $self->{mode}) ||
      ($self->{mode} eq "client")) {
    $self->{msg} .= "SERVERHOST must be assigned\n" if (! 
$self->{Config}->{SERVERHOST});
    $self->{msg} .= "Cannot write to $self->{Config}->{CLIENTTMPDIR}\n" unless 
((-w $self->{Config}->{CLIENTTMPDIR}) && (-d _));
    $self->{msg} .= "Cannot access $self->{Config}->{CLIENTDBPATH}\n" unless 
((-r $self->{Config}->{CLIENTDBPATH}) && (-d _));
    $self->{msg} .= "Cannot write to $self->{Config}->{CLIENTIMAGEPATH}\n" 
unless ((-w $self->{Config}->{CLIENTIMAGEPATH}) && (-d _));
    $self->{msg} .= "Wrong image format\n" if (($self->{Config}->{IMGFORMAT} ne 
"GIF") && ($self->{Config}->{IMGFORMAT} ne "PNG"));
    foreach $db (keys %{$self->{DB}}) {
      $self->{msg} .= "Cannot read $db! Forgot to create database? Use --init 
in case\n" if (! -r "$self->{Config}->{CLIENTDBPATH}/$db.rrd")
    } 
  }
  
  #
  # Check Server's only config
  #
  if ((! defined $self->{mode}) ||
      ($self->{mode} eq "server")) {
    $self->{msg} .= "Cannot write to $self->{Config}->{SERVERTMPDIR}\n" if (! 
-w $self->{Config}->{SERVERTMPDIR});
    
    #
    # The following only works on Posix systems (ie. not on NT)
    #
    $self->{msg} .= "Cannot execute $self->{Config}->{FWBINPATH}/fw\n" if (! -x 
"$self->{Config}->{FWBINPATH}/fw")
  }    
  
  return $self->{msg};
}

sub InRange() {
  
  #
  # Determines whether an IP address belongs to a network
  #
  my ($op, $ip, $net, $mask) = @_;
  
#
# Converts all human readable values into computer usable format
#
my $ipnum=ip2num($ip);
my $netnum=ip2num($net);
my $bcastnum=broadcast($net,$mask);

my $res = (($ipnum >= $netnum) &&
           ($ipnum <= ($netnum + $bcastnum)));
return ($op =~ /eq/)? $res : !$res;
}

sub ip2num {

  #
  # Given an IP address, it returns its long int version
  #
  my $ip = shift;
  my @numip=split /\./ , $ip;
  return Math::BigInt->new(((shift @numip)<<24) +
                           ((shift @numip)<<16) +
                           ((shift @numip)<<8) +
                           (shift @numip));
}

sub broadcast {

  #
  # Given a network and netmask addresses, it returns the long int
  # version of the relative broadcast 
  #
  my ($ip,$mask) = @_;

  my $base=ip2num($ip);
  return Math::BigInt->new((2**(32-$mask))-1);
}

sub Trim {
  my $x = shift;
  $x =~ s/^\s+//;
  $x =~ s/\s+$//;
  return $x;
}


=================================================
        Archives of this mailing list's 
         messages ca be retrieved from
 //www.freelists.org/archives/fwgold-users 
=================================================
     To unsubscribe from this maling list
send a mail to fwgold-users-request@xxxxxxxxxxxxx
     with the word "unsubscribe" as subject.
=================================================
   To administer your account visit the site :
   //www.freelists.org/cgi-bin/lsg2.cgi
=================================================

Other related posts: