Blame lib/Logwatch.pm

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