#!/sbin/sh

################################################################################
#
#	Copyright  2006 by Sun Microsystems, Inc. All Rights Reserved.
#	Copyright 1992-95 AT&T Global Information Solutions
#
# ident "@(#)lusync.sh 5.13     06/04/11 SMI"
#
# PUBLIC USAGE:
#  lusync [ -N ] [ -d localDb ] BE_Name [ sync_list_file ]
#
# FUNCTION:	copies files listed in "file" from the BE "BE_Name" to
#		the current BE. If "file" is not specified, lusync
#		will default to using /etc/lu/synclist (${LU_SYNCLIST}).
#		The "file" can contain the names of files or directories.
#		If a directory is specified, all of the contents of that
#		directory will be copied (including subdirectories.)
#
# PRIVATE USAGE:
#  lusync -c [ file | - ] [ -p prefix ] -d localDb
#    - check if file (or list from stdin) should be synchronized
#  lusync -r -d localDb
#    - report on how each file to be synchronized would be synchronized
#  lusync -R ( unchanged | source | target | sourcetarget ) -d localDb
#    - same as -r but only list files that would be synchronized by class given
#  lusync -u -d localDb -m mountPoint [ -s syncList ] -t ( initial | source | target )
#    - update synclist entries in specified database for class given
#  lusync -i -d localDb [ -t ( initial | source | target ) ]
#    - initialize (reset) synclist entries in database
#  lusync -b beName [ -D rootDevice ] [ -S ( vfstab | devnm ) ]
#    - revise database to take into account root device changes for specified BE
#
# INPUT:	NONE
# OUTPUT:	NONE
#
################################################################################

LU_PROG_FULL_PATH="$0"
LU_PROG_NAME="`basename ${LU_PROG_FULL_PATH}`"; export LU_PROG_NAME

# Temporary file definitions.
TMP_LUDB="/tmp/.lusync.ludb.$$"

TMP_INITIAL="/tmp/.lusync.initial.$$"
TMP_SOURCE="/tmp/.lusync.source.$$"
TMP_TARGET="/tmp/.lusync.target.$$"
TMP_FILELIST="/tmp/.lusync.filelist.$$"

TMP_LUTAB="/tmp/.lusync.lutab.$$"
TMP_LUTAB_BKUP="/tmp/.lusync.lutab-bkup.$$"

TMP_ICF="/tmp/.lusync.icf.$$"
TMP_FINAL_LIST="/tmp/.lusync.synclist.$$"
TMP_MAIL_ROOT="/tmp/.lusync.mail.root.$$"

TMP_CPIO_ERR="/tmp/.lusync.cpio_err.$$"
TMP_CPIO_OUTPUT="/tmp/.lusync.cpio_output.$$"

[ -f "$TMP_CPIO_OUTPUT" ] && /bin/rm -f "$TMP_CPIO_OUTPUT"

TMP_SYNC_OVERWRITE="/tmp/.lusync.overwrite.$$"
TMP_SYNC_APPEND="/tmp/.lusync.append.$$"

# Tool locations.
CPIO_PATCH="/usr/bin/cpio"

# "output action" definitions (see output_action)
OA_DONOTHING='0'
OA_UNCHANGED='1'
OA_SOURCE='2'
OA_TARGET='3'
OA_SOURCETARGET='4'

# "comparison action" definitions (see compare_synclist_item)
CA_NODIFF='0'
CA_INITIALSOURCEDIFF='1'
CA_INITIALTARGETDIFF='2'
CA_SOURCETARGETSAME='3'
CA_ALLDIFF='4'

# "existence action" definitions (see compare_synclist_item)
EA_ALL='0'
EA_INITIAL='1'
EA_SOURCE='2'
EA_TARGET='3'
EA_INITIALSOURCE='4'
EA_INITIALTARGET='5'
EA_SOURCETARGET='6'
EA_NONE='7'

trap "" 1 2 3 15
	
################################################################################
# Name:		send_mail_to_root
# Description:	Send mail to root (or the user defined in MAILLOGTO).
# Local Prefix:	smtr_
# Arguments:	$1 = synchronization list file name.
#		$2 = file containing text to send as body of message.
# Example:	send_mail_to_root "/etc/lu/synclist" "/tmp/errs.txt"
# Returns:	<none> 
################################################################################

send_mail_to_root()
{
	smtr_syncList="$1"
	smtr_mailFile="$2"

	# Identify the user to send the error results to.
	smtr_mailToUser='root'
	[ -n "${MAILLOGTO}" ] && smtr_mailToUser="${MAILLOGTO}"

	# Determine the name of the file to hold the mail to be sent.
	smtr_tmpMailFile="${smtr_mailFile}.tmp"

  	# Send mail to root regarding problems faced during cpio.
	echo `gettext "WARNING: cpio failed due to the reasons listed \
below."` > ${smtr_tmpMailFile}
	echo `gettext "This error occurred while trying to synchronize \
the files listed in:"`" ${smtr_syncList}." >> ${smtr_tmpMailFile}
	echo `gettext "The reason for failure may or may not be critical."` \
>> ${smtr_tmpMailFile}
	echo `gettext "It is left to the system administrator to decide, \
whether the error is fatal or not."` >> ${smtr_tmpMailFile}
	echo "------------------------------------------------------\n" \
 >> ${smtr_tmpMailFile}      
  	/bin/sed '/^[0-9][0-9]*[ 	][ 	]*blocks/d' ${smtr_mailFile} >> ${smtr_tmpMailFile}
	/bin/mailx -s "lusync problems" "${smtr_mailToUser}" < ${smtr_tmpMailFile}
	/bin/rm -f ${smtr_tmpMailFile}
}
	
################################################################################
# Name:		usage
# Description:	output command line usage information; then call exit_script to 
#		terminate execution.
# Local Prefix:	<none>
# Arguments:	$1 = exit code for script ("" defaults to "1").
# Example:	usage 1
# Returns:	<none> 
################################################################################

usage()
{
	${LUPRINTF} -p2 "`gettext 'USAGE: %s BE_name [ sync_list_file ]'`" \
"${LU_PROG_NAME}"
	[ -z "$1" ] && exit_script 3
	exit_script "$1"
}

################################################################################
# Name:	 	exit_script
# Description:	Perform cleanup operations and exit this script.
# Local Prefix:	<none>
# Arguments:	$1 = optional exit value for script ("" or none is = "0")
# Example:      exit_script "0"
# Returns:	<none>
################################################################################

exit_script()
{
	/bin/rm -f $TMP_MAIL_ROOT $TMP_CPIO_ERR $TMP_ICF $TMP_LUTAB \
$TMP_FINAL_LIST ${TMP_LUDB} ${TMP_INITIAL} ${TMP_SOURCE} \
${TMP_TARGET} ${TMP_LUTAB_BKUP} ${TMP_CPIO_OUTPUT} ${TMP_FILELIST}

	# Determine the exit status code.

	retcode="0"
	[ -n "$1" ] && retcode="$1"

	exit "${retcode}"
}

################################################################################
# Name:		compare_synclist_item
# Description:	Given the path name of an item, and three files that contain
#		initial, source, or target <beSyncList> entries, locate the
#		specified item in the three files and return the results of
#		how the items compare. Also output the results of which items
#		actually exist.
#
#		The <beSyncList> entries are stored in /etc/lu/ludb.local.xml
#		on the target boot environment. When a new boot environment is
#		created, the /etc/lu/synclist file is scanned and all files and
#		directories on the current boot environment are passed through
#		/usr/lib/lu/compare (see the function perform_be_db_update) and
#		the results are stored as "initial" entries in ludb.local.xml.
#		When the new boot environment is activated, the same files and
#		directories on the current and new boot environment are scanned
#		with the "source" and "target" results being added to the file.
#
#		This function is used to determine the "state" of a single file
#		or directory, given files containing the initial, source, and
#		target entries that have been extracted from ludb.local.xml.
# Local Prefix:	ci_
# Arguments:	$1 = file name to compare
#		$2 = first file (initial) to locate $1 entry in
#		$3 = second file (source) to locate $1 entry in
#		$4 = third file (target) to locate $1 entry in
# Returns:	$CA_NODIFF:		I == S, I == T, S == T
#		$CA_INITIALSOURCEDIFF:	I != S, I == T, S != T
#		$CA_INITIALTARGETDIFF:	I == S, I != T, S != T
#		$CA_SOURCETARGETSAME:	I != S, I != T, S == T
#		$CA_ALLDIFF:		I != S, i != T, S != T
# Outputs:	EA_ALL='0' -		$1, $2, $3 exist
#		EA_INITIAL='1' -	$1 exist; $2, $3 !exist
#		EA_SOURCE='2' -		$2 exist; $1, $3 !exist
#		EA_TARGET='3' -		$3 exist; $1, $2 !exist
#		EA_INITIALSOURCE='4' -	$1, $2 exist; $3 !exist
#		EA_INITIALTARGET='5' -	$1, $3 exist; $2 !exist
#		EA_SOURCETARGET='6' -	$2, $3 exist; $1 !exist
#		EA_NONE='7' -		$1, $2, $3 !exist
################################################################################

