#!/bin/sh
# $Header: update_dbclients.sh,v 1.17 2002/02/01 21:02:57 fmr Stab $
#bcpyrght
#***************************************************************************
#* $VRTScprght: Copyright 1993 - 2002 VERITAS Software Corporation, All Rights Reserved $ *
#***************************************************************************
#ecpyrght

#
#  Places to possibly update when adding new products and/or platforms:
#
#  function get_pkg_info
#  function get_DB_name (includes 32/64 bit code)
#  function parse_params_and_setup
#  variable All_Extension_List in MAIN
#

USAGE ()
{
	echo ""
	echo "USAGE:"
	echo "   $0 [-ForceInstall] <dbextension_name> -ClientList <filename>"
	echo "or"
	echo "   $0 [-ForceInstall] <dbextension_name> <hardware_type> <operating_system>"
	exit 22
}

trap trapped 1 2 3 15

trapped ()
{
	rm -f ${TMPDIR}/skipped_clients.$$
	rm -f ${TMPDIR}/update_dbclients.${DATE_TIME}
	rm -f ${CLIENTS}
	rm -f ${CLIENTS}.*
	rm -f ${TMPDIR}/NB_clientlist.error
	rm -rf ${TMPDIR}/upd_dbconf_*
	rm -rf ${TMPDIR}/dbclient_update
        exit 1
}

TMPDIR=${TMPDIR:-/tmp}
NB_PATH=/usr/openv/netbackup

#
#  Define the Prompt function.
#

case "`echo 'x\c'`" in
	'x\c')
		Prompt()
		{
		echo -n "$*"
		}
		;;
	x)
		Prompt()
		{
		echo "$*\c"
		}
		;;
	*)
		Prompt()
		{
		echo -n "$*"
		}
		;;
esac

#
#  This function provides information about the database extension
#  provided in the first parameter.  Notice that the extension "names"
#  match the text displayed in the menus from cdrom_install_dbext and
#  install_dbext except blanks have been replaced by underscores to
#  keep the name a single "word".  Information obtained includes the
#  equivalent package name (the name in the db extension directory
#  and tar.Z file names), the database extension class type name (spit
#  out by bpcllist -U) and the name of the runscript for the extension,
#  if there is one.
#

get_pkg_info ()
{
	pkg_name=""
	run_script=""
	case "${1}" in
		SQL-BackTrack)
			pkg_name="BACKTRACK"
			class_name="DataTools-SQL-BackTrack"
			run_script="install_obsi"
			;;
		DB2)
			pkg_name="${1}"
			class_name="DB2"
			run_script="install_db2"
			;;
		Informix)
			pkg_name="INFORMIX"
			class_name="Informix-On-BAR"
			run_script="install_infxbsa"
			;;
		Lotus_Notes)
			pkg_name="LOTUS"
			class_name="Lotus-Notes"
			;;
		Oracle)
			pkg_name="OEBU"
			class_name="Oracle"
			;;
		Oracle_on_UNIX_Advanced_BLI_Agent)
			pkg_name="PROXY"
			class_name="Oracle"
			run_script="install_oracle"
			;;
		SAP)
			pkg_name="${1}"
			class_name="SAP"
			;;
		Sybase)
			pkg_name="SYBASE"
			class_name="Sybase"
			run_script="install_sybackup"
			;;
		Teradata)
			pkg_name="TERADATA"
			class_name="NCR-Teradata"
			run_script="install_tdata"
			;;
		*)
			echo ""
			echo "The ${1} extension has no database package name."
			quit 2
			echo ""
			;;
	esac
}

#
#  This function provides a mapping from the NetBackup UNIX hardware and
#  OS names (directory names in /usr/openv/netbackup/client) to their
#  database extension equivalents (fields in the .tar.Z file names).  For
#  example, DataGeneral and UNIX map to dgux.  Note that some platforms have two
#  possible configurations (32 bit and 64 bit) for some products.  Both files
#  are shipped, if they exist, but only one is actually installed (script
#  install_dbext decides).  Also notice the 64 bit names are not a valid client
#  type on the client picklist. This is different than the db_name2 case where
#  for some platforms, more than one client platform may apply HOWEVER no one
#  product has more than one configuration for that platform.  The linking of
#  client directories is also accounted for here.  For example, Solaris2.6 and
#  Solaris7 are linked in /usr/openv/netbackup/client and here they both map to
#  solaris2.6.  There is no bailout or default case.  Anything not mentioned
#  here (such as PCs or old platforms) is automatically skipped.
#

get_DB_name ()
{
	NB_hw="${1}"
	NB_os="${2}"
	db_name="Unknown"
	db_name2=""
	dual_config="aAhHaA"

	case "${NB_hw}" in
		ALPHA)
			if [ "${NB_os}" = "OSF1_V4" -o "${NB_os}" = "OSF1_V5" ] ; then
				db_name="alpha"
			fi
			;;
		DataGeneral)
			if [ "${NB_os}" = "UNIX" ] ; then
				db_name="dgux"
			fi
			;;
		HP9000-700 | HP9000-800)
			if [ "${NB_os}" = "HP-UX10.20" ] ; then
				db_name="hp10.20"
			elif [ "${NB_os}" = "HP-UX11.00" ] ; then
				db_name="hp11.00"
				dual_config="hp11_64"
			fi
			;;
		Linux | INTEL)
			if [ "${NB_os}" = "RedHat" ] ; then
				db_name="linux"
			fi
			;;
		NCR)
			if [ "${NB_os}" = "UNIX" ] ; then
				db_name="ncr"
			fi
			;;
		RS6000)
			if [ "${NB_os}" = "AIX4.2" ] ; then
				db_name="rs6000_42"
			elif [ "${NB_os}" = "AIX4.3" ] ; then
				db_name="rs6000_43"
				db_name2="rs6000_42"
				dual_config="rs6000_43_64"
			fi
			;;
		Sequent)
			if [ "${NB_os}" = "DYNIX420" ] ; then
				db_name="sequent4.2"
			fi
			;;
		C910_920 | SGI)
			if [ "${NB_os}" = "IRIX6" ] ; then
				db_name="sgi6"
			elif [ "${NB_os}" = "IRIX65" ] ; then
				db_name="sgi65"
				db_name2="sgi6"
				dual_config="sgi65_64"
			fi
			;;
		Solaris | Sun4)
			if [ "${NB_os}" = "Solaris2.6" ] ; then
				db_name="solaris2.6"
			elif [ "${NB_os}" = "Solaris7" -o \
			       "${NB_os}" = "Solaris8" -o \
			       "${NB_os}" = "Solaris9" ] ; then
				db_name="solaris2.6"
				dual_config="solaris7_64"
			fi
			;;
	esac
}

#
#  This function takes a string as input and echos it out
#  as a question with a prompt for an answer.  yes is the
#  default.  Returns 0 for yes and 1 for no.
#

confirm_yes ()
{
	Valid=1
	while [ ${Valid} = 1 ] ; do
		Prompt "${1} (y/n) [y] "
		read ans
		: ${ans:=y}
		echo ""
		case ${ans} in
			N*|n*)
				return 1
				;;
			Y*|y*)
				return 0
				;;
			    *)
				echo "You have entered an invalid option."
				echo ""
				;;
		esac
	done
}

#
#  This function takes a string as input and echos it out
#  as a question with a prompt for an answer.  no is the
#  default.  Returns 1 for yes and 0 for no.
#

