#!/usr/bin/perl -w
#---------------------------------
#
#  File System Saint v1.1
#  Last modified: 09/15/05
#  http://www.unixgeeks.org/saint/
#
# Copyright (c) 2004-2005 Joshua Fritsch <joshua@unixgeeks.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
############################################################
############################################################
#
# This version was based on the original File System Saint
# but all code was re-written from scratch. There are many
# similarities, but the differences are enough to warrant
# the use of a new excutable name, "saint" so as to avoid
# confusion between the two.
#
# Credit for the original File System Saint goes to 
# Srebrenko Sehic <haver@insecure.dk> and kula@insecure.dk
#
# Copyright (c) 2000-2002 Srebrenko Sehic <haver@insecure.dk>
# All Rights Reserved
#
# http://www.insecure.dk/fss
#
############################################################
############################################################

use strict;

BEGIN
{
  my @mods = qw(Digest::SHA Digest::MD5 Getopt::Std File::Find );
	my $moderr = 0;
	foreach (@mods) {
	  if (!eval "require $_") {
		  warn "$_ unavailable.\n";
			$moderr = 1;
		}
		if ($moderr == 1) {
			die "\nERROR: Missing critical module $_! Quitting.\n\n";
		}
	}
}
		  

use Digest::SHA;
use Digest::MD5;
use Getopt::Std;
use File::Find;
use IO::File;

#
# We include sbin so we can find sendmail
#
$ENV{PATH} = "/usr/bin:/usr/local/bin:/usr/sbin:/usr/local/sbin:/usr/lib";

## Ensure the output is as expected for variables set from shell commands.
chomp (my $hostname = `hostname`);
if ($hostname !~ /[a-zA-Z0-9\-\.]*/) {
  print "ERROR! Hostname may only contain letters, numbers, dashes and dots.\n";
	print "The hostname value received was: $hostname\n";
	die "Possible security breach. Exiting.\n";
}

chomp (my $date = `date +%m-%d-%Y`);
if ($date !~ /\d\d\-\d\d\-\d\d\d\d/) {
  print "ERROR! Invalid date format!\n";
	print "Expected: DD-MM-YYYY     Received: $date\n";
	die "Possible security breach. Exiting.\n";
}

chomp (my $moment = `date +%H:%M`);
if ($moment !~ /\d\d\:\d\d/) {
  print "ERROR! Invalid time format!\n";
	print "Expected: HH:MM     Received: $moment\n";
	die "Possible security breach. Exiting.\n";
}

# Global variables
#my $sendmail = "/usr/sbin/sendmail";
chomp(my $sendmail = `/usr/bin/which sendmail`);
my $sha = new Digest::SHA;
my $md5 = new Digest::MD5;
my (@include, @exclude, %opt);
my $fssname = "File System Saint";
my $version = "1.1";
my $fsshome = "/usr/local/.saint"; ## Default location of FSS files.
my $filecount = 0;
my $newcount = 0;
my $dircount = 0;
my $delcount = 0;
my $changecount = 0;
my $alertstat = 0;
my %DB; ## Tie-in to Berkeley DB
my %DB2; ## Deep scan
my $normaldb; ## Different OS's write the DB name different ways. Check which.


## Process options
getopts('c:d:e:hil:LqsSvwY',\%opt);

### Options
#  ---------
# -c <config file> Config: Specify a config file. Defaults to saint.conf
# -d <database>  Database: Specify a database file. Defaults to <hostname>.db
# -e <email addr>   Email: Email any changes to the address specified
# -h                 Help: Show usage information.
# -i           Initialize: Generate a new database.
# -l <logfile>    Logfile: Set logfile. Default is saint.log. 
# -L                Links: Include checks on symbolic links. (skipped by default)
# -q                Quiet: Suppress all output except for changes and errors.
# -s                 Scan: Use the DB as a reference and check all known files.
# -S            Deep Scan: Use a new scan as a ref thus accounting for new files.
# -v              Verbose: Print detailed information to terminal.
# -w                 What: Don't just report a change, report *what* changed.
# -Y                 Yack: Print extensive information to the log.

my %reftable = (
	0 => "Checksum",
	1 => "Permissions",
	2 => "UID",
	3 => "GID",
	4 => "Size",
	5 => "mtime",
	);