compare_synclist_item()
{
	ci_fileName="$1"
	ci_compare1File="$2"
	ci_compare2File="$3"
	ci_compare3File="$4"
	ci_exist1=""
	ci_exist2=""
	ci_exist3=""

	# entry debugging info

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 6 "`gettext 'Compare \
Item (entry)   filename <%s> file1 <%s> file2 <%s> file3 <%s>'`" \
"${ci_fileName}" "${ci_compare1File}" "${ci_compare2File}" "${ci_compare3File}"

	# Extract compare data for initial entry.

	if [ -s "${ci_compare1File}" ] ; then
		# Find the item in the compare file.
		ci_cmp1ItemData=`\
			/bin/fgrep "${ci_fileName}&#9;" "${ci_compare1File}" 2>/dev/null |\
			/etc/lib/lu/ludo xml_decode_string |\
			/bin/cut -d'	' -f2`
		[ -n "${ci_cmp1ItemData}" ] && ci_exist1="yes"
	else
		# no source compare file - make it look like the file does not match
		ci_cmp1ItemData="initial-file-does-not-exist"
	fi

	# Extract compare data for source entry.

	if [ -s "${ci_compare2File}" ] ; then
		# Find the item in the compare file.
		ci_cmp2ItemData=`\
			/bin/fgrep "${ci_fileName}&#9;" "${ci_compare2File}" 2>/dev/null |\
			/etc/lib/lu/ludo xml_decode_string |\
			/bin/cut -d'	' -f2`
		[ -n "${ci_cmp2ItemData}" ] && ci_exist2="yes"
	else
		# no source compare file - make it look like the file does not match
		ci_cmp2ItemData="source-file-does-not-exist"
	fi

	# Extract compare data for target entry.

	if [ -s "${ci_compare3File}" ] ; then
		# Find the item in the compare file.
		ci_cmp3ItemData=`\
			/bin/fgrep "${ci_fileName}&#9;" "${ci_compare3File}" 2>/dev/null |\
			/etc/lib/lu/ludo xml_decode_string |\
			/bin/cut -d'	' -f2`
		[ -n "${ci_cmp3ItemData}" ] && ci_exist3="yes"
	else
		# no target compare file - make it look like the file does not match
		ci_cmp3ItemData="target-file-does-not-exist"
	fi

	# determine existence return code.

	if [ -z "${ci_exist1}" ] ; then
		# initial does not exist
		if [ -z "${ci_exist2}" -a -z "${ci_exist3}" ] ; then
			ci_existence="${EA_NONE}"
		elif [ -z "${ci_exist2}" -a -n "${ci_exist3}" ] ; then
			ci_existence="${EA_TARGET}"
		elif [ -n "${ci_exist2}" -a -z "${ci_exist3}" ] ; then
			ci_existence="${EA_SOURCE}"
		else
			ci_existence="${EA_SOURCETARGET}"
		fi
	else
		# initial exists
		if [ -z "${ci_exist2}" -a -z "${ci_exist3}" ] ; then
			ci_existence="${EA_INITIAL}"
		elif [ -z "${ci_exist2}" -a -n "${ci_exist3}" ] ; then
			ci_existence="${EA_INITIALTARGET}"
		elif [ -n "${ci_exist2}" -a -z "${ci_exist3}" ] ; then
			ci_existence="${EA_INITIALSOURCE}"
		else
			ci_existence="${EA_ALL}"
		fi
	fi

	# output existence return code to stdout.

	echo  "${ci_existence}\c"

	# $1 == $2, $1 == $3, $2 == $3: all identical (CA_NODIFF)

	if [ "${ci_cmp2ItemData}" = "${ci_cmp1ItemData}" -a \
"${ci_cmp3ItemData}" = "${ci_cmp1ItemData}" ] ; then
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 7 "`gettext 'Compare \
Item (0) existence <%s>: <%s> = <%s> && = <%s>'`" "${ci_existence}" "${ci_cmp1ItemData}" \
"${ci_cmp2ItemData}" "${ci_cmp3ItemData}"
		return ${CA_NODIFF}
	fi

	# $1 == $3, $1 != $2, $2 != $3: CA_INITIALSOURCEDIFF

	if [ "${ci_cmp2ItemData}" != "${ci_cmp1ItemData}" -a \
"${ci_cmp3ItemData}" = "${ci_cmp1ItemData}" ] ; then
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 7 "`gettext 'Compare \
Item (1) existence <%s>: <%s> != <%s> && = <%s>'`" "${ci_existence}" "${ci_cmp1ItemData}" \
"${ci_cmp2ItemData}" "${ci_cmp3ItemData}"
		return ${CA_INITIALSOURCEDIFF}
	fi

	# $1 != $3, $1 == $2, $2 != $3: CA_INITIALTARGETDIFF

	if [ "${ci_cmp2ItemData}" = "${ci_cmp1ItemData}" -a \
"${ci_cmp3ItemData}" != "${ci_cmp1ItemData}" ] ; then
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 7 "`gettext 'Compare \
Item (2) existence <%s>: <%s> = <%s> && != <%s>'`" "${ci_existence}" "${ci_cmp1ItemData}" \
"${ci_cmp2ItemData}" "${ci_cmp3ItemData}"
		return ${CA_INITIALTARGETDIFF}
	fi

	# $1 != $3, $1 != $2, $2 == $3: CA_SOURCETARGETSAME

	if [ "${ci_cmp2ItemData}" = "${ci_cmp3ItemData}" ] ; then
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 7 "`gettext 'Compare \
Item (3) existence <%s>: <%s> = <%s> && != <%s>'`" "${ci_existence}" "${ci_cmp1ItemData}" \
"${ci_cmp2ItemData}" "${ci_cmp3ItemData}"
		return ${CA_SOURCETARGETSAME}
	fi

	# $1 != $3, $1 != $2, $3 != $2: all in conflict

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 7 "`gettext 'Compare \
Item (4) existence <%s>: <%s> != <%s> && != <%s>'`" "${ci_existence}" "${ci_cmp1ItemData}" \
"${ci_cmp2ItemData}" "${ci_cmp3ItemData}"
	return ${CA_ALLDIFF}
}

################################################################################
# Name:		perform_be_check_databases
# Description:	Check to see if any BE databases need to be updated; if so,
#		update the appropriate databases to reflect the changes.
# Local Prefix:	pcd_
# Arguments:	$1 = name of BE to check database for.
#		$2 = name of real root device for BE ('' to determine from /).
#		$3 = root check sequence (vfstab or devnm) - if null, then
#			use devnm first, if that fails, then use vfstab.
# Returns:	0 - successful.
#		1 - unsuccessful.
# Outputs:	<none>
################################################################################

perform_be_check_databases()
{
	pcd_beName="$1"
	pcd_rootDevice="$2"
	pcd_rootMethod="$3"

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Checking \
for LU database changes for BE <%s>.'`" "${pcd_beName}"

	# Check for root slice change; if it does not change, then
	# do not perform any other checks - just return. If it does
	# change, then the database is already updated. Do other
	# checks and push the database changes.
	perform_be_check_root_slice "${pcd_beName}" "${pcd_rootDevice}" \
"${pcd_rootMethod}"
	ret="$?"

	#	0 - the root slice was not changed.
	#	1 - the root slice was changed.
	#	2 - the root slice could not be changed.
	
	[ "${ret}" -eq 0 ] && return 0
	[ "${ret}" -ne '1' ] && return 1

	# The root slice was changed - do further checks.

	# Check to see if all lutab entries are present and correct.
	perform_be_check_lutab_entries "${pcd_beName}"

	# Output entry for synchronization log.
	${LUPRINTF} -Ifelp2 "`gettext 'Updating Live Upgrade databases \
on all boot environments.'`"

	# Propagate the lutab file changes to all BEs.
	${LUBIN}/luupdall ${LU_LUTAB_FILE}
	[ "$?" -eq "0" ] || return 1
	return 0
}

