#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/lib/nim/methods/c_sync_root.sh 1.34 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1993,2009 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)27        1.34  src/bos/usr/lib/nim/methods/c_sync_root.sh, cmdnim, bos720  5/26/09  09:44:11
#
#   COMPONENT_NAME: CMDNIM
#
#   FUNCTIONS: ./usr/lib/nim/methods/c_sync_root.sh
#
#   ORIGINS: 27 83
#
#
#   (C) COPYRIGHT International Business Machines Corp. 1993
#   All Rights Reserved
#   Licensed Materials - Property of IBM
#   US Government Users Restricted Rights - Use, duplication or
#   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
#

# include common NIM shell defines/functions
NIMPATH=${0%/*}
NIMPATH=${NIMPATH%/*}
[[ ${NIMPATH} = ${0} ]] && NIMPATH=/usr/lpp/bos.sysmgt/nim
NIM_METHODS="${NIMPATH}/methods"
. ${NIM_METHODS}/c_sh_lib

#---------------------------- local defines     --------------------------------

#---------------------------- module globals    --------------------------------
REQUIRED_ATTRS="location"
OPTIONAL_ATTRS="installp_flags filesets bundle"
location=""
installp_flags=""
filesets=""
bundle=""

# The following are names of files that will hold lists of fileset names
# for a full root sync op.
cleanup_sync_list=${TMPDIR}/$$.cleanup
apply_sync_list=${TMPDIR}/$$.applies
commit_sync_list=${TMPDIR}/$$.commits
deinstl_sync_list=${TMPDIR}/$$.deinstls
reject_sync_list=${TMPDIR}/$$.rejects

#---------------------------- ck_instroot_cust_sz   ----------------------------
#
# NAME: ck_instroot_cust_sz
#
# FUNCTION:
#		Uses the value saved in INSTROOT_CUST_SZ during c_instspot
#		to ensure that enough space exists in /usr for the 
#		root_sync on the inst_root of the SPOT.  Expands if 
#		necessary.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#               o No checking is done for auto-expand flag since by now,
#		  after the cust has been done on the SPOT, it would be silly
#		  to fail due to a shortage of a few hundred K (which is all
#		  there is likely to be short for the root_sync).
#
#		o calls error if cannot expand filesystem
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#               global:
#			INSTROOT_CUST_SZ
#
# RETURNS: (int)
#               0   = success
#
# OUTPUT:
#-------------------------------------------------------------------------------

function ck_instroot_cust_sz {

	# May as well keep going if no root size to work with.
	[[ -s "${INSTROOT_CUST_SZ}" ]] || return 0

	# NOTE: required space should already contain a fudge factor
	required=`${CAT} ${INSTROOT_CUST_SZ}`
	available=`${DF} /usr | ${AWK} '(NR>1) {print $3}'`

	# expand if necessary
	if (( ${required} > ${available} ))
	then
		let "additional=required-available"
		# try to expand the filesystem
		${CHFS} -a size=+${additional} /usr 2>${ERR} 1>&2 || \
                                err_from_cmd "${CHFS} /usr"

	fi
	${RM} ${INSTROOT_CUST_SZ}

	return 0

} # end of ck_instroot_cust_sz

#---------------------------- prep_bundle      --------------------------------
#
# NAME: prep_bundle
#
# FUNCTION:
#               prepares environment to use an installp bundle file
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#               calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#               global:
#                       bundle
#                       bundle_access
#
# RETURNS: (int)
#               0   = success
#
# OUTPUT:
#-------------------------------------------------------------------------------

function prep_bundle {
        [[ -z "${bundle}" ]] && return 0

        bundle_access=${TMPDIR}/bundle
        # local or remote file?
        if [[ ${bundle} = ?*:/* ]]
        then

                # remote - setup local access
                nim_mount ${bundle}

                # copy the bundle into /tmp so installp will have access to it
                ${CP} ${access_pnt} ${bundle_access} 2>${ERR} || \
                        err_from_cmd "${CP} ${access_pnt} ${bundle_access}"

                # unmount
                nim_unmount ${access_pnt}

        else

                # already local - just copy it
                ${CP} ${bundle} ${bundle_access} 2>${ERR} || \
                        err_from_cmd "${CP} ${bundle} ${bundle_access}"

        fi

} # end of prep_bundle

#---------------------------- lppchk_uv_root  --------------------------------
#
# NAME: lppchk_uv_root
#
# FUNCTION:
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#			1						= pathame of the root directory to sync
#		global:
#			installp_flags
#			filesets
#			bundle
#			tmp_files
#			chroot
#
# RETURNS: (int)
#		0							= success
#		1							= failure
#
# OUTPUT:
#-------------------------------------------------------------------------------
function lppchk_uv_root {
	typeset root=${1}
	typeset op=""
	typeset name=""
	typeset lev=""

	# setup the chroot environment using the specified root directory
	setup_chroot_env ${root}

	# Now ensure that the server's libraries mounted into the SPOT 
	# will be used.
	${SET_CHROOT_LIBPATH}

	# perform the lppchk operation to find out what needs to be done
        # to sync up the current root.
	${chroot} ${LPPCHK} -uv | \
		while read op name lev
		do
    			case ${op} in
       				-C)  echo ${name} ${lev} >> ${cleanup_sync_list}
           			;;
       				-a)  echo ${name} ${lev} >> ${apply_sync_list}
           			;;
      				-ac) echo ${name} ${lev} >> ${apply_sync_list}
      		  	     	     echo ${name} ${lev} >> ${commit_sync_list}
				;;
       				-c)  echo ${name} ${lev} >> ${commit_sync_list}
				;;
       				-u)  echo ${name} ${lev} >> ${deinstl_sync_list}
				;;
       				-r)  echo ${name} ${lev} >> ${reject_sync_list}
				;;
       				ERR) # do nothing
				;;
				\?)  # do nothing
				;;
			esac
		done

} # end of lppchk_uv_root

#---------------------------- sync_root         --------------------------------
#
# NAME: sync_root
#
# FUNCTION:
#		performs the specified installp operation on the specified root directory
#			by setting up a chroot environment
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#			1						= pathame of the root directory to sync
#		global:
#			installp_flags
#			filesets
#			bundle
#			tmp_files
#			chroot
#
# RETURNS: (int)
#		0							= success
#		1							= failure
#
# OUTPUT:
#-------------------------------------------------------------------------------
function sync_root {
	typeset root=${1}
	typeset name=${root##*/}
	typeset f_flag=""

	# Setup the chroot environment using the specified root directory.
	# (if we're doing a full_sync, we should have already done this.)
	if [[ -z "${full_sync}" ]]
	then
		setup_chroot_env ${root}
	fi

	# Ensure that the server's libraries mounted into the SPOT 
	# will be used.
	${SET_CHROOT_LIBPATH}

	# use a bundle file?
	if [[ -n "${bundle}" ]]
	then

		# copy the bundle into the root directory
		${CP} ${bundle} ${root}/bundle 2>${ERR} || err_from_cmd ${CP}
		tmp_files="${tmp_files} ${root}/bundle"

		# use the bundle
		f_flag="-f /bundle"

	fi

	export INUCLIENTS=yes

	# If we're syncing a shared_root resource (identified by its
	# $SHROOT_CUSTDIR directory), the recfgct is made at boot time
	if [[ ! -d ${root}/${SHROOT_CUSTDIR} ]]
	then
		${chroot} ${RECFGCT} 2>${ERR}
	fi

	# perform the installp operation
	${chroot} ${INSTALLP} -e ${INSTALLP_LOG} ${installp_flags} ${f_flag} \
		${filesets} </dev/null 2>${ERR} 1>&2

	rc=$?
	# Don't exit if doing a complete sync op
	if [[ -n "${full_sync}" ]]
	then
		if [[ $rc -ne 0 ]] 
		then
			warning_from_cmd ${INSTALLP}
		fi
	else 	
		if [[ $rc -ne 0 ]]
		then
			err_from_cmd ${INSTALLP}
		fi
		exit 0
	fi

} # end of sync_root

