#!/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 foo@example.com -e bar@example.com
' . $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;