################################################################################
# Name:		perform_be_check_root_slice
# Description:	Check to see if the root slice has changed; if so, 
#		update the appropriate databases to reflect this change.
# Local Prefix:	pcb_
# Arguments:	$1 = name of BE to check database for.
#		$2 = name of real root device for BE ('' to determine from /).
#		$3 = root check sequence (vfstab or devnm) - if null, then
#			use devnm first, if that fails, then use vfstab.
# Returns:	0 - the root slice was not changed.
#		1 - the root slice was changed.
#		2 - the root slice could not be changed.
# Outputs:	<none>
################################################################################

perform_be_check_root_slice()
{
	pcb_beName="$1"
	pcb_rootDevice="$2"
	pcb_rootMethod="$3"

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Checking \
for root slice change for BE <%s>.'`" "${pcb_beName}"

	# Determine root slice as recorded in BE database.
	pcb_rootSlice=`${LUETCBIN}/ludo get_root_slice_from_be_name "${pcb_beName}"`
	[ "$?" -eq 0 ] || return 2

	# If alternative root method specified, try that first.
	# If any specific method fails, then try each method in sequence.
	if [ -z "${pcb_rootDevice}" -a -n "${pcb_rootMethod}" ] ; then
		if [ "${pcb_rootMethod}" = 'devnm' ] ; then
			pcb_rootDevice="`/usr/sbin/devnm / 2>/dev/null | /bin/cut -f1 -d' '`"
		elif [ "${pcb_rootMethod}" = 'vfstab' -a -r "/etc/vfstab" ] ; then
			pcb_rootDevice=`$LUBIN/lucomm_del "/etc/vfstab" | \
/bin/awk ' { if ( $3 == "/" ) { print $1; exit 0 } } '`
		fi
	fi

	# If no real root slice provided or found, determine root slice actually
	# booted from.
	if [ -z "${pcb_rootDevice}" ] ; then
		pcb_rootDevice="`/usr/sbin/devnm / 2>/dev/null | /bin/cut -f1 -d' '`"
		if [ -z "${pcb_rootDevice}" -a -r "/etc/vfstab" ] ; then
			pcb_rootDevice=`$LUBIN/lucomm_del "/etc/vfstab" | \
/bin/awk ' { if ( $3 == "/" ) { print $1; exit 0 } } '`
		fi
		# If no root slice found (impossible?) return error.
		[ -n "${pcb_rootDevice}" ] || return 2
	fi

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 5 "`gettext 'Root slice \
<%s> root device <%s>.'`" "${pcb_rootSlice}" "${pcb_rootDevice}"

	# The root slice is determined from the BE configuration files.
	# The root device is determined from examining where "/" is mounted.
	# If the two are different, the BE configuration files must be updated.
	[ "${pcb_rootSlice}" = "${pcb_rootDevice}" ] && return 0

	# Output entry for synchronization log.
	${LUPRINTF} -Ifelp2 "`gettext 'Boot environment <%s> root slice \
changed from <%s> to <%s>.'`" "${pcb_beName}" "${pcb_rootSlice}" \
"${pcb_rootDevice}"

	# Get the ID of this BE.
	pcb_beId=`${LUETCBIN}/ludo get_be_id "${pcb_beName}"`
	[ "$?" -eq 0 ] || return 2

	# edit ${LUTAB} to set the status field of BE corresponding to ID
	/bin/rm -f ${TMP_LUTAB} ${TMP_LUTAB_BKUP} 2>/dev/null 1>&2
	while read pcb_line ; do
		echo $pcb_line | /bin/grep "^${pcb_beId}:/:.*:1$" 1>/dev/null 2>&1
		if [ "$?" -eq 0 ] ; then
			pcb_line="${pcb_beId}:/:${pcb_rootDevice}:1"
			${LUPRINTF} -lp2D 6 "`gettext 'Changing <%s> \
to <%s> beId <%s> in <%s>.'`" "${pcb_rootSlice}" "${pcb_rootDevice}" "${pcb_beId}" \
"${LU_LUTAB_FILE}"
		fi
		${LUPRINTF} +X -a "${TMP_LUTAB}" '%s' "${pcb_line}"
		[ "$?" -eq 0 ] || return 2
	done < "${LU_LUTAB_FILE}"

	# Backup current lutab file, then move newly created on over it, 
	# restoring the backup on error
	/bin/cp -p ${LU_LUTAB_FILE} ${TMP_LUTAB_BKUP} 2>/dev/null
	ERRMSG=`/bin/mv -f ${TMP_LUTAB} ${LU_LUTAB_FILE} 2>&1`
	if [ "$?" -ne "0" ] ; then
	  /bin/cp -p ${TMP_LUTAB_BKUP} ${LU_LUTAB_FILE}
	  ${LUPRINTF} -Eelp2 '%s' "${ERRMSG}"
	  return 2
	fi

	# Successful - return indicating database (lutab) has been updated.
	return 1
}

################################################################################
# Name:		perform_be_check_lutab_entries
# Description:	Check to see if the the lutab file has proper entries; if not,
#		update the database as required.
# Local Prefix:	pcl_
# Arguments:	$1 = name of BE to check database for.
# Returns:	0 - the database was not changed.
#		1 - the database was changed.
#		2 - the database could not be changed.
# Outputs:	<none>
################################################################################

perform_be_check_lutab_entries()
{
	pcl_beName="$1"

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Checking \
for lutab entry consistency for BE <%s>.'`" "${pcl_beName}"

	# edit ${LUTAB} to set the status field of BE corresponding to ID
	/bin/rm -f ${TMP_LUTAB} ${TMP_LUTAB_BKUP} 2>/dev/null 1>&2
	while read pcl_line ; do
		# Parse line as much as we need to
		pcl_lutabId=`echo $pcl_line | /bin/cut -d':' -f1`
		pcl_lutabField2=`echo $pcl_line | /bin/cut -d':' -f3`
		pcl_lutabType=`echo $pcl_line | /bin/cut -d':' -f4`

		# Determine if field '2' contains a bootable device.
		echo "${pcl_lutabField2}" | /bin/grep '^/dev/dsk/.*' 2>/dev/null 1>&2
		pcl_isDiskDevice="$?"

		# Always preserve existing entry.
		${LUPRINTF} +X -a "${TMP_LUTAB}" '%s' "${pcl_line}"
		[ "$?" -eq 0 ] || return 2

		# If a type 1 (root slice) entry, see if a type 2 (boot device)
		# entry exists. If not and root slice is bootable device, create
		# type 2 entry.
		if [ "${pcl_lutabType}" = "1" -a "${pcl_isDiskDevice}" -eq '0' ] ; then
			/bin/grep "^${pcl_lutabId}:.*:.*:2" "${LU_LUTAB_FILE}" \
1>/dev/null 2>&1
			if [ "$?" -ne '0' ] ; then
				# Output entry for synchronization log.
				${LUPRINTF} -lp2D 6 "`gettext 'Adding missing \
entry <%s> to <%s>.'`" "${pcl_lutabId}:boot-device:${pcl_lutabField2}:2" \
"${LU_LUTAB_FILE}"

				# Output missing entry to database.
				${LUPRINTF} +X -a "${TMP_LUTAB}" '%s' \
"${pcl_lutabId}:boot-device:${pcl_lutabField2}:2"
				[ "$?" -eq 0 ] || return 2
			fi
		fi
	done < "${LU_LUTAB_FILE}"

	# If database was not updated, return without making changes permanent.
	/bin/cmp -s "${LU_LUTAB_FILE}" "${TMP_LUTAB}" 2>/dev/null 1>&2
	[ "$?" -eq '0' ] && return 0

	# Backup current lutab file, then move newly created on over it, 
	# restoring the backup on error
	/bin/cp -p ${LU_LUTAB_FILE} ${TMP_LUTAB_BKUP} 2>/dev/null
	ERRMSG=`/bin/mv -f ${TMP_LUTAB} ${LU_LUTAB_FILE} 2>&1`
	if [ "$?" -ne "0" ] ; then
	  /bin/cp -p ${TMP_LUTAB_BKUP} ${LU_LUTAB_FILE}
	  ${LUPRINTF} -Eelp2 '%s' "${ERRMSG}"
	  return 2
	fi

	# Successful - return indicating database (lutab) has been updated.
	return 1
}

