#!/bin/sh
# $Id: update_dbclients.sh,v 1.15.10.1 2004/01/15 16:44:31 $
#***************************************************************************
#* $VRTScprght: Copyright 1993 - 2004 VERITAS Software Corporation, All Rights Reserved $ *
#***************************************************************************

#
#  Places to possibly update when adding new products and/or platforms:
#
#  function get_pkg_info
#  function get_DB_name
#  function parse_params_and_setup
#  variable All_Extension_List in MAIN
#

USAGE ()
{
	${ECHO} ""
	${ECHO} "USAGE:"
	${ECHO} "   $0 [-ForceInstall] <dbagent_name> -ClientList <filename>"
	${ECHO} "or"
	${ECHO} "   $0 [-ForceInstall] <dbagent_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
}

# INSERT fn.set_echo_var
#---------- set_echo_var -- $Revision: 1.2 $ ------------
#
#		This function is a case statement sets
#		the ECHO variable
#		with the appropriate path & flags.

#Define Echo to allow escape characters
case "`uname -s`" in
	Linux*)
		unset POSIXLY_CORRECT
		ECHO="/bin/echo -e"
		;;
	SunOS*)
		ECHO="/usr/bin/echo"
		;;
	*)
		ECHO="echo"
		;;
esac

# INSERT fn.prompt
#---------- Prompt () -- $Revision: 1.1 $ ------------
#	Prompt () - determines the flags to use to print
#			a line without a newline char
#
# 	*** This function uses the set_echo_var function -${ECHO} to 
#		define the Prompt function.
#
#	calling signature - Prompt "some string"
#	return value - none

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


# INSERT fn.confirm
#---------- confirm () -- $Revision: 1.4 $ ------------
#
#	confirm () - Takes three parameters:
#		     1: "y" or "n" to be displayed as the default
#		     2:  a prompt string to be displayed to the user
#		     3:  help text string (optional)
#
#			It is expected that the Trace_File variable
#			has been set to a file or /dev/null.
#			
#			*** This function uses the Prompt function
#				which uses the set_echo_var function - ${ECHO}.
#
#	calling signature:	confirm y "some string" [ "help text" ] 
#					or
#				confirm n "some string" [ "help text" ]
#
#	returns:		0 for Yes, 1 for No		

confirm ()
{
	help=${3}
	Q=""
	if [ -n "${help}" ]; then 
		Q=",?"
	fi
	Prompt "${2} [y,n${Q}] (${1}) "
	valid=0
	until [ ${valid} -ne 0 ]
	do
		#read ans and if it is empty, initialize it with the default
		read ans
		: ${ans:=${1}}


		#write to the tracefile

		${ECHO} ${ans} >> ${Trace_File}


		case "${ans}" in
			Y*|y*)
				valid=1
				return 0 ;;
			N*|n*)
				valid=1
				return 1 ;;
			?*)
				${ECHO} ""
				${ECHO} ${help}
				${ECHO} ""
				Prompt "${2} [y,n${Q}] (${1}) "
				;;
			*)
				${ECHO} ""
				Prompt "${ans} is invalid input.  Enter [y,n${Q}] (${1}) " 
				;;
		esac
	done
}

# INSERT fn.get_number
#---------- get_number () -- $Revision: 1.2 $ ------------
#
#	get_number () - Takes four parameters, three are mandatory.
#			Parameter one:   string displayed to the user
#				  two:   minimum value
#				  three: maximum value
#				  four (optional):  default
#
#			The input must be a number inbetween the min and max.
#			The number is placed in the VALUE variable.
#			*** This function uses the Prompt function and the prompt
#				function uses the set_echo_var function - ${ECHO}.
#			*** It is expected that the Trace_File variable has been 
#				set to a file or /dev/null.
#
#	calling signature:
#			get_number "some string" min_value max_value -d default_value
#				or
#			get_number "some string" min_value max_value

