#!/sbin/sh

#################################################################################################
#
#	Copyright (c) 1999-2005 by Sun Microsystems, Inc. All Rights Reserved.
#	Use is subject to license terms.
#	Copyright 1992-95 AT&T Global Information Solutions
#
# ident "@(#)lucompare.sh 5.9     05/07/08 SMI"
#
# USAGE:                   lucompare  [ -i <in_file> | -t ]
#                                          [ -o <out_file> ] "be_name"
#					   [ -C <create_compare_file> ]
# FUNCTION:                compare contents of current BE with "be_name"
# INPUT:                   target "be_name", the be_name should be
#                          enclosed within quotes.
# OUTPUT:                  list of files which differ, along with the
#                          reason.
# DEV:                     RAMAN
#
# Note: performance testing has proven that "/sbin/sh" provides the best performance over
# /bin/sh and /bin/ksh.
#
#################################################################################################

# set -x

TMP_RESULT_FILE="/tmp/.lucompare.results.tmp.$$"

cleanup()
{

	trap "" 1 2 3 15

	# if only the root slice is mounted umount it.

	# if $dest directory is present, it indicates that ABE slices are mounted,
	# so we unmount it. 

	if [ -d "$dest" ]
	then
		$LUBIN/luumount -f -i "$ABE_ICF" 2> /dev/null
		[ -d "$dest" ] && /bin/rmdir $dest
	fi

	# Remove all temporary files.

	/bin/rm -f $ABE_ICF $CBE_ICF $TMP_USR $TMP_VAR $ERR_MSG_LOG

	exit 1

}

comp_exit()
{

	echo "$LU_PROG_NAME: "`gettext "ERROR: "`"$1" >& 2
	# Unmount the ABE slices, as we have come across some error, and we 
	# want to exit.

	$LUBIN/luumount -f -i "$ABE_ICF" >& 2
	/bin/rm -f $ABE_ICF $CBE_ICF $TMP_USR $TMP_VAR $ERR_MSG_LOG $COPYLOCK
	exit $2

}

check_be_busy()
{
  # Verify that there is no COPYLOCK held by $BE_NAME
	lulib_validate_lulock
	if [ -f "$COPYLOCK" ]
	then
		. $COPYLOCK
		if [ "$CL_TARGET_BE" = "$BE_NAME" ]
		then
			echo "\n$LU_PROG_NAME: "`gettext "ERROR: Cannot compare. A COPYLOCK exists for the BE:"`" \"$BE_NAME\"" >& 2
			exit 1
		fi
	fi
}
	
usage()
{
	echo `gettext "Usage: "`"$LU_PROG_NAME "`gettext "[ -X ] \
[ -i in_file | -t ] [ -o out_file ] [ -C create_compare_file ] \
BE_name"` >& 2
	echo `gettext 'Any BE_name should be enclosed in single quotes.'` >& 2
	exit 1
}

error_exit()
{
	echo "$LU_PROG_NAME: "`gettext "ERROR: "`"$1" >& 2
	/bin/rm -f $ABE_ICF $CBE_ICF $ERR_MSG_LOG
	exit 1
}

error_exit2()
{
        echo "$LU_PROG_NAME: "`gettext "ERROR: "`"$1" >& 2
        /bin/rm -f $ABE_ICF $CBE_ICF $ERR_MSG_LOG
        exit 2
}

spec_exit()
{
	echo "$LU_PROG_NAME: "`gettext "ERROR: "`"$1" >& 2

	# Unmount the root slice of ABE, as we have come across some error, and
	# we want to exit.
	/bin/rm -f $COPYLOCK

	exit $2
}

LU_PROG_NAME=`/bin/basename $0`
T_FLG=0
I_FLG=0
O_FLG=0
C_FLG=0
OOPT=""
TEMP_OUT_FILE="/tmp/temp_out.$$"
ERR_MSG_LOG=/tmp/lucompare.err

# parse the options

