#!/usr/bin/perl -w

=head1 NAME

octo_uparser - Octopussy UParser program

=head1 SYNOPSIS

octo_uparser <device>

=head1 DESCRIPTION

octo_uparser is the program used by the Octopussy Project 
to parse Unknown Logs for each Device

=cut

use strict;
use bytes; # Fix the 'Malformed UTF-8 character' warning
use utf8;
use File::Copy;
use File::Path;
use Octopussy;

my $PROG_NAME = "octo_uparser";
my $dir_pid = Octopussy::Directory("running");
my $device = $ARGV[0];

my @services;
my %service;
my %dir_data = ();
my (%known_msgs, @unknown_msgs) = ((), (), ());
my ($file, $last_file) = (undef, undef);
my $pid_file = Octopussy::PID_File($PROG_NAME . "_$device");
my $exit_request = 0;

=head1 FUNCTIONS

=head2 Exit()

Softly stops the uparser 
(current file is fully parsed before exiting)

=cut
sub Exit
{
	$exit_request = 1;
}

=head2 Init()

Inits UParser

=cut
sub Init()
{
	my $dconf = Octopussy::Device::Configuration($device);
	my $storage = Octopussy::Storage::Default();
	@services = Octopussy::Device::Services($device);	

	$dir_data{"Unknown"} = Octopussy::Storage::Directory_Unknown($device);
	foreach my $serv (@services)
 	{
		$dir_data{$serv} = Octopussy::Storage::Directory_Service($device, $serv);
  	my @msg_to_parse = ();
   	my @messages = Octopussy::Service::Messages($serv);
		AAT::Syslog($PROG_NAME, "Init Service: $serv - " 
			. ($#messages + 1) . " messages");
   	foreach my $m (@messages)
    { 
			my $regexp = Octopussy::Message::Pattern_To_Regexp($m);
      push(@{$service{$serv}{msgs}}, 
				{ re => qr/^$regexp\s*[^\t\n\r\f -~]?$/i } );
		}
	}
	($file, $last_file) = (undef, undef);
	(%known_msgs, @unknown_msgs) = ((), ());
}

=head2 Write_Logfile($file, $logs)

Writes Known Logs '$logs' into Logfile '$file'

=cut
sub Write_Logfile($$)
{
	my ($logfile, $logs) = @_;

	if ($#{@{$logs}} >= 0)
	{
		my $dir = $logfile;
		$dir =~ s/^(.+)\/.+?\.log/$1\//g;
		Octopussy::Create_Directory($dir);
		if (defined open(FILE, "|gzip >> $logfile"))
		{
    	foreach my $log (@{$logs})
    		{ print FILE "$log\n"; }
    	close(FILE);
		}
		else
    {
    	print "Unable to open file '$logfile'\n";
    	AAT::Syslog("octo_uparser", "UNABLE_OPEN_FILE", $logfile);
  	}
	}
}


=head2 Write_Unknown_Logfile($file, $logs)

Writes Unknown Logs '$logs' into Logfile '$file'

=cut
sub Write_Unknown_Logfile($$)
{
  my ($logfile, $logs) = @_;
	$logfile =~ s/^.+\/Incoming\//$dir_data{"Unknown"}\/$device\/Unknown\//;
#	$logfile =~ s/\/Incoming\//\/Unknown\//g;
  
	if ($#{@{$logs}} >= 0)
  {
    my $dir = $logfile;
    $dir =~ s/^(.+)\/.+?\.log/$1\//g;
    Octopussy::Create_Directory($dir);
		if (defined open(FILE, "|gzip >> $logfile"))
		{
    	foreach my $log (@{$logs})
      	{ print FILE "$log\n"; }
    	close(FILE);
		}
		else
    {
      print "Unable to open file '$logfile'\n";
      AAT::Syslog("octo_uparser", "UNABLE_OPEN_FILE", $logfile);
    }
  }
}

=head2 Line_Handler($line, $serv, $msg, $known_msgs, $match)

Handles line $line for Service $serv & Message $msg

=cut
sub Line_Handler($$$$$)
{
	my ($line, $serv, $msg, $known_msgs, $match) = @_;

 	if (($line =~ $msg->{re}) || ($line =~ /last message repeated \d+ time/))
  {
    push(@{$known_msgs->{$serv}}, $line);
		$$match = 1;
  }
}

=head2 Service_Handler($serv, $total, $y, $m, $d, $hour, $min, $file_new)

Handles Service $serv

=cut
sub Service_Handler($$$$$$$$)
{
	my ($serv, $total, $y, $m, $d, $hour, $min, $file_new) = @_;	

 	my $service_dir = $serv;
 	$service_dir =~ s/ /_/g;
 	my $dir = "$dir_data{$serv}/$device/$service_dir/$y/$m/$d/";
 	AAT::Syslog($PROG_NAME, "Device: $device - Service: $serv "
  	. "Date: $d/$m/$y $hour:$min - Events: "
    . ($#{$known_msgs{$serv}} + 1) . " / $total");
	Write_Logfile("$dir$file_new", \@{$known_msgs{$serv}});	
}

=head2 File_Handler($file)

Handles file $file

=cut
sub File_Handler($)
{
	my $file = shift;

	if ($file =~ /\/(\d+)\/(\d+)\/(\d+)\/(msg_(\d\d)h(\d\d)\.log)/)	
	{
		my ($y, $m, $d, $file_new, $hour, $min) = ($1, $2, $3, $4, $5, $6);
    (%known_msgs, @unknown_msgs) = ((), ());
		my $total = 0;	
		if (defined open(FILE, ($file =~ /.+\.gz$/ ? "zcat" : "cat") ." \"$file\" |"))
		{
			while (<FILE>)
 			{
  			my $line = $_;
   			chomp($line);
				my $match = 0;
    		foreach my $serv (@services)
    		{
   				foreach my $msg (@{$service{$serv}{msgs}})
      		{ 
						Line_Handler($line, $serv, $msg, \%known_msgs, \$match); 
						last if ($match);
					}
					last if ($match);
				}
				push(@unknown_msgs, $line)	if (! $match);
				$total++;
			}
			close(FILE);
			unlink $file;	
		}
		else
   	{
   		print "Unable to open file '$file'\n";
    	AAT::Syslog("octo_uparser", "UNABLE_OPEN_FILE", $file);
   	}
		foreach my $serv (keys %known_msgs)
			{	Service_Handler($serv, $total, $y, $m, $d, $hour, $min, $file_new); }
		Write_Unknown_Logfile($file, \@unknown_msgs);
	}
}

#
# MAIN
#

$SIG{HUP} = \&Init;
$SIG{USR1} = \&Exit;

AAT::Syslog($PROG_NAME, "Device: $device Started !");
my @files = Octopussy::Logs::Unknown_Files($device);
exit()	if ($#files < 0);
Init();
foreach my $file (@files)
{
	chomp($file);	
 	File_Handler($file); 
	exit	if ($exit_request);
}
AAT::Syslog($PROG_NAME, "Device: $device Stopped !");

=head1 AUTHOR

Sebastien Thebert <octo.devel@gmail.com>

=head1 SEE ALSO

octo_dispatcher, octo_extractor, octo_parser, octo_reporter, octo_scheduler

=cut