################################################################################
# Name:		perform_be_check_file_conflict
# Description:	Given a database file and the name of a file, output the name
#		to stdout if the file is not in conflict between the source
#		and the target boot environments.
# Local Prefix:	pcf_
# Arguments:	$1 = local database containing <beSyncList> entries to check.
#		$2 = file to check for conflict. If the file is "-", then a
#			list of files is read from stdin.
#		$3 = prefix to apply to each file name checked.
# Returns:	0 - the file is not in conflict (always if $2 = "-").
#		1 - the file is in conflict (only if $2 != "-").
# Outputs:	== "" - the file is in conflict and should not be copied.
#		!= "" - the file is not in conflict and can be copied.
################################################################################

perform_be_check_file_conflict()
{
	pcf_db="$1"
	pcf_fileName="$2"
	pcf_fileNamePrefix="$3"

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Checking \
for conflict for file <%s>.'`" "${pcf_fileName}"

	# if no file name then just return
	if [ -z "${pcf_fileName}" ] ; then
		return 1
	fi

	# If no database, no conflict, force copy.
	if [ ! -s "${pcf_db}" ] ; then
		echo "${pcf_fileName}"
		return 0
	fi

	# If the file to check is a "-": if not then is name of file to check
	if [ "${pcf_fileName}" != '-' ] ; then
		pcf_result=`perform_the_be_check_file_conflict "${pcf_db}" \
"${pcf_line}" "${pcf_fileNamePrefix}"`
		pcf_ret=$?

		# if conflict then skip copying
		[ "${pcf_ret}" -ne '0' ] && return ${pcf_ret}

		# if no name to copy then skip copying
		[ -z "${pcf_result}" ] && return 1

		# item should be synchronized - output name
		echo "${pcf_result}"
		return 0
	fi

	# file to check is "-": read lines from standard input and check each line
	# read - output names that are NOT rejected.
	while read pcf_line ; do
		# check the next file in the list
		pcf_result=`perform_the_be_check_file_conflict "${pcf_db}" \
"${pcf_line}" "${pcf_fileNamePrefix}"`

		# if conflict then skip copying
		[ "$?" -ne '0' ] && continue

		# if no name to copy then skip copying
		[ -z "${pcf_result}" ] && continue

		# item should be synchronized - output name
		echo "${pcf_result}"
	done
	return 0
}

################################################################################
# Name:		perform_the_be_check_file_conflict
# Description:	Given a database file and the name of a file, output the name
#		to stdout if the file is not in conflict between the source
#		and the target boot environments.
# Local Prefix:	pcf_
# Arguments:	$1 = local database containing <beSyncList> entries to check.
#		$2 = file to check for conflict.
#		$3 = prefix to apply to each file name checked.
# Returns:	0 - the file is not in conflict (always if $2 = "-").
#		1 - the file is in conflict (only if $2 != "-").
# Outputs:	== "" - the file is in conflict and should not be copied.
#		!= "" - the file is not in conflict and can be copied.
################################################################################

perform_the_be_check_file_conflict()
{
	ptc_db="$1"
	ptc_fileName="$2"
	ptc_fileNamePrefix="$3"

	# generate final name

	ptc_fullFileName=`echo "${ptc_fileNamePrefix}${ptc_fileName}" | \
/bin/sed 's%//%/%g'`

	# entry debugging info 
	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Checking \
for conflict for path <%s>.'`" "${ptc_fullFileName}"

	# Retrieve the line for this item from the initial synclist.
	/bin/grep '^<beSyncList ' < "${ptc_db}" | \
	/bin/fgrep ' type="initial" ' | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/fgrep " item=\"${ptc_fullFileName}&#9;" | \
	/bin/cut -d'"' -f 6 > ${TMP_INITIAL}

	# Retrieve the line for this item from the source synclist.
	/bin/grep '^<beSyncList ' < "${ptc_db}" | \
	/bin/fgrep ' type="source" ' | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/fgrep " item=\"${ptc_fullFileName}&#9;" | \
	/bin/cut -d'"' -f 6 > ${TMP_SOURCE}

	# Retrieve the line for this item from the target synclist.
	/bin/grep '^<beSyncList ' < "${ptc_db}" | \
	/bin/fgrep ' type="target" ' | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/fgrep " item=\"${ptc_fullFileName}&#9;" | \
	/bin/cut -d'"' -f 6 > ${TMP_TARGET}

	###
	### look up entry in initial list
	###

	ptc_exists=`compare_synclist_item "${ptc_fullFName}" "${TMP_INITIAL}" \
"${TMP_SOURCE}" "${TMP_TARGET}"`
	ptc_ret="$?"

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 5 "`gettext '\
Check conflict <%s> = ret (%s) exists (%s)'`" "${ptc_fullFileName}" \
"${ptc_ret}" "${ptc_exists}"

	# See if SOURCE has changed since TARGET created:
	# If INITIAL and TARGET are identical (that is, if SOURCE is
	# different - CA_INITIALSOURCEDIFF), then if both SOURCE and TARGET exist,
	# or if SOURCE exist and TARGET does not,  then SOURCE has been changed
	# since TARGET was created - copy

	if [ "${ptc_ret}" = "${CA_INITIALSOURCEDIFF}" ] ; then
		
		# Check whether the source and initial are different
		# or if the source exist and target does not, then
		# source has been edited after lucreate or target entry
		# has been deleted.

		if [ "${ptc_exists}" = "${EA_ALL}" ] || \
			[ "${ptc_exists}" = "{EA_SOURCE}" ]; then 
			# file exists in initial / source / target
			echo "${ptc_fileName}"
			return 0
		fi
	fi

	# See if SOURCE has been created since TARGET created:
	# If INITIAL, TARGET, and SOURCE are all different, then if the SOURCE
	# is the only existing file, then SOURCE was created since TARGET was
	# creqted - copy.

	if [ "${ptc_ret}" = "${CA_ALLDIFF}" ] ; then
		# source, initial, and target all different
		if [ "${ptc_exists}" = "${EA_SOURCE}" ] ; then
			# source file created (initial and target do not exist)
			echo "${ptc_fileName}"
			return 0
		fi
	fi

	return 1 # DO NOT COPY
}

################################################################################
# Name:		output_action
# Description:	Given an action to perform, an action type to report, and
#		a file name to report:
#		- if no action type to report is specified, output the action
#		- if an action type to report is specified, output the action
#			only if the type of action matches
# Local Prefix:	oa_
# Arguments:	$1 = action to perform:
#			$OA_DONOTHING - do nothing
#			$OA_UNCHANGED - unchanged
#			$OA_SOURCE - source
#			$OA_TARGET - target
#			$OA_SOURCETARGET - sourcetarget
#		$2 = restrict actions to report to given action
#			If set (to initial, source, target, sourcetarget) then
#			only those type of actions are reported.
#			== "" to report all actions
#		$3 = file name to report action on
# Returns:	<none>
# Outputs:	one line of the following may be output:
#		  sourcetarget <name> - name in conflict on source and target
#		  source <name> - name in conflict on source only
#		  unchanged <name> - name not in conflict on source or target
#		  target <name> - name in conflict on target only
#		If optional type is specified, only that type of conflict 
#		will be reported.
################################################################################

output_action()
{
	oa_action="$1"
	oa_report_action="$2"
	oa_fileName="$3"

	# Action: 0 unknown, 1 unchanged, 2 source, 3 target, 4 sourcetarget
	case ${oa_action} in
	${OA_DONOTHING})
		;;
	${OA_UNCHANGED})
		if [ -z "${oa_report_action}" ] ; then
			echo 'unchanged '"${oa_fileName}"
		elif [ "${oa_report_action}" = 'unchanged' ] ; then
			echo "${oa_fileName}"
		fi
		;;
	${OA_SOURCE})
		if [ -z "${oa_report_action}" ] ; then
			echo 'source '"${oa_fileName}"
		elif [ "${oa_report_action}" = 'source' ] ; then
			echo "${oa_fileName}"
		fi
		;;
	${OA_TARGET})
		if [ -z "${oa_report_action}" ] ; then
			echo 'target '"${oa_fileName}"
		elif [ "${oa_report_action}" = 'target' ] ; then
			echo "${oa_fileName}"
		fi
		;;
	${OA_SOURCETARGET})
		if [ -z "${oa_report_action}" ] ; then
			echo 'sourcetarget '"${oa_fileName}"
		elif [ "${oa_report_action}" = 'sourcetarget' ] ; then
			echo "${oa_fileName}"
		fi
		;;
	esac
}