## Require a valid email address
if ($opt{e} && ($opt{e} !~ /\@/)) {die "Valid email address required.\n"; }

## Print options listed above and some simple commands
if ($opt{h}) { &show_usage; }

## Can't scan and init at the same time
if ($opt{i} && ($opt{s} || $opt{S})) { &show_short; }

## Can't scan and deep scan at the same time
if ($opt{s} && $opt{S}) { &show_short; }

## Need to init or scan
if ((!$opt{i}) && (!$opt{s}) && (!$opt{S})) { &show_short; }

# Read and process the config file
&parse_config;

## Use default DB if not already set
if (!$opt{d}) { $opt{d} = "$fsshome/${hostname}.db"; }

## Force the user to either change $fsshome or create /usr/local/.saint
if (!(-d $fsshome)) { die "$fsshome does not exist\n"; }

## Used to check the validity of a previous DB
my $cksum = "$fsshome/.$hostname.db.cksum";

## Open the logfile
if (!$opt{l} || ($opt{l} eq 1)) { $opt{l} = "$fsshome/saint.log"; }
open(LOG,">>$opt{l}") or die "Can't open logfile $opt{l} : $!\n";


print LOG "\nSaint initialized at $moment on $date.\n";
print LOG "-" x 45 . "\n\n";

if ($opt{v}) {
  print "\nSaint initialized at $moment on $date.\n";
  print "-" x 45 . "\n\n";
}

## Open a temporary file to store scan results
my $tmpfile = "/tmp/saint.$$";
open(TMPFILE,">$tmpfile");

## Initialize the DB (overwrite previous)
if ($opt{i}) {
  &do_create;
}

# Compare the DB entries against current file status
# This scan uses the DB as a reference and checks files
# accordingly. Thus, it will miss any files added since
# initialization. Use the deep scan to check for the
# addition of files as well as compare the DB.
if ($opt{s}) {
  &do_scan;
}

if ($opt{S}) {
  &do_deepscan;
}

my $runtime = (time - $^T);
print LOG "End of run. Process time: $runtime second(s).\n";
print LOG "\n" . "#" x 45 . "\n\n";
close(LOG);
chmod (0600,$opt{l});

close(TMPFILE);
unlink($tmpfile);

########################################
########################################
##                                    ##
##           END MAIN BODY            ##
##                                    ##
##        SUBROUTINES FOLLOW          ##
##                                    ##
########################################
########################################


########################################
########################################
##           parse_config             ##
##                                    ##
##    Set options and populate the    ##
##    include/exclude file lists      ##
########################################
########################################

sub parse_config
{

  if ((!$opt{c}) || (!(-f $opt{c}))) {
	  $opt{c} = "saint.conf";
	}
  if (!(-f $opt{c})) {
	  $opt{c} = "$fsshome/saint.conf";
	}
	if (!(-f $opt{c})) {
	  die "No configuration file found: $!\n";
	}



	open(CONF, $opt{c}) or die "Couldn't open configuration file $opt{c} : $!\n";

  ## Initialize the mode for reading the config file.
	my $flag = 0;
	
	while(<CONF>)
	{
	  chomp $_;
		s/#.*$//; ## Remove comments
		s/^\s+//; ## Remove any preceeding whitespace
		next unless length;

		if (m/^\s*\[INCLUDE\]\s*$/) { $flag = 1; next; }
		if (m/^\s*\[EXCLUDE\]\s*$/) { $flag = 2; next; }
		if (m/^\s*\[OPTIONS\]\s*$/) { $flag = 3; next; }


		if ($flag == 1) { ## Add an entry to the include list
		  push(@include,$_);
		} elsif ($flag == 2) { ## Add an entry to the exclude list
		  push (@exclude,$_);
		} elsif ($flag == 3) { ## Define default variables

		  if (m/(^\s*SAINT_HOME\s*=)(.*)($)/) {
			  $fsshome = $2;
			} elsif (m/(^\s*SAINT_DB\s*=)(.*)($)/) {
			  $opt{d} = $2;
			} elsif (m/(^\s*LOGFILE\s*=)(.*)($)/) {
			  if (!$opt{l}) { $opt{l} = $2; }
			}

		} elsif ($flag == 0 ) {
      die "Error in configuration file $opt{c} : $!\n";
		} else { die "Unexpected error: $!\n"; }
	}
}

