#!/bin/sh

# Script to investigate a FreeBSD system and fix some errors
# Script to perform various maintance jobs in a FreeBSD
#
# Minas Dasygenis
# 2017,2018,2019,2020,2021,2022
# http://arch.ece.uowm.gr
#
# To silent some messages set the enviromental varialbe SILENT to YES
# export SILENT=YES
# setenv SILENT YES
# or uncoment the following line:
# SILENT=YES
#
# To override detected versions do use the following:
#
# export OVERARCH=i386
# export OVERREL=13.0-RELEASE
#
# setenv OVERARCH i386
# setenv OVERREL 13.0-RELEASE
# 
# OVERARCH=i386 OVERREL=13.0-RELEASE $0
#
# Future Extension: Consult Obsolete Files: https://raw.githubusercontent.com/musec/freebsd/master/ObsoleteFiles.inc
#
# 2020.07.28 Added |tee /dev/null to freebsd-update in order to remove the blocking prompts, like [END]
# 2022.04.28 Added support for env variables arch/relname

version=1
note1="Value1 is $OVERARCH"
note2="Value is $OVERREL"

# Lets check if freebsd-update supports no cron
freebsd-update --help |  grep -q not-running-from-cron
if [ $? -eq 0 ] ; then
	nocron="--not-running-from-cron"
else
	nocron=""
fi


# Do we have a location to store pkgs?
if [ -d /public/.pkgtmp ]; then
	pkgextra='-o PKG_CACHEDIR=/public/.pkgtmp'
	echo "USING [$pkgextra]"
else
	pkgextra=""
fi

note="PKGExtra is: $pkgextra"

if [ x"$OVERARCH" == "x" -o "$OVERREL" == "x" ] ; then
	note="NO OVERRIDE Detected"
	# Returns MAJOR.MINOR-RELEASE, like 12.2-RELEASE
	myuname=`uname -r  | sed -E 's/(^.*RELEASE).*$/\1/'`
	# Returns arch, like i386 or amd64
	arch=`uname -m`
else
	myuname=$OVERREL
	arch=$OVERARCH
	echo "DETECTED Env Override (OVERREL/OVERARCH) for ARCH: [$arch]/[$myuname]"
fi

#Used for automatic answer
autoyes=0
mybasedir="/home/$myuname"
PATH=/rescue:$PATH
export PATH


# The link that carries the official kernel and base files
#basefiles="ftp://ftp.fr.freebsd.org/mirrors/ftp.freebsd.org/releases/"
basefiles="http://ftp.fr.freebsd.org/pub/FreeBSD/releases/"
basefiles="http://ftp.gr.freebsd.org/pub/FreeBSD/releases/"


# Download the requisite check at: http://arch.ece.uowm.gr/mdasyg/misc/check_requisite_library_files.sh
requisite_files_check="/scripts/system/check_requisite_library_files.sh"

#The location of our save file
configfile=/root/.freebsdGOLDEN

if [ -z "$TMP" ] ; then
	TMP=/home/tmp
fi

IDS=$TMP/IDS
ids=$TMP/IDS_files
IDS_symlinks=$TMP/IDS_symlinks
IDS_regfile=$TMP/IDS_regfile
pkgB=$TMP/IDS_pkgB
lsfiles_gold=$TMP/IDS_lsfiles_gold
emptydirs=$TMP/IDS_emptydirs
findoptions="( -path boot/GENERIC -o -path boot/kernel.old1 -o -path usr/local -o -path usr/tmp -o -path boot/kernel.old ) -prune -o"

# ONLY for freebsd-ids
# ignore these objects in freebsd-update ids creation (symlinks/wrong permissions/...)
ids_ignorefiles="
(/etc/mail/aliases)|(/usr/.cache)|(passwd)|(/group)|(/inetd.conf)|(hosts)|(/syslog.conf)|(ttys)|(/csh.cshrc)|(/motd)|(/profile)|(.db)|(/services)|(/shells)|(/etc/aliases)|(/etc/ssh/)|(/sysctl.conf)|(/var/spool)|(/locate.rc)|(/etc/mail/aliases)|(/var/spool/mqueue)|(/.cshrc)|(.k5login)|(/.login)|(/etc/ntp.conf)|(/csh.login)|(/csh.logout)|(/dumpdates)|(/var/run)|(/root)|(/etc/crontab)|(/pam.d/sshd)|(/login.conf)|(/nsswitch.conf)|(/pam.d/telnetd)|(/security/audit_control)|(/security/audit_user)|(locate.database)
"