get_number ()
{
	#grab the default param if given
	if [ $# -gt 4 ] ; then
		if [ "${4}" = "-d" ] ; then
			defaultNum=${5}
		else
			defaultNum="none"
		fi		
	else
		defaultNum="none"
	fi

	valid=0
	until [ ${valid} -ne 0 ]
	do
		if [ ${defaultNum} = "none" ] ; then
			Prompt "${1} [${2} - ${3}]: "
		else

			Prompt "${1} [${2} - ${3}] (default: ${defaultNum}): "
		fi

		read VALUE
		#read ans and if it is empty, initialize it with the default
		VALUE=${VALUE:=${defaultNum}}


		if [ ${VALUE} = "none" ] ; then
			${ECHO} "" >> ${Trace_File} #this way "none" won't be written
		else
			${ECHO} ${VALUE} >> ${Trace_File}
		fi


		#check to see if input is really a number
		echo ${VALUE} | egrep -e \(\^[0-9]\+\$\) > /dev/null

		#if a number & in range
		if [ $? -eq 0 ] ; then
			if [ ${VALUE} -ge ${2} -a ${VALUE} -le ${3} ] ; then
				return
			fi
		fi 

		if [ "${VALUE}" != "none" ] ; then #this handles so that "none" won't be printed to the screen	
			${ECHO} "ERROR: ${VALUE} is invalid input. Please try again."
			${ECHO} ""
		fi
	done
}

# INSERT fn.set_tar_options
#---------- set_tar_options -- $Revision: 1.9 $ ------------
#
#		This function takes one parameter, a
#		string which is the MACHINE or platform type.
#		It sets two variables: TAR_C_OPTIONS and
#			TAR_X_OPTIONS
#
#		NOTE: The v flag is not handled in this function. If you want 
#		to add the v flag, you need to set it in your scripts.
#		For example, place the v before the variable like so, v${TAR_X_OPTIONS}.
#		The v must go before the variable because in the ALPHA|SGI
#		case statement you can't put the v after the 20 blocking factor.
#
#		calling signature:
#			set_tar_options "MachineType"

set_tar_options ()
{
	case "${1}" in
		ALPHA* | alpha* | OSF1* | SGI* | sgi6* | IRIX* )

			TAR_C_OPTIONS="cf" #same as default

			TAR_X_OPTIONS="xbpf 20"
			#x - extract
			#b - use blocking factor
			#p - use original tarred file protection properties
			#f - use given filename
			#20 - portable blocking factor
			;;
		HP* | hp* )

			TAR_C_OPTIONS="cOfA"
			#c - create new archive
			#O - write a pre-POSIX format archive
			#f - use given filename
			#A - suppress warning messages for ACL entries

			TAR_X_OPTIONS="xpfA"
			#x - extract
			#p - use original tarred file protection properties
			#f - use given filename
			#A - suppress warning messages for ACL entries	
			;;

		* )
			TAR_C_OPTIONS="cf"
			#c - create new archive
			#f - use given filename

			TAR_X_OPTIONS="xpf"
			#x - extract
			#p - use original tarred file protection propeties
			#f - use given filename
			;;
	esac
}



#
#  This function provides information about the database agent
#  provided in the first parameter.  Notice that the agent "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 agent directory
#  and tar.Z file names), the database agent class type name (spit
#  out by bppllist -U) and the name of the runscript for the agent,
#  if there is one.
#

get_pkg_info ()
{
	pkg_name=""
	run_script=""
	case "${1}" in
		DB2)
			pkg_name="${1}"
			class_name="DB2"
			if [ ${DB2_level} -lt 50 ] ; then
				run_script="install_db2"
			fi
			;;
		Informix)
			pkg_name="INFORMIX"
			class_name="Informix-On-BAR"
			if [ ${INFORMIX_level} -lt 50 ] ; then
				run_script="install_infxbsa"
			fi
			;;
		Lotus_Notes)
			pkg_name="LOTUS"
			class_name="Lotus-Notes"
			;;
		Oracle)
			pkg_name="OEBU"
			class_name="Oracle"
			if [ ${OEBU_level} -lt 50 ] ; then
				run_script="install_oracle"
			fi
			;;
		SAP)
			pkg_name="${1}"
			class_name="SAP"
			;;
		Sybase)
			pkg_name="SYBASE"
			class_name="Sybase"
			if [ ${SYBASE_level} -lt 50 ] ; then
				run_script="install_sybackup"
			fi
			;;
		*)
			${ECHO} ""
			${ECHO} "The ${1} agent 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 agent equivalents (fields in the .tar.Z file names).  For
