#!/usr/bin/perl ############################################################################### # # Copyright (c) 2018 Artica Soluciones Tecnologicas S.L. # # grep_log 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; use IO::Compress::Zip qw(zip $ZipError); use MIME::Base64; # Be verbose my $Verbose = 0; # Index file storage directory, with a trailing '/' my $Idx_dir=($^O =~ /win/i)?'.\\':'/tmp/'; # Log file my $Log_file = ''; # 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; ############################################################################### # 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 \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 = ; ($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; # Matched line id my $matched_line = 0; $/ = undef; $data = ; $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 = shift; my $zdata; return unless defined($data) and $$data ne ''; if (!zip($data => \$zdata)) { error_msg("Compression error: $ZipError"); return; } print STDOUT "\n"; } ############################################################################### ############################################################################### ## Main ############################################################################### ############################################################################### # Check command line parameters if ($#ARGV < 0) { print_help(); exit 1; } $Log_file = $ARGV[0]; # 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 . 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;