# We ignore these files/directories
# for IDS_not_on_local
ignorelsfilesnotonlocal='
-e usr/lib32
-e usr/.cache
-e usr/local/bin/scripts
'

# We ignore these files/directories
# for IDS_not_on_global
# FIX for: "The following files are present on local system but not on golden:"
# find list
ignorelsfiles="
-e boot/modules/if_wg.ko
-e usr/ports
-e usr/local/www
-e boot/modules/if_re.ko
-e etc/ntp.conf
-e etc/mail/aliases
-e /etc/mail/aliases
-e boot/modules/vbox
-e boot/zfs/zpool.cache
-e boot/loader.conf
-e boot/entropy
-e usr/bin/ldd32
-e usr/lib32
-e usr/compat
-e libexec/ld-elf32
-e boot/modules/linker.hints
-e boot/linker.hints
-e usr/extra
-e boot/firmware
-e usr/home
-e usr/opt
-e usr/.cache
-e .sujournal
-e usr/local/bin/scripts
"


# Ignore these folders that are empty on local file system
ignoreemptydirs="
-e usr/local/www/
-e usr/local/bin/scripts
-e usr/local/etc
-e usr/.snap
-e usr/.cache
-e usr/compat/linux/sys
-e usr/compat/linux/proc
-e usr/compat/linux/dev/shm
"




#
# List of folders to verify that no excess files exist, compared to the Golden Image
#
folders_to_check="boot/ bin/ usr/ bin/ lib/ libexec/ rescue/ sbin/ "


#
# If the flag SILENT is enabled, then some messages are not printed
#
if [ x"$SILENT" = x"YES" ] ; then
        silent=1
else
        silent=0
fi


#
# We do require a TMP file
#
if [ ! -d $TMP ] ; then
	echo "Please set TMP variable. Currently: [$TMP]"
	echo " "
	exit 9
fi

#
# Synchronize master/passwd database just to make sure everything is in order
#
/usr/sbin/pwd_mkdb -p /etc/master.passwd > /dev/null 2>&1






#
# Clean created files, tidy up
#
clean()
{
for i in  $ids $pkgB $IDS $IDS_regfile $IDS_symlinks $lsfiles_gold $TMP/IDS_not_on_global $TMP/IDS_not_on_local $emptydirs
do
	if [ -f $i ] ; then 
			rm $i ; 
			if [ $silent -eq 0 ] ; then
				echo "(clean function) Removing created files by this script: $i" ; 
			fi
	fi
done
}






#
# Tasks to update freebsd unattended (base + packages)
#
fbsd_update_base()
{
unlock
freebsd-update $nocron fetch 	| tee /dev/null
freebsd-update $nocron install	| tee /dev/null
if [ $? -eq 0 ] ; then
                echo "We executed freebsd-update on the LOCAL system, so we have to clear old IDS files"
		$0 clean
		#Also, lets recreate the motd to be in sync..
		/etc/rc.d/motd onerestart > /dev/null 2>&1
        	fi
}



fbsd_update_pkg()
{
env ASSUME_ALWAYS_YES=YES pkg $pkgextra update
env ASSUME_ALWAYS_YES=YES pkg $pkgextra autoremove
env ASSUME_ALWAYS_YES=YES pkg $pkgextra clean -a
env ASSUME_ALWAYS_YES=YES pkg $pkgextra upgrade
}




#
# Update packages and base system using the silent flag...
#
update_local()
{
if [ $silent -eq 0 ] ; then
	fbsd_update_base
	fbsd_update_pkg
else
	fbsd_update_base  >/dev/null 2>&1
	fbsd_update_pkg  >/dev/null 2>&1
fi
}









