pandorafms/pandora_agents/unix/plugins/grep_log_module

321 lines
8.1 KiB
Perl
Executable File

#!/usr/bin/perl
###############################################################################
#
# Copyright (c) 2012 Ramon Novoa <rnovoa@artica.es>
# Copyright (c) 2012-2023 Pandora FMS.
#
# grep_log_module Perl script to search log files for a matching pattern. The
# last searched position is saved in an index file so that
# consecutive runs do not return the same results. The log file
# inode number is also saved to detect log rotation.
#
# 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; 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.
#
###############################################################################
use strict;
use File::Basename;
BEGIN {
eval {
require MIME::Base64;
};
}
# Output format (module or log_module).
my $Output = 'log_module';
# Be verbose
my $Verbose = 0;
# Index file storage directory, with a trailing '/'
my $Idx_dir='/tmp/';
# Log file
my $Log_file = '';
# Module name
my $Module_name = "default_log";
# Index file
my $Idx_file = '';
# Log file position index
my $Idx_pos = 0;
# Log file inode number
my $Idx_ino = '';
# Log file size
my $Idx_size = 0;
# Regular expression to be matched
my $Reg_exp = '';
# Define encode_base64 if it is not available via MIME::Base64.
my $encode_sub = defined(&MIME::Base64::encode_base64) ? \&MIME::Base64::encode_base64 : sub {
my ($str, $endl) = @_;
my @ALPHABET = ('A'..'Z', 'a'..'z', 0..9, '+', '/');
my $str_len = length($str);
my $str_base64 = '';
for (my $i = 0; $i < $str_len; $i += 3) {
my $chunk = substr($str, $i, 3);
my $chunk_len = length($chunk);
my $num = 0;
$num |= ord(substr($chunk, 0, 1)) << 16 if ($chunk_len >= 1);
$num |= ord(substr($chunk, 1, 1)) << 8 if ($chunk_len >= 2);
$num |= ord(substr($chunk, 2, 1)) if ($chunk_len == 3);
my $enc_1 = ($num & 0xfc0000) >> 18;
my $enc_2 = ($num & 0x03f000) >> 12;
my $enc_3 = ($num & 0x000fc0) >> 6;
my $enc_4 = ($num & 0x00003f);
$str_base64 .= $ALPHABET[$enc_1];
$str_base64 .= $ALPHABET[$enc_2];
$str_base64 .= $chunk_len >= 2 ? $ALPHABET[$enc_3] : '=';
$str_base64 .= $chunk_len == 3 ? $ALPHABET[$enc_4] : '=';
}
return $str_base64;
};
###############################################################################
# SUB error_msg
# Print an error message and exit.
###############################################################################
sub error_msg ($) {
my $err_msg = $_[0];
if (! -z $err_msg) {
print(stderr "[error] $err_msg.\n");
}
exit 1;
}
###############################################################################
# SUB print_help
# Print a help message.
###############################################################################
sub print_help () {
print "Usage: $0 <log_file> <module_name> <pattern>\n";
}
###############################################################################
# SUB log_msg
# Print a log message.
###############################################################################
sub log_msg ($) {
my $log_msg = $_[0];
if (! -z $log_msg && $Verbose == 1) {
print(stdout "[log] $log_msg.\n");
}
}
###############################################################################
# SUB load_idx
# Load index file.
###############################################################################
sub load_idx () {
my $line;
my $current_ino;
my $current_size;
log_msg("Loading index file $Idx_file");
open(IDXFILE, $Idx_file) || error_msg("Error opening file $Idx_file: " .
$!);
# Read position and date
$line = <IDXFILE>;
($Idx_pos, $Idx_ino, $Idx_size) = split(' ', $line);
close(IDXFILE);
# Reset the file index if the file has changed
$current_ino = (stat($Log_file))[1];
$current_size = -s "$Log_file";
if ($current_ino != $Idx_ino || $current_size < $Idx_size) {
log_msg("File changed, resetting index");
$Idx_pos = 0;
$Idx_ino = $current_ino;
}
$Idx_size = $current_size;
return;
}
###############################################################################
# SUB save_idx
# Save index file.
###############################################################################
sub save_idx () {
log_msg("Saving index file $Idx_file");
open(IDXFILE, "> $Idx_file") || error_msg("Error opening file $Idx_file: "
. $!);
print (IDXFILE $Idx_pos . " " . $Idx_ino . " " . $Idx_size);
close(IDXFILE);
return;
}
###############################################################################
# SUB create_idx
# Create index file.
###############################################################################
sub create_idx () {
my $first_line;
log_msg("Creating index file $Idx_file");
open(LOGFILE, $Log_file) || error_msg("Error opening file $Log_file: " .
$!);
# Go to EOF and save the position
seek(LOGFILE, 0, 2);
$Idx_pos = tell(LOGFILE);
close(LOGFILE);
# Save the file inode number
$Idx_ino = (stat($Log_file))[1];
# Save the index file
save_idx();
return;
}
###############################################################################
# SUB parse_log
# Parse log file starting from position $Idx_pos.
###############################################################################
sub parse_log () {
my $line;
log_msg("Parsing log file $Log_file");
# Open log file for reading
open(LOGFILE, $Log_file) || error_msg("Error opening file $Log_file: " .
$!);
# Go to starting position.
seek(LOGFILE, $Idx_pos, 0);
# Parse log file
my @data;
while ($line = <LOGFILE>) {
if ($line =~ m/$Reg_exp/i) {
push (@data, $line);
}
}
$Idx_pos = tell(LOGFILE);
close(LOGFILE);
# Save the index file
save_idx();
return @data;
}
###############################################################################
# SUB parse_log
# Print log data to stdout.
###############################################################################
sub print_log (@) {
my @data = @_;
# No data
if ($#data < 0) {
return;
}
# Log module
if ($Output eq 'log_module') {
my $output = "<log_module>\n";
$output .= "<source><![CDATA[" . $Module_name . "]]></source>\n";
$output .= "<encoding>base64</encoding>\n";
$output .= "<data><![CDATA[";
$output .= &$encode_sub(join('', @data), '');
$output .= "]]></data>";
$output .= "</log_module>\n";
print stdout $output;
}
# Regular module
else {
my $output = "<module>\n";
$output .= "<name><![CDATA[" . $Module_name . "]]></name>\n";
$output .= "<type><![CDATA[async_string]]></type>\n";
$output .= "<datalist>\n";
foreach my $line (@data) {
$line =~ s/\]\]/]]]]><![CDATA[/g;
$output .= "<data><value><![CDATA[$line]]></value></data>\n";
}
$output .= "</datalist>\n";
$output .= "</module>\n";
print stdout $output;
}
}
###############################################################################
###############################################################################
## Main
###############################################################################
###############################################################################
# Check command line parameters
if ($#ARGV != 2) {
print_help();
exit 1;
}
$Log_file = $ARGV[0];
$Module_name = $ARGV[1];
$Reg_exp = $ARGV[2];
# Create index file storage directory
if ( ! -d $Idx_dir) {
mkdir($Idx_dir) || error_msg("Error creating directory $Idx_dir: "
. $!);
}
# Check that log file exists
if (! -e $Log_file) {
error_msg("File $Log_file does not exist");
}
# Create index file if it does not exist
$Idx_file=$Idx_dir . $Module_name . "_" . basename($Log_file) . ".idx";
if (! -e $Idx_file) {
create_idx();
exit 0;
}
# Load index file
load_idx();
# Parse log file
my @data = parse_log();
# Print output to stdout
print_log (@data);
exit 0;