while [ $# -ne 0 ] ; do
  while getopts C:ti:o:X option
  do
	  case $option in 
	  C) if [ "$C_FLG" -eq 1 ]
	     then
		  usage
	     fi
		  C_FLG=1
		  C_IN_FILE=`echo $OPTARG | /bin/sed 's/ /___/g;s%/%_:_%g'`
		  [ -z "$C_IN_FILE" ] && usage
		  if [ ! -r "$C_IN_FILE" ]
		  then
			  echo "$LU_PROG_NAME: "`gettext "ERROR: file not readable:"`" $C_IN_FILE" >& 2
			  exit 1
		  else
			  OOPT="$OOPT -C $C_IN_FILE"
		  fi;;
	  t) if [ "$T_FLG" -eq 1 ]
	     then
		  usage
	     fi
		  T_FLG=1;;
	  i) if [ "$I_FLG" -eq 1 ]
	     then
		  usage
	     fi
		  I_FLG=1
		  IN_FILE=$OPTARG
		  [ -z "$IN_FILE" ] && usage
		  if [ ! -r "$IN_FILE" ]
		  then
			  echo "$LU_PROG_NAME: "`gettext "ERROR: file not readable:"`" $IN_FILE" >& 2
			  exit 1
		  else
			  IOPT="-f $IN_FILE"
		  fi;;
	  o) if [ "$O_FLG" -eq 1 ]
	     then
		  usage
	     fi
		  O_FLG=1
		  OUT_FILE=$OPTARG
		  [ -z "$OUT_FILE" ] && usage
		  > $OUT_FILE
		  if [ $? != 0 ] 
		  then
			  echo "$LU_PROG_NAME: "`gettext "ERROR: Cannot create file:"`" $OUT_FILE" >& 2
			  exit 1
		  fi
		  OOPT="$OOPT -o $OUT_FILE" ;;
	  X) # -X - set XML output mode.
	      lulib_set_output_format 'xml'
	      ;;
	  \?) usage ;;
	  esac
  done

  # Found either end of arguments, +option, or non-option argument; shift out
  # what has been processed so far; if a non-option argument is present
  # capture it and continue processing the command line arguments.
  shift `/bin/expr $OPTIND - 1`
  OPTIND=1
  if [ $# -ne 0 -a "$1" = '+X' ] ; then
      # +X - set TEXT output mode.
      lulib_set_output_format 'text'
      shift
  else
    break
  fi
done

# -i and -t option is not allowed.

[ "$I_FLG" -eq 1 -a "$T_FLG" -eq 1 ] && usage

# verify that only one argument viz, be_name is present

if [ $# -gt 1 ] ; then
  echo "$LU_PROG_NAME: " "`gettext 'The boot environment name should be enclosed in single quotes.'`"
fi

[ $# -ne 1 ] && usage

BE_NAME=$1

ABE_ICF="/tmp/abe_icf.$$"
CBE_ICF="/tmp/cbe_icf.$$"

[ -s /etc/default/lu ] && . /etc/default/lu

LUBIN=${LUBIN:=/usr/lib/lu}

COPYLOCK=${COPYLOCK:=/etc/lu/COPY_LOCK}

# Dot in lulib

if [ ! -f "$LUBIN/lulib" ]
then
	error_exit `gettext "lulib is not present"`
fi

. $LUBIN/lulib

# Check for existence and non-zero size of lutab file.

if [ ! -f ${LU_LUTAB_FILE} -o ! -s ${LU_LUTAB_FILE} ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'No boot environments are configured on this system.'`"
  exit 1
fi

# Validate the target BE

if [ "${LU_DEBUG}" -ne "0" ]
then
	echo "Verifying that \"$BE_NAME\" is a valid BE name..."
fi

lulib_validate_be "${BE_NAME}"
if [ "$?" -ne "0" ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'Unable to use the target boot environment <%s>.'`" "${BE_NAME}"
  exit 1
fi

check_be_busy

# Get the current BE name

CURR_BE=`lulib_lucurr`

if [ $? != 0 ]
then
	error_exit2 `gettext "Could not determine the name of the active boot environment"`
fi


if [ "$C_FLG" != "1" -a "$CURR_BE" = "$BE_NAME" ]
then
	error_exit "\"$BE_NAME\" "`gettext "is the active boot environment; Cannot compare with itself."`
fi

if [ "$C_FLG" = "1" -a "$O_FLG" = "0" ]
then
	error_exit `gettext "-o option must be used with -C"`
fi

# signal handling  for cleanup

trap "cleanup ; exit $?" 1 2 3 15

echo "CL_STATUS=\"COMPARING\"" > $COPYLOCK
echo "CL_TARGET_BE=\"$BE_NAME\"" >> $COPYLOCK

TMP_USR="/tmp/.spec_usr.$$"
TMP_VAR="/tmp/.spec_var.$$"

# Get the ABE_ICF

echo `gettext "Determining the configuration of"`" \"$BE_NAME\"...\c"

$LUBIN/lumk_iconf "$BE_NAME" > $ABE_ICF
$LUBIN/lumk_iconf "$CURR_BE" > $CBE_ICF

if [ $? != 0 ]
then
	rm -f $COPYLOCK
	error_exit2 `gettext "Could not determine the ICF for"`" \"$BE_NAME\""
fi

echo ""

# Root slice of ABE is mounted, to do the special processing for /usr and
# /var filesystems.

${LUETCBIN}/ludo filter_shared_and_swap $CBE_ICF $ABE_ICF | /bin/nawk -F: ' $2 == "/" { printf("%s %s\n", $3, $4) }' |
while read bdevice fstyp
do
	if [ -z "$bdevice" -o ! -b $bdevice -z "$fstyp" ]
	then
		rm -f $COPYLOCK
		error_exit `gettext "Invalid ICF format"`
	fi

	real_fstyp="`lulib_fstyp $bdevice`"
	if [ $? != 0 ]
	then
		rm -f $COPYLOCK
		error_exit `gettext "Cannot determine file system type on:"`" <$bdevice>."
	fi

	if [ "$real_fstyp" != "$fstyp" ] 
	then
		rm -f $COPYLOCK
		error_exit `gettext "File system on"`" $bdevice ($real_fstyp) "`gettext "does not match configuration file"`" ($fstyp)"
	fi
done

if [ "$?" != 0 ]
then
	rm -f $COPYLOCK
	exit 1
fi

# if <out_file> is specified, then output the header to the out_file,
# provided -c option is not specified.

if [ "$C_FLG" != "1" ]
then
	if [ "$O_FLG" -eq 1 ]
	then
    		echo "        < $CURR_BE" > $OUT_FILE
    		echo "        > $BE_NAME" >> $OUT_FILE
	else
		echo "        < $CURR_BE" 
		echo "        > $BE_NAME" 
	fi
fi


# Mount  the slices of ABE.

if [ "$C_FLG" = "1" -a "$CURR_BE" = "$BE_NAME" ]
then
	dest=/
else
	dest=`LU_OUTPUT_FORMAT=text $LUBIN/lumount -i "$ABE_ICF" 2>${TMP_RESULT_FILE}`
	if [ $? -ne 0 ] ; then
	  [ -s "${TMP_RESULT_FILE}" ] && ${LUPRINTF} -elp2 '%R' < "${TMP_RESULT_FILE}"
	  ${LUPRINTF} -Eelp2 "`gettext 'Unable to mount the boot environment <%s>.'`" "${BE_NAME}"
	  /bin/rm -f "${TMP_RESULT_FILE}"
	  exit 1
	fi
        /bin/rm -f "${TMP_RESULT_FILE}"
fi

if [ $? != 0 ]
then
	comp_exit `gettext "Failed to mount the slices of"`" \"$BE_NAME\"." 2
fi

# Start the comparison of the files or file systems on ABE.

if [ "$I_FLG" -eq 1 ]
then

	# If -i option is specified, compare only the list of files specified

	/bin/sed -e 's/[ 	]*//g' -e 's/\/[\/]*/\//g' -e 's/\/$//g' -e '/^[ 	]*$/d' $IN_FILE | $LUBIN/compare -m $dest -S -M -f - $OOPT

	[ $? = 0 ] || spec_exit `gettext "Comparison failed"`

else

	# Compare the ABE file  systems.

	# Determine the file systems to be compared on ABE.

	compul_list=`${LUETCBIN}/ludo filter_shared_and_swap $ABE_ICF $CBE_ICF | /bin/awk -F: '{ printf("%s ", $2)}'`

	[ $? != 0 ] && comp_exit `gettext "Could not determine the file systems of the active boot environment:"`" \"$CURR_BE\"" 2

	# For each of the file systems determined for comparison do the 
	# the comparison.

	for filesys in $compul_list
	do
		echo `gettext "Comparing"`" $filesys ...\c"
		
		# If -t option is specified, compare only text files, else
		# compare all files irrespective of the nature of the file.

		if [ "$T_FLG" -eq 1 ]
		then

		# Only text files need to be compared. We do a file(1) command
		# on each file and grep only for text files.

			/bin/find $filesys -mount -exec file -h {} \;| \
			/bin/egrep '.*:[ 	][ 	]*.*text' | \
			/bin/sed 's/:[ 	][ 	]*.*$//g' | \
			$LUBIN/compare -m $dest -S -M -f - $OOPT 

		else

		# Compare all files irrespective of the nature of the file.

			$LUBIN/compare -p $filesys -m $dest -S -M $OOPT

		fi

		[ $? = 0 ] || comp_exit `gettext "Comparison failed"` 1

		echo "\n"`gettext "Compare complete for"`" $filesys."

	done

	if [ "$C_FLG" = "1" ]
	then
		echo "\n"`gettext "Comparing current file stats with original stats for"`" $BE_NAME...\c"
		/bin/sort -u "$C_IN_FILE" > /tmp/$$.compdiff1
		echo "..\c"
		/bin/sort -u $OUT_FILE > /tmp/$$.compdiff2
		echo "..\c"
    		echo "        < "`gettext "Original"`" $BE_NAME" > $OUT_FILE
    		echo "        > "`gettext "Current"`" $BE_NAME" >> $OUT_FILE
echo "
File types and Field definitions:

*Block special device node (BLKSPC):
   Device name: owner: group: link count (hard links): permissions: BLKSPC:
   major/minor number:
*Character special device node (CHRSPC):
   Device name: owner: group: link count (hard links): permissions: CHRSPC:
   major/minor number:
*Directories (DIR):
   Directory name: owner: group: link count (dirs within): permissions: DIR:
*First-in-first-out file (FIFO):
   FIFO name: owner: group: link count: permissions: FIFO:
*Regular files (REGFIL):
   File name: owner: group: link count (hard links): permissions: REGFIL:
   size in bytes: file check-sum (cksum):
*Symbolic link (SYMLINK):
   link name: owner: group: link count (always 1): permissions: SYMLINK:
   size of link name:

Differences:
" >> $OUT_FILE
		/bin/diff /tmp/$$.compdiff1 /tmp/$$.compdiff2 | /bin/egrep "^>|^<" >> $OUT_FILE	
		echo "\n"`gettext "Comparison is complete and saved in"`" $OUT_FILE."
	fi

# Finally check whether the mount points are created.
# This is because $LUBIN/compare compares files(and directories) which are 
# non mount points. So we check whether the  mount points are also
# present on the ABE. This part of the code was added to remove the need
# for mountfilter utility.

	mount_list=""

	/usr/sbin/mount | /bin/awk '{print $1}' | /bin/sort | 
	while read line
	do

		for ind in $compul_list
		do
			[ "$line" = "$ind" ]  && continue 2
		done

	# Check whether $line is a mount point under an already mounted point.
	# If so we do not want to  check for the existence of  the directory.

		for ind in $mount_list
		do
			val=`expr "$line" : "$ind"`
			[ "$val" = "`echo $ind | /bin/awk '{print length($0)}'`" ] && continue 2
		done

	# $line is a mount point, not on a mount point(sub mount point).
	# So store it in variable $mount_list, with a '/' appended. The '/' 
	# appended, so that when a mount point which is mounted on $line is 
	# visited, we can segregate it, with our logic of expr(1) command.

		mount_list="$mount_list ${line}/"

	# if $line is the mount point of the ABE slices, we know for sure that
	# it will not be present on ABE. So we skip the checking of this
	# mount point on ABE.

		[ "$line" = "$dest" ] && continue

# Check for the directory $dest/$line

		if [ ! -d $dest/$line ]
		then
			if [ $O_FLG -eq 1 ]
			then
				echo "$line "`gettext "mount point does not exist"` >> $OUT_FILE
			else
				echo "$line "`gettext "mount point does not exist"`
			fi
		fi

	done

fi

# Unmount the ABE slices after the comparison is complete.

if [ "$CURR_BE" != "$BE_NAME" ]
then
	$LUBIN/luumount -f -i "$ABE_ICF"
	if [ $? != 0 ]
	then
		echo "$LU_PROG_NAME: "`gettext "ERROR: Cannot unmount boot environment:"`" \"$BE_NAME\"" >& 2
		/bin/rm -f $ABE_ICF $CBE_ICF $ERR_MSG_LOG $COPYLOCK
		exit 2
	fi
fi

# Remove the $ABE_ICF file.

/bin/rm -f $ABE_ICF $CBE_ICF $ERR_MSG_LOG $COPYLOCK
exit 0