#
# Bring the files to create the directory of full golden image of the given release
#
fetch_golden_image()
{
if [ "$arch" = "amd64" ] ; then
	packs="base.txz kernel.txz lib32.txz"
else
	packs="base.txz kernel.txz"
fi


which wget > /dev/null 2>&1
if [ $? -ne 0 ] ; then
	wget=0
else
	wget=1
	urldownload=wget
fi

which fetch > /dev/null 2>&1
if [ $wget -eq 0 -a  $? -ne 0 ] ; then
	fetch=0
else
	fetch=1
	urldownload=fetch
fi

if [ $fetch -eq 0 -a $wget -eq 0 ] ; then
	echo "[ERROR] Mandatory tool wget/fetch not found"
	exit 4
fi

basedir=$mybasedir
if [ -x /scripts/system/mm.sh ] ; then
	( /scripts/system/mm.sh $basedir ) > /dev/null 2>&1
fi

if [ ! -d "$basedir" ] ; then
	ok=0
	while [ $ok -eq 0 ] ; do
		echo "Do you want me to create the directory [$basedir]? y/n"
		read ans
		if [ x$ans = xy ] ; then
			mkdir -vp "$basedir"
			mkdir -vp "$basedir/var/db/freebsd-update/"
			ok=1
		elif [ x$ans = xn ] ; then
			echo "I will not create this as you requested, and thus I exit."
			exit 4
		else
			echo "Please only y or n"
			echo " "
		fi
	done
fi

for file in $packs
do
	if [ -f $TMP/$file ] ; then
		echo "Deleting [$file] for a fresh copy"
		rm $TMP/$file
	fi
sleep 1
done
sleep 1

for ifile in $packs
do
cd $TMP && $urldownload \
${basefiles}/$arch/$arch/${myuname}/$ifile

note="On tar we preserve hard links and sparce files"
if [ $? -eq 0 ] ; then
	tar -C $basedir/ -xvSpf $TMP/$ifile && rm $TMP/$ifile
	if [ $? -ne 0 ] ; then
		echo "Error in untarring [$TMP/$ifile] to [$basedir]"
		exit 9
	fi
else
	echo "Error in downloading [$ifile] to [$TMP]"
	exit 4
fi
done


}



#
# Delete the golden image
#
delete_golden_image()
{
basedir=$1
check_basedir $basedir
if [  -d "$basedir" ] ; then
	echo "I will remove [$basedir]. Are you sure? (y/n)" 
	read yn
	if [ "$yn" = "y" ];  then
		chflags -R noschg $basedir
		rm -rfv $basedir
	else
		echo "Sorry this is not a valid answer"
	fi

fi

}




#
# Update the golden image using the latest binary patches
#
update_golden_image()
{
basedir=$1
check_basedir $basedir
myunameRUN=`uname -r  | sed -E 's/(^.*RELEASE).*$/\1/'`

if [ $myunameRUN != $myuname  ] ; then
	echo "=======>>>>>> UPDATING Golden is not currently possible"
	echo "=======>>>>>> Different versions of running kernel [$myunameRUN] and specified kernel [$myuname]"
	sleep 5
	return
fi

if [  -d "$basedir" ] ; then
	echo "Updating [$basedir]"
	echo -n "Starting: "
	echo -n 3
	sleep 1
	echo -n 2
	sleep 1
	echo -n 1
	sleep 1
	echo 0
	freebsd-update -b $basedir \
	$nocron -d $basedir/var/db/freebsd-update/ fetch  | tee /dev/null
	freebsd-update -b $basedir \
	$nocron -d $basedir/var/db/freebsd-update/ install | tee /dev/null

	if [ $? -eq 0 ] ; then
		echo "We executed freebsd-update on the GOLDEN image, so we are re-executing lsfiles_gold creation on golden. Please wait."
		lsfiles_golden $basedir
	fi
else
	echo "Basedir [$basedir] absent"
	exit 5
fi

#No need to update lsfiles_gold here, it has already been performed (if we have updated golden)
}












#
# Create a list of Golden files using lsfiles_gold, in order to compare
# our list of files and the list of golden files
#
lsfiles_golden()
{
	basedir=$1
	check_basedir $basedir
	#Lets create the golden list of lsfiles_gold
	echo "Creating list of GOLDEN files located at [$basedir]..."
	cd $basedir && find $folders_to_check $findoptions -type f -print > $basedir/lsfiles_gold
	rc=$?
	if [ $rc -ne 0 ] ; then
		echo "*** ERROR at creating lsfiles_gold at lsfiles_golden() ***"
	fi
return $rc
}









#
# Perform an IDS check on the golden image. It should not be
# necessary, but just to be sure, why not?
#
ids_golden()
{
	basedir=$1
	check_basedir $basedir
	echo "Checking IDS of golden image [$basedir]"
	# IDS not needed usually, everything is in order
	freebsd-update -b $basedir \
	$nocron -d $basedir/var/db/freebsd-update/ IDS | tee /dev/null

	lsfiles_golden $basedir
}






