|
Packit |
57988d |
#!/usr/bin/perl
|
|
Packit |
57988d |
#
|
|
Packit |
57988d |
# $Id$
|
|
Packit |
57988d |
|
|
Packit |
57988d |
package Logwatch;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
use strict;
|
|
Packit |
57988d |
use Exporter;
|
|
Packit |
57988d |
use POSIX qw(strftime);
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=head1 NAME
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Logwatch -- Utility functions for Logwatch Perl modules.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=head1 SYNOPSIS
|
|
Packit |
57988d |
|
|
Packit |
57988d |
use Logwatch ':sort';
|
|
Packit |
57988d |
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
## Show CountOrder()
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# Sample Data
|
|
Packit |
57988d |
my %UnknownUsers = (jb1o => 4, eo00 => 1, ma3d => 4, dr4b => 1);
|
|
Packit |
57988d |
my $sortClosure = CountOrder(%UnknownUsers);
|
|
Packit |
57988d |
foreach my $user (sort $sortClosure keys %UnknownUsers) {
|
|
Packit |
57988d |
my $plural = ($UnknownUsers{$user} > 1) ? "s" : "";
|
|
Packit |
57988d |
printf " %-8s : %2d time%s\n", $user, $UnknownUsers{$user}, $plural;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
## Show TotalCountOrder()
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# Sample Data
|
|
Packit |
57988d |
my %RelayDenied = ( some.server => {you@some.where => 2, foo@bar.com => 4},
|
|
Packit |
57988d |
other.server => { foo@bar.com => 14 }
|
|
Packit |
57988d |
);
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $sub = TotalCountOrder(%RelayDenied);
|
|
Packit |
57988d |
foreach my $relay (sort $sub keys %RelayDenied) {
|
|
Packit |
57988d |
print " $relay:\n";
|
|
Packit |
57988d |
my $countOrder = CountOrder(%{$RelayDenied{$relay}});
|
|
Packit |
57988d |
foreach my $dest (sort $countOrder keys %{$RelayDenied{$relay}}) {
|
|
Packit |
57988d |
my $plural = ($RelayDenied{$relay}{$dest} > 1) ? "s" : "";
|
|
Packit |
57988d |
printf " %-36s: %3d Time%s\n", $dest,
|
|
Packit |
57988d |
$RelayDenied{$relay}{$dest}, $plural;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
use Logwatch ':ip';
|
|
Packit |
57988d |
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
## Show SortIP()
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# Sample Data
|
|
Packit |
57988d |
@ReverseFailures = qw{10.1.1.1 172.16.1.1 10.2.2.2 192.168.1.1 };
|
|
Packit |
57988d |
@ReverseFailures = sort SortIP @ReverseFailures;
|
|
Packit |
57988d |
{ local $" = "\n "; print "Reverse DNS Failures:\n @ReverseFailures\n" }
|
|
Packit |
57988d |
|
|
Packit |
57988d |
-or-
|
|
Packit |
57988d |
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
## Show LookupIP()
|
|
Packit |
57988d |
##
|
|
Packit |
57988d |
foreach my $ip (sort SortIP @ReverseFailures) {
|
|
Packit |
57988d |
printf "%15s : %s\n", $ip, LookupIP($ip);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=head1 DESCRIPTION
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This module provides utility functions intended for authors of Logwatch
|
|
Packit |
57988d |
scripts. The purpose is to abstract commonly performed actions into a
|
|
Packit |
57988d |
set of generally available subroutines. The subroutines can optionally
|
|
Packit |
57988d |
be imported into the local namespace.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=over 4
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
our @ISA = qw{Exporter};
|
|
Packit |
57988d |
our @EXPORT;
|
|
Packit |
57988d |
our @EXPORT_OK;
|
|
Packit |
57988d |
our %EXPORT_TAGS = (sort => [qw(CountOrder TotalCountOrder SortIP)],
|
|
Packit |
57988d |
ip => [qw(LookupIP SortIP)],
|
|
Packit |
57988d |
dates => [qw(RangeHelpDM GetPeriod TimeBuild TimeFilter)],
|
|
Packit |
57988d |
);
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Exporter::export_ok_tags(qw{sort ip dates});
|
|
Packit |
57988d |
|
|
Packit |
57988d |
$EXPORT_TAGS{all} = [@EXPORT, @EXPORT_OK];
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<CountOrder(%hash [, $coderef ])>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function returns a closure suitable to be passed to Perl's C<sort>
|
|
Packit |
57988d |
builtin. When two values are passed to the closure, it compares the
|
|
Packit |
57988d |
numeric values of those keys in C<%hash>, and if they're equal, the
|
|
Packit |
57988d |
lexically order of the keys. Thus:
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $sortClosure = CountOrder(%UnknownUsers);
|
|
Packit |
57988d |
foreach my $user (sort $sortClosure keys %UnknownUsers) {
|
|
Packit |
57988d |
my $plural = ($UnknownUsers{$user} > 1) ? "s" : "";
|
|
Packit |
57988d |
printf " %-8s : %2d time%s\n", $user, $UnknownUsers{$user}, $plural;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Will print the keys and values of C<%UnknownUsers> in frequency order,
|
|
Packit |
57988d |
with keys of equal values sorted lexically.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
The optional second argument is a coderef to be used to sort the keys in
|
|
Packit |
57988d |
an order other than lexically. (a reference to C<SortIP>, for example.)
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# Use a closure to abstract the sort algorithm
|
|
Packit |
57988d |
sub CountOrder(\%;&) {
|
|
Packit |
57988d |
my $href = shift;
|
|
Packit |
57988d |
my $coderef = shift;
|
|
Packit |
57988d |
return sub {
|
|
Packit |
57988d |
# $a & $b are in the caller's namespace, moving this inside
|
|
Packit |
57988d |
# guarantees that the namespace of the sort is used, in case
|
|
Packit |
57988d |
# it's different (admittedly, that's highly unlikely), at a
|
|
Packit |
57988d |
# miniscule performance cost.
|
|
Packit |
57988d |
my $package = (caller)[0];
|
|
Packit |
57988d |
no strict 'refs'; # Back off, man. I'm a scientist.
|
|
Packit |
57988d |
my $A = $ {"${package}::a"};
|
|
Packit |
57988d |
my $B = $ {"${package}::b"};
|
|
Packit |
57988d |
use strict 'refs'; # We are a hedge. Please move along.
|
|
Packit |
57988d |
# Reverse the count, but not the compare
|
|
Packit |
57988d |
my $count = $href->{$B} <=> $href->{$A};
|
|
Packit |
57988d |
return $count if $count;
|
|
Packit |
57988d |
if (ref $coderef) {
|
|
Packit |
57988d |
$a = $A;
|
|
Packit |
57988d |
$b = $B;
|
|
Packit |
57988d |
&$coderef();
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
($A cmp $B);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<TotalCountOrder(%hash [, $coderef ])>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function returns a closure similar to that returned by
|
|
Packit |
57988d |
C<CountOrder()>, except that it assumes a hash of hashes, and totals the
|
|
Packit |
57988d |
keys of each sub hash. Thus:
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $sub = TotalCountOrder(%RelayDenied);
|
|
Packit |
57988d |
foreach my $relay (sort $sub keys %RelayDenied) {
|
|
Packit |
57988d |
print " $relay:\n";
|
|
Packit |
57988d |
my $countOrder = CountOrder(%{$RelayDenied{$relay}});
|
|
Packit |
57988d |
foreach my $dest (sort $countOrder keys %{$RelayDenied{$relay}}) {
|
|
Packit |
57988d |
my $plural = ($RelayDenied{$relay}{$dest} > 1) ? "s" : "";
|
|
Packit |
57988d |
printf " %-36s: %3d Time%s\n", $dest,
|
|
Packit |
57988d |
$RelayDenied{$relay}{$dest}, $plural;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Will print the relays in the order of their total denied destinations
|
|
Packit |
57988d |
(equal keys sort lexically), with each sub hash printed in frequency
|
|
Packit |
57988d |
order (equal keys sorted lexically)
|
|
Packit |
57988d |
|
|
Packit |
57988d |
The optional second argument is a coderef to be used to sort the keys in
|
|
Packit |
57988d |
an order other than lexically. (a reference to C<SortIP>, for example.)
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub TotalCountOrder(\%;&) {
|
|
Packit |
57988d |
my $href = shift;
|
|
Packit |
57988d |
my $coderef = shift;
|
|
Packit |
57988d |
my $cache = {};
|
|
Packit |
57988d |
return sub {
|
|
Packit |
57988d |
# $a & $b are in the caller's namespace, moving this inside
|
|
Packit |
57988d |
# guarantees that the namespace of the sort is used, in case
|
|
Packit |
57988d |
# it's different (admittedly, that's highly unlikely), at a
|
|
Packit |
57988d |
# miniscule performance cost.
|
|
Packit |
57988d |
my $package = (caller)[0];
|
|
Packit |
57988d |
no strict 'refs'; # Back off, man. I'm a scientist.
|
|
Packit |
57988d |
my $A = $ {"${package}::a"};
|
|
Packit |
57988d |
my $B = $ {"${package}::b"};
|
|
Packit |
57988d |
use strict 'refs'; # We are a hedge. Please move along.
|
|
Packit |
57988d |
my ($AA, $BB);
|
|
Packit |
57988d |
|
|
Packit |
57988d |
foreach my $tuple ( [\$A, \$AA], [\$B, \$BB] ) {
|
|
Packit |
57988d |
my $keyRef = $tuple->[0];
|
|
Packit |
57988d |
my $totalRef = $tuple->[1];
|
|
Packit |
57988d |
|
|
Packit |
57988d |
if (exists($cache->{$$keyRef})) {
|
|
Packit |
57988d |
$$totalRef = $cache->{$$keyRef};
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
grep {$$totalRef += $href->{$$keyRef}->{$_}}
|
|
Packit |
57988d |
keys %{$href->{$$keyRef}};
|
|
Packit |
57988d |
$cache->{$$keyRef} = $$totalRef;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
my $count = $BB <=> $AA;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
return $count if $count;
|
|
Packit |
57988d |
if (ref $coderef) {
|
|
Packit |
57988d |
$a = $A;
|
|
Packit |
57988d |
$b = $B;
|
|
Packit |
57988d |
&$coderef();
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
($A cmp $B);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<SortIP>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function is meant to be passed to the perl C<sort> builtin. It
|
|
Packit |
57988d |
sorts a list of "dotted quad" IP addresses by the values of the
|
|
Packit |
57988d |
individual octets.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub canonical_ipv6_address {
|
|
Packit |
57988d |
my @a = split /:/, shift;
|
|
Packit |
57988d |
my @b = qw(0 0 0 0 0 0 0 0);
|
|
Packit |
57988d |
my $i = 0;
|
|
Packit |
57988d |
# comparison is numeric, so we use hex function
|
|
Packit |
57988d |
while (defined $a[0] and $a[0] ne '') {$b[$i++] = hex(shift @a);}
|
|
Packit |
57988d |
@a = reverse @a;
|
|
Packit |
57988d |
$i = 7;
|
|
Packit |
57988d |
while (defined $a[0] and $a[0] ne '') {$b[$i--] = hex(shift @a);}
|
|
Packit |
57988d |
@b;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub SortIP {
|
|
Packit |
57988d |
# $a & $b are in the caller's namespace.
|
|
Packit |
57988d |
my $package = (caller)[0];
|
|
Packit |
57988d |
no strict 'refs'; # Back off, man. I'm a scientist.
|
|
Packit |
57988d |
my $A = $ {"${package}::a"};
|
|
Packit |
57988d |
my $B = $ {"${package}::b"};
|
|
Packit |
57988d |
$A =~ s/^::(ffff:)?(\d+\.\d+\.\d+\.\d+)$/$2/;
|
|
Packit |
57988d |
$B =~ s/^::(ffff:)?(\d+\.\d+\.\d+\.\d+)$/$2/;
|
|
Packit |
57988d |
use strict 'refs'; # We are a hedge. Please move along.
|
|
Packit |
57988d |
if ($A =~ /:/ and $B =~ /:/) {
|
|
Packit |
57988d |
my @a = canonical_ipv6_address($A);
|
|
Packit |
57988d |
my @b = canonical_ipv6_address($B);
|
|
Packit |
57988d |
while ($a[1] and $a[0] == $b[0]) {shift @a; shift @b;}
|
|
Packit |
57988d |
$a[0] <=> $b[0];
|
|
Packit |
57988d |
} elsif ($A =~ /:/) {
|
|
Packit |
57988d |
-1;
|
|
Packit |
57988d |
} elsif ($B =~ /:/) {
|
|
Packit |
57988d |
1;
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
my ($a1, $a2, $a3, $a4) = split /\./, $A;
|
|
Packit |
57988d |
my ($b1, $b2, $b3, $b4) = split /\./, $B;
|
|
Packit |
57988d |
$a1 <=> $b1 || $a2 <=> $b2 || $a3 <=> $b3 || $a4 <=> $b4;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<LookupIP($dottedQuadIPaddress)>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function performs a hostname lookup on a passed in IP address. It
|
|
Packit |
57988d |
returns the hostname (with the IP in parentheses) on success and the IP
|
|
Packit |
57988d |
address on failure. Results are cached, so that many calls with the same
|
|
Packit |
57988d |
argument don't tax the resolver resources.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
For (new) backward compatibility, this function now uses the $DoLookup
|
|
Packit |
57988d |
variable in the caller's namespace to determine if lookups will be made.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# Might as well cache it for the duration of the run
|
|
Packit |
57988d |
my %LookupCache = ();
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub LookupIP {
|
|
Packit |
57988d |
my $Addr = $_[0];
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# OOPS! The 4.3.2 scripts have a $DoLookup variable. Time for some
|
|
Packit |
57988d |
# backwards compatible hand-waving.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# for 99% of the uses of this function, assuming package 'main' would
|
|
Packit |
57988d |
# be sufficient, but a good perl hacker designs so that the other 1%
|
|
Packit |
57988d |
# isn't in for a nasty suprise.
|
|
Packit |
57988d |
my $pkg = (caller)[0];
|
|
Packit |
57988d |
|
|
Packit |
57988d |
if ($ENV{'LOGWATCH_NUMERIC'} == 1 )
|
|
Packit |
57988d |
{ return $Addr; }
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# Default to true
|
|
Packit |
57988d |
my $DoLookup = 1;
|
|
Packit |
57988d |
{
|
|
Packit |
57988d |
# An eval() here would be shorter (and probably clearer to more
|
|
Packit |
57988d |
# people), but QUITE a bit slower. This function should be
|
|
Packit |
57988d |
# designed to be called a lot, so efficiency is important.
|
|
Packit |
57988d |
local *symTable = $main::{"$pkg\::"};
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# here comes the "black magic," (this "no" is bound to the
|
|
Packit |
57988d |
# enclosing block)
|
|
Packit |
57988d |
no strict 'vars';
|
|
Packit |
57988d |
if (exists $symTable{'DoLookup'} && defined $symTable{'DoLookup'}) {
|
|
Packit |
57988d |
*symTable = $symTable{'DoLookup'};
|
|
Packit |
57988d |
$DoLookup = $symTable;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# "Socket" is used solely to get the AF_INET() and AF_INET6()
|
|
Packit |
57988d |
# constants, usually 2 and 10, respectively. Using Socket is
|
|
Packit |
57988d |
# preferred because of portability, and should be in the standard
|
|
Packit |
57988d |
# Perl distribution.
|
|
Packit |
57988d |
eval "use Socket"; my $hasSocket = $@? 0 : 1;
|
|
Packit |
57988d |
return $Addr unless($DoLookup && $hasSocket);
|
|
Packit |
57988d |
|
|
Packit |
57988d |
return $LookupCache{$Addr} if exists ($LookupCache{$Addr});
|
|
Packit |
57988d |
|
|
Packit |
57988d |
$Addr =~ s/^::ffff://;
|
|
Packit |
57988d |
my $PackedAddr;
|
|
Packit |
57988d |
my $name = "";
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# there are other module functions that do this more gracefully
|
|
Packit |
57988d |
# (such as inet_pton), but we can't guarantee that they are available
|
|
Packit |
57988d |
# in every system, so we use the built-in gethostbyaddr.
|
|
Packit |
57988d |
if ($Addr =~ /^[\d\.]*$/) {
|
|
Packit |
57988d |
$PackedAddr = pack('C4', split /\./,$Addr);
|
|
Packit |
57988d |
$name = gethostbyaddr($PackedAddr,AF_INET());
|
|
Packit |
57988d |
} elsif ($Addr =~ /^[0-9a-zA-Z:]*/) {
|
|
Packit |
57988d |
$PackedAddr = pack('n8', canonical_ipv6_address($Addr));
|
|
Packit |
57988d |
$name = gethostbyaddr($PackedAddr, AF_INET6());
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
if ($name) {
|
|
Packit |
57988d |
my $val = "$Addr ($name)";
|
|
Packit |
57988d |
$LookupCache{$Addr} = $val;
|
|
Packit |
57988d |
return $val;
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
$LookupCache{$Addr} = $Addr;
|
|
Packit |
57988d |
return ($Addr);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<RangeHelpDM()>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function merely prints out some information about --range to STDERR.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub RangeHelpDM {
|
|
Packit |
57988d |
eval "use Date::Manip"; my $hasDM = $@ ? 0 : 1;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
if ($hasDM) {
|
|
Packit |
57988d |
print STDERR "\nThis system has the Date::Manip module loaded, and therefore you may use all\n";
|
|
Packit |
57988d |
print STDERR "of the valid --range parameters.\n";
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
print STDERR "\nThis system does not have Date::Manip module loaded, and therefore\n";
|
|
Packit |
57988d |
print STDERR "the only valid --range parameters are \"yesterday\", \"today\", or \"all\".\n";
|
|
Packit |
57988d |
print STDERR "The Date::Manip module can be installed by using either of:\n";
|
|
Packit |
57988d |
print STDERR " apt-get install libdate-manip-perl (recommended on Debian)'\n";
|
|
Packit |
57988d |
print STDERR " cpan -i 'Date::Manip'\n";
|
|
Packit |
57988d |
print STDERR " perl -MCPAN -e 'install Date::Manip'\n";
|
|
Packit |
57988d |
print STDERR "\nFollowing is a description of the full capabilities available if\n";
|
|
Packit |
57988d |
print STDERR "Date::Manip is available.\n";
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
print STDERR <<"EOT";
|
|
Packit |
57988d |
|
|
Packit |
57988d |
The format of the range option is:
|
|
Packit |
57988d |
--range \"date_range [period]\"
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Parameter date_range (and optional period) must be enclosed in double quotes
|
|
Packit |
57988d |
if it is more than one word. The default for date_range is \"yesterday\".
|
|
Packit |
57988d |
Valid instances of date_range have one of the following formats:
|
|
Packit |
57988d |
|
|
Packit |
57988d |
yesterday
|
|
Packit |
57988d |
today
|
|
Packit |
57988d |
all
|
|
Packit |
57988d |
date1
|
|
Packit |
57988d |
between date1 and date2
|
|
Packit |
57988d |
since date1
|
|
Packit |
57988d |
|
|
Packit |
57988d |
For the above, date1 and date2 have values that can be parsed with the
|
|
Packit |
57988d |
Date::Manip perl module.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Valid instances of the optional parameter period have one of the following
|
|
Packit |
57988d |
formats:
|
|
Packit |
57988d |
for (that|this) (year|month|day|hour|minute|second)
|
|
Packit |
57988d |
for those (years|months|days|hours|minutes|seconds)
|
|
Packit |
57988d |
|
|
Packit |
57988d |
The period defines the resolution of the date match. The default is
|
|
Packit |
57988d |
\"for that day\".
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Examples:
|
|
Packit |
57988d |
|
|
Packit |
57988d |
|
|
Packit |
57988d |
--range today
|
|
Packit |
57988d |
--range yesterday
|
|
Packit |
57988d |
--range \"4 hours ago for that hour\"
|
|
Packit |
57988d |
--range \"-3 days\"
|
|
Packit |
57988d |
--range \"since 2 hours ago for those hours\"
|
|
Packit |
57988d |
--range \"between -10 days and -2 days\"
|
|
Packit |
57988d |
--range \"Apr 15, 2005\"
|
|
Packit |
57988d |
--range \"first Monday in May\"
|
|
Packit |
57988d |
--range \"between 4/23/2005 and 4/30/2005\"
|
|
Packit |
57988d |
--range \"2005/05/03 10:24:17 for that second\"
|
|
Packit |
57988d |
|
|
Packit |
57988d |
(The last entry might be used by someone debugging a log or filter.)
|
|
Packit |
57988d |
|
|
Packit |
57988d |
A caution about efficiency: a range of \"yesterday for those hours\"
|
|
Packit |
57988d |
will search for log entries for the last 24 hours, and is innefficient
|
|
Packit |
57988d |
because it searches for individual matches for each hour. A range of
|
|
Packit |
57988d |
\"yesterday\" will search for log entries for the previous day, and
|
|
Packit |
57988d |
it searches for a single date match.
|
|
Packit |
57988d |
EOT
|
|
Packit |
57988d |
;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<GetPeriod()>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function returns the period, which is the part after the "for (those|that|this) "
|
|
Packit |
57988d |
in a range
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub GetPeriod {
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $range = lc $ENV{"LOGWATCH_DATE_RANGE"} || "yesterday";
|
|
Packit |
57988d |
my ($period) =
|
|
Packit |
57988d |
($range =~ /for\s+(?:those|that|this)\s+(year|month|day|hour|minute|second)s?\s*$/);
|
|
Packit |
57988d |
if ($range eq 'all') {
|
|
Packit |
57988d |
$period = 'all';
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
unless ($period) { $period = "day"; }
|
|
Packit |
57988d |
return($period);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<TimeBuild()>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function returns an array of integers denoting time since the epoch
|
|
Packit |
57988d |
(Jan. 1, 1970). Each entry represents a timestamp for the period that will
|
|
Packit |
57988d |
that will need to be looked up to create the filter.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub TimeBuild {
|
|
Packit |
57988d |
my @time_t;
|
|
Packit |
57988d |
my $time = time;
|
|
Packit |
57988d |
eval "use Date::Manip"; my $hasDM = $@ ? 0 : 1;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
if ($hasDM) {
|
|
Packit |
57988d |
eval 'Date_TimeZone();';
|
|
Packit |
57988d |
if ($@) {
|
|
Packit |
57988d |
die "ERROR: Date::Manip unable to determine TimeZone.\n\nExecute the following command in a shell prompt:\n\tperldoc Date::Manip\nThe section titled TIMEZONES describes valid TimeZones\nand where they can be defined.\n";
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $range = lc $ENV{"LOGWATCH_DATE_RANGE"} || "yesterday";
|
|
Packit |
57988d |
my $period = GetPeriod;
|
|
Packit |
57988d |
$range =~ s/for\s+(?:those|that|this)\s+((year|month|day|hour|minute|second)s?)\s*$//;
|
|
Packit |
57988d |
my ($range1, $range2) = ($range =~ /^between\s+(.*)\s+and\s+(.*)\s*$/);
|
|
Packit |
57988d |
if ($range =~ /^\s*since\s+/) {
|
|
Packit |
57988d |
($range1) = ($range =~ /\s*since\s+(.*)/);
|
|
Packit |
57988d |
$range2 = "now";
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
if ($range1 && $range2 && $hasDM) {
|
|
Packit |
57988d |
# range between two dates specified
|
|
Packit |
57988d |
my $date1 = ParseDate($range1);
|
|
Packit |
57988d |
my $date2 = ParseDate($range2);
|
|
Packit |
57988d |
if ($date1 && $date2) {
|
|
Packit |
57988d |
if (Date_Cmp($date1, $date2) > 0) {
|
|
Packit |
57988d |
# make sure date1 is earlier
|
|
Packit |
57988d |
my $switch_date = $date1;
|
|
Packit |
57988d |
$date1 = $date2;
|
|
Packit |
57988d |
$date2 = $switch_date;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
while (Date_Cmp($date1, $date2) < 0) {
|
|
Packit |
57988d |
$time_t[++$#time_t] = UnixDate($date1, "%s");
|
|
Packit |
57988d |
$date1 = DateCalc($date1, "+1 $period");
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
$time_t[++$#time_t] = UnixDate($date2, "%s");
|
|
Packit |
57988d |
} else { # $date1 or $date2 not valid
|
|
Packit |
57988d |
# set to zero, which indicates it is not parsed
|
|
Packit |
57988d |
$time_t[0] = 0;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
# either a single date or we don't have Date::Manip
|
|
Packit |
57988d |
if ($range eq 'yesterday') {
|
|
Packit |
57988d |
$time_t[0] = $time-86400;
|
|
Packit |
57988d |
} elsif ($range eq 'today') {
|
|
Packit |
57988d |
$time_t[0] = $time;
|
|
Packit |
57988d |
} elsif ($range eq 'all') {
|
|
Packit |
57988d |
# set arbitrarily to 1
|
|
Packit |
57988d |
$time_t[0] = 1;
|
|
Packit |
57988d |
} elsif ($hasDM) {
|
|
Packit |
57988d |
$time_t[0] = UnixDate($range, "%s") || 0;
|
|
Packit |
57988d |
} else {
|
|
Packit |
57988d |
$time_t[0] = 0;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# this is an optimization when we use Date::Manip, and
|
|
Packit |
57988d |
# the period is either 'month' or 'year'. It is intended
|
|
Packit |
57988d |
# to reduce the number of archived logs searched.
|
|
Packit |
57988d |
# We use the second day of month or year to account for
|
|
Packit |
57988d |
# different timezones.
|
|
Packit |
57988d |
if ($time_t[0] && $hasDM) {
|
|
Packit |
57988d |
my $mod_date = ParseDateString("epoch $time_t[0]");
|
|
Packit |
57988d |
if ($period =~ /^month|year$/) {
|
|
Packit |
57988d |
# set to beginning of month
|
|
Packit |
57988d |
$mod_date =~ s/\d\d\d\d:\d\d:\d\d$/0200:00:00/;
|
|
Packit |
57988d |
if ($period =~ /^year$/) {
|
|
Packit |
57988d |
# set to beginning of year
|
|
Packit |
57988d |
$mod_date =~ s/\d\d0100:00:00/010200:00:00/;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
$time_t[0] = UnixDate($mod_date, "%s");
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
return(@time_t);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=pod
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<TimeFilter($date_format)>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
This function returns a regexp to filter by date/time
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
|
|
Packit |
57988d |
sub TimeFilter {
|
|
Packit |
57988d |
my ($format) = $_[0];
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $SearchDate;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my $range = lc $ENV{"LOGWATCH_DATE_RANGE"} || "yesterday";
|
|
Packit |
57988d |
my $debug = $ENV{"LOGWATCH_DEBUG"} || 0;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
my @time_t = TimeBuild();
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# get period
|
|
Packit |
57988d |
my $period = GetPeriod;
|
|
Packit |
57988d |
if ($debug > 5) {
|
|
Packit |
57988d |
print STDERR "\nTimeFilter: Period is $period\n";
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
# we need the following bracketed section because of 'last'
|
|
Packit |
57988d |
{
|
|
Packit |
57988d |
if ($period eq 'second') {last;}
|
|
Packit |
57988d |
$format =~ s/%S/../;
|
|
Packit |
57988d |
if ($period eq 'minute') {last;}
|
|
Packit |
57988d |
$format =~ s/%M/../;
|
|
Packit |
57988d |
if ($period eq 'hour') {last;}
|
|
Packit |
57988d |
$format =~ s/%H/../;
|
|
Packit |
57988d |
if ($period eq 'day') {last;}
|
|
Packit |
57988d |
$format =~ s/%a/.../;
|
|
Packit |
57988d |
$format =~ s/%d/../;
|
|
Packit |
57988d |
$format =~ s/%e/../;
|
|
Packit |
57988d |
if ($period eq 'month') {last;}
|
|
Packit |
57988d |
$format =~ s/%b/.../;
|
|
Packit |
57988d |
$format =~ s/%m/../;
|
|
Packit |
57988d |
if ($period eq 'year') {last;}
|
|
Packit |
57988d |
$format =~ s/%y/../;
|
|
Packit |
57988d |
$format =~ s/%Y/..../;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
$SearchDate .= "(";
|
|
Packit |
57988d |
|
|
Packit |
57988d |
for my $time (@time_t) {
|
|
Packit |
57988d |
if ($time) {
|
|
Packit |
57988d |
$SearchDate .= strftime($format, localtime($time)) . "|";
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
else {
|
|
Packit |
57988d |
# the following is a string guaranteed to not match
|
|
Packit |
57988d |
$SearchDate .= "Range \"$range\" not understood. ";
|
|
Packit |
57988d |
print STDERR "ERROR: Range \"$range\" not understood\n";
|
|
Packit |
57988d |
RangeHelpDM;
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
# get rid of last character (usually the extra "|")
|
|
Packit |
57988d |
if (length($SearchDate) > 1) {
|
|
Packit |
57988d |
chop($SearchDate);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
$SearchDate .= ")";
|
|
Packit |
57988d |
if ($debug> 5) {
|
|
Packit |
57988d |
# DebugSearchDate sometimes makes it more readable - not used
|
|
Packit |
57988d |
# functionally
|
|
Packit |
57988d |
my $DebugSearchDate = $SearchDate;
|
|
Packit |
57988d |
$DebugSearchDate =~ tr/:/ /;
|
|
Packit |
57988d |
$DebugSearchDate =~ tr/\./ /;
|
|
Packit |
57988d |
$DebugSearchDate =~ tr/ //s;
|
|
Packit |
57988d |
print STDERR "\nTimeFilter: SearchDate is $SearchDate\n";
|
|
Packit |
57988d |
print STDERR "\nTimeFilter: Debug SearchDate is $DebugSearchDate\n";
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
return ($SearchDate);
|
|
Packit |
57988d |
}
|
|
Packit |
57988d |
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=back
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=head1 TAGS
|
|
Packit |
57988d |
|
|
Packit |
57988d |
In addition to importing each function name explicitly, the following
|
|
Packit |
57988d |
tags can be used.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=over 4
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<:sort>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Imports C<CountOrder>, C<TotalCountOrder and C<SortIP>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<:ip>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Imports C<SortIP> and C<LookupIP>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<:dates>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Imports C<RangeHelpDM GetPeriod TimeBuild TimeFilter>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=item I<:all>
|
|
Packit |
57988d |
|
|
Packit |
57988d |
Imports all importable symbols.
|
|
Packit |
57988d |
|
|
Packit |
57988d |
=cut
|
|
Packit |
57988d |
|
|
Packit |
57988d |
1;
|
|
Packit |
57988d |
|
|
Packit |
57988d |
# vi: shiftwidth=3 tabstop=3 et
|
|
Packit |
57988d |
|