#  example, DataGeneral and UNIX map to dgux.  The db_name2 case is for
#  some platforms where 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, Solaris7 and
#  Solaris8 are linked in /usr/openv/netbackup/client and here they both map to
#  solaris7.  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=""

	case "${NB_hw}" in
		ALPHA)
			if [ "${NB_os}" = "OSF1_V5" ] ; then
				db_name="alpha_5"
			fi
			;;
		HP9000-700 | HP9000-800)
			if [ "${NB_os}" = "HP-UX11.00" ] ; then
				db_name="hp11.00"
			elif [ "${NB_os}" = "HP-UX11.11" ] ; then
				db_name="hp11.11"
				db_name2="hp11.00"
			fi
			;;
		Linux | INTEL)
			if [ "${NB_os}" = "RedHat2.4" ] ; then
				db_name="linux2.4"
			fi
			;;
		RS6000)
			if [ "${NB_os}" = "AIX4.3.3" ] ; then
				db_name="rs6000_433"
			elif [ "${NB_os}" = "AIX5" ] ; then
				db_name="rs6000_51"
			fi
			;;
		C910_920 | SGI)
			if [ "${NB_os}" = "IRIX65" ] ; then
				db_name="sgi65"
			fi
			;;
		Solaris | Sun4)
			if [ "${NB_os}" = "Solaris7" -o "${NB_os}" = "Solaris8" -o \
			     "${NB_os}" = "Solaris9" ] ; then
				db_name="solaris7"
			fi
			;;
	esac
}

#
#  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
}