#
# Usually the golden image directory is located on an NFS disk
# in order to be used by many FreeBSD servers
# so The structure is usually /mnt/nfs/XXX/XXX/TTT
# so we grab the first structure /mnt/nfs and try to mount it (using entry in /etc/fstab)
#
check_basedir()
{
basedir=$1

if [ -x /scripts/system/mm.sh ] ; then
	( /scripts/system/mm.sh $basedir ) > /dev/null 2>&1
fi

	if [ ! -d "$basedir" ] ; then
		#Lets try to mount it in case that does not exist
		basemount=`echo $basedir | cut -f 1-3 -d "/"`
		timeout 5 mount $basemount > /dev/null 2>&1
		rc=$?
		#checking again whether basedir exists
		if [ ! -d "$basedir" ] ; then
			echo " "
		 echo "Golden Image Folder [$basedir] seems not to exist. Tried to mount the base mountpoint [$basemount] and got [$rc]. Mount it manually or set it as 2nd parameter."
		echo "Please Create it: mkdir -p $basedir"
		echo " "
			exit 7
		fi
	fi
}








#
# We will create/examine the IDS files of our FreeBSD System
#
create_IDS()
{
if [ -z "$1" ] ; then
	basedir=$mybasedir
else
	basedir=$1
fi
check_basedir $basedir
#First lets check if the IDS files that exist on our $TMP are older than 30 days. If they are older DO delete it
/usr/bin/find $IDS -maxdepth 1 -type f  -mtime +30  -delete > /dev/null 2>&1

if [ ! -f $IDS ] ; then
	if [ $silent -eq 0 ] ; then
		echo "IDS log file [$IDS] not found. I will create one. Running: freebsd-update IDS"
	fi
	freebsd-update IDS | grep -E -v '(/root)|(hosts)' > $IDS
else
	if [ $silent -eq 0 ] ; then
		echo "IDS log file [$IDS] is found. I will use this."
	fi
fi

if [ ! -s "$basedir/lsfiles_gold" ] ; then
	if [ $silent -eq 0 ] ; then
		echo "DELETING [$basedir/lsfiles_gold] because it has 0 bytes"
	fi
	rm "$basedir/lsfiles_gold" > /dev/null 2>&1
	lsfiles_golden $basedir
fi
	

if [ ! -s "$lsfiles_gold" ] ; then
	if [ $silent -eq 0 ] ; then
		echo "DELETING [$lsfiles_gold] because it has 0 bytes"
	fi
	rm "$lsfiles_gold" > /dev/null 2>&1
fi

if [ ! -f "$lsfiles_gold" ]; then
	#we have to create the local list of files
	if [ $silent -eq 0 ] ; then
		echo "CREATING [$lsfiles_gold]"
	fi
	cd / &&  find $folders_to_check $findoptions -type f -print > $lsfiles_gold
fi


create_emptydirs

cat $IDS | 	grep 'but should have SHA' | grep -v -E "$ids_ignorefiles" | grep -v /libexec/ | grep -v /etc/mail/ |  cut -f 1 -d" "> $ids

if [ ! -f $basedir/lsfiles_gold ]; then
	lsfiles_golden $basedir
fi


if [ ! -s $TMP/IDS_not_on_local ] ; then
 grep -Fvxf $lsfiles_gold  $basedir/lsfiles_gold | grep -v $ignorelsfilesnotonlocal |  > $TMP/IDS_not_on_local
fi

if [ -s $TMP/IDS_not_on_local ]; then
		echo "vvvv The following files are present on golden but not on local: "
		sleep 1
		less $TMP/IDS_not_on_local
		echo "^^^^ The previous files are present on golden but not on local. "
		sleep 3
fi




if [ ! -s $TMP/IDS_not_on_global ] ; then
 grep -Fvxf $basedir/lsfiles_gold $lsfiles_gold | grep -v $ignorelsfiles  > $TMP/IDS_not_on_global
fi


if [ -s $TMP/IDS_not_on_global ] ; then
		echo "The following files are present on local system but not on golden: "
		sleep 1
		cat $TMP/IDS_not_on_global | grep -v $ignorelsfiles | less
		echo "The previous files are present on local system but not on golden: "
		sleep 3
fi

if [ -s "$emptydirs" ] ; then
		echo "The following directories are empty on the local file system and should be removed: "
		sleep 1
		less $emptydirs
		echo "........................................."
		for iemptydir in `cat $emptydirs`
		do
			echo -n "Removing empty dir: "
			cd / && rmdir -v $iemptydir
		done
		echo "The previous directories were empty and I removed them"
		sleep 3
fi

if [ -x $requisite_files_check ] ; then
 if [ $silent -eq 0 ] ; then
 	echo "Examing dynamic libraries consistency (check_requisite_library_files.sh): STARTING"
 fi

 $requisite_files_check

 if [ $silent -eq 0 ] ; then
 	echo "Examing dynamic libraries consistency (check_requisite_library_files.sh): COMPLETED"
 fi

fi
} #end create_IDS()