################################################################################
# Name:		perform_be_db_report
# Description:	Given a database file and an optional type of conflict to check,
#		return a list of files that are in conflict.
# Local Prefix:	pbr_
# Arguments:	$1 = local database containing <beSyncList> entries to check.
#		$2 = type of conflict to report (initial, source, target).
# Returns:	<none>
# Outputs:	For each file one line of the following will be output:
#		  sourcetarget <name> - name in conflict on source and target
#		  source <name> - name in conflict on source only
#		  unchanged <name> - name not in conflict on source or target
#		  target <name> - name in conflict on target only
#		If optional type is specified, only that type of conflict 
#		will be reported.
################################################################################

perform_be_db_report()
{
	pbr_db="$1"
	pbr_type="$2"

	# entry debugging info.

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Generating \
<beSyncList> report on db <%s> of type <%s>.'`" "${pbr_db}" "${pbr_type}"

	# If no database, no files to report on.

	[ ! -s "${pbr_db}" ] && return 0

	# Generate initial, source, and target lists.

	/bin/grep '^<beSyncList ' < "${pbr_db}" | \
	/bin/fgrep ' type="source" ' | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/cut -d'"' -f 6 > ${TMP_SOURCE}

	/bin/grep '^<beSyncList ' < "${pbr_db}" | \
	/bin/fgrep ' type="target" ' | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/cut -d'"' -f 6 > ${TMP_TARGET}

	/bin/grep '^<beSyncList ' < "${pbr_db}" | \
	/bin/fgrep ' type="initial" ' | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/cut -d'"' -f 6 > ${TMP_INITIAL}

	# Generate unique list of all files to report on.

	/bin/grep '^<beSyncList ' < "${pbr_db}" | \
	/bin/fgrep ' action="OVERWRITE" ' | \
	/bin/cut -d'"' -f 6 | \
	/etc/lib/lu/ludo xml_decode_string | \
	/bin/cut -d'	' -f1 | \
	/bin/sort | \
	/bin/uniq > ${TMP_FILELIST}

	##
	## Scan the INITIAL entries that existed on the source boot
	## environment when the target boot environment was created.
	## For each entry that existed initially, determine the status
	## of that entry (see if it changed) in the SOURCE or TARGET
	##

	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 4 "`gettext '************ \
Checking against initial, source and target lists.'`"

	while read pbr_fileName ; do
		# compare INITIAL with SOURCE
		pbr_exists=`compare_synclist_item "${pbr_fileName}" "${TMP_INITIAL}" \
"${TMP_SOURCE}" "${TMP_TARGET}"`
		pbr_ret="$?"

		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 5 "`gettext 'Compare \
<%s> = ret (%s) exists (%s)'`" "${pbr_fileName}" "${pbr_ret}" "${pbr_exists}"

		case ${pbr_ret} in
		${CA_NODIFF}) # I == S, I == T, S == T
			pbr_action=${OA_UNCHANGED}
			;;

		${CA_INITIALSOURCEDIFF}) # S changed (I == T, I != S)
			case ${pbr_exists} in
			${EA_ALL})		# I, S, T exist
				pbr_action=${OA_SOURCE}
				;;
			${EA_INITIAL})		# I exists: S, T deleted
				pbr_action=${OA_TARGET}
				;;
			${EA_SOURCE})		# S created: I, T do not exist
				pbr_action=${OA_SOURCE}
				;;
			${EA_TARGET})		# T created: I, S do not exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIALSOURCE})	# T deleted: I, S exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIALTARGET})	# S deleted: I, T exist
				pbr_action=${OA_SOURCE}
				;;
			${EA_SOURCETARGET})	# S, T created: I does not exist
				pbr_action=${OA_TARGET}
				;;
			${EA_NONE})		# none exist
				pbr_action=${OA_TARGET}
				;;
		    	esac
			;;

		${CA_INITIALTARGETDIFF}) # T changed (I != T, I == S)
			case ${pbr_exists} in
			${EA_ALL})		# I, S, T exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIAL})		# I exists: S, T deleted
				pbr_action=${OA_TARGET}
				;;
			${EA_SOURCE})		# S created: I, T do not exist
				pbr_action=${OA_SOURCE}
				;;
			${EA_TARGET})		# T created: I, S do not exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIALSOURCE})	# T deleted: I, S exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIALTARGET})	# S deleted: I, T exist
				pbr_action=${OA_TARGET}
				;;
			${EA_SOURCETARGET})	# S, T created: I does not exist
				pbr_action=${OA_TARGET}
				;;
			${EA_NONE})		# none exist
				pbr_action=${OA_TARGET}
				;;
		    	esac
			;;

		${CA_SOURCETARGETSAME}) # (I != T, I != S, T == S)
			pbr_action=${OA_TARGET}
			;;

		${CA_ALLDIFF}) # all changed (I != T, I != S, T != S)
			case ${pbr_exists} in
			${EA_ALL})		# I, S, T exist
				pbr_action=${OA_SOURCETARGET}
				;;
			${EA_INITIAL})		# I exists: S, T deleted
				pbr_action=${OA_TARGET}
				;;
			${EA_SOURCE})		# S created: I, T do not exist
				pbr_action=${OA_SOURCE}
				;;
			${EA_TARGET})		# T created: I, S do not exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIALSOURCE})	# T deleted: I, S exist
				pbr_action=${OA_TARGET}
				;;
			${EA_INITIALTARGET})	# S deleted: I, T exist
				pbr_action=${OA_TARGET}
				;;
			${EA_SOURCETARGET})	# S, T created: I does not exist
				pbr_action=${OA_SOURCETARGET}
				;;
			${EA_NONE})		# none exist
				pbr_action=${OA_TARGET}
				;;
		    	esac
			;;

		*) # unknown
			pbr_action=${OA_SOURCETARGET} # sourcetarget
			;;

		esac

		output_action "${pbr_action}" "${pbr_type}" "${pbr_fileName}"
	done < ${TMP_FILELIST}

	return 0
}

################################################################################
# Name:		perform_be_db_initialization
# Description:	Given a database file and an optional type of conflict to clear,
#		clear any <beSyncList> entries from the database file.
# Local Prefix:	pbi_
# Arguments:	$1 = local database containing <beSyncList> entries to check.
#		$2 = type of conflict to clear (initial, source, target).
#			If $2 is "" then all <beSyncList> entries are cleared.
# Returns:	<none>
# Outputs:	<none>
################################################################################

perform_be_db_initialization()
{
	pbi_db="$1"
	pbi_type="$2"

	# If no database, no files to report on.
	[ ! -s "${pbi_db}" ] && return 0

	# If no type specified, remove all synclist entries, else, remove just those
	# synclist entries specified.
	/bin/rm -f "${TMP_LUDB}"
	if [ -z "${pbi_type}" ] ; then
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext '\
Removing all <beSyncList> elements from database <%s>.'`" "${pbi_db}"
		/bin/grep -v '^<beSyncList ' < "${pbi_db}" > "${TMP_LUDB}"
	else
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext '\
Removing all <beSyncList> <type=%s> elements from database <%s>.'`" "${pbi_type}" "${pbi_db}"
		/bin/grep -v "^<beSyncList type=\"${pbi_type}\" " < "${pbi_db}" > "${TMP_LUDB}"
	fi

	/bin/cp -f "${TMP_LUDB}" "${pbi_db}"
	return 0
}

################################################################################
# Name:		perform_be_db_update
# Description:	Given a database file, a mount point where files can be found,
#		the type of entries to create, and a synclist file to use,
#		remove all entries from the database of the type specified, and
#		for each entry in the synclist file update the database with
#		compare information with the type specified.
# Local Prefix:	pbu_
# Arguments:	$1 = local database containing <beSyncList> entries to create.
#		$2 = mount point where files can be found to create compare
#			entries for.
#		$3 = type of entries to make (initial, source, target).
#		$4 = synclist file to use.
# Returns:	<none>
# Outputs:	<none>
################################################################################