confirm_no ()
{
	Valid=1
	while [ ${Valid} = 1 ] ; do
		Prompt "${1} (y/n) [n] "
		read ans
		: ${ans:=n}
		echo ""
		case ${ans} in
			Y*|y*)
				return 1
				;;
			N*|n*)
				return 0
				;;
			    *)
				echo "You have entered an invalid option."
				echo ""
				;;
		esac
	done
}

#
#  This function takes a string as input and echos it out
#  as a question with a prompt for an answer.  As long as
#  something is entered, it is considered valid.  A <cr> is
#  considered invalid, since there is no default.
#

get_value ()
{
	Valid=1
	while [ ${Valid} = 1 ]
	do
		Prompt "${1} "
		read gv_ans
		: ${gv_ans:=x2y8z5}
		case ${gv_ans} in
			x2y8z5)
				echo "You have entered an invalid option."
				echo ""
				;;
			*)
				Valid=0
				;;
		esac
	done
}

#
#  Get one numeric value from the user.  The first parameter is
#  a string that is echoed out as a question with a prompt for
#  an answer.  The second parameter is the minimum value that
#  will be accepted and the third parameter is the highest value
#  that will be accepted.  The default answer is the fourth
#  parameter.  If the answer is a number, we check to make sure it's
#  in the range established by the input parameters.  If the answer
#  is anything else or it wasn't in the range, the user is prompted
#  again.  The variable VALUE contains the final answer.
#

get_number()
{
	echo ""
	echo "${1}"
	Prompt "Must be in the range ${2} - ${3} (default: ${4}): "
	valid=0
	until [ ${valid} -ne 0 ]
	do
		read VALUE
		VALUE=${VALUE:=${4}}
		echo ${VALUE} | egrep -e \(\^[0-9]\+\$\) > /dev/null
		if [ $? -ne 0 ] ; then
			Prompt "Must be in the range ${2} - ${3}, try again: "
		else
			if [ ${VALUE} -ge ${2} -a ${VALUE} -le ${3} ] ; then
				valid=1
			else
				Prompt "Must be in the range ${2} - ${3}, try again: "
			fi
		fi
	done
}

#
#  This function parses the command line parameters (if any) and
#  does some setup (like determining the hostname, hardware, etc.).
#

parse_params_and_setup ()
{
	#
	#  If HARDWARE has no value, then either we aren't on a server or the
	#  version file was corrupted.  Determine where the tr command resides
	#  and what tar options should be used.
	#

	if [ -f ${NB_PATH}/version ] ; then
		HARDWARE=`head -1 ${NB_PATH}/version | cut -f2 -d" "`
	else
		HARDWARE=NONE
	fi

	HASYP=TRUE
	TR=/bin/tr
	ISSUN=FALSE

	case "${HARDWARE}" in
		ALPHA)
			tar_opts="xvbpf 20"
			;;
		HP*)
			TR=/usr/bin/tr
			tar_opts="-xvpf"
			;;
		LINUX*)
			TR=/usr/bin/tr
			tar_opts="-xvpof"
			;;
		NCR | SOLARIS)
			tar_opts="-xvpof"
			;;
		PYRAMID)
			tar_opts="xvpobf 20"
			;;
		RS6000)
			tar_opts="-xvpf"
			;;
		SEQUENT*)
			HASYP=FALSE
			TR=/usr/bin/tr
			tar_opts="-xvpof"
			;;
		SGI)
			tar_opts="-xvpbf 20"
			;;
		SUN4)
			ISSUN=TRUE
			tar_opts="-xvpof"
			;;
		*)
			echo "