#---------------------------- full_sync_root -------------------------------
#
# NAME: full_sync_root
#
# FUNCTION:
#		
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#			1						= pathame of the root directory to sync
#		global:
#			installp_flags
#			filesets
#			bundle
#			tmp_files
#			chroot
#
# RETURNS: (int)
#		0							= success
#		1							= failure
#
# OUTPUT:
#-------------------------------------------------------------------------------
function full_sync_root {

	# Is this a shared_root?
	if [[ -d "${1}/${SHROOT_CUSTDIR}" ]]
	then	
		# Remove shared_root directory
		${RM} -r ${1} 2>${ERR} || err_from_cmd "${RM} -rf ${1}"

		# Recreate it
		${MKDIR} -m ${DEFAULT_PERMS} ${1} 2>${ERR} ||            \
			err_from_cmd "${MKDIR} -m ${DEFAULT_PERMS} ${1}"

		# Populate it
		${NIM_METHODS}/c_mkshroot -alocation=${1}                \
		                          -aspot=${location} 2>${ERR} || \
			err_from_cmd "c_mkshroot"		

		exit 0
	fi

	# No, regular root
	typeset orig_flags="${installp_flags}"
	lppchk_uv_root ${1}
	if [[ -s "${cleanup_sync_list}" ]]
	then
		installp_flags="-C ${orig_flags}"	
		sync_root ${1}
	fi

	if [[ -s "${apply_sync_list}" ]]
	then
		installp_flags="-aXg ${orig_flags}"	
		bundle="${apply_sync_list}"
		sync_root ${1}
	fi

	if [[ -s "${commit_sync_list}" ]]
	then
		installp_flags="-cg ${orig_flags}"	
		bundle="${commit_sync_list}"
		sync_root ${1}
	fi

	if [[ -s "${deinstl_sync_list}" ]]
	then
		installp_flags="-ugX ${orig_flags}"	
		bundle="${deinstl_sync_list}"
		sync_root ${1}
	fi

	if [[ -s "${reject_sync_list}" ]]
	then
		installp_flags="-rgX ${orig_flags}"	
		bundle="${reject_sync_list}"
		sync_root ${1}
	fi

	exit 0

} # end of full_sync_root