create_emptydirs(){
if [ ! -f "$emptydirs" ] ; then
	#we jave to create the empty directories list
	if [ $silent -eq 0 ] ; then
		echo "CREATING [$emptydirs]"
	fi
	cd / &&  find $folders_to_check -type d -empty  | grep -v $ignoreemptydirs > $emptydirs
fi
}


# 
# copy files that are present on the Golden image but 
# not on our base system
#
fix_missing_files()
{
basedir=$mybasedir
check_basedir $basedir


if [ -s $TMP/IDS_not_on_local ]; then
	echo "The following files are present on golden but not on local: "
	sleep 3
	less $TMP/IDS_not_on_local
	echo  -n "I will copy them on local in: " 
	echo -n "3" ; sleep 1
	echo -n "2" ; sleep 1
	echo -n "1" ; sleep 1
	echo "0" 
	nr=`cat $TMP/IDS_not_on_local | wc -l`
	counter=1
	cat $TMP/IDS_not_on_local | while read line ; do 
			if [ ! -f /$line ] ; then
				echo -n "[$counter / $nr] "
				mydir=`dirname $line`
				if [ ! -d /$mydir ] ; then mkdir -vp /$mydir ; fi
				if [ -L /$line ] ; then rm /$line ; fi
				cp -vPa $basedir/$line /$line 
				chown root:wheel /$line 
				sleep 1
			else
				echo "file [/$line] exists, I do not overwrite it..."
			fi
			#this also may count files alreary present, but at least the counter will be accurate
			counter=`expr $counter + 1`
			done

echo "Moving to phase Present At local file system NOT on golden file system."
echo -n "." ; sleep 1 ; echo -n "."; sleep 1 ;  echo -n "." ; sleep 1 ; echo .
fi

if [ -s $TMP/IDS_not_on_global ] ; then
	echo "The following files are present on local system but not on golden: "
	sleep 3
	cat $TMP/IDS_not_on_global | grep -v $ignorelsfiles | less
	echo  -n "I will move them out of the way in: "
	echo -n "3" ; sleep 1
	echo -n "2" ; sleep 1
	echo -n "1" ; sleep 1
	echo "0" 
	nr=`cat $TMP/IDS_not_on_global | grep -v $ignorelsfiles | wc -l`
	counter=1
	cat $TMP/IDS_not_on_global | grep -v $ignorelsfiles |  while read line ; do
	echo  -n "[$counter / $nr] $line"
	mkdir -p /usr/local/backup
	if [ -f "/$line" ] ; then
		chflags noschg /$line > /dev/null 2>&1
		mv -v /$line /usr/local/backup
		#Lets save the date of the last modification
		date > /usr/local/backup/DASYGENIS_freebsd_ids_BACKUPDIR 2>/dev/null
		sleep 1
	else
		echo "$line : file abducted by UFO..."
	fi
	counter=`expr $counter + 1`
	done
fi

echo "Examining packages that have wrong dependencies (pkg check)"
pkg check -d

pkgB_list


}





pkgB_list()
{
if [ ! -f $pkgB ]; then
	echo "Examing dynamic libraries using pkg (long process...)"
	( pkg check -B | grep -v Checking )   >  $pkgB 2>&1
fi
}




fix_pkgB()
{
pkgB_list

if [ -s $pkgB ] ; then
	listfiles=`cat $pkgB | grep fstat | sed 's@^.*failed for(\(.*\)).*$@\1@'`
	dependencies=`for line in $listfiles
		do
			pkg which $line  | awk '{print $NF}'
		done | uniq`

for i in $dependencies
do
	pkg $pkgextra install -y -f  $i
done
fi
}





