#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/lib/nim/methods/c_update.sh 1.3.1.5 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2002,2005 
# 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 
# @(#)32 1.3.1.5 src/bos/usr/lib/nim/methods/c_update.sh, cmdnim, bos720 9/26/05 15:38:48

#
#   COMPONENT_NAME: CMDNIM
#
#   FUNCTIONS: ./usr/lib/nim/methods/c_update.sh
#
#   ORIGINS: 27
#
#
#   (C) COPYRIGHT International Business Machines Corp. 2002
#   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="source packages mount_opts rm_images installp_bundle gencopy_flags show_progress"
location=""
source=""
packages=""
installp_bundle=""
gencopy_flags=""
src_access=""
show_progress=""

#---------------------------- prep_source       --------------------------------
#
# NAME: prep_source
#
# FUNCTION:
#		prepares the source of the LPP images for use
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#			source				= source of LPP images
#		global:
#			access_pnt			= local access point; declared in c_sh_lib
#								  initialized in nim_mount function
#
# RETURNS: (int)
#		0						= success
#
# OUTPUT:
#-------------------------------------------------------------------------------
function prep_source {

	typeset source=${1}

	# what kind of source?
	case ${source} in

		/dev/cd+([0-9]))
			# source is CDROM; pass the device to gencopy
			src_access=${source}
		;;

		/dev/rmt+([0-9]))
			# source is tape; nothing to do because gencopy handles block size
			src_access=${source}
		;;

		*)
			# assume source is a directory; make sure we've got local access
			nim_mount ${source}
			src_access=${access_pnt}
		;;

	esac

} # end of prep_source

#---------------------- convert_packages_to_filenames --------------------------
#
# NAME: convert_packages_to_filenames
#
# FUNCTION:
#		creates a list of file names based on package/fileset [level] names.
#		this removes prefixes.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#			location			= location of packages (directory of lpp_source)
#		global:
#			packages			= list of packages with optional levels
#			installp_bundle		= bundle file with list of packages
#			bundle_access		= local access point for installp bundle file
#
# RETURNS: (int)
#		0						= success
#		1						= failure
#
# OUTPUT:
#		stores the list of filenames in ${TMPDIR}/file_names.list
#
#-------------------------------------------------------------------------------
function convert_packages_to_filenames {

	# parameters
	typeset location=${1}

	# local variables
	typeset i=                # loop variable
	typeset -i index=0        # number of packages
	typeset -i k=1            # loop variable; the arrays start at index 1
	typeset new_packages=     # array of parsed package_names from packages
	typeset levels=           # array of parsed levels from packages
	typeset found_file=       # file name that matched output from gencopy -L
	typeset not_found=        # list of files not found or can't delete

	# if an installp bundle is used, store the contents of the file in packages
	if [[ -n ${installp_bundle} ]]
	then
		packages="$( ${CAT} ${bundle_access} | ${AWK} '$0 !~ /#/ { print $0 }' )"
		[[ -z ${packages} ]] && error ${ERR_BUNDLE_FILE} ${bundle_access}
	fi

	
	# convert the list of packages/filesets with levels to file_names.
	# the expected input is a list of package names with corresponding
	# levels optional (i.e. bos.net 5.1.0.25 perl.rte bos 5.1.0.10 ...)


	# determine if there is a level associated with each package name
	# store the list of packages in the array new_packages and
	# store the list of associated package levels in the array levels
	# also remove any prefixes on the packages i.e. "R:cdrecord"
	for i in ${packages}
	do
		# is this a V.R.M.F level (i.e. 5.1.0.25)
		if [[ ${i} = [1-9].[0-9].[0-9].+([0-9])* ]]
		then
			# yes, store in the array of levels
			levels[$index]="${i}"
		else
			# not a level, must be a package name
			(( index = index + 1 ))
			# remove any prefixes (i.e. 'J:' or 'U:') for ISJE and UDI packages
			new_packages[$index]="${i##*:}"
			levels[$index]=""
		fi	
	done
		
	# store the output from 'gencopy -L -d location' in TMPDIR/gencopy.list
	# the output of 'gencopy -L' is "file_name:package:fileset:v.r.m.f:*"
	${GENCOPY} -L -d ${location} >${TMPDIR}/gencopy.list 2>${ERR} || \
		err_from_cmd ${GENCOPY}

	# get a list of file_names from the specified packages and store the list
	# in TMPDIR/file_names.list

	>${TMPDIR}/file_names.list

	while (( k <= index ))
	do
		# was a level specified for this package?
	    if [[ -n ${levels[$k]} ]]
		then
			# if the user specified name matches the package name or matches 
			# the fileset name of an update image from the gencopy -L output
			# and the levels match, then print the file_name
			found_file=$( ${AWK} 'BEGIN { FS=":" } \
				($2 == name || ($3 == name && $5 !~ /[IRJP]/)) && $4 == level { print $1 }' \
			name=${new_packages[$k]} level=${levels[$k]} ${TMPDIR}/gencopy.list 2>${ERR} )
			[[ $? -ne 0 ]] && err_from_cmd ${AWK}
			if [[ -n $found_file ]]; then
				${ECHO} "$found_file" >>${TMPDIR}/file_names.list
			else
				not_found="${not_found}\t${new_packages[$k]} ${levels[$k]}\n"
			fi
		else
			# if the user specified name matches the package name or matches 
			# the fileset name of an update image from the gencopy -L output,
			# then print the file_name
			found_file=$( ${AWK} 'BEGIN { FS=":" } \
				($2 == name || ($3 == name && $5 !~ /[IRJP]/)) { print $1 }' \
			name=${new_packages[$k]} ${TMPDIR}/gencopy.list 2>${ERR} )
			[[ $? -ne 0 ]] && err_from_cmd ${AWK}
			if [[ -n $found_file ]]; then
				${ECHO} "$found_file" >>${TMPDIR}/file_names.list
			else
				not_found="${not_found}\t${new_packages[$k]}\n"
			fi
		fi
		found_file=
		(( k = k + 1 ))
	done
	
	if [[ -n ${not_found} ]]
	then
		warning ${ERR_CANT_RM_FILESETS} "$( echo ${not_found} )"
	fi

	# success
	return 0

} # end of convert_packages_to_filenames