########################################
########################################
##                                    ##
##          pre_process               ##
##                                    ##
##  Skip over any excluded files,     ##
##  dot files and whitespace.         ##
##                                    ##
########################################
########################################

sub pre_process
{

## Read in file from init or scan
  my $file = shift;

  foreach (@exclude) {
    if ("$file" =~ "$_") {
		  if ($opt{v}) { print "Excluding $file\n"; }
			if ($opt{i} && $opt{Y}) { print LOG "Excluding $file\n"; }
      return 1;
		}
	}

    if ( $file =~ m/^\.\.?$/) { ## Skip '.' and '..'
      return 1;
    } elsif ( !-f $file && !-d $file) { ## Verify we have a regular file or directory
      return 1;
    } elsif ( -l $file && !$opt{L} ) { ## Do we want to stat links?
      return 1;
    } else {
      return($file) ## Send back a verified filename
    }

}

########################################
########################################
##                                    ##
##            do_create               ##
##                                    ##
## Create and populate a new database ##
##                                    ##
##                                    ##
########################################
########################################

sub do_create
{
  my $dbhash;
	my $runtime;

  if (!$opt{q} && !$opt{S}) {
	  print "Initializing the database...\n";
	  print LOG " " x 10 . "::Creating the database::\n\n";
	}

  ## Explicitly remove a prior DB if it exists
  unlink ("$opt{d}");
  unlink ("$opt{d}.db");
  unlink ("$opt{d}.dir");
  unlink ("$opt{d}.pag");
	
	## Scan requested filesystems and pass to DB recorder
  dbmopen(%DB,$opt{d},0600);

	## Some implementations of the DB act differently. We'll
	## compensate here.
	if ((-f "$opt{d}.dir") && (-f "$opt{d}.pag")) { $normaldb = 1; }
	elsif ( -f "$opt{d}.db") { $normaldb = 2; }
	elsif ( -f $opt{d} ) { $normaldb = 0; }
	else { die "Unexpected DB error. Check the name. $!\n"; }


  find(\&do_init, @include); ## Scan requested files

	dbmclose(%DB);

	$runtime = (time - $^T);
	if (!$opt{q} && !$opt{S}) { print "Complete: $filecount file(s) added, $dircount directories scanned in $runtime seconds.\n"; }

	## Take a checksum of the database to prevent tampering
	if ($normaldb == 1) {
	  $dbhash = &calc_hash("$opt{d}.pag") . &calc_hash("$opt{d}.dir");
	} elsif ($normaldb == 2) {
	  $dbhash = &calc_hash("$opt{d}.db");
	} else {
	  $dbhash = &calc_hash("$opt{d}");
	}

	open(CKSUM, ">$cksum") or die "Can't open $cksum: $!\n";
	print CKSUM $dbhash;
	close(CKSUM);
	chmod(0600, $cksum);


}

########################################
########################################
##                                    ##
##              do_init               ##
##                                    ##
## Retrieve statistics for each file  ##
##                                    ##
########################################
########################################

sub do_init
{

  my $filearg = $File::Find::name;
	my $file = &pre_process($filearg); ## Skip excludes

	## Being safe - - no directories should be passed in.
	if ((!-d $file) && ($file ne "1")) {
	  
		########################### LSTAT ###########################
		# This kinda sucks. The array returned by lstat doesn't tell
		# you much unless you know what to look for. Here's a list of
		# all 13 values because I'm tired of looking them up
		#
		# 0 - Device number
		# 1 - Inode number
		# 2 - Mode (permissions)
		# 3 - Number of hard links to the file
		# 4 - UID
		# 5 - GID
		# 6 - Device identifier (special files only)
		# 7 - Size (in bytes)
		# 8 - atime
		# 9 - mtime
		#10 - ctime
		#11 - Blocksize for system I/O
		#12 - Actual number of blocks allocated
		########################################################

	  my ($mode, $uid, $gid, $size, $mtime) = (lstat($file))[2,4,5,7,9];
		my $full_hash = &calc_hash($file);
		my $all = "$full_hash,$mode,$uid,$gid,$size,$mtime"; 

		## The unpack command converts the filename string
		## to a hex string, obscuring it in the database.
		if ($opt{i}) {
		  $DB{unpack("H*", $file)} = $all;
			if ($opt{Y}) { print LOG "Recording $file\n"; }
			if ($opt{v}) { print "Recording $file\n"; }
		} elsif ($opt{S}) {
		  $DB2{unpack("H*", $file)} = $all;
		} else { die "Unexpected error: $!\n"; }

	  $filecount++;
		
	} elsif ((-d $file) && ($file ne "1")) { $dircount++; }

}