#
#  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

	set_tar_options ${HARDWARE}

	case "${HARDWARE}" in
		ALPHA | RS6000 | SOLARIS | SGI)
			;;	
		HP*)
			TR=/usr/bin/tr
			;;
		LINUX*)
			TR=/usr/bin/tr
			;;
		SUN4)
			ISSUN=TRUE
			;;
		*)
			${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 agent 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 agent 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 agents to be chosen and expect
	#  -ClientList <filename>.
	#

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

		#
		#  Verify that if a particular agent 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 agent 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 agent 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 agent package type name
#  (for example, OEBU).  The second is the os type name (for example,
#  solaris7).  Both of these parameters are fields in the tar.Z file
#  names that make up the database agent 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 agent 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 agent 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/agent level mismatch.
#  Variable ret contains the final return value for this function.
#

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

	Z_name="${PROD}.${OS}.tar.Z"
	i_locked=0
	am_i_master=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 agent files to
		#  itself, it only needs to run the install_dbext script.
		#  Use bpclntcmd so we aren't fooled by virtual names.
		#

		${NB_PATH}/bin/bpclntcmd -is_local_host ${CLIENT} > /dev/null 2>&1
		if [ $? -ne 0 ] ; 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
						#
						#  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
		else
			am_i_master=1
		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 [ ${am_i_master} -ne 1 ] ; 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, bpplclients 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 agent software out to clients not yet configured into
#  any agent class (initial type install).  For an upgrade, where clients
#  have been configured into agent 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 agent type.  Once a list of clients is
#  available, we next see if the database agent software for that type
#  is loaded.  If so, information is added to the ${CLIENTS}.final.dbclients
#  file.  This is the file that determines what agent software gets pushed
#  to what client.  Every specified database agent 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/bpplclients -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/bpplclients -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 agent 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/bpplclients -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 agent software is present.  If no tar.Z files
		#  for the current agent, 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 agent os name
					#  matches the client's hardware and
					#  operating system.
					#

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

						#
						#  Oracle is the only agent that is built
						#  specifically for HP11i.  Had to use db_name2
						#  set to hp11.00 in order for the other agents
						#  to continue to ship hp11.00 to HP11i.  Whack
						#  db_name2 for Oracle if the client OS is HP11i
						#  so that Oracle only ships the HP11i tar file,
						#  not both HP client versions.
						#

						if [ "${product}" = "Oracle" -a "${class_os}" = "HP-UX11.11" ] ; then
							db_name2=""
						fi

						#
						#  If the agent 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}" >> ${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 agent software"
		${ECHO} "         available for that type of client"
		${ECHO} "  3.  the matching database agent software was not"
		${ECHO} "         loaded on the server"
		if [ "${ClientList}" = "" ] ; then
			${ECHO} "  4.  they did not belong to any of the specified database"
			${ECHO} "         agent 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 agent combo we plan to ship, see if there
#  is a runscript for that agent.  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 agent.  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 agent'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 agent 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
	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" "`

		#
		#  If we haven't figured out the number of questions for
		#  this agent 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.
		#

		#
		#  Are we dealing with a 5.0 or better package for db2, informix,
		#  or sybase?  If so, set a flag so we won't run the runscript.
		#  Always reset *_Qs so we reassess whether to ask a runscript
		#  question every time.  This is in case we're shipping an
		#  old version to one platform and a new one to another.
		#  Oracle is here because we changed the number of questions.
		#

		if [ "${product}" = "DB2" -o "${product}" = "Informix" -o \
		     "${product}" = "Oracle" -o "${product}" = "Sybase" ] ; then
			tmp_ver_level=`cat ${NB_PATH}/dbext/${pkg}.${os}.version | cut -f2 -d" "`
			ext_level=`expr ${tmp_ver_level} : '\([0-9.]*\)'`
			majmin_level=`${ECHO} ${ext_level} | cut -f1,2 -d"." | sed -e 's/\.//'`
			eval ${pkg}_level=${majmin_level}
			eval ${pkg}_Qs=""
		fi

		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 v${TAR_X_OPTIONS} - 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 agent
		#  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}" ] ; 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 agent.
				#

				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 y "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 n "Do you have additional responses for the previous question?"
									then
										:
									else
										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 y "${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 ####################

TMPDIR=${TMPDIR:-/tmp}
NB_PATH=/usr/openv/netbackup
Trace_File=/dev/null #used in fn.* functions

LC_CTYPE=C
export LC_CTYPE

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

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

# Make sure we're on an active node or a standalone machine
# before we allow the user to push to the clients.

${NB_PATH}/bin/cluster/cluster_active > /dev/null 2>&1
if [ $? -ne 0 ] ; then
	${ECHO} "
This machine appears to be an inactive member of a cluster.  Pushing
software to clients must occur on the active member of the cluster.
"
	exit 0
fi

#
#  This is the list of all possible UNIX database agents, 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="DB2
Informix
Lotus_Notes
Oracle
SAP
Sybase"

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

#  Used later in ask_runscript_questions.

DB2_level=0
INFORMIX_level=0
OEBU_level=0
SYBASE_level=0

parse_params_and_setup $*

${NB_PATH}/bin/admincmd/bpauthorize -test_admin >/dev/null
status=$?
if [ ${status} -ne 0 ] ; then
	${ECHO} ""
	${ECHO} "You do not have the proper authorization to perform"
	${ECHO} "this task.  Please resolve and try again."
	exit ${status}
fi

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 agent 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 agent 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 agent 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} -d ${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 y "Do you want to upgrade the client${plural} now?"
	then
		:
	else
		${ECHO} "
You will need to upgrade the client${plural} later with
update_dbclients <dbagent_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
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 agent ${product}"
		${ECHO} "        -- ${class_hw} hardware running ${class_os}"
		dbclient_update ${pkg_name} ${os} ${client} ${JobIndex} & \
		PiD=$!
		wait $PiD
		Ustat=$?
		${ECHO} ""
		if [ ${Ustat} -eq 1 ] ; then
			${ECHO} "        ${client}: Add-On Product 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 agent 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 agent 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 agent software"
	${ECHO} "for ${adj} client update${pl}, you need to execute:"
	${ECHO} ""
	${ECHO} "$0 -ForceInstall <dbagent_name> -ClientList <filename>"
	${ECHO} ""
	${ECHO} "or"
	${ECHO} ""
	${ECHO} "$0 -ForceInstall <dbagent_name> <hardware_type> <operating_system>"
fi

${ECHO} ""

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

quit 0