#
# According to the IDS results of freebsd-update
# try to fix permissions on our base system
#
fix_perms()
{
if [ ! -f $IDS ] ; then
	if [ $silent -eq 0 ] ; then
		echo "It is required to create the check files....."
	fi
	$0 check
fi

#Lets fix permissions
echo "Fixing Permissions:"
eval `grep permission $IDS | awk '{print "chmod -v "$8,$1";"}' 2>/dev/null` 
echo "Fixing Ownership:"
eval `grep 'should be owned' $IDS  | tr -d '.' | awk '{print "chown -v "$NF,$1";"}' 2>/dev/null`
}










#
# According to the IDS results, try to create the messages that 
# either something is a regfile and should be link or vise versa
#
create_regfilelist()
{
if [ $silent -eq 0 ] ; then
 	echo "Calling: create_regfilelist(): STARTING"
fi

if [ ! -f $IDS_regfile ] ; then
	cat $IDS | grep -v -E "$ids_ignorefiles" |  grep 'is a regular file, but should be a symlink' |\
	cut -f 1 -d " " > $IDS_regfile

	cat $IDS | grep -v -E "$ids_ignorefiles" |  grep ' is a symlink, but should be a regular file' |\
	cut -f 1 -d " " >> $IDS_regfile

fi

if [ $silent -eq 0 ] ; then
 	echo "Calling: create_regfilelist(): COMPLETED"
fi
}











#
# Fix regular files accoring to the list of IDS regfile
# that is produced by freebsd-update IDS
#
fix_regular_file()
{
create_regfilelist


while read line
do
#Lets check if original exist
if [ -f $mybasedir/$line ] ; then
	if [ -f $line ] ; then
		#echo "Deleting: [$line]"
		rm $line
	fi
	echo "Replacing: [$line] with golden file"
	cp -av $mybasedir/$line $line
fi

done < $IDS_regfile
}








#
# Some people lock / and /usr for security purposes. Lets remount RW 
#
unlock()
{
	/sbin/mount -u -w /     > /dev/null 2>&1
	/sbin/mount -u -w /usr  > /dev/null 2>&1
	/sbin/mount -u -w /var  > /dev/null 2>&1

}









#
# Create the list of symlinks in order to be fed
# to the proper function for fixing
#
create_symlinks_list()
{

if [ $silent -eq 0 ] ; then
 	echo "Calling create_symlinks_list(): STARTING"
fi

if [ ! -f $IDS_symlinks ] ; then
        cat $IDS | grep 'is a symlink' |\
	 grep 'but should be a symlink' |\
	 grep -v -E "$ids_ignorefiles" |\
	 sed -E 's@^([^ ]*).* ([^ ]*)\.$@\1 - \2@g' > $IDS_symlinks
fi

if [ $silent -eq 0 ] ; then
 	echo "Calling create_symlinks_list(): COMPLETED"
fi
}




#
# Clean empty dirs
#
fix_emptydirs()
{
#create the list $emptydirs if not present
create_emptydirs
cwd=`pwd`

nothingelsetoremove=0

while [ $nothingelsetoremove -eq 0 ] ; do
I_did_remove_a_directory=0	#originally we have not removed any directory
counter=1
nr=`cat $emptydirs | wc -l`

while read line
do
dir=/"$line"

# Directories matching an expression are skipped..
echo $dir | grep -q $ignoreemptydirs
if [ $? -eq 0  ] ; then
	continue
fi


if [ -d "$dir" ] ; then
	#If this is a mounted dir (even empty) just skip it
	mount | grep -q " $dir " && continue
	echo -n "[$counter / $nr] Removing [$dir]: "
        sleep 1
        rmdir "$dir"
	if [ $? -eq 0 ] ; then
		echo "OK" 
		I_did_remove_a_directory=1
	else
		echo "FAIL"
	fi
else
        note="-----> DASYGENIS WARNING: Empty Directory [$dir] migrated to another system and does not exist anymore here."
fi

counter=`expr $counter + 1`
done < $emptydirs
#If we have removed some directories then re-run creation
if [ $I_did_remove_a_directory -eq 1 ] ; then
	rm $emptydirs
	create_emptydirs
else
	#Nothing removed, so we can escape the loop
	nothingelsetoremove=1
fi

done
}