perform_be_db_update()
{
	pbu_db="$1"
	pbu_srcMountPt="$2"
	pbu_type="$3"
	pbu_syncList="$4"

	# Remove all synclist entries of the type specified.
	/bin/rm -f "${TMP_LUDB}"
	if [ -s "${pbu_db}" ] ; then
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext '\
Removing all <beSyncList> <type=%s> elements from database <%s>.'`" \
"${pbu_type}" "${pbu_db}"
		/bin/grep -v "^<beSyncList type=\"${pbu_type}\" " < "${pbu_db}" > "${TMP_LUDB}"
	fi

	# Add synclist entries for the type specified.
	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Updating \
<beSyncList> elements on db <%s> of type <%s>.'`" "${pbu_db}" "${pbu_type}"
	$LUBIN/lucomm_del ${pbu_syncList} | while read pbu_itemName pbu_itemAction ; do
		[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 4 "`gettext '\
Processing synclist item <%s> action <%s>.'`" "${pbu_itemName}" "${pbu_itemAction}"
		${LUBIN}/compare -t -c -m "${pbu_srcMountPt}" -M -S -p "${pbu_itemName}" | \
		while read pbu_details ; do
			[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D 5 \
"`gettext 'details for synclist item <%s> = <%s>'`" "${pbu_itemName}" "${pbu_details}"
			${LUPRINTF} -X -T 'beSyncList' -a "${TMP_LUDB}" \
'type="%s" action="%s" item="%s"' "${pbu_type}" "${pbu_itemAction}" "${pbu_details}"
		done
	done

	/bin/cp -f "${TMP_LUDB}" "${pbu_db}"
	return 0
}

################################################################################
# Name:		perform_be_synchronization
# Description:	Given the name of the previous BE, the synclist file, and the
#		name of the current BE, synchronize files from the previous BE
#		onto the current BE.
# Local Prefix:	pbs_
# Arguments:	$1 = name of the previous BE to synchronize files from.
#		$2 = synclist file to use to drive synchronization.
#		$3 = name of the current BE to synchronize files to.
#		$4 = sync database to use to drive synchronization.
#		$5 = dryrun flag
# Returns:	<none>
# Outputs:	<none>
################################################################################

perform_be_synchronization()
{
	pbs_oldBE="$1"
	pbs_syncList="$2"
	pbs_currBE="$3"
	pbs_db="$4"
	pbs_dryrun="$5"

	# If LU_SYNC_FILES set to "NO" do not do synchronization.

	if [ "$LU_SYNC_FILES" != "YES" -a "$LU_SYNC_FILES" != "yes" ] ; then
		${LUPRINTF} -Ielp2 "`gettext 'You have configured the \
/etc/default/lu file to bypass BE file synchronization: no synchronization \
done.'`"
		return 0
	fi

	# output initial 'doing sync' message.

	${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Synchronizing BE <%s> from \
BE <%s> using list <%s>.'`" "${pbs_currBE}" "${pbs_oldBE}" "${pbs_syncList}"

	if [ ! -f ${pbs_syncList} ] ; then
		${LUPRINTF} -Eelp2 "`gettext 'The synchronization list file \
<%s> does not exist: no synchronization done.'`" "${INFILE}"
		return 1
	fi

	# do not synchronize the current BE to itself.

	if [ "${pbs_currBE}" = "${pbs_oldBE}" ] ; then
		${LUPRINTF} -Eelp2 "`gettext 'The BE to synchronize from <%s> \
is the same as the current (active) BE: no synchronization done.'`" \
"${pbe_currBE}"
		return 1
	fi

	# Mount the slices of ABE.

	ID=`${LUETCBIN}/ludo get_be_id "${pbs_oldBE}" || return 1`
	if [ ! -f /etc/lu/ICF.$ID ] ; then
		$LUBIN/lumk_iconf "${pbs_oldBE}" > $TMP_ICF || return 1
	else
		/bin/cp /etc/lu/ICF.$ID $TMP_ICF
	fi
	dest=`LU_OUTPUT_FORMAT=text $LUBIN/lumount -i $TMP_ICF || return 1`

	cd "$dest"

	# If there is any core move it to core.$$
	# This is because cpio might core dump, and we need to determine that

	[ -f $dest/core.$$ ] && /bin/rm -f $dest/core.$$
	[ -f $dest/core ] && /bin/mv $dest/core $dest/core.$$

	# output status message

	/bin/date > $TMP_MAIL_ROOT
	${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Synchronization of \
selected files is in progress...'`"

	# Synchronize PREPEND files.

	${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Prepending selected \
files...'`"

	$LUBIN/lucomm_del ${pbs_syncList} | \
		/bin/awk ' { if( $2 == "PREPEND" ) print $1 } ' | \
		while read ENTRY ; do
		# Remove the leading "/" from the path 
		ENTRY="`echo ${ENTRY} | /bin/sed 's%^/%%'`"
		if [ -r "${ENTRY}" ] ; then
			${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Prepending \
to <%s>'`" "${ENTRY}"
			if [ -z "${pbs_dryrun}" ] ; then
				/bin/cat "${ENTRY}" "/${ENTRY}" > /tmp/.lusyncP2$$
				/bin/cp -f "/tmp/.lusyncP2$$" "/${ENTRY}"
				/bin/rm -f "/tmp/.lusyncP2$$"
			fi
		elif [ -f "${ENTRY}" ] ; then
			${LUPRINTF} -Welp2a "${TMP_MAIL_ROOT}" "`gettext '<%s> \
cannot be read - it will not be prepended.'`" "${ENTRY}"
		else
			${LUPRINTF} -Ielp2a "${TMP_MAIL_ROOT}" "`gettext '<%s> \
does not exist - it will not be prepended.'`" "${ENTRY}"
		fi
	done

	# Synchronize APPEND files.

	${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Appending selected \
files...'`"

	$LUBIN/lucomm_del ${pbs_syncList} | \
		/bin/awk ' { if( $2 == "APPEND" ) print $1 } ' | \
		while read ENTRY ; do
		# Remove the leading "/" from the path 
		ENTRY="`echo ${ENTRY} | /bin/sed 's%^/%%'`"
		if [ -r "${ENTRY}" ] ; then
			${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Appending \
to <%s>'`" "${ENTRY}"
			if [ -z "${pbs_dryrun}" ] ; then
				/bin/cat "${ENTRY}" >> /${ENTRY}
			fi
		elif [ -f "${ENTRY}" ] ; then
			${LUPRINTF} -Welp2a "${TMP_MAIL_ROOT}" "`gettext '<%s> \
cannot be read - it will not be appended.'`" "${ENTRY}"
		else
			${LUPRINTF} -Ielp2a "${TMP_MAIL_ROOT}" "`gettext '<%s> \
does not exist - it will not be appended.'`" "${ENTRY}"
		fi
	done

	# Determine list of files to be overwritten.

	${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Overwriting selected \
files...'`"

	> $TMP_FINAL_LIST
	$LUBIN/lucomm_del ${pbs_syncList} | \
		/bin/awk ' { if( $2 != "PREPEND" && $2 != "APPEND" ) print $1 } ' | \
		while read ENTRY ; do
		# Remove the leading "/" from the path 
		ENTRY="`echo ${ENTRY} | /bin/sed 's%^/%%'`"
		if [ -r "${ENTRY}" ] ; then
			echo "${ENTRY}" >> $TMP_FINAL_LIST
		elif [ -f "${ENTRY}" ] ; then
			${LUPRINTF} -Welp2a "${TMP_MAIL_ROOT}" "`gettext '<%s> \
cannot be read - it will not be overwritten.'`" "${ENTRY}"
		else
			${LUPRINTF} -Ielp2a "${TMP_MAIL_ROOT}" "`gettext '<%s> \
does not exist - it will not be overwritten.'`" "${ENTRY}"
		fi
	done

	# Copy all 'OVERWRITE' files and directories specified in the synclist file.
	# Use "lusync -c" to filter out any files that are in conflict between the
	# previous boot environment and the currently active boot environment.

	${LUPRINTF} -lp2D - "`gettext 'Files/directories to check to \
overwrite:\n%R\nDone.'`" < $TMP_FINAL_LIST

	if [ -z "${pbs_dryrun}" ] ; then
		/bin/find `/bin/cat $TMP_FINAL_LIST` '!' -local -prune -o -print 2>/dev/null | \
		${LUETCBIN}/lusync -c '-' -p '/' -d "${pbs_db}" | \
		$CPIO_PATCH -vpcdum / 2>$TMP_CPIO_ERR >>$TMP_CPIO_OUTPUT
		RET_VAL=$?
	else
		/bin/find `/bin/cat $TMP_FINAL_LIST` '!' -local -prune -o -print 2>/dev/null | \
		${LUETCBIN}/lusync -c '-' -p '/' -d "${pbs_db}" >>$TMP_CPIO_OUTPUT
		RET_VAL=$?
	fi

	# In the above find - cpio command we do not use tee, since for tee to
	# work we have to redirect stderr to stdout, which defeats the purpose
	# of the error coming on stderr.

	if [ -f $dest/core ] ; then
		# cpio core dumped - similar to cpio failing but different message.
		ERR_FLG=1
		# Move the core file out of the way so it can be examined later.
		/bin/mv -f $dest/core $dest/core.cpio.$$
		# output error messages to root mail and stderr.
		if [ -s "${TMP_CPIO_OUTPUT}" ] ; then
			${LUPRINTF} -Eelp2a "${TMP_MAIL_ROOT}" "%R" < $TMP_CPIO_OUTPUT
		fi
		if [ -s "${TMP_CPIO_ERR}" ] ; then
			${LUPRINTF} -Eelp2a "${TMP_MAIL_ROOT}" "%R" < $TMP_CPIO_ERR
		fi
		${LUPRINTF} -Eelp2a "${TMP_MAIL_ROOT}" "`gettext 'lusync \
failed due to fatal error of cpio/find (core file found).'`"
	elif [ "$RET_VAL" != 0 ] ; then
		# cpio failed to copy the list of specified files.
		ERR_FLG=1
		# output error messages to root mail and stderr.
		if [ -s "${TMP_CPIO_OUTPUT}" ] ; then
			${LUPRINTF} -Eelp2a "${TMP_MAIL_ROOT}" "%R" < $TMP_CPIO_OUTPUT
		fi
		if [ -s "${TMP_CPIO_ERR}" ] ; then
			${LUPRINTF} -Eelp2a "${TMP_MAIL_ROOT}" "%R" < $TMP_CPIO_ERR
		fi
		${LUPRINTF} -Eelp2a "${TMP_MAIL_ROOT}" "`gettext 'Synchronization \
failed.'`"
	else
		# cpio succeeded in copying the list of specified files.
		ERR_FLG=0
		# output messages to root mail and stdout.
		if [ -s "${TMP_CPIO_OUTPUT}" ] ; then
			${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" \
"Files overwritten:\n%R" < $TMP_CPIO_OUTPUT
		fi
		${LUPRINTF} -lp1a "${TMP_MAIL_ROOT}" "`gettext 'Synchronization of \
selected files complete.'`"
	fi

	# Restore saved core file (if present).

	[ -f "$dest/core.$$" ] && /bin/mv $dest/core.$$ $dest/core

	# Change to the root directory so we don't cause the umount to fail.

	cd /

	# Unmount the ABE slices after the comparison is complete.

	$LUBIN/luumount -f -i $TMP_ICF

	# Send a mail to root on problems encountered during cpio on file systems
	# other than special processing.

	if [ "$ERR_FLG" -eq 1 -a -z "${pbs_dryrun}" ] ; then
		send_mail_to_root "${pbs_syncList}" "${TMP_MAIL_ROOT}"
	fi

	# Return success/failure return code.

	return ${ERR_FLG}
}

################################################################################
# Name:		<main>
# Description:	Main code (outside of any function definitions) - executed at script startup.
# Local Prefix:	<none>
# Arguments:	$0...$n = All arguments specified by user on command line that invoked this script.
################################################################################

# Dot the defaults file.

if [ ! -s /etc/default/lu ] ; then
  echo "${LU_PROG_NAME}: ""`gettext 'ERROR: Live Upgrade not installed properly \
(/etc/default/lu not found).'`"
  exit 1
fi
. /etc/default/lu

# Default global variables we expect to be set from /etc/default/lu.

LUBIN=${LUBIN:=/usr/lib/lu}
COPYLOCK=${COPYLOCK:=/etc/lu/COPY_LOCK}

# Dot the Live Upgrade library functions.

if [ ! -s $LUBIN/lulib ] ; then
  echo "${LU_PROG_NAME}: ""`gettext 'ERROR: The Live Upgrade product is not \
installed properly (${LUBIN}/lulib not found).'`"
  exit 1
fi

. $LUBIN/lulib

  ############################################################################
  ################ Command line option and argument processing ###############
  ############################################################################

# Reset all command line parse flags to default values.
flag_b='' # -b - check to see if root slice has changed. (PRIVATE)
flag_b_value='' # value for 'flag_b'. (PRIVATE)
flag_c='' # -c n - check file to see if in conflict between source and target BE. (PRIVATE)
flag_c_value='' # value for 'flag_c'. (PRIVATE)
flag_d='' # -d n - location of database to operate on. (PRIVATE)
flag_d_value='' # value for 'flag_d'. (PRIVATE)
flag_D='' # -D n - root device for BE. (PRIVATE)
flag_D_value='' # value for 'flag_D'. (PRIVATE)
flag_i='' # -i - initialize sync database. (PRIVATE).
flag_l='' # -l f - log file path.
flag_m='' # -m n - mount point where source files from synclist can be found. (PRIVATE)
flag_m_value='' # value for flag_m. (PRIVATE)
flag_N='' # -N - dryrun. (PRIVATE)
flag_o='' # -o f - output file path.
flag_p='' # -p prefix - path prefix to apply to all path names. (PRIVATE)
flag_p_value='' # value for flag_p. (PRIVATE)
flag_r='' # -r - report on comparison of sync databases. (PRIVATE)
flag_R='' # -R n - report on comparison of sync databases with specific type. (PRIVATE)
flag_R_value='' # value for flag_R. (PRIVATE)
flag_s='' # -s - sync list file to use (default is synclist on /). (PRIVATE)
flag_s_value='' # value for 'flag_s'. (PRIVATE)
flag_S='' # -S - source to use for root (vfstab or devnm). (PRIVATE)
flag_S_value='' # value for 'flag_S'. (PRIVATE)
flag_t='' # -t n - type of database entry (initial | source | target). (PRIVATE)
flag_t_value='' # value for 'flag_t'. (PRIVATE)
flag_u='' # -u - update sync database. (PRIVATE)
flag_x='' # -x n = set debug level to n (PRIVATE).

while [ $# -ne 0 ] ; do
  while getopts b:c:d:D:il:m:No:p:rR:s:S:t:ux:X c
  do
    case $c in
      b) # -b - check to see if root slice has changed. (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_b_value}" "${OPTARG}" "-b"
	 flag_b='1'
	 flag_b_value="${OPTARG}"
	 ;;
      c) # -c n - check file to see if in conflict between source and target BE. (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_c_value}" "${OPTARG}" "-c"
	 flag_c='1'
	 flag_c_value="${OPTARG}"
	 ;;
      d) # -d n - location of database to operate on. (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_D_value}" "${OPTARG}" "-D"
         flag_d='1'
         flag_d_value="${OPTARG}"
         ;;
      D) # -D n - root device for BE. (PRIVATE)
         flag_D='1'
         flag_D_value="${OPTARG}"
         ;;
      i) # -i - initialize sync database. (PRIVATE).
         flag_i='1'
         ;;
      l) # -l f - error log file path.
	 # This overrides the LU_ERROR_LOG_FILE setting read from /etc/default/lu
	 lulib_cannot_duplicate_option "${flag_l}" "${OPTARG}" "-l"
	 ${LUPRINTF} -lp2D - "`gettext 'Verifying that the error log file <%s> \
specified can be created and appended to.'`" "${OPTARG}"
	 ERRMSG="`${LUPRINTF} -c \"${OPTARG}\" 2>&1`"
	 if [ $? -ne 0 ] ; then
	   [ -n "${ERRMSG}" ] && ${LUPRINTF} -elp2 '%s' "${ERRMSG}"
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to -l option may not be \
created or appended to.'`" "${OPTARG}"
	   exit_script 3
	 fi
	 flag_l="${OPTARG}"
	 lulib_set_error_log_file "${flag_l}"
	 ;;
      m) # -m n - mount point where synclist files can be found. (PRIVATE)
         flag_m='1'
         flag_m_value="${OPTARG}"
         ;;
      N) # -N - dryrun for BE syncronization. (PRIVATE).
	 flag_N='1'
	 ;;
      o) # -o f - output file path.
	 # This overrides the LU_SESSION_LOG_FILE setting read from /etc/default/lu
	 lulib_cannot_duplicate_option "${flag_o}" "${OPTARG}" "-o"
	 ${LUPRINTF} -lp2D -  "`gettext 'Verifying that the session log file \
<%s> can be created and appended to.'`" "${OPTARG}"
	 ERRMSG="`${LUPRINTF} -c \"${OPTARG}\" 2>&1`"
	 if [ $? -ne 0 ] ; then
	   [ -n "${ERRMSG}" ] && ${LUPRINTF} -elp2 '%s' "${ERRMSG}"
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to -o option may not be \
created or appended to.'`" "${OPTARG}"
	   exit_script 3
	 fi
	 flag_o="${OPTARG}"
	 lulib_set_session_log_file "${flag_o}"
	 ;;
      p) # -p prefix - path prefix to apply to all path names. (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_p_value}" "${OPTARG}" "-p"
	 flag_p='1'
	 flag_p_value="${OPTARG}"
	 ;;
      r) # -r - report on comparison of sync databases. (PRIVATE)
         flag_r='1'
         ;;
      R) # -R n - report on comparison of sync databases with specific type. (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_R_value}" "${OPTARG}" "-R"
         case "${OPTARG}" in
           unchanged|source|target|sourcetarget) # valid flag
             flag_R='1'
             flag_R_value="${OPTARG}"
	     ;;
	   *) # invalid flag
	     ${LUPRINTF} -Eelp2 "`gettext 'The <-R> option must be one of <unchanged, source, target, sourcetarget>.'`"
	     usage 3
	     ;;
	 esac
	 ;;
      s) # -s - sync list file to use (default is synclist on /). (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_s_value}" "${OPTARG}" "-s"
	 flag_s='1'
	 flag_s_value="${OPTARG}"
	 ;;
      s)  # -S - source to use for root (vfstab or devnm). (PRIVATE)
	 lulib_cannot_duplicate_option "${flag_S_value}" "${OPTARG}" "-S"
         case "${OPTARG}" in
           vfstab|devnm) # valid flag
             flag_S='1'
             flag_S_value="${OPTARG}"
	     ;;
	   *) # invalid flag
	     ${LUPRINTF} -Eelp2 "`gettext 'The <-S> option must be one of <vfstab, devnm>.'`"
	     usage 3
	     ;;
	 esac
	 ;;
      t) # -t n - type of database entry (initial | source | target). (PRIVATE)
         case "${OPTARG}" in
           initial|source|target) # valid flag
             flag_t='1'
             flag_t_value="${OPTARG}"
	     ;;
	   *) # invalid flag
	     ${LUPRINTF} -Eelp2 "`gettext 'The <-t> option must be one of <initial, source, target>.'`"
	     usage 3
	     ;;
	 esac
	 ;;
      u) # -u - update sync database. (PRIVATE)
         flag_u='1'
         ;;
      x) # -x n - set debug level to n (PRIVATE).
	 # This overrides the default setting read from /etc/default/lu
	 lulib_cannot_duplicate_option "${flag_x}" "${OPTARG}" "-x"
	 /bin/test "${OPTARG}" -ge 0 2>/dev/null
	 if [ $? -gt 1 ] ; then
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to -x option is not a \
number.'`" "${OPTARG}"
	   usage 3
	 fi
	 flag_x="${OPTARG}"
	 lulib_set_debug "${flag_x}"
	 ;;
      X) # -X - set XML output mode.
	  lulib_set_output_format 'xml'
	  ;;
      \?) # unknown - option.
	  usage 3
	  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

