#!/usr/bin/perl -w

use strict;
use warnings;

# $Id: check_log,v 1.2 2006/11/27 14:41:18 fany Exp $

{

    package _::undef;
    sub new { bless [], __PACKAGE__ }
    sub readline { }
}

use File::ReadBackwards ();
use Getopt::Long qw(GetOptions);
use noris::NetSaint ();
use Time::ParseDate qw(parsedate);

use constant PARSEDATE_OPTIONS => ( PREFER_PAST => 1, WHOLE => 1 );

sub my_parsedate {
    my ( $option_name, $option_value ) = @_;
    defined( my $date = parsedate( $option_value, PARSEDATE_OPTIONS ) )
      or die
qq(Ich verstehe das bei -$option_name bergebene Datum "$option_value" nicht.\n);
    $date;
}

my ( $CriticalAge, $WarningAge );
GetOptions(
    'critical-age=s' => sub { $CriticalAge = &my_parsedate },
    'help|?' =>
      sub { exec perldoc => -F => $0 or die "exec('perldoc -F $0'): $!\n" },
    'pattern=s'     => \my $Pattern,
    'warning-age=s' => sub { $WarningAge = &my_parsedate },
) or exit 1;

die "Es muss ein -pattern angegeben werden.\n" unless defined $Pattern;

my $NetSaint = noris::NetSaint->new or die;

my $line;

for ( my ( $age, $log ), my $last_time = '', my $fh = new _::undef ; ; ) {
    until ( defined( $line = $fh->readline ) ) {
        defined( $log = shift )
          or die +(
            defined $age ? "Nur $age Sekunde" . ( $age != 1 && 'n' ) : 'Keine' )
          . " Log-Daten vorhanden.\n";
        $fh = File::ReadBackwards->new($log)
          or die qq(Kann "$log" nicht ffnen: $!\n);
    }
    last if $line =~ /$Pattern/o;
}

unless ( defined $line ) {
    $NetSaint->update( Critical => 'Keine einschlgige Log-Zeile gefunden.' );
}
elsif ( $line !~
/^(\w\w\w (?:[ 0123]\d \w\w\w [ 2]\d\d\d \d\d:\d\d:\d\d \w\w|\w\w\w [ 123]\d \d\d:\d\d:\d\d)) /
  )
{
    $NetSaint->update( Critical => "Kein Timestamp gefunden: $line" );
}
elsif ( !defined( my $timestamp = parsedate( $1, PARSEDATE_OPTIONS ) ) ) {
    $NetSaint->update( Critical => "Kann Timestamp nicht analysieren: $line" );
}
else {
    my $age_in_seconds = time - $timestamp;
    $NetSaint->update(
          defined $CriticalAge && $timestamp < $CriticalAge ? 'Critical'
        : defined $WarningAge  && $timestamp < $WarningAge  ? 'Warning'
        : 'OK' => "$age_in_seconds Sekunde"
          . ( $age_in_seconds != 1 && 'n' )
          . " alte Log-Zeile gefunden: $line"
    );
}

__END__

=head1 NAME

check_log -- Nagios-Plugin zur Suche nach Log-Eintrgen

=head1 SYNOPSE

  check_log /var/log/omni/inet.log	\
    -critical-age '30 hours ago'	\
    -pattern ' \[ADMINISTRATOR\.GORLEBEN@gorleben\.winzone\.noris\.de\] : \.util$'

Durchsucht L<rckwrts|File::ReadBackwards> die angegebenen Log-Dateien (aber
jede fr sich rckwrts. d. h. hier zuerst C</var/log/exim4/mainlog>, dann ggf.
C</var/log/exim4/mainlog.1>), bis es auf eine zum angegebenen
L<Muster|-pattern regulrer_Ausdruck> passende Zeile stt.

=head1 NOTWENDIGE ARGUMENTE

=over 4

=item -pattern regulrer_Ausdruck

Muster, nach dem gesucht werden soll

=back

=head1 OPTIONEN

=over 4

=item -critical-age Zeitpunkt

ist die letzte passende Zeile lter als der hier in einem fr
L<Time::ParseDate::parsedate()|Time::ParseDate> verstndlichen Format angegebene
Zeitpunkt, wird ein kritischer Alarm erzeugt.

=item -warning-age Zeitpunkt

ist die letzte passende Zeile lter als der hier in einem fr
L<Time::ParseDate::parsedate()|Time::ParseDate> verstndlichen Format angegebene
Zeitpunkt, wird eine Warnung erzeugt.

=item help|?

um (nur) diese Dokumentation anzeigen zu lassen

=back

=head1 AUTOR

 Martin H. Sluka <fany@noris.net>
 fr die noris network AG
 RT#303993

=cut