#
# Create the symbolink links on the base system
#
fix_symlinks()
{

create_symlinks_list

cwd=`pwd`
while read line
do
orig=`echo $line | cut -f 1 -d " "`
target=`echo $line | cut -f 3 -d " "`
newcd=`dirname $orig`

if [ -d $newcd ] ; then
cd $newcd
        if [ -f "$target" ] ; then
        echo "[$orig]--->[$target]"
        sleep 1
        rm $orig
        ln -v -s $target $orig
        else
                echo "-----> WARNING: Target [$target] does not exist at cd [$newcd]"
        fi
else
        echo "-----> WARNING: Directory [$newcd] does not exist"
fi

done < $IDS_symlinks
}












#
# If a file fails on IDS, then just copy it from the golden  image
#
fix_ids()
{

if [ ! -s $ids ] ; then
	echo "Filesize of [$ids] is not greater than 0 so nothing to do at fix_ids()"
	return
fi

check_basedir $mybasedir

sleep 2
clear
for file in `cat $ids` ; do
	if [ -f ${basedir}/$file ] ; then 
		if [ ! -f $file ] ; then
			echo "File [$file] at original filesystem does not exist originally"
		else
			diff -q $file $basedir/$file  > /dev/null 2>&1
			if [ $? -eq 0 ] ; then echo "File: [$file] is the same with GOLDEN file, skipping."; sleep 1 ;  continue ; fi
			echo "File: [$file] OLD<->NEW diffs"
			sleep 1
			# diff $file $basedir/$file
			if [ $autoyes -eq 0 ] ; then
				sdiff $file $basedir/$file | less -F
			else
				sdiff $file $basedir/$file | less -F -e
			fi
			ok=1
			while [ $ok -eq 1 ] ; do
				if [ $autoyes -eq 0 ] ; then
					echo -n "======================>   Replace y/n/a <======================> "
					read mychar
				else
					echo " "
					echo " "
					echo "----------------------> AUTOMATIC REPLY YES <----------------------"
					mychar=y
				fi

				if [ x"$mychar" = x"y" -o x"$mychar" = x"a" ] ; then
					cp -fpv $basedir/$file $file && echo "COPIED" || echo "COPY FAILED"
					ok=0
					sleep 1
					if [ x"$mychar" = x"a" ]  ; then
						echo '****SETTING AUTOMATIC REPLY FROM NOW ON****'
						autoyes=1
					fi
				elif [ x"$mychar" = x"n" ] ; then
					echo "Skipping... [$file]"
					sleep 1
					ok=0
				else
					echo "Please retry"
					sleep 1
					continue
				fi
			done
		fi

	else
		echo "File [$basedir]/[$file] which has wrong IDS according to file, does not exist in base image. Maybe base image is incomplete....skipping"
		continue
	fi
done
}





# ----------------------
# MAIN script starts here
# ----------------------






#
# Define mybasedir location
#
# If the user gave 2 parameters, then the second is the 
# golden image location
if [ $# -eq 2 ] ; then
	if [ -d $2 ]; then
		mybasedir=$2/$arch
	else
		echo "You gave [$2] as the golden image location but I cannot seem to access this. Please check and re-execute."
		exit 5
	fi
else
	if [ -f $configfile ]; then
		if [ $silent -eq 0 ] ; then
			echo "Reading $configfile"
		fi

		. $configfile
		#Check if the golden dir carries the
		#MAJOR.MINOR name. If not then we will
		#use a subfolder
		echo $GOLDEN | grep -q '[0-9][0-9]\.[0-9]'
		if [ $? -ne 0 ]; then
			mybasedir=$GOLDEN/$myuname/$arch
		else
			mybasedir=$GOLDEN/$arch
		fi
	fi
fi


if [ $silent -eq 0 ] ; then
	echo "GOLDEN image set at [$mybasedir]"
	goldendir=`echo $mybasedir | sed "s@\(^.*\)/$myuname/$arch@\1@g"`
	#If there is a config file then check if it needs updating
	if [ -f $configfile ] ; then
		grep -q "$goldendir" "$configfile"
		if [ $? -ne 0 ] ; then
			echo "GOLDEN=$goldendir" > $configfile
			if [ $silent -eq 0 ] ; then
				echo "Updating config file [$configfile]."
			fi
		fi
	else
		#no config file, save our config
		if [ $silent -eq 0 ] ; then
			echo "****[INFO] Creating config file [$configfile]."
		fi
		echo "GOLDEN=$goldendir" > $configfile
	fi
fi



#
# Execution states of this script
#
case $1 in
check)
	clean
	create_IDS $mybasedir
	create_regfilelist
	create_symlinks_list
	cat $ids
	cat $IDS_symlinks
	cat $IDS_regfile
	#if size greater than zero
	if [ -s "$ids" -o -s "$IDS_symlinks" -o -s "$IDS_regfile" -o -s "$TMP/IDS_not_on_global" -o -s "$TMP/IDS_not_on_local" ] ; then
		if [ $silent -eq 0 ] ; then
			echo "*** Problems detected as indicated ***"
		fi
		exit 5
	else
		if [ $silent -eq 0 ] ; then
			echo "*** NO Problems detected ***"
		fi
		exit 0
	fi
	;;