#---------------------------- update_lpp_source --------------------------------
#
# NAME: update_lpp_source
#
# FUNCTION:
#		adds or removes packages to or from an existing lpp_source using gencopy
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		1) the list of packages MUST be acceptable for gencopy
#		2) calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#		global:
#			location			= location of target lpp_source
#			packages			= list of packages to be added or removed
#			installp_bundle		= bundle file with list of packages
#			bundle_access		= local access point for installp bundle file
#			src_access			= local access point for source of LPP images
#
# RETURNS: (int)
#		0						= success
#		1						= failure
#
# OUTPUT:
#-------------------------------------------------------------------------------
function update_lpp_source {

	# local variables
	typeset i=               # loop variable
	typeset k=               # loop variable
	typeset rm_list=         # list of removed files
	typeset -i file_found=0  # set to 1 if a file is found
	typeset show_gencopy_output="-bv"  # print copied files
	typeset show_rm_output="-e"        # print removed files

	if [[ $show_progress = "no" ]]
	then
		show_gencopy_output=""
		show_rm_output=""
    fi

	# determine if this is an addition or removal of packages
	if [[ -n ${rm_images} ]]
	then
		# REMOVAL

		# convert the list of packages/filesets with levels to file_names
		# this function stores the list in TMPDIR/file_names.list
	    convert_packages_to_filenames ${location}

		# ensure the list of filenames was created
		[[ ! -s ${TMPDIR}/file_names.list ]] && \
			error ${ERR_FINDING_PACKAGES} ${location} "${packages}"

		# remove the files from the location
		for i in $( ${CAT} ${TMPDIR}/file_names.list )
		do
			if [[ "${rm_list}" = *"\t${i}\n"* ]]
			then
				# already removed, so continue
				continue
			fi

			# Do a find to locate the file since it may be in any number
			# of subdirectories.  While only one file should be found, do
			# a for loop in case the file is in multiple locations.
			for k in $( ${FIND} ${location} -name ${i} )
			do
				if [[ -s ${k} ]]
				then
					# run RM with the -r flag to handle ismp directories
					# RM sends all output to stderr, capture stderr and
					# display it if there was no error and show_progress is yes
					${RM} -r ${show_rm_output} ${k} 2>&1 >${TMPDIR}/rm.out
					if [[ $? != 0 ]]; then
						${CP} ${TMPDIR}/rm.out ${ERR}
						err_from_cmd ${RM}
					else
						[[ $show_progress != "no" ]] && ${CAT} ${TMPDIR}/rm.out
					fi

					file_found=1
				fi
			done
				
			if [[ $file_found -eq 1 ]]
			then
				# file was found; add to list of remove packages
				rm_list="${rm_list}\t${i}\n"
			else
				# warning - unable to find the file
				warning ${ERR_FILE_NOT_FND} ${i} ${location}
			fi
			file_found=0

		done

		# NOTE: do not rebuild the .toc since M_UPDATE will call
		# check_lpp_source and this function will rebuild the .toc

	else
		# ADDITION

		# if packages is a file, then pass the file to gencopy with -f
		[[ -n ${installp_bundle} ]] && packages="-f ${bundle_access}"

		# use gencopy to copy the filesets from the source to the target
		# NOTE: gencopy will update the .toc file
		${GENCOPY} ${gencopy_flags} ${show_gencopy_output} -t ${location} \
			-d ${src_access} ${packages} 2>${ERR} || \
			err_from_cmd ${GENCOPY}
	fi

	# SUCCESS
	return 0

} # end of update_lpp_source
	
#----------------------------------- c_update ----------------------------------
#
# NAME: c_update
#
# FUNCTION:
#		updates an lpp_source type NIM resource
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#		calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#		parameters:
#		global:
#
# RETURNS: (int)
#		0	= success
#		1	= failure
#
# OUTPUT:
#-------------------------------------------------------------------------------

# signal processing
trap cleanup 0
trap err_signal 1 2 11 15

# NIM initialization
nim_init

# initialize local variables
typeset c=""

# set parameters from command line
while getopts :a:qv 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}\"
				;;

		q)		# show attr info
				cmd_what
				exit 0
				;;

		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

# need to ensure the location attribute was specified
ck_attrs

# set default flags for gencopy
gencopy_flags=${gencopy_flags:-${DEFAULT_GENCOPY_FLAGS}}
gencopy_flags=$( ck_gencopy_flags "${gencopy_flags}" )

# don't check for errors, since this should have been done in m_update

# ensure that the location is a directory and a JFS or JFS2 filesystem;
# we cannot update a CDROM filesystem
${C_STAT} -a location=${location} -a vfstype="0 3" -a no_error_msg="yes" \
	2>/dev/null || error ${ERR_INCORRECT_FS} ${location}

# if this is an addition, prepare the source
if [[ -n ${source} ]]
then
	# if a source was specified, prepare the source device for use
	# NOTE: this function will set the global variable src_access
	prep_source ${source}
fi

# if a bundle file is used, prepare the bundle
if [[ -n "${installp_bundle}" ]]
then

	# setup local access
	prep_bundle

fi

# update the lpp_source
update_lpp_source

# finished
exit 0
