#!/usr/bin/perl
#
# Copyright (C) 2013, Jason Self
#
# This program gives you software freedom; you can copy, modify, convey,
# and/or redistribute it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program in a file called 'GPLv3.txt'. If not, see 
# http://www.gnu.org/licenses/gpl-3.0-standalone.html or write to the:
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor
# Boston, MA 02110-1301, USA.
#
# Documentation (in pod format) at the bottom of this file

use strict;
no strict 'refs';
use warnings;
no warnings qw(once);

use Getopt::Long;
use Pod::Usage;
use YAML qw(LoadFile);
use File::ReadBackwards;
use Date::Manip;

#-----------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------

my $file_lastrun = "user-tracker.run";
my $file_log = "user-tracker.log";

my ($output, $help, $man, $now, $last_run, %last_login);
our ($date, $user);

#-----------------------------------------------------------------------------------------

#-- print usage if the script is not called properly
pod2usage(2) unless
  GetOptions( 'output=s' => \$output, 'help|?' => \$help, man => \$man);
pod2usage(-exitstatus => 0, -verbose => 2) if $man;
pod2usage(1) if $help;

$now = scalar localtime;

#-- read output log and store values in %last_login
$output = $file_log if not defined $output;
if ( open LOG, "<$output" )
{
  while ( <LOG> )
  {
    chomp;
    ($date, $user) = split ' - ', $_;
    $last_login{$user} = $date;
  }
}
else
{
  warn "Can't open $output: $! ... probably first run\n";
}
open LOG, ">$output" or die "Can't open $output: $!\n";

if ( open LASTRUN,"<$file_lastrun" )
{
  my $date = <LASTRUN>;
  $last_run = ParseDate($date);
} 
else
{
  warn "Can't open $file_lastrun: $! ... parsing all lines of the log files\n";
  $last_run = ParseDate("Jan 01 00:00 1970");
}

#-- $config is a reference to a structure like this:
#   $config = {
#          '/var/log/mail.log' => [
#                                 {
#                                   'pattern' => '^(.{15}).*Login: user=<([^>]+).*',
#                                   'first' => 'date',
#                                   'second' => 'user'
#                                 },
#                                 { ...
#                                 ],
#          '/var/log/auth.log' => [ ... etc
my $config = LoadFile("user-tracker.cfg") or die "Can't open config file: $!\n";

foreach my $file ( sort keys %{$config} )
{
  my $fh = File::ReadBackwards->new($file) or warn "Can't open $file: $!\n" and next;
  while ( defined(my $line = $fh->readline()) )
  {
    foreach my $element ( @{$config->{$file}} )
    {
      ($date, $user) = (undef, undef);
      
      #-- store values in $date and $user
      (${$element->{first}}, ${$element->{second}}) = ($line =~ $element->{pattern});
      
      #-- skip line if $date not defined
      next if ( not defined $date );
      
      #-- stop  processing the file if the date is earlier than $last_run
      last if (  Date_Cmp(ParseDate($date),$last_run) < 0 );

      #-- line matches, update %last_login
      if ( not exists $last_login{$user} or Date_Cmp(ParseDate($date),ParseDate($last_login{$user})) > 0 )
      {
        $last_login{$user} = $date;  
      }
    }
  }
}

#-- save to output file
foreach ( sort { ParseDate($last_login{$a}) cmp ParseDate($last_login{$b}) } keys %last_login )
{
  print LOG "$last_login{$_} - $_\n";
}

open LASTRUN, ">$file_lastrun" or die "Can't open $file_lastrun: $!\n";
print LASTRUN $now;
close LASTRUN

__END__

#-----------------------------------------------------------------------------------------

=pod

=head1 NAME

User Tracker

=head1 SYNOPSIS

usertracker.pl [-o output] [-h] [-man]

=head1 ARGUMENTS

=over

=item -o output

name of output file, if not provided, write to file specified in $file_log

=item -h, --help

display help information and exits

=item -man

display a more detailed help information, including author and changelog

=back

=head1 DESCRIPTION

Scans specified log files for information about logged-in users and adds the list of 
logged users to a file

=head1 AUTHOR

Jason Self (j@jxself.org)

=head1 CHANGELOG

=over 

=item Feb 03, 2013 : v1.0

- Initial release

=back

=cut