ids_golden)
	ids_golden $mybasedir
	;;
update_golden|update_remote|update_global)
	update_golden_image $mybasedir
	;;
flush_golden|delete_golden)
	delete_golden_image $mybasedir
	;;
update|update_local)
	unlock
	update_local
	;;
update_pkg)
	unlock
	fbsd_update_pkg
	;;
create|create_golden)
	fetch_golden_image
	$0 update_golden
	;;
fix_perms)
	unlock
	create_IDS
	fix_perms
	;;
fix_ids)
	unlock
	create_IDS
	fix_ids
	;;
fix_regular_files)
	unlock
	create_IDS
	fix_regular_file
	;;
fix_symlinks)
	unlock
	create_IDS
	fix_symlinks
	;;
fix_missing_files)
	unlock
	clean
	fix_missing_files
	;;
fix_emptydirs)
	unlock
	clean
	fix_emptydirs
	;;
fix_pkgB)
	unlock
	fix_pkgB
	;;
fix)
	unlock
	fix_perms
	fix_regular_file
	fix_symlinks
	fix_ids
	fix_missing_files
	fix_emptydirs
	fix_pkgB
	$requisite_files_check fix
	#Also, lets recreate the motd to be in sync..
	/etc/rc.d/motd onerestart > /dev/null 2>&1
	;;
clean)	
	clean
	;;
*)
	if [ $# -eq 1 ] ; then
		echo "You gave: [$1] which is not valid"
	fi
	echo "fix_IDS_freebsd_errors.sh: A script by Minas Dasygenis to keep FreeBSD in a good shape. Run it regularly to ensure 100% correctness. Version $version"
	echo "`basename $0` check/update_golden/delete_golden/update_local/update_pkg/ids_golden/create_golden/fix/fix_perms/fix_ids/fix_regular_files/fix_missing_files/fix_emptydirs/fix_pkgB/clean"
	echo "--------------------------------------------------------------------------------------------------------------------"
	echo "check: Check the base system for typical incosistencies. Full range of tests"
	echo "update_golden: binary patch the directory carrying the golden image of our release"
	echo "delete_golden: delete the golden basedirectory"
	echo "update_local: Perform binary base+package update to our system"
	echo "update_pkg: Update only local packages"
	echo "ids_golden: Perform an IDS check using freebsd-update on the golden image"
	echo "create_golden: Fetch and create the golden files"
	echo "fix: Fix possible errors on the base system"
	echo "fix_perms: Fix possible permissions errors on the base system"
	echo "fix_ids: Fix possible ids errors on the base system"
	echo "fix_regular_file: Fix possible errors on the regular files"
	echo "fix_missing_files: Fix any missing files on the base system compared to golden. Copy missing files to base. Remove obsolete files from base."
	echo "fix_emptydirs: Delete empty directories from base system"
	echo "fix_pkgB: Fix problems that are reported from pkg -B"
	echo "clean: Perform cleanup"
	echo "--------------------------------------------------------------------------------------------------------------------"
	echo "STEP1: $0 update_local"
	echo "STEP2: $0 check"
	echo "STEP3: $0 fix"
	echo "PLEASE use absolute path name, or the executable should be in the path"
	exit 7
	;;
esac





# The /var/spool/mqueue should carry always +x otherwise the create_per_user script misbehaves
if  [ -d /var/spool/mqueue ] ; then
	chmod go+x /var/spool/mqueue > /dev/null 2>&1
fi