########################################
########################################
##                                    ##
##             do_scan                ##
##                                    ##
## Read the database and compare      ##
## against current file values        ##
##                                    ##
########################################
########################################

sub do_scan
{

	if (!$opt{q}) { print TMPFILE ":: Standard Scan ::\n\n"; }
	print LOG ":: Standard Scan ::\n\n";

	## Some implementations of the DB act differently. We'll
	## compensate here.
	if ((-f "$opt{d}.dir") && (-f "$opt{d}.pag")) { $normaldb = 1; }
	elsif ( -f "$opt{d}.db") { $normaldb = 2; }
	elsif ( -f $opt{d} ) { $normaldb = 0; }
	else { die "Unexpected DB error. Check the name. $!\n"; }

  &do_validate;

	dbmopen(%DB,$opt{d},0600);

	foreach (sort keys(%DB)) {
	  ## Revert hex string to an ASCII string
	  my $file = pack("H*",$_);
		if ($file eq "0") { next; }
		if (!(-f $file)) { 
		  print TMPFILE "Deleted: $file\n";
		  print LOG "Deleted: $file\n";
			$delcount++;
			next;
		}
		## Search this file for "LSTAT" for a table of lstat values
    my ($mode, $uid, $gid, $size, $mtime) = (lstat($file))[2,4,5,7,9];
    my $full_hash = &calc_hash($file);
		my $all = "$full_hash,$mode,$uid,$gid,$size,$mtime"; 
		if ($all ne $DB{$_}) {
		  print TMPFILE "Changed: $file\n";
		  print LOG "Changed: $file\n";

			if ($opt{w}) {

				my @valid = split /,/, $DB{$_};
				my @status = split /,/, $all;
				my $loopcount = 0;

				foreach (@valid) {
					if ($_ eq $status[$loopcount]) {
					$loopcount++;
					next;
					} else {
					print TMPFILE "*** $reftable{$loopcount} changed ***\n";
					print TMPFILE "Expected: $_\n";
					print TMPFILE "Found   : $status[$loopcount]\n\n";
					print LOG "*** $reftable{$loopcount} changed ***\n";
					print LOG "Expected: $_\n";
					print LOG "Found   : $status[$loopcount]\n\n";
					$loopcount++;
					}
				}
					print TMPFILE "~." x 20 . "\n\n";
			}

			$changecount++;
		}
	}

	dbmclose(%DB);

	my $runtime = (time - $^T);
	print LOG "Complete: $changecount file(s) changed. $delcount file(s) deleted.\n";

	if ((!$opt{q}) || (($opt{q}) && (($changecount > 0) || ($delcount > 0)))) {
	$alertstat = 1;
	print TMPFILE "Complete: $changecount file(s) changed. $delcount file(s) deleted.\n";
	print TMPFILE "Scan took $runtime seconds.\n";
	}

	if ($opt{e} && $alertstat == 1) {
		open (SENDMAIL, "|$sendmail -oi -t") or die "Can't fork for sendmail: $!\n";
		print SENDMAIL "From: Saint <saint\@$hostname>\n";
		print SENDMAIL "To: $opt{e}\n";
		print SENDMAIL "Subject: Saint Report for $hostname on $date.\n\n";
	}

	## Close tmp file and re-open for reading
	## (Yes, I know I could have opened it with +>)
	close(TMPFILE);
	open(TMPFILE,"$tmpfile");
	while (<TMPFILE>) {
	print $_;
	if ($opt{e} && $alertstat == 1) {
		print SENDMAIL $_;
	}

	
	}
	close(TMPFILE);
	unlink($tmpfile);
	if ($opt{e} && $alertstat == 1) { close(SENDMAIL); }
	
}