${NB_PATH}/version not found or corrupted.
\"${HARDWARE}\" is an unknown hardware type.
$0 should be executed on a master server.
"
			quit 1
			;;
	esac

	#
	#  If -ForceInstall is the first parameter, bpinst will not check
	#  that the client version and the db extension version are the
	#  same before shipping the file over.
	#
	#  If Install (a purposely undocumented option) is first, it means
	#  update_dbclients is being executed from the administrator interfaces
	#  such as Java; therefore don't ask any questions and expect
	#  a specific database extension name plus -ClientList <filename>.
	#
	#  If either of these are on the list but not first, the parameter
	#  count will be off (on purpose) and the user gets a usage
	#  statement.
	#

	if [ $# -ge 1 ] ; then
		if [ "${1}" = "Install" ] ; then
			Install=TRUE
			shift
		elif [ "${1}" = "-ForceInstall" ] ; then
			ForceInstall=""
			shift
		fi
	fi

	#
	#  If three parameters are still left, the dbext name (could be ALL)
	#  is first.  If the second parameter is -ClientList, the third is then
	#  the file that contains the list, otherwise, a particular hardware
	#  and os level were specified (could be ALL ALL).
	#
	#  At this point, if Install is used (was invoked by an admin
	#  interface), don't allow ALL extensions to be chosen and expect
	#  -ClientList <filename>.
	#

	if [ $# = 3 ] ; then
		dbext=${1}

		#
		#  Verify that if a particular extension was specified, it
		#  is valid.  We allow a little leniency on capitalization.
		#

		if [ "${dbext}" != "ALL" ] ; then
			found=0
			for ext in ${All_Extension_List}; do
				orig=`echo ${ext} | ${TR} "[a-z]" "[A-Z]"`
				cmdline=`echo ${dbext} | ${TR} "[a-z]" "[A-Z]"`
				if [ "${orig}" = "${cmdline}" ] ; then
					found=1
					dbext=${ext}
					break
				fi
			done
			if [ ${found} -eq 0 ] ; then
				echo ""
				echo "${dbext} is not a valid database extension name.  Possible"
				echo "names are:"
				echo ""
				echo "${All_Extension_List}"
				echo ""
				quit 1
			fi
		else
			if [ "${Install}" = "TRUE" ] ; then
				echo ""
				echo "When using the Install parameter, a specific database extension name is required."
				echo ""
				quit 1
			else
				dbext="${All_Extension_List}"
			fi
		fi

		if [ "${2}" = "-ClientList" ] ; then
			ClientList=${3}
			hardware="ALL"

			if [ ! -f ${ClientList} -o ! -s ${ClientList} ] ; then
				echo ""
				echo "The ClientList file ${ClientList} doesn't exist or is empty."
				echo ""
				quit 1
			fi
		else
			if [ "${Install}" = "TRUE" ] ; then
				echo ""
				echo "The Install option expects the -ClientList <filename> option."
				echo ""
				quit 1
			else
				hardware=${2}
				os_name=${3}
				if [ "${hardware}" = "ALL" -a "${os_name}" != "ALL" ] ; then
					echo ""
					echo "Invalid hardware type \"${hardware}\" and/or operating system \"${os_name}\"."
					USAGE
				fi
			fi

			#
			#  Verify that if a particular hardware and os level
			#  were specified, the software has been loaded.  Notice
			#  that we expect the hardware and os level to match the
			#  names in the NetBackup client directory.
			#

			if [ "${hardware}" != "ALL" -a ! -d "${NB_PATH}/client/${hardware}/${os_name}" ] ; then
				echo ""
				echo "Invalid hardware type \"${hardware}\" and/or operating system \"${os_name}\" or"
				echo "that type of client software has not been loaded."
				USAGE
			fi
		fi
	else
		USAGE
	fi

	#
	#  Determine the system's hostname.  Use hostname if available and strip
	#  away any domain information.
	#

	if type hostname | grep 'not found' >/dev/null
	then
		MASTER=`uname -n | sed 's/\..*$//'`
	else
		MASTER=`hostname | sed 's/\..*$//'`
	fi

	#
	#  Get the domainname into DOMAIN without a leading '.' (if there is
	#  one).  Had one customer who we expected to have access to domainname
	#  (HASYP was TRUE) but domainname was part of an optional (not installed)
	#  product.  So make sure the command exists.  If there is no domain or
	#  domainname is not present, set it to something that will not be matched.
	#

	if [ "${HASYP}" = "TRUE" ] ; then
		if type domainname | grep 'not found' >/dev/null
		then
			DOMAIN=""
		else
			DOMAIN=`domainname | sed 's/^\.//'`
		fi
	fi
	if [ "${DOMAIN}" = "" ] ; then
		DOMAIN=ZZZZZZZZ
	fi
}

#
#  The first parameter is the database extension package type name
#  (for example, OEBU).  The second is the os type name (for example,
#  solaris2.6).  Both of these parameters are fields in the tar.Z file
#  names that make up the database extension packages.  The third
#  parameter is the client we will be updating.  The last parameter
#  is the number of this job (JobIndex).  First, we make sure we're not
#  trying to update the server before we start shipping files.  If not,
#  ship the .Z file over.  If we failed, see if it's because the client
#  and extension version levels don't match.  We keep track of how many
#  of those we encounter.  If we succeeded, find out if it's because the
#  client already has that extension version present.  If so, don't bother
#  shipping the version file.  Otherwise, ship the version file over.  If
#  that succeeded, ship over install_dbext.  At this point, all files
#  should be present on the client.  If no problems have occurred yet,
#  see if we had problems shipping back the .dbext.conf file (if we
#  created one) during the ask_runscript_questions function.  If not, send
#  over a tiny script that executes install_dbext for this product on the
#  client.  If the client affected is the master server, we pass the
#  ForceInstall variable so that version checking will be done (or not)
#  based on how update_dbclients was called.  Bring back the trace output
#  from that run and add it to the LOGFILE.  If the client was the master,
#  here's where we check if we have a client/extension level mismatch.
#  Variable ret contains the final return value for this function.
#

dbclient_update()
{
	ret=0
	PROD=${1}
	OS=${2}
	CLIENT=${3}
	DUAL=${4}
	Job=${5}
	Output=${JOBOUTDIR}/${CLIENT}.${PROD}.${OS}.$$.${Job}
	exec > ${Output} 2>&1

	Z_name="${PROD}.${OS}.tar.Z"
	Z_dual_name="${PROD}.${DUAL}.tar.Z"
	i_locked=0

	echo ""
	echo ">>>>>>>>"
	echo ">>>>>>>>   ${CLIENT} ..."
	echo ">>>>>>>>"
	echo ""

	#
	#  Create a lock directory on the client in order to avoid
	#  having more than one product/platform job updating the same
	#  client at the same time.  For example, if shipping Oracle HP11.00 to
	#  two clients that happen to be the same (guava and guava.min.ov.com).
	#  If I can't create the directory, an update could already be in
	#  progress so abandon the rest of this spawned job.
	#

	cat <<MY_EOF > ${TMPDIR}/${CLIENT}.${PROD}.${Job}_lock.sh
#!/bin/sh
if [ ! -d "/tmp/${PROD}.${OS}" ] ; then
	mkdir /tmp/${PROD}.${OS}
else
	exit 1
fi
MY_EOF
	${NB_PATH}/bin/bpinst -MISC -X -Z ${CLIENT}.${PROD}.${Job}_lock.sh -src ${TMPDIR} -dest ${NB_PATH}/bin -host ${CLIENT} -d
	if [ $? -ne 0 ] ; then
		Stat=`grep "runscript failed" ${Output}`
		if [ $? = 0 ] ; then
			echo ""
			echo "Directory /tmp/${PROD}.${OS} exists on client ${CLIENT}."
			echo "An update of ${PROD}.${OS} is possibly in progress."
			echo "Quitting this attempt."
			echo ""
		fi
		ret=3
	else

		#
		#  The master doesn't need to ship any of the extension files to
		#  itself, it only needs to run the install_dbext script.
		#
	
		if [ "${CLIENT}" != "${MASTER}" -a "${CLIENT}" != "${MASTER}.${DOMAIN}" ] ; then
			${NB_PATH}/bin/bpinst -MISC ${ForceInstall} -Z ${Z_name} -src ${NB_PATH}/dbext -dest ${NB_PATH}/dbext -host ${CLIENT} -d
			if [ $? -ne 0 ] ; then
				Stat=`tail -2 ${Output} | grep "version files do not match - cannot update"`
				if [ $? = 0 ] ; then
					ret=2
				else
					ret=3
				fi
			else
				Stat=`tail -2 ${Output} | grep "version files match - no update"`
				if [ $? = 0 ] ; then
					ret=1
				else
					${NB_PATH}/bin/bpinst -MISC -Z ${PROD}.${OS}.version -src ${NB_PATH}/dbext -dest ${NB_PATH}/dbext -host ${CLIENT} -d
					if [ $? -ne 0 ] ; then
						ret=3
					else

						#
						#  Address issue of one product having more than one
						#  configuration for the same machine.  For example,
						#  hp11.00 and hp11_64.  Still need to use ${ForceInstall}
						#  in case only the second configuration already has been
						#  shipped at some point and they want to clobber it.
						#

						if [ -f ${NB_PATH}/dbext/${Z_dual_name} ] ; then
							${NB_PATH}/bin/bpinst -MISC ${ForceInstall} -Z ${Z_dual_name} -src ${NB_PATH}/dbext -dest ${NB_PATH}/dbext -host ${CLIENT} -d
							if [ $? -ne 0 ] ; then
								ret=3
							else
								Stat=`tail -2 ${Output} | grep "version files match - no update"`
								if [ $? = 0 ] ; then
									ret=1
								else
									${NB_PATH}/bin/bpinst -MISC -Z ${PROD}.${DUAL}.version -src ${NB_PATH}/dbext -dest ${NB_PATH}/dbext -host ${CLIENT} -d
									if [ $? -ne 0 ] ; then
										ret=3
									fi
								fi
							fi
						fi

						if [ ${ret} -eq 0 ] ; then

							#
							#  Ship install_dbext only once to a particular client.
							#  Add .done to the client name to make the string
							#  more unique since grep -w doesn't work on
							#  all platforms.
							#

							grep ${CLIENT}.done ${CLIENTS}.track > /dev/null 2>&1
							if [ $? -ne 0 ] ; then
								while [ ${i_locked} -ne 1 ] ; do
									if [ -f ${JOBOUTDIR}/.${CLIENT}.sh.lock ] ; then
										sleep 2
									else
										touch ${JOBOUTDIR}/.${CLIENT}.sh.lock
										i_locked=1
									fi
								done
								grep ${CLIENT}.done ${CLIENTS}.track > /dev/null 2>&1
								if [ $? -ne 0 ] ; then
									${NB_PATH}/bin/bpinst -MISC -Z install_dbext -src ${NB_PATH}/dbext -dest ${NB_PATH}/dbext -host ${CLIENT} -d
									if [ $? -ne 0 ] ; then
										ret=3
									else
										echo "${CLIENT}.done" >> ${CLIENTS}.track
									fi
								fi
								rm -f ${JOBOUTDIR}/.${CLIENT}.sh.lock
							fi
						fi
					fi
				fi
			fi
		fi
	
		if [ ${ret} -eq 0 ] ; then
			cl=`echo ${CLIENT} | ${TR} '\.' '_' | ${TR} '-' '_'`
			if eval [ \"\${shipped_conf_${cl}}\" = \"failed\" ] ; then
				ret=3
				echo "Unable to update dbext config file on ${CLIENT}."
				echo "Run ${NB_PATH}/dbext/install_dbext directly on client ${CLIENT}."
			else
				if [ "${CLIENT}" != "${MASTER}" -a "${CLIENT}" != "${MASTER}.${DOMAIN}" ] ; then
					cat <<MY_EOF > ${TMPDIR}/${CLIENT}.${PROD}.${Job}.sh
#!/bin/sh
cd ${NB_PATH}/dbext
rm -f ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file
./install_dbext -B ${PROD} ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file
MY_EOF
				else
					cat <<MY_EOF > ${TMPDIR}/${CLIENT}.${PROD}.${Job}.sh
#!/bin/sh
cd ${NB_PATH}/dbext
rm -f ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file
./install_dbext ${ForceInstall} -B ${PROD} ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file
MY_EOF
				fi
				${NB_PATH}/bin/bpinst -MISC -X -Z ${CLIENT}.${PROD}.${Job}.sh -src ${TMPDIR} -dest ${NB_PATH}/bin -host ${CLIENT} -d
				if [ $? -ne 0 ] ; then
					ret=3
				fi
	
				#
				#  Get trace file even if the script execution failed.
				#
	
				${NB_PATH}/bin/bpgp from ${CLIENT} ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file.tmp
				if [ $? -ne 0 ] ; then
					ret=3
				else
					echo ""
					echo "	*** start of trace output from ${CLIENT} ***"
					cat ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file.tmp
					echo ""
					echo "	*** end of trace output from ${CLIENT} ***"
					echo ""
				fi
				Stat=`tail -3 ${Output} | grep "version files do not match - cannot update"`
				if [ $? = 0 ] ; then
					ret=2
				fi
				rm -f ${TMPDIR}/${CLIENT}.${PROD}.${Job}.trace_file.tmp
				rm -f ${TMPDIR}/${CLIENT}.${PROD}.${Job}.sh
			fi
		fi

		#
		#  Clean up the lock directory that I created at the
		#  beginning of this job.
		#

		cat <<MY_EOF > ${TMPDIR}/${CLIENT}.${PROD}.${Job}_unlock.sh
#!/bin/sh
if [ -d "/tmp/${PROD}.${OS}" ] ; then
	rm -rf /tmp/${PROD}.${OS}
fi
MY_EOF
		${NB_PATH}/bin/bpinst -MISC -X -Z ${CLIENT}.${PROD}.${Job}_unlock.sh -src ${TMPDIR} -dest ${NB_PATH}/bin -host ${CLIENT} -d
	fi
	if [ ${ret} -eq 3 -o ${ret} -eq 2 ] ; then
		echo ""
		echo "Update of client ${CLIENT} failed."
	elif [ ${ret} -eq 1 ] ; then
		echo ""
		echo "Update of client ${CLIENT} has been skipped."
	elif [ ${ret} -eq 0 ] ; then
		echo ""
		echo "Update of client ${CLIENT} was successful."
	fi
	rm -f ${TMPDIR}/${CLIENT}.${PROD}.${Job}_lock.sh
	rm -f ${TMPDIR}/${CLIENT}.${PROD}.${Job}_unlock.sh

	return ${ret}
}

#
#  This function first checks to make sure that bpdbm is running.  We need
#  it for some of the commands we use (bpinst, bpclclients etc).  If a
#  ClientList was provided by the user, we sort and uniq it, and do minimal
#  checks on formatting but no configuration checks are made.  This is how
#  a site can ship extension software out to clients not yet configured into
#  any extension class (initial type install).  For an upgrade, where clients
#  have been configured into extension classes, no ClientList is necessary
#  because if no ClientList was provided, we first get a list of all unique
#  clients and then prune it down based on whether each client belongs to a
#  class with the right database extension type.  Once a list of clients is
#  available, we next see if the database extension software for that type
#  is loaded.  If so, information is added to the ${CLIENTS}.final.dbclients
#  file.  This is the file that determines what extension software gets pushed
#  to what client.  Every specified database extension is checked so that the
#  "final" file contains the total list.  Then we figure out which clients we
#  skipped and display that info to the user.   If we skipped everything, we
#  quit.
#

determine_clients ()
{
	if [ ! -f ${NB_PATH}/bin/bpps ]; then
		echo ""
		echo "${NB_PATH}/bin/bpps doesn't exist."
		echo "$0 should be executed on a master server."
		echo ""
		quit 2
	else
		${NB_PATH}/bin/bpps | grep bpdbm > /dev/null
		if [ $? -ne 0 ]; then
			echo ""
			echo "${NB_PATH}/bin/bpdbm is not executing."
			echo "Please make sure the NetBackup daemons have been started."
			echo ""
			quit 2
		fi
	fi

	#
	#  Create a list of valid clients or use the ClientList file provided.
	#

	if [ "${ClientList}" = "" ] ; then
		${NB_PATH}/bin/admincmd/bpclclients -allunique -noheader > ${CLIENTS} 2>/dev/null
		if [ "${hardware}" != "ALL" ] ; then
			grep ${hardware} ${CLIENTS} | grep ${os_name} > ${CLIENTS}.tmp
			mv ${CLIENTS}.tmp ${CLIENTS}
		fi
		if [ ! -s ${CLIENTS} ] ; then
			echo ""
			if [ "${hardware}" = "ALL" ] ; then
				echo "No clients have been configured."
			else
				echo "No clients have been configured with hardware type"
				echo "\"${hardware}\" and operating system \"${os_name}\"."
			fi
			echo ""
			quit 2
		fi
	else

		#
		#  Since this is provided by the user, let's make sure it's
		#  sorted on the client name field and has unique clients in it.
		#

		if [ "${ISSUN}" = "TRUE" ] ; then
			sort -u +2 -3 ${ClientList} > ${CLIENTS}
		else
			sort -u -k 3,3 ${ClientList} > ${CLIENTS}
		fi

		#
		#  Make a small attempt to verify the contents of the client
		#  list.  It will notice empty lines or missing fields but not
		#  extra ones.  Notice I have to touch a file on error since
		#  some shells use a subshell for the cat/while and quit won't
		#  actually exit the program.
		#

		cat ${CLIENTS} |
		while read field1 field2 field3
		do
			if [ "${field1}" = "" -o "${field2}" = "" -o \
			     "${field3}" = "" ] ; then
				touch ${TMPDIR}/NB_clientlist.error
				break
			fi
		done

		if [ -f ${TMPDIR}/NB_clientlist.error ] ; then
			echo ""
			echo "File ${ClientList} is improperly formatted.  There"
			echo "are empty lines and/or missing fields.  Execute"
			echo "${NB_PATH}/bin/admincmd/bpclclients -allunique -noheader"
			echo "to see an example of the proper format."
			echo ""
			rm -f ${TMPDIR}/NB_clientlist.error
			quit 2
		fi
	fi

	rm -f ${CLIENTS}.final.dbclients
	for product in ${dbext}; do

		#
		#  If we didn't get a client list from the user, let's try and
		#  prune down the list to all clients belonging to any classes
		#  of the specified database extension class type.  If a client
		#  list was provided, we don't prune.
		#

		rm -f ${CLIENTS}.maybe.tmp
		if [ "${ClientList}" = "" ] ; then
			get_pkg_info ${product}
			product_clients=`${NB_PATH}/bin/admincmd/bpclclients -allunique -noheader -ct ${class_name} 2>/dev/null | cut -f3 -d" "`

			if [ "${product_clients}" != "" ] ; then
				/bin/cat ${CLIENTS} |
				while read class_hw class_os client
				do
					for pclient in ${product_clients}; do
						if [ "${client}" = "${pclient}" ] ; then
							echo ${class_hw} ${class_os} ${client} >> ${CLIENTS}.maybe.tmp
							break
						fi
					done
				done
			fi
		else
			cp ${CLIENTS} ${CLIENTS}.maybe.tmp
		fi

		#
		#  If we have clients, check to see if their corresponding
		#  database extension software is present.  If no tar.Z files
		#  for the current extension, go on to the next one.
		#

		if [ -s ${CLIENTS}.maybe.tmp ] ; then
			get_pkg_info ${product}
			os_name_list=`ls ${NB_PATH}/dbext/*.tar.Z 2> /dev/null | grep ${pkg_name} | sed -e 's/\.tar\.Z$//' | cut -f2- -d'.'`

			if [ "${os_name_list}" != "" ] ; then
				/bin/cat ${CLIENTS}.maybe.tmp |
				while read class_hw class_os client
				do

					#
					#  Find out what extension os name
					#  matches the client's hardware and
					#  operating system.
					#

					get_DB_name ${class_hw} ${class_os}
					for os in ${os_name_list}; do

						#
						#  If the extension os name matches any of
						#  the tar.Z files found, add it to the
						#  list of final clients.  Include a bunch
						#  of fields we'll need later.  Don't break
						#  after we have a hit since we could have
						#  two possible platform types for one machine.
						#  For example, sgi6 and sgi65 would apply
						#  to an sgi machine running 6.5.
						#

						if [ "${db_name}" = "${os}" -o \
						     "${db_name2}" = "${os}" ] ; then
							echo "${class_hw} ${class_os} ${client} ${product} ${pkg_name} ${os} ${dual_config}" >> ${CLIENTS}.final.dbclients
						fi
					done
				done
			fi
			rm -f ${CLIENTS}.maybe.tmp
		fi
	done

	#
	#  Now let's figure out what we skipped.  First, take the original
	#  list of clients and see if they're on the final list.  If not,
	#  they go on the skipped list.  If there was no final list, the
	#  whole original list ends up on the skipped list.
	#

	rm -f ${TMPDIR}/skipped_clients.$$
	/bin/cat ${CLIENTS} |
	while read class_hw class_os client
	do
		if [ -f ${CLIENTS}.final.dbclients ] ; then
			grep ${class_hw} ${CLIENTS}.final.dbclients | grep ${class_os} | grep "${client} " > /dev/null
			if [ $? -ne 0 ] ; then
				echo "        ${client} - ${class_os}" >> ${TMPDIR}/skipped_clients.$$
			fi
		else
			echo "        ${client} - ${class_os}" >> ${TMPDIR}/skipped_clients.$$
		fi
	done

	#
	#  If we had skipped clients, display them and list the possible
	#  reasons.  Also tell them where the complete list is in TMPDIR.
	#

	if [ -s ${TMPDIR}/skipped_clients.$$ ] ; then
		echo ""
		echo "The following clients with the OS type listed were skipped"
		echo "due to one of the following reasons:"
		echo ""
		echo "  1.  they are non-UNIX clients (which cannot be installed"
		echo "         or upgraded from the server)"
		echo "  2.  there is no specified database extension software"
		echo "         available for that type of client"
		echo "  3.  the matching database extension software was not"
		echo "         loaded on the server"
		if [ "${ClientList}" = "" ] ; then
			echo "  4.  they did not belong to any of the specified database"
			echo "         extension class types"
		fi
		echo ""
		echo "        Client Name - OS Type"
		echo "        ---------------------"

		rm -f ${CLIENTS}.tmp
		sort -u ${TMPDIR}/skipped_clients.$$ > ${CLIENTS}.tmp
		mv ${CLIENTS}.tmp ${TMPDIR}/skipped_clients.$$

		Entries=`wc -l ${TMPDIR}/skipped_clients.$$ | sed s/' '/''/g | cut -d '/' -f1`
		if [ ${Entries} -gt 9 ] ; then
			head -9 ${TMPDIR}/skipped_clients.$$
			echo "        ..."
			echo "        There are ${Entries} clients in this list."
		else
			cat ${TMPDIR}/skipped_clients.$$
		fi
		echo ""
		echo "File ${TMPDIR}/skipped_clients.$$ contains the complete list of skipped clients."

		#
		#  If there's no final list, we can't have anything to do and
		#  everything was skipped.  Bail out.
		#

		if [ ! -f ${CLIENTS}.final.dbclients ] ; then
			quit 2
		fi
	fi
}

#
#  For each client and extension combo we plan to ship, see if there
#  is a runscript for that extension.  If so, see if the runscript asks
#  any questions.  If so, see if that client has a config file with the
#  answers to those questions.  Only if the answer isn't in the config file,
#  do we ask the user.  If there is a default answer, we show it to the user.
#  The default answer is equal to the value set by the previous client for
#  that extension.  The answers are then saved in a config file and shipped
#  back to the client so that when install_dbext is run later in this script,
#  it has access to them.
#

ask_runscript_questions ()
{
	#
	#  Initialize a variable for each extension's number of questions.
	#

	for ext in ${dbext} ; do
		get_pkg_info ${ext}
		eval ${pkg_name}_Qs=""
	done

	#
	#  Pick out the clients from the final list.  Also sort -u the client
	#  list in case more than one extension is going to the same client.
	#  Then initialize variables stating whether this client has a dbext
	#  config file and whether it's been modified.  Note the translation
	#  from . to _ in the client name since the shell doesn't like dots in
	#  a variable name (ex. guava.min.ov.com -> guava_min_ov_com).  And
	#  another translation required for clients with hyphens (ex.
	#  guava-1 -> guava_1).
	#

	rm -f ${CLIENTS}.client_list
	/bin/cat ${CLIENTS}.final.dbclients |
	while read class_hw class_os client product pkg os dual
	do
		echo "${client}" >> ${CLIENTS}.client_list
	done
	client_list=`/bin/cat ${CLIENTS}.client_list | sort -u`
	rm -f ${CLIENTS}.client_list
	for client in ${client_list} ; do
		client=`echo ${client} | ${TR} '\.' '_' | ${TR} '-' '_'`
		eval ${client}_has_conf=""
		eval ${client}_conf_modified=""
	done

	#
	#  Start reading from the final "to do" list.  Skip the fields
	#  class_hw and class_os.  The reason this while loop is more
	#  convoluted than just reading from the final dbclients file is
	#  because questions get asked (which read from stdin).
	#

	num_lines=`wc -l ${CLIENTS}.final.dbclients | sed s/' '/''/g | cut -d '/' -f1`
	line_no=1
	while [ ${line_no} -le ${num_lines} ]; do
		final_line=`head -${line_no} < ${CLIENTS}.final.dbclients | tail -1`
		client=`echo ${final_line} | cut -f3 -d" "`
		product=`echo ${final_line} | cut -f4 -d" "`
		pkg=`echo ${final_line} | cut -f5 -d" "`
		os=`echo ${final_line} | cut -f6 -d" "`
		dual=`echo ${final_line} | cut -f7 -d" "`

		#
		#  If we haven't figured out the number of questions for
		#  this extension yet, find out if there is a runscript.
		#  If not, set the number of questions to 0.  If there is,
		#  unbundle the runscript and look for the keyword NUM_QS.
		#  If it's not there, set the number of questions to 0,
		#  otherwise set it to what was found in the runscript.
		#  If the number was not 0, initialize a set of variables
		#  for each question.  If we've already set the number
		#  of questions, just create a directory for this client in
		#  TMPDIR.
		#

		if eval [ \"\${${pkg}_Qs}\" = \"\" ] ; then
			get_pkg_info ${product}
			if [ "${run_script}" = "" ] ; then
				eval ${pkg}_Qs=0
			else
				if [ ! -d ${TMPDIR}/upd_dbconf_${client} ] ; then
					mkdir ${TMPDIR}/upd_dbconf_${client}
				fi
				(
				cd ${TMPDIR}/upd_dbconf_${client}
				zcat ${NB_PATH}/dbext/${pkg}.${os}.tar.Z | /bin/tar ${tar_opts} - usr/openv/netbackup/bin/${run_script} >/dev/null 2>/dev/null
				)
				eval ${pkg}_Qs=`grep "^NUM_QS" ${TMPDIR}/upd_dbconf_${client}/usr/openv/netbackup/bin/${run_script} 2>/dev/null | cut -f2 -d"="`
				if eval [ \"\${${pkg}_Qs}\" = \"\" ] ; then
					eval ${pkg}_Qs=0
				else
					index=1
					while eval [ ${index} -le \${${pkg}_Qs} ]
					do
						line=`grep "^Q${index}=" ${TMPDIR}/upd_dbconf_${client}/usr/openv/netbackup/bin/${run_script} | cut -f2 -d"="`
						eval ${pkg}_Q${index}=`echo ${line} | cut -f1 -d"-" | sed -e 's/$/"/'`
						eval ${pkg}_Q${index}_var=`echo ${line} | cut -f2 -d"-"`
						eval ${pkg}_Q${index}_rep=`echo ${line} | cut -f3 -d"-"`
						eval ${pkg}_Q${index}_plat=\"`echo ${line} | cut -f4 -d"-" | ${TR} "," " " | sed -e 's/"$//'`\"
						eval ${pkg}_Q${index}_ans=""
						eval ${pkg}_Q${index}_deflt_ans=""
						index=`expr ${index} + 1`
					done
				fi
				rm -rf ${TMPDIR}/upd_dbconf_${client}/usr
			fi
		else
			if [ ! -d ${TMPDIR}/upd_dbconf_${client} ] ; then
				mkdir ${TMPDIR}/upd_dbconf_${client}
			fi
		fi

		#
		#  At this point, the number of questions for this extension
		#  is either 0 or some other number.  If it's 0, we go on
		#  to the next line in the "to do" list.   Otherwise, see if
		#  we have a .dbext.conf file for this client and whether we've
		#  ever tried to get one.  If not, go get it.  Set whether we
		#  were successful in finding one.
		#

		if eval [ \${${pkg}_Qs} -ne 0 ] ; then
			cl=`echo ${client} | ${TR} '\.' '_' | ${TR} '-' '_'`
			if eval [ ! -s ${TMPDIR}/upd_dbconf_${client}/.dbext.conf -a \
				  \"\${${cl}_has_conf}\" = \"\" ] ; then
				${NB_PATH}/bin/bpgp from ${client} ${NB_PATH}/ext/.dbext.conf ${TMPDIR}/upd_dbconf_${client}/.dbext.conf > /dev/null 2>&1
				if [ $? -ne 0 ] ; then
					eval ${cl}_has_conf=no
				else
					eval ${cl}_has_conf=yes
				fi
			fi

			index=1
			while eval [ ${index} -le \${${pkg}_Qs} ]
			do
				asked_question=0
				plat_good=0
				eval ${pkg}_Q${index}_ans=""
				eval rep=\${${pkg}_Q${index}_rep}
				eval plat=\${${pkg}_Q${index}_plat}
				eval var=\${${pkg}_Q${index}_var}

				#
				#  See if we're on a valid platform for this question.
				#  If so and there is a .dbext.conf file for this client,
				#  see if the variable that answers this question is in
				#  there.  If so use that as the answer and set the
				#  default answer to that too.  All answers are put
				#  in a file.
				#

				for pl in ${plat} ; do
					if [ "${pl}" = "ALL" -o "${os}" = "${pl}" -o "${dual}" = "${pl}" ] ; then
						plat_good=1
						break
					fi
				done

				if eval [ \"\${${cl}_has_conf}\" = \"yes\" -a \
					  ${plat_good} -eq 1 ] ; then
					grep "^${var}=" ${TMPDIR}/upd_dbconf_${client}/.dbext.conf 2>/dev/null | cut -f2 -d"=" > ${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans
					if [ -s ${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans ] ; then
						eval ${pkg}_Q${index}_ans=${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans
						eval ${pkg}_Q${index}_dflt_ans=\${${pkg}_Q${index}_ans}
					fi
				fi

				#
				#  If we couldn't find the answer for this question
				#  in the dbext conf file for this client or there
				#  was no dbext conf file AND we're dealing with a
				#  platform where this question should have an answer,
				#  ask for it.  If the rep for this question is R,
				#  we repeat the question.  All answers are put in a
				#  file.  If there is a default answer, we show it to
				#  the user.  The default answer is equal to the value
				#  Set by the previous client for this extension.
				#

				if eval [ \"\${${pkg}_Q${index}_ans}\" = \"\" -a \
					  ${plat_good} -eq 1 ] ; then
					echo ""
					echo "**********"
					echo ""
					echo ""
					echo "-----> Client ${client}"
					echo ""
					use_default=0
					if eval [ \"\${${pkg}_Q${index}_dflt_ans}\" != \"\" ] ; then
						eval echo \"\${${pkg}_Q${index}}\"
						echo ""
						echo "The previous response to this question was:"
						eval cat \${${pkg}_Q${index}_dflt_ans}
						echo ""
						if confirm_yes "Use the previous response?"
						then
							eval ${pkg}_Q${index}_ans=\${${pkg}_Q${index}_dflt_ans}
							use_default=1
						fi
					fi
					if [ ${use_default} -eq 0 ] ; then
						good_list=0
						while [ ${good_list} -ne 1 ] ; do
							done=0
							while [ ${done} -ne 1 ] ; do
								eval get_value \"\${${pkg}_Q${index}}\"
								echo ${gv_ans} >> ${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans
								if [ "${rep}" = "NR" ] ; then
									done=1
								else
									echo ""
									if confirm_no "Do you have additional responses for the previous question?"
									then
										done=1
									fi
								fi
							done
							eval nl=`wc -l ${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans | sed s/' '/''/g | cut -d '/' -f1`
							if [ ${nl} -le 1 ] ; then
								verb="Is"
								adj="this"
								plural=""
							else
								verb="Are"
								adj="these"
								plural="s"
							fi
							echo ""
							echo "You have specified ${adj} value${plural} for the question:"
							echo ""
							cat ${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans
							echo ""
							if confirm_yes "${verb} ${adj} value${plural} correct?"
							then
								good_list=1
							else
								rm -f ${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans
							fi
						done
						eval ${pkg}_Q${index}_ans=${TMPDIR}/upd_dbconf_${client}/${pkg}_Q${index}_ans
					fi

					eval ${pkg}_Q${index}_dflt_ans=\${${pkg}_Q${index}_ans}
					asked_question=1
				fi

				#
				#  If the user was asked a question, update the
				#  dbext conf file for this client and set a
				#  variable to show we modified it.
				#

				if [ ${asked_question} -eq 1 ] ; then
					eval cat \${${pkg}_Q${index}_ans} |
					while read line
					do
						echo "${var}=${line}" >> ${TMPDIR}/upd_dbconf_${client}/.dbext.conf
					done
					eval ${cl}_has_conf=yes
					eval ${cl}_conf_modified=yes
				fi
				index=`expr ${index} + 1`
			done
		fi
		line_no=`expr ${line_no} + 1`
	done

	#
	#  All questions for all clients are done.  If the dbext conf
	#  file was modified for a client, ship it back now so later, when
	#  we ship the rest of the files and execute install_dbext, the
	#  new values are available.
	#

	for client in ${client_list} ; do
		cl=`echo ${client} | ${TR} '\.' '_' | ${TR} '-' '_'`
		eval shipped_conf_${cl}=""
		if eval [ -s ${TMPDIR}/upd_dbconf_${client}/.dbext.conf -a \
			  \"\${${cl}_conf_modified}\" = \"yes\" ] ; then
			${NB_PATH}/bin/bpinst -MISC -Z .dbext.conf -src ${TMPDIR}/upd_dbconf_${client} -dest ${NB_PATH}/ext -host ${client} -d >/dev/null 2>/dev/null
			if [ $? -ne 0 ] ; then
				eval shipped_conf_${cl}="failed"
			fi
		fi
		rm -rf ${TMPDIR}/upd_dbconf_${client}
	done
	echo ""
	echo "**********"
}

#
#  The value of the parameter passed in determines what type
#  of exit we are doing.  Clean up the appropriate files.
#  Used even for an error free run.  If the first parameter
#  is:
#
#  1 - used when you're not root, the parameters aren't
#      valid or version isn't in /usr/openv/netbackup.
#      No files have actually been created yet.
#
#  2 - used if there are no clients to upgrade.
#
#  3 - used if we were ready to do the updates but the user
#      decided to do it later.
#

quit ()
{
	if [ ${1} = 0 ] ; then
		rm -f ${CLIENTS}
		rm -f ${CLIENTS}.count
		rm -f ${CLIENTS}.mis_count
		rm -f ${CLIENTS}.track
		rm -f ${CLIENTS}.final.dbclients
		echo "File ${LOGFILE} contains the update trace."
	elif [ ${1} = 1 ] ; then
		\:
	elif [ ${1} = 2 ] ; then
		rm -f ${CLIENTS}.tmp ${CLIENTS}
		rm -f ${LOGFILE}
	elif [ ${1} = 3 ] ; then
		rm -f ${CLIENTS}.final.dbclients
		echo "File ${CLIENTS} contains the complete list of UNIX clients."
	else
		rm -f ${CLIENTS}.tmp ${CLIENTS}
		rm -f ${CLIENTS}.final.dbclients
	fi
	exit ${1}
}

######################################
###
###  MAIN
###
######################################

LC_CTYPE=C
export LC_CTYPE

#
#  Make sure this is being run from root.
#

ISROOT=`/usr/bin/id | egrep "^uid=0\("`
if [ "${ISROOT}" = "" ] ; then
	echo ""
	echo "$0 must be run while logged in as root."
	echo ""
	quit 1
fi

#
#  This is the list of all possible UNIX database extensions, since we
#  don't push to non-UNIX clients from a UNIX server.  These names
#  should match the text displayed in the menus from cdrom_install_dbext
#  and install_dbext except replace blanks with underscores to keep the
#  name a single "word".  These same names should be listed in the
#  get_pkg_info function.
#

All_Extension_List="SQL-BackTrack
DB2
Informix
Lotus_Notes
Oracle
Oracle_on_UNIX_Advanced_BLI_Agent
SAP
Sybase
Teradata"

ForceInstall="-enforce_version_check"
ClientList=""
Install=FALSE

parse_params_and_setup $*

DATE_TIME=`/bin/date +%m-%d-%H%M`.$$
CLIENTS=${TMPDIR}/NB_DBCLIENT_LIST.${DATE_TIME}

#
#  Create a log file for output of update_dbclients.
#

LOGFILE=${TMPDIR}/update_dbclients.${DATE_TIME}
rm -f ${LOGFILE}
JOBOUTDIR=${TMPDIR}/dbclient_update

determine_clients

#
#  At this point, there must be clients to update.
#
#  Sort on field 5 (the database extension package name, ex. OEBU),
#  rather than the the class hardware type, in hopes of lowering
#  possible conflicts while updating.  Plus then the ask_runscript_questions
#  function flows nicer because questions for each extension happen
#  in a group.  If they decide to upgrade later, we leave the CLIENTS
#  file around.  Create one with only the class_hw, class_os and client
#  fields, in case they turn around and use it as input via -ClientList.
#

rm -f ${CLIENTS} ${CLIENTS}.tmp
if [ "${ISSUN}" = "TRUE" ] ; then
	sort +4 -5 ${CLIENTS}.final.dbclients > ${CLIENTS}.tmp
else
	sort -k 5,5 ${CLIENTS}.final.dbclients > ${CLIENTS}.tmp
fi
mv ${CLIENTS}.tmp ${CLIENTS}.final.dbclients
cat ${CLIENTS}.final.dbclients | cut -f1-3 -d" " | sort -u > ${CLIENTS}

#
#  Find out how many updates we'll be doing and set some defaults so
#  that we don't overload the server.  Use NUM_CL to account for how
#  many clients we are updating.  Use NUM_SHIPS to account for how many
#  updates we are actually doing.  All calculations should be done off
#  NUM_SHIPS.  NUM_CL is based off CLIENTS and not CLIENTS.final.dbclients
#  so that the count matches what we display later (eliminates the fact we
#  can make multiple updates to the same client).
#

NUM_CL=`wc -l ${CLIENTS} | sed s/' '/''/g | cut -d '/' -f1`
NUM_SHIPS=`wc -l ${CLIENTS}.final.dbclients | sed s/' '/''/g | cut -d '/' -f1`

if [ ${NUM_SHIPS} -gt 15 ] ; then
	Default=15
else
	Default=${NUM_SHIPS}
fi
if [ ${NUM_SHIPS} -gt 30 ] ; then
	Max_Num=30
else
	Max_Num=${NUM_SHIPS}
fi

echo ""
echo "**********"
echo ""
if [ ${NUM_CL} -gt 1 ] ; then
	plural="s"
else
	plural=""
fi
if [ ${NUM_SHIPS} -gt 1 ] ; then
	verb="are"
	sh_plural="s"
else
	verb="is"
	sh_plural=""
fi
echo "There ${verb} a total of ${NUM_SHIPS} update${sh_plural} required to"
echo "upgrade ${NUM_CL} database extension client${plural}."
echo ""

if [ ${NUM_SHIPS} -gt 9 ] ; then
	head -9 ${CLIENTS}.final.dbclients | cut -f1-4 -d" " | sort -u
	echo "  ... ... "
	echo ""
	echo "File ${CLIENTS} contains the complete list of clients."
else
	cat ${CLIENTS}.final.dbclients | cut -f1-4 -d" " | sort -u
fi

#
#  If we'll be doing more than one update, find out how many
#  the user wants to do within our previously set defaults.
#  If Install is set (coming from an admin interface), use
#  the default without asking.
#

if [ ${NUM_SHIPS} -ne 1 ] ; then
	if [ "${Install}" = "TRUE" ] ; then
		VALUE=${Default}
	else
		get_number "Enter the number of simultaneous updates you wish to take place." 1 ${Max_Num} ${Default}
	fi
else
	VALUE=1
fi

#
#  Determine approximately how long it will take to do the
#  updates and let the user know.  Give them an opportunity
#  to do it later.  If Install is set (coming from an admin
#  interface), don't ask, just do it.
#

MinTime=`expr \( ${NUM_SHIPS} + 1 \) \/ \( ${VALUE} \* 2 \)`
MaxTime=`expr \( \( 2 \* ${NUM_SHIPS} \) + 1 \) \/ ${VALUE}`
if [ ${MinTime} -lt 1 ] ; then
	MinTime=1
fi
if [ ${MaxTime} -le ${MinTime} ] ; then
	MaxTime=`expr ${MinTime} + 1`
fi
echo "
The upgrade will likely take ${MinTime} to ${MaxTime} minutes."
if [ "${Install}" = "FALSE" ] ; then
	if confirm_yes "Do you want to upgrade the client${plural} now?"
	then
		:
	else
		echo "
You will need to upgrade the client${plural} later with
update_dbclients <dbextension_name> -ClientList <filename>
"
		quit 3
	fi
fi

#
#  At this point, the user has committed to update the clients.
#  Ask the questions from the runscripts, if necessary.  If
#  Install is set (coming from an admin interface), we can't
#  answer any questions, so skip this step.
#

if [ "${Install}" = "FALSE" ] ; then
	ask_runscript_questions
fi

#
#  Start spawning jobs to ship files to the clients based on
#  how many simultaneous updates were specified.
#

echo
MostJobs=${VALUE}
index=0
while [ ${index} -lt ${MostJobs} ]
do
	echo "Available" > ${CLIENTS}.${index}
	index=`expr ${index} + 1`
done

#
#  Create a holding area for the output from the jobs
#  we will be spawning.
#

if [ -d ${JOBOUTDIR} ] ; then
	rm -rf ${JOBOUTDIR}
fi
mkdir ${JOBOUTDIR}
chmod 700 ${JOBOUTDIR}

rm -f ${CLIENTS}.track
echo "0" > ${CLIENTS}.count
echo "0" > ${CLIENTS}.mis_count
/bin/cat ${CLIENTS}.final.dbclients |
while read class_hw class_os client product pkg_name os dual
do
	index=0
	while [ ${index} -lt ${MostJobs} ]
	do
		Available=`cat ${CLIENTS}.${index}`
		if [ "${Available}" = "Available" ] ; then
			echo "In Use" > ${CLIENTS}.${index}
			JobIndex=${index}
			break
		fi
		index=`expr ${index} + 1`
		if [ ${index} -ge ${MostJobs} ] ; then
			sleep 1
			index=0
		fi
	done

	if [ "${hardware}" = "ALL" -o \
	  \( "${hardware}" = "${class_hw}" -a "${os_name}" = "${class_os}" \) ] ; then
		(
		echo "Updating client ${client} with database extension ${product}"
		echo "        -- ${class_hw} hardware running ${class_os}"
		dbclient_update ${pkg_name} ${os} ${client} ${dual} ${JobIndex} & \
		PiD=$!
		wait $PiD
		Ustat=$?
		echo ""
		if [ ${Ustat} -eq 1 ] ; then
			echo "        ${client}: Extension version files match - no update needed."
			NumMatch=`cat ${CLIENTS}.count`
			expr ${NumMatch} + 1 > ${CLIENTS}.count
		elif [ ${Ustat} -eq 2 ] ; then
			echo "        ${client}: update failed, see ${LOGFILE}"
			NumMisMatch=`cat ${CLIENTS}.mis_count`
			expr ${NumMisMatch} + 1 > ${CLIENTS}.mis_count
		elif [ ${Ustat} -eq 3 ] ; then
			echo "        ${client}: update failed, see ${LOGFILE}"
		elif [ ${Ustat} -eq 0 ] ; then
			echo "        ${client}: update complete"
		else
			echo "        ${client}: unknown exit status, see ${LOGFILE}"
		fi
		echo ""
		echo "Available" > ${CLIENTS}.${JobIndex}
		)&
	fi
done

#
#  Check the update job availability files and clean them
#  up as the jobs finish.
#

index=0
while [ ${index} -lt ${MostJobs} ]
do
	if [ -f ${CLIENTS}.${index} ] ; then
		Available=`cat ${CLIENTS}.${index}`
		if [ "${Available}" = "Available" ] ; then
			rm -f ${CLIENTS}.${index}
		else

			#
			#  Hit one that's not done, keep cycling
			#  until it is.
			#

			sleep 1
			continue
		fi
	fi
	index=`expr ${index} + 1`
done

#
#  Now that all jobs are finished, cat to the LOGFILE.
#  This avoids the problem of having more than one
#  spawned job trying to cat to the LOGFILE at the same
#  time and having the LOGFILE be incomplete.
#

for file in `ls ${JOBOUTDIR} | grep -v '\.lock'` ; do
	cat ${JOBOUTDIR}/${file} >> ${LOGFILE}
	rm -f ${JOBOUTDIR}/${file}
done
rm -rf ${JOBOUTDIR}

NumMatch=`cat ${CLIENTS}.count`
NumMisMatch=`cat ${CLIENTS}.mis_count`
nm_pl=""
nmm_pl=""
if [ ${NumMatch} -ne 0 -o ${NumMisMatch} -ne 0 ] ; then
	echo ""
	echo "**********"
	if [ ${NumMatch} -ne 0 ] ; then
		if [ ${NumMatch} -gt 1 ] ; then
			nm_pl="s"
		fi
		echo ""
		echo "${NumMatch} client update${nm_pl} had up-to-date software according to"
		echo "the database extension version file."
	fi
	if [ ${NumMisMatch} -ne 0 ] ; then
		if [ ${NumMisMatch} -gt 1 ] ; then
			nmm_pl="s"
		fi
		echo ""
		echo "${NumMisMatch} client update${nmm_pl} failed because the client version"
		echo "level does not match the database extension version level."
	fi
	pl=""
	adj="this"
	if [ "${nm_pl}" = "s" -o "${nmm_pl}" = "s" ] ; then
		pl="s"
		adj="these"
	fi
	echo ""
	echo "To force the installation of the database extension software"
	echo "for ${adj} client update${pl}, you need to execute:"
	echo ""
	echo "$0 -ForceInstall <dbextension_name> -ClientList <filename>"
	echo ""
	echo "or"
	echo ""
	echo "$0 -ForceInstall <dbextension_name> <hardware_type> <operating_system>"
fi

echo ""

#
#  Clean up the files in ${TMPDIR}.
#

quit 0