# Fixup debug, session log, and error log settings
lulib_fixup_startup_settings

  ######################################################################################
  ############ Validate all command line arguments and options as possible #############
  ######################################################################################

# Check for existence and non-zero size of lutab file.
if [ ! -f "/etc/lutab" -o ! -s "/etc/lutab" ] ; then
	${LUPRINTF} -Eelp2 "`gettext 'No BEs are configured on this system.'`"
	exit_script 1
fi

# Determine the name of the current BE.
CURR_BE=`lulib_lucurr`
if [ "$?" -ne "0" ] ; then
	${LUPRINTF} -Eelp2 "`gettext 'Unable to determine the name of the current active BE.'`"
	exit_script 1
fi

# Set default synclist file to use if none specified
[ -z "${flag_s_value}" ] && flag_s_value="${LU_SYNCLIST}"

# Make sure not more than one operation command was specified.
if [ -n "${flag_b}${flag_c}${flag_i}${flag_u}${flag_r}${flag_R}" \
-a "${flag_b}${flag_c}${flag_i}${flag_u}${flag_r}${flag_R}" != '1' ] ; then
	${LUPRINTF} -Eelp2 "`gettext 'Only one option from <-b, -c, -i, -u, -r, -R> may be specified.'`"
	usage
fi

# Perform the appropriate operation.
if [ -n "${flag_b}" ] ; then
	# check to see if root slice has changed:
	# -b beName [ -D rootDevice ]
	perform_be_check_databases "${flag_b_value}" "${flag_D_value}" "${flag_S_value}"