#---------------------------- lslpp_root         --------------------------------
#
# NAME: lslpp_root
#
# FUNCTION:
#		displays LSLPP info for the specified root
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#			1						= pathame of the root directory to sync
#		global:
#			chroot
#
# RETURNS: (int)
#		0							= success
#		1							= failure
#
# OUTPUT:
#-------------------------------------------------------------------------------
function lslpp_root {
	typeset root=${1}

	# setup the chroot environment using the specified root directory
	setup_chroot_env ${root}

	# Ensure that the server's libraries mounted into the SPOT 
	# will be used.
	${SET_CHROOT_LIBPATH}

	# display the LSLPP info
	${chroot} ${LSLPP} -al 2>${ERR} || err_from_cmd ${LSLPP}

	exit 0

} # end of lslpp_root

#---------------------------- c_sync_root       --------------------------------
#
# NAME: c_sync_root
#
# FUNCTION:
#		performs installp operations on diskless/dataless root parts
#		this is done by mounting the SPOT over each clients root, then chrooting
#			into the client's root and performing the requested installp operation
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		this script will stop when the first time installp returns an error
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#		global:
#
# RETURNS: (int)
#		0							= installp success on all roots
#		1							= installp or other error encountered
#
# OUTPUT:
#-------------------------------------------------------------------------------
# signal processing
trap cleanup 0
trap err_signal 1 2 11 15

# NIM initialization
nim_init

# initialize local variables
typeset c=""
typeset sync_one=""
typeset list_only=""
typeset params=""
typeset root=""
typeset name=""
typeset rc=0
typeset bundle_access=""
typeset full_sync=""

