#!/usr/local/bin/perl -w

# Globals ---------------------------------------------------------------------

# Sane default location of the mtree executable.
# Can be changed with the --mtree option.
my $mtree="/usr/sbin/mtree";
# Sane default location of the mtree file checksum database.
# Can be changed with the --checksum-file option.
my $checksum_file="/usr/mtree/fs.mtree";
# Sane default location of the mtree file exclude list.
# Can be changed with the --exclude-file option.
my $exclude_file="/usr/mtree/fs.exclude";
# Stores the executable name, mainly to refer to ourselves in help.
my $executable=$0;
# Stores the list of filesystem changes reported by mtree.
my $changes="";
# Stores the list of e-mail addresses to send results to.
my @emails;
# Whether or not to scan for file changes.
# (default behavior, disabled in case of -uo)
my $scan_for_changes=1;
# Whether or not to update the checksums. (requires -u flag)
my $update=undef;
# Top level directory to monitor for changes.
# (can be edited with the -p option.)
my $path="/";
# Whether or not to print scan results to stdout.
# (Default behavior, see -q option to disable.)
my $print_results=1;
# Path to the sendmail executable.
# (see --sendmail option to change.)
my $sendmail="/usr/sbin/sendmail";
# Logfile location.
# (see -l option to change.)
my $log="/var/log/mtree.log";
# e-mail reply-to address. (see --reply-to option)
my $reply_to=undef;
# e-mail subject (see --subject option).
my $subject="Filesystem changes for " . `date`;

# Display script usage & help. ------------------------------------------------

sub show_help
{
 print '
 Usage: ' . $executable . ' [OPTION] ...
 Show or E-mail out a list of changes to the file system.

 mtree operation options:

   -u,  --update        Updates the file checksum database after
                        showing/mailing changes.
   -uo, --update-only   Only update the file checksum database.
   -p,  --path          Top level folder to monitor (default: /)
   -q,  --quiet         Do not output scan results to stdout or any
                        other output.

 Path configuration options:

   -l,  --log           Logfile location
                        (default: /var/log/mtree.log)
        --mtree         Set the location of the mtree executable.
                        (default is /usr/sbin/mtree)
        --checksum-file Set the location of the file containing the
                        mtree file checksums.
                        (defaul: /usr/mtree/fs.mtree)
        --exclude-file  Set the location of the file containing the
                        list of files and folders to exclude from the
                        mtree scan. (default is /usr/mtree/fs.exclude)

 E-mail options:

   -e,  --email         Adds specified e-mail address as destination.
        --sendmail      Set the location of the sendmail executable.
                        (default: /usr/sbin/sendmail)
        --reply-to      Set the e-mail reply-to address.
        --subject       Sets The e-mail subject.

 Misc options:

   -h,  --help          Display this help text.


 Example usage:

   ' . $executable . ' -uo
   ' . $executable . ' -u -q -e [email protected] -e [email protected]
   ' . $executable . ' /var/www --mtree /usr/local/sbin/mtree

';

}

# Parses a command line argument and it's param. ------------------------------

sub parse_commandline_argument
{
 my $arg = shift;
 my $param = shift;
 if (substr($arg,0,1) eq '-')
 {
   if ($arg eq '--mtree')
   {
     $mtree = $param;
   }
   if ($arg eq '--sendmail')
   {
     $sendmail = $param;
   }
   if ($arg eq '-q' or $arg eq '--quiet')
   {
     $print_results = undef;
   }
   if ($arg eq '--reply-to')
   {
     $reply_to = $param;
   }
   if ($arg eq '--subject')
   {
     $subject = $param;
   }
   if ($arg eq '--checksum-file')
   {
     $checksum_file = $param;
   }
   if ($arg eq '-l' or $arg eq '--log')
   {
     $log = $param;
   }
   if ($arg eq '--exclude-file')
   {
     $exclude_file = $param;
   }
   if ($arg eq '-h' or $arg eq '--help')
   {
     show_help();
     exit 0;
   }
   if ($arg eq '-e' or $arg eq '--email')
   {
     if ($param =~ m/\@/)
     {
       push(@emails,$param);
     }
     else
     {
       die "Invalid e-mail address: $param\n";
     }
   }

   if ($arg eq '-u' or $arg eq '--update')
   {
     $update=1;
   }
   if ($arg eq '-uo' or $arg eq '--update-only')
   {
     $update=1;
     $scan_for_changes=undef;
   }
 }
}

# Script entry point. ---------------------------------------------------------

# Parse commandline arguments.
my $argc=0;
foreach my $argument(@ARGV)
{
 chomp($argument);
 if ($argc != $#ARGV)
 {
   my $next_argument = $ARGV[$argc+1];
   chomp($next_argument);
   parse_commandline_argument($argument,$next_argument);
 }
 else
 {
   parse_commandline_argument($argument);
 }
 $argc++;
}

# Check if we have all the necesary components.

(-x $mtree) or die "$mtree is not executable.\n";
(-w $checksum_file) or die "$checksum_file is not writeable.\n";
(-r $exclude_file) or die "$exclude_file is not readable.\n";
if ($scan_for_changes)
{
 (-w $log) or die "$log is not writeable.\n";
}
if ($#emails >= 1)
{
 (-x $sendmail) or die "$sendmail is not executable.\n";
}

if ($print_results)
{
 print "\nScanning for changes...\n";
}

# Get the list of changed files if desired.
if ($scan_for_changes)
{

 $changes=`$mtree -f $checksum_file -X $exclude_file -p $path`;

 # If there are no changes since last scan, then
 # we're done with everything.
 # <= 3 to account for \n\r and maybe a space...
 if (length($changes) <= 3 )
 {
   if ($print_results)
   {
     print "All done.\n";
   }
   exit 0;
 }

 # Write changes to log file.
 open LOGFILE,">>$log" or die $!;
 print LOGFILE $changes;
 close LOGFILE;

 # Output changes if desired.
 if ($print_results)
 {
   print "$changes\n";
 }

 # E-mail out changes if desired.
 foreach my $mail(@emails)
 {
   if ($print_results)
   {
     print "E-mailing $mail ...\n";
   }
   chomp($mail);
   open(SENDMAIL, "|$sendmail -t") or die "Cannot open $sendmail: $!";
   print SENDMAIL "To: $mail\n";
   if ($reply_to)
   {
     chomp($reply_to);
     print SENDMAIL "Reply-to: $reply_to\n";
   }
   if ($subject)
   {
     chomp($subject);
     print SENDMAIL "Subject: $subject\n";
   }
   print SENDMAIL "Content-type: text/plain\n\n";
   print SENDMAIL $changes;
   close(SENDMAIL);
 }

}

# Update checksum file if desired.
if ($update)
{
 if ($print_results)
 {
   print "Updateing checksums...\n";
 }
 system("$mtree -c -X $exclude_file -p $path > $checksum_file");
}

if ($print_results)
{
 print "All done.\n";
}


# done.
exit 0;