########################################
########################################
##                                    ##
##           do_deepscan              ##
##                                    ##
## Scan the system and compare to     ##
## existing database. Catches any     ##
## files that have been added.        ##
##                                    ##
########################################
########################################

sub do_deepscan
{

	if (!$opt{q}) { print TMPFILE ":: Deep Scan ::\n\n"; }
	print LOG ":: Deep Scan ::\n\n";


	## Some implementations of the DB act differently. We'll
	## compensate here.
	if ((-f "$opt{d}.dir") && (-f "$opt{d}.pag")) { $normaldb = 1; }
	elsif ( -f "$opt{d}.db") { $normaldb = 2; }
	elsif ( -f $opt{d} ) { $normaldb = 0; }
	else { die "Unexpected DB error. Check the name. $!\n"; }

  &do_validate;

	my $timestamp = $^T;
	my $pid = $$;
	my $dsdb = "$fsshome/.deepscan.$pid.$timestamp";
	my $dsdbd = "$dsdb.dir";
	my $dsdbp = "$dsdb.pag";

  dbmopen(%DB2,$dsdb,0600);

  find(\&do_init, @include); ## Scan requested files

  dbmopen (%DB,$opt{d},0600);

	foreach (sort keys(%DB)) {
	  ## Revert hex string to an ASCII string
	  my $file = pack("H*",$_);
	  my $key = $_;
		if ($file eq "0") { next; }

		if ($DB2{$key} && ("$DB2{$key}" eq "$DB{$key}")) {
		  $filecount++;
			delete($DB2{$key});
		} elsif ($DB2{$key} && ("$DB2{$key}" ne "$DB{$key}")) {
		  print TMPFILE "Changed: $file\n";
		  print LOG "Changed: $file\n";

			if ($opt{w}) {

				my @valid = split /,/, $DB{$key};
				my @status = split /,/, $DB2{$key};
				my $loopcount = 0;

				foreach (@valid) {
					if ($_ eq $status[$loopcount]) {
					$loopcount++;
					next;
					} else {
					print TMPFILE "*** $reftable{$loopcount} changed ***\n";
					print TMPFILE "Expected: $_\n";
					print TMPFILE "Found   : $status[$loopcount]\n\n";
					print LOG "*** $reftable{$loopcount} changed ***\n";
					print LOG "Expected: $_\n";
					print LOG "Found   : $status[$loopcount]\n\n";
					$loopcount++;
					}
				}
					print "~." x 20 . "\n\n";
					print LOG "~." x 20 . "\n\n";
			}

			$changecount++;
			delete($DB2{$key});
		} elsif (!($DB2{$key})) {
		  print TMPFILE "Deleted: $file\n";
		  print LOG "Deleted: $file\n";
			$delcount++;
		}
	}

	### Process remainder of %DB2

	foreach (sort keys(%DB2)) {
	  ## Revert hex string to an ASCII string
	  my $file = pack("H*",$_);
	  my $key = $_;
		if ($file eq "0") { next; }
		print TMPFILE "New    : $file\n";
		print LOG "New    : $file\n";
		$newcount++;
	}
		  

	dbmclose(%DB);
	dbmclose(%DB2);

  unlink ($dsdb);
  unlink ($dsdbd);
  unlink ($dsdbp);


	my $runtime = (time - $^T);
	print LOG "Complete: $changecount file(s) changed. $delcount file(s) deleted. $newcount new file(s).\n";


	if ((!$opt{q}) || (($opt{q}) && (($changecount > 0) || ($delcount > 0) || ($newcount > 0)))) {
		$alertstat = 1;
	  print TMPFILE "Complete: $changecount file(s) changed. $delcount file(s) deleted. $newcount new file(s).\n";
	  print TMPFILE "Scan took $runtime seconds.\n";
	}

	if ($opt{e} && $alertstat == 1) {
		open (SENDMAIL, "|$sendmail -oi -t") or die "Can't fork for sendmail: $!\n";
		print SENDMAIL "From: Saint <saint\@$hostname>\n";
		print SENDMAIL "To: $opt{e}\n";
		print SENDMAIL "Subject: Saint Report for $hostname on $date.\n\n";
	}

	## Close tmp file and re-open for reading
	## (Yes, I know I could have opened it with +>)
	close(TMPFILE);
	open(TMPFILE,"$tmpfile");
	while (<TMPFILE>) {
	print $_;
	if ($opt{e} && $alertstat == 1) {
		print SENDMAIL $_;
	}

	
	}
	close(TMPFILE);
	unlink($tmpfile);
	if ($opt{e} && $alertstat == 1) { close(SENDMAIL); }
	
}

########################################
########################################
##                                    ##
##             calc_hash              ##
##                                    ##
##    Calculate the full checksum     ##
##                                    ##
########################################
########################################

sub calc_hash
{
	my $file = shift;
	my $sha_rtn = &calc_sha($file);
	my $md5_rtn = &calc_md5($file);
	my $full_cksum = $sha_rtn . $md5_rtn;
	chomp $full_cksum;
	return ($full_cksum);
}

########################################
########################################
##                                    ##
##             calc_md5               ##
##                                    ##
##     Calculate the MD5 checksum     ##
##                                    ##
########################################
########################################

sub calc_md5
{
  my $file = shift;
  open(FILE, $file) or return 0;
  binmode(FILE);
  $md5->addfile(*FILE);
  return $md5->b64digest;
	close(FILE);
}

########################################
########################################
##                                    ##
##             calc_sha               ##
##                                    ##
##    Calculate the SHA checksum      ##
##                                    ##
########################################
########################################

sub calc_sha
{
  my $file = shift;
  open(FILE, $file) or return 0;
  binmode(FILE);
  $sha->addfile(*FILE);
  return $sha->b64digest;
	close(FILE);
}


########################################
########################################
##                                    ##
##           do_validate              ##
##                                    ##
##   Validate an existing database    ##
##                                    ##
########################################
########################################

sub do_validate
{
  my $check;

  if ($normaldb == 1) {
	  $check = &calc_hash("$opt{d}.pag") . &calc_hash("$opt{d}.dir");
	} elsif ($normaldb == 2) {
		$check = &calc_hash("$opt{d}.db");
	} else {
	  $check = &calc_hash("$opt{d}");
	}


  open(CKSUM, "<$cksum") or die "Error opening checksum $cksum : $!\n";
	while(<CKSUM>) {
	  if ($_ ne $check) {
		  print TMPFILE "#### ERROR! Checksum invalid! ####\n";
			print TMPFILE "Expected: $_\n";
			print TMPFILE "Found   : $check\n";
			die "Bad Checksum.\n";
		}
	}
	if (!$opt{q}) { print TMPFILE "Database checksum valid.\n"; }
	close(CKSUM);
}

########################################
########################################
##                                    ##
##           show_usage               ##
##                                    ##
##  Display full usage information    ##
##                                    ##
########################################
########################################

sub show_usage
{
print <<EOF

$fssname v$version

Usage: $0 [options]

Options
---------
 -c <config file> Config: Specify a config file. Defaults to saint.conf
 -d <database>  Database: Specify a database file. Defaults to <hostname>.db
 -e <email addr>   Email: Email any changes to the address specified
 -h                 Help: Show usage information.
 -i           Initialize: Generate a new database.
 -l <logfile>    Logfile: Set logfile. Default is saint.log. 
 -L                Links: Include checks on symbolic links. (skipped by default)
 -q                Quiet: Suppress all output except for changes and errors.
 -s                 Scan: Use the DB as a reference and check all known files.
 -S            Deep Scan: Use a new scan as a ref thus accounting for new files.
 -v              Verbose: Print detailed information to terminal.
 -w                 What: Don't just report a change, report *what* changed.
 -Y                 Yack: Print extensive information to the log.

Use of Initialize or one of the Scan modes is required.

Create/Re-initialize the database:

$0 -i

Standard system check:

$0 -s

EOF
;

exit 1;
}

########################################
########################################
##                                    ##
##           show_short               ##
##                                    ##
##  Display short usage information   ##
##                                    ##
########################################
########################################

sub show_short
{

print <<EOF

$fssname v$version

Usage: $0 [options]

Run "$0 -h" for more information.

EOF
;

exit 1;
}