elif [ -n "${flag_i}" ] ; then
	# initialize synclist entries: 
	# -i -d <dbFile> [ -t (initial|source|target) ]
	lulib_must_have_option "${flag_d}" "-i" "-d"
	lulib_cannot_have_options "${flag_m}${flag_s}${flag_p}" "-i" "-m, -s, -p"
	perform_be_db_initialization "${flag_d_value}" "${flag_t_value}"
elif [ -n "${flag_u}" ] ; then
	# update synclist entries: 
	# -u -d <dbFile> -m <mountPt> -t (initial|source|target)> [ -s <syncFile> ]
	lulib_must_have_option "${flag_d}" "-u" "-d"
	lulib_must_have_option "${flag_m}" "-u" "-m"
	lulib_must_have_option "${flag_t}" "-u" "-t"
	perform_be_db_update "${flag_d_value}" "${flag_m_value}" "${flag_t_value}" "${flag_s_value}"
elif [ -n "${flag_r}" ] ; then
	# report on conflicting files:
	# -r -d <dbFile>
	lulib_must_have_option "${flag_d}" "-r" "-d"
	lulib_cannot_have_options "${flag_m}${flag_s}${flag_t}${flag_p}" "-r" "-m, -s, -t, -p"
	perform_be_db_report "${flag_d_value}" ''
elif [ -n "${flag_R}" ] ; then
	# report on conflicting files:
	# -R (unchanged|source|target|sourcetarget) -d <dbFile>
	lulib_must_have_option "${flag_d}" "-R" "-d"
	lulib_cannot_have_options "${flag_m}${flag_s}${flag_t}${flag_p}" "-R" "-m, -s, -t, -p"
	perform_be_db_report "${flag_d_value}" "${flag_R_value}"
elif [ -n "${flag_c}" ] ; then
	# check if file can be synchronized:
	# -c <fileName> -d <dbFile> -p <pathprefix>
	lulib_must_have_option "${flag_d}" "-r" "-d"
	lulib_cannot_have_options "${flag_m}${flag_s}${flag_t}" "-c" "-m, -s, -t"
	perform_be_check_file_conflict "${flag_d_value}" "${flag_c_value}" "${flag_p_value}"
else
	# None of the BE sync database operations was specified, 
	# perform the standard BE synchronization function:
	# lusync <lastBEname> [<synclist_file>]
	[ $# -lt 1 -o $# -gt 2 ] && usage
	[ -n "${flag_m}${flag_s}${flag_t}" ] && usage
	BE_NAME="$1"
	INFILE="${LU_SYNCLIST}"
	LUDB="${LU_DB_LOCAL}"
	[ $# -eq 2 ] && INFILE="$2"
	[ -n "${flag_d}" ] && LUDB="${flag_d_value}"
	${LUPRINTF} -fIlp1 "`gettext '%s: Synchronizing current boot \
environment <%s> from previous boot environment <%s> using synchronization \
list file <%s>.'`" "`/bin/date`" "${CURR_BE}" "${BE_NAME}" "${INFILE}"
	perform_be_synchronization "${BE_NAME}" "${INFILE}" "${CURR_BE}" "${LUDB}" "${flag_N}"
	ret="$?"
	${LUPRINTF} -fIlp1 "`gettext '%s: Synchronization completed.'`" \
"`/bin/date`"
	[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Return \
code <%s>.'`" "${ret}"
	exit_script "${ret}"
fi

# Capture return code and exit.
ret="$?"
[ "${LU_DEBUG}" -ne '0' ] && ${LUPRINTF} -lp2D - "`gettext 'Return code <%s>.'`" "${ret}"
exit_script "${ret}"