# set parameters from command line
while getopts :a:lqsvf c
do
	case ${c} in

		a)		# validate the attr ass
				parse_attr_ass "${OPTARG}"

				# include the assignment for use in this environment
				eval ${variable}=\"${value}\"
		;;

		f)		# Doing a full sync op 
				full_sync=TRUE
		;;
		l)		# list LPP info
				list_only=TRUE
		;;

		q)		# show attr info
				cmd_what
				exit 0
		;;

		s)		# perform one root sync only
				sync_one=TRUE
		;;

		v)		# verbose mode (for debugging)
				set -x
				for i in $(typeset +f)
				do
					typeset -ft $i
				done
		;;

		\?)	# unknown option
				error ${ERR_BAD_OPT} ${OPTARG}
		;;
	esac
done
shift OPTIND-1

# check for missing attrs
ck_attrs

# do lslpp stuff and exit if that's what we were called to do
if [[ -n "${list_only}" ]]
then

	lslpp_root ${1}

fi

# were neither filesets nor bundle file specified?
if [[ -z "${filesets}" ]] && [[ -z "${bundle}" ]]
then
	if [[ -z "${installp_flags}" ]]
	then
		full_sync=TRUE

	elif [[ "${installp_flags}" = *a* ]]
	then
			filesets=all		
	fi
# either filesets or bundles were specified, how about installp flags?
elif [[ -z "${installp_flags}" ]]
then
		# nope, assume apply operation.
		installp_flags="-agX"
fi

# make sure "Or" is in the installp_flags
[[ "${installp_flags}" != *Or* ]] && installp_flags="-Or ${installp_flags}"

# make sure dashes are acceptable
installp_flags=$( ck_installp_flags "${installp_flags}" )

# perform one root sync (if that's what we were called to do)
if [[ -n "${sync_one}" ]]
then

	
	if [[ -n "${full_sync}" ]]
	then
		# perform all installp ops necessary to sync root
		full_sync_root ${1}
	else
		# If syncing the inst_root of a /usr SPOT, 
		#   a) do some space checking and auto-expand if necessary.
		#   b) prep bundle file if necessary (since we didn't follow
		#      the normal path of execution for this sync op).
                if [[ ${1} = "/usr/lpp/bos/inst_root" ]]
                then
			[[ "${installp_flags}" = *a* ]] && ck_instroot_cust_sz
                        [[ -n "${bundle}" ]] && prep_bundle && \
                                       bundle=${bundle_access}
                fi

		# perform installp op that was specified
		sync_root ${1}
	fi
fi
# else, perform multiple root syncs in parallel

# Before doing multiple root syncs, do some semantic checking on any user 
# specified arguments.  (Do it here rather than prior to sync_root call so 
# that it's not performed for every client -- not necessary.)

# was bundle specified?
if [[ -n "${bundle}" ]]
then

	# check for errors
	[[ -n "${filesets}" ]] && \
		error ${ERR_ATTR_CONFLICT} bundle filesets

	# setup local access
	prep_bundle

elif [[ ${installp_flags} = *u* ]] || [[ ${installp_flags} = *c* ]] || \
     [[ ${installp_flags} = *+([ -])r* ]]
then

	# this is a remove or commit operation: filesets must be specified
	[[ -z "${filesets}" ]] && error ${ERR_MISSING_ATTR} filesets

fi

# for each root pathname given as an operand...
for root
do

	# sync this root directory in the background
	${C_SYNC_ROOT} -s -a location=${location} \
		-a installp_flags="${installp_flags}" \
		${bundle:+"-abundle=${bundle_access}"} \
		${filesets:+-afilesets="${filesets}"} \
		${full_sync:+-f} \
		${root} 2>${TMPDIR}/${root##*/}.failed &

done

# wait for all syncs to finish
wait

# look for failures
failed=$( ${LS} -1 ${TMPDIR}/*.failed 2>/dev/null )
if [[ -n "${failed}" ]]
then

	for root in ${failed}
	do

		# make sure the operation really failed (ie, there's output in the file)
		[[ ! -s ${root} ]] && continue

		name=${root%.failed}
		name=${name##*/}

		print "\t${name}"

		rc=${ERR_ROOT_SYNC}

	done

fi

# all done
exit ${rc}

