#! /bin/bash
#############################################################################
#                                                                           #
#           DO NOT CHANGE THIS FILE - IT WILL BE OVERWRITTEN !!!            #
#                                                                           #
#############################################################################
# NAME
#	qc_shell_std.LIB	-	Library of shell functions for QAD Cloud scripts
#
# DESCRIPTION
#	The following functions will be provided by qc_shell_std.LIB
#		logMsg			- Will write standardize messages to ${qcLOGFILE}
#		dispHelp		- Display help information based on the header
#		getEnv			- Will set DLC, PATH, qcDBLIST, qcLOC, qcENV, 
#							qcLOGFILE and qcPORT
#		errorHandler	- Verify previous command and exit based on ${?}
#		checkUser		- Verify if user is root and set qcCMD
#		notifyZabbbix	- Sent success/failure to zabbix
#		get_psc_version	- Will return the Progress version in the
#                         format: <ver>:<subver>:<SP>:<HF> or -1 if failed
#		getDC			- Provide the DataCenter for server based on
#						  IP-range or -1 if nothing found
#		checkLK			- Will return 0 if the process is not running
#
#	The following global variables will be set
#		qcUTILS			- Locations of the QAD Cloud utilities
#		qcDC			- The name of the DataCenter
#		PROSQL_LOCKWAIT_TIMEOUT
#
# SYNTAX
#	. lib/qc_shell_std.LIB
#
# AUTHOR
#	Written by John Brink (j6b@qad.com)
#
# REVISION HISTORY
#	1.0	02/19/2016 j6b	- Initial version
#	1.1	03/01/2016 j6b	- Add global qcUTILS variable
#	1.2	05/25/2016 j6b	- Add qcADMINPORT for multiple env on 1 server
#	1.3	08/02/2016 j6b	- Use dirname for multiple environments 
#	1.4	09/06/2016 j6b	- Add function YabEnabled
#	1.5	02/08/2017 j6b	- Add function notifyZabbix
#	1.6	09/08/2017 j6b  - Add qcZABBIX variable and modify notifyZabbix
#	1.7	10/17/2017 j6b	- Change permissions if logs directory created
#	1.8	11/14/2018 j6b	- Add function BoomiInstalled
#	1.9	06/03/2020 j6b	- CLDT-270 - Add get_psc_version
#	2.0	12/16/2020 j6b	- CLDT-284 - Add getDC function
# -------------------------------------------------------------------------
# vim:ts=4

##------------------##
## Define Functions ##
##------------------##

# Functions and Procedures
logMsg() {

	########################################################################
	# Usage (standard)
	#	logMsg <loglevel> <message>
	#
	# Usage (for loglevel "WARNING" and "CRITICAL")
	#	logMsg <loglevel> {{--cat|-c} <category>} {{--obj|-o} <objname>} \
	#	   {{--trigger|-t} <trigger>} {{--execpath|-e} <execution path>} \
	#	   {{--act|-a} <action>} {{--res|-r} <result>} [{--msg|-m} <msg>]
	#
	# Description
	#	Initially this function was used to write some logging, but with
	#	the addition of Cassandra it has been extended for the loglevels
	#	"WARNING" and "CRITICAL".
	#	For these loglevels it's necessary that the main script will be
	#	running as root AND the PMLOGFILE variable is set to
	#	/var/log/sh-tools.log
	#
	# Author
	#	Written by John Brink (j6b@qad.com)
	#
	# Revision history
	#	1.0	02/19/2016 j6b - Initial version
	#	1.1	02/12/2020 j6b - Support for Cassandra logging
	#	1.2	01/20/2021 j6b - CLDT-370 - Update number of fields for DataLake
	# ----------------------------------------------------------------------

	# Setting local variables
	typeset _time="$(date "+%D-%H:%M:%S.%3N")"
	typeset _pmtime="$(date "+%Y-%m-%dT%H:%M:%S.%3N")"

	PMLOGFILE=${PMLOGFILE:-${qcLOGFILE}}

	local _prog=$(basename ${0})
	local _cat=""
	local _obj=""
	local _trigger=""
	local _execpath=""
	local _action=""
	local _result=""
	local _msg=""

	# Main
	if [[ ${#} -gt 2 ]] && ( [[ "$1" = "CRITICAL" ]] || [[ "$1" = "WARNING" ]] ) ; then
		# Reading input parameters
		_severity=${1}
		shift
		while [[ ${#} -gt 0 ]] ; do
			case "${1}" in
			  "--cat"|"-c")			_cat="${2}"
									shift 2;;
			  "--obj"|"-o")			_obj="${2}"
									shift 2;;
			  "--trigger"|"-t")		_trigger="${2}"
									shift 2;;
			  "--execpath"|"-e")	_execpath="${2}"
									shift 2;;
			  "--action"|"-a")		_action="${2}"
									shift 2;;
			  "--result"|"-r")		_result="${2}"
									shift 2;;
			  "--msg"|"-m")			_msg="${2}"
									shift 2;;
			  *)					_msg="${@}"
									break;;
			esac
		done
		echo "${_pmtime}|${_severity}|${_prog}|${_cat}|${_obj}|${_trigger}|${_execpath}|${_action}|${_result}|${_msg}" >> ${PMLOGFILE}
	elif [[ ${#} = 2 ]] ; then
		echo "${_prog}: ${_time} $1 $2" >> ${qcLOGFILE}
		tty -s && echo "${_prog}: ${_time} $1 $2"
	else
		echo "${_prog}: ${_time} $@" >> ${qcLOGFILE}
		tty -s && echo "${_prog}: ${_time} $@"
	fi

}

dispHelp() {

	# Display help information based on the header of the script

	sed -n '/^# NAME$/,/^#$/ {
		/^#$/d
		/^# NAME$/d
		s/.*-[ \t]\(.*$\)/\1/p
		}' ${0}

	sed -n '/^# SYNTAX$/,/^#$/ {
		/^#$/d
		s/.*SYNTAX$/Usage:/
		s/^#[ \t]/  /
		p
		}' ${0}
}

errorHandler() {

	# Standard error handling

	_retcode=${1}
	_retcrit=${2}
	_retmesg=${3}

	if [[ ! ${_retcode} == 0 ]]
	then
		logMsg "${_retcrit}" "${_retmesg} - ERRNO: ${_retcode}"
		echo "   Use ${qcLOGFILE} for more info"
		exit ${_retcode}
	fi
}

getEnv() {

	########################################################################
	# Usage
	#	getEnv [env]
	#
	# Description
	#	Will set a number of variables based on the configuration file
	#	in /usr/local/etc or based on the parameter that is supplied
	#
	# Result
	#	The following parameters will be set:
	#	  DLC		- The installation directory of PROGRESS
	#	  qcDBLIST	- List of database with complete pathname
	#	  qcLOC		- Provides the QAD installation root directory
	#	  qcENV		- Provides the QAD environment name (like prod, test, dr)
	#	  qcPORT	- Port where the Progress AdminServer is running
	#	  qcLOGFILE	- Name of logfile (${_logdir}/$(basename ${0} .sh).log
	#	  qcCUST	- Customized program that needs to be executed
	#	  qcADMINPORT - Additional port for AdminServer if multiple instances are running on 1 server
	#	  qcZABBIX	- Zabbix server IP address that is used for this server
	#
	# Author
	#	Written by John Brink (j6b@qad.com)
	#
	# Revision history
	#	1.0	02/19/2016 j6b - Initial version
	#	1.1	05/25/2016 j6b - Add qcADMINPORT for multiple env on 1 server
	#	1.2	08/02/2016 j6b - Use dirname for multiple environments 
	#	1.3	09/08/2017 j6b - Add qcZABBIX because the use of multiple Zabbix servers
	# ----------------------------------------------------------------------

	# Variable definitions
	_confdir=/usr/local/etc		# Location of the configuration files
	_me=$(basename ${0})		# Name of calling program
	A_cfg=( )					# Array of configuration files

	if [[ -d ${_confdir} ]]
	then
		A_cfg=(${_confdir}/progress*.cfg)
		if [[ ${#A_cfg[*]} == 1 ]]
		then
			qcLOC=$(grep "^enviro=" ${A_cfg[0]}| cut -d'=' -f2)
			qcENV=$(grep "^qadenv=" ${A_cfg[0]}| cut -d'=' -f2)
			DLC=$(grep "^DLC" ${A_cfg[0]} | cut -d'=' -f2)
			qcDBLIST=$(grep "^databaselist" ${A_cfg[0]} | cut -d'=' -f2)
			qcPORT=$(grep "^adminServerPort" ${A_cfg[0]} | cut -d'=' -f2)
			qcZABBIX=$(grep "^server" ${A_cfg[0]} | cut -d'=' -f2)
		else
			# Multiple or no progress*.cfg file(s) are found
			# Returns the QAD environment the script is ran in
			qcLOC=$(echo $(cd $(dirname ${0}) ; echo $(pwd) | awk -F "/" '{ print $2 }'))

			if [[ ! -d /${qcLOC}/apps/mfgpro ]]
			then
				case ${#} in
				  1)	qcLOC=${1}
						qcENV=${qcLOC}
						DLC=/${qcENV}/apps/dlc
						qcDBLIST=/${qcENV}/db/${qcENV}db/*.db 
						case ${qcENV} in
						  "prod")	qcPORT=18000;;
						  "qond")	qcPORT=18000;;
						  "test")	qcPORT=17000;;
						  "train")	qcPORT=16000;;
						  "devl")	qcPORT=15000;;
						  "migr")	qcPORT=14000;;
						  "base")	qcPORT=13000;;
						  *)		qcPORT=20931;;
						esac
				  		;;
				  *) 	echo "ERROR - Invalid environment: /${qcLOC}/apps/mfgpro doesn't exist" 
				  		exit 99
						;;
				esac
			elif [[ -f ${_confdir}/progress_agent-${qcLOC}.cfg ]]; then
				# Configuration file found
				_cfgfile=${_confdir}/progress_agent-${qcLOC}.cfg
				qcLOC=$(grep "^enviro=" ${_cfgfile}| cut -d'=' -f2)
				qcENV=$(grep "^qadenv=" ${_cfgfile}| cut -d'=' -f2)
				DLC=$(grep "^DLC" ${_cfgfile} | cut -d'=' -f2)
				qcDBLIST=$(grep "^databaselist" ${_cfgfile} | cut -d'=' -f2)
				qcPORT=$(grep "^adminServerPort" ${_cfgfile} | cut -d'=' -f2)
				qcZABBIX=$(grep "^server" ${_cfgfile} | cut -d'=' -f2)
				else
					# No configuration file found
					qcENV=${qcLOC}
					DLC=/${qcENV}/apps/dlc
					qcDBLIST=/${qcENV}/db/${qcENV}db/*.db 
					case ${qcENV} in
					  "prod")	qcPORT=18000;;
					  "qond")	qcPORT=18000;;
					  "test")	qcPORT=17000;;
					  "train")	qcPORT=16000;;
					  "devl")	qcPORT=15000;;
					  "migr")	qcPORT=14000;;
					  "base")	qcPORT=13000;;
					  *)		qcPORT=20931;;
					esac
			fi
		fi
	else
		echo "ERROR - ${_confdir} could not be found"
		exit 99
	fi

	# Set logging variables
	_logdir=/${qcLOC}/apps/mfgpro/scripts/logs
	if [[ ! -d ${_logdir} ]] ; then
		mkdir -p ${_logdir}
		chown mfg: ${_logdir}
		chmod g+w ${_logdir}
	fi

	qcLOGFILE=${_logdir}/${_me%.sh}.log

	# Set customized program
	qcCUST=/${qcLOC}/apps/mfgpro/scripts/xx${_me}

	# Set -adminport value to accomodate multiple AdminServers
	qcADMINPORT=$((qcPORT + 1))

	PATH=${DLC}:${DLC}/bin:${PATH}
	export qcENV qcLOC DLC qcDBLIST qcPORT PATH qcLOGFILE qcCUST qcADMINPORT qcZABBIX
}

function checkUser() {

	# checkUser will verify the user who's running the script
	# if the user is 'root' is set the variable qcCMD to execute a sudo

	_user=$(id | cut -d'(' -f2 | cut -d')' -f1)

	if [[ ${_user} == "root" ]] || [[ ${_user} == "mfg" ]]
	then
		if [[ ${_user} == "root" ]]
		then
			# Only switch to mfg if user exists
			grep "mfg" /etc/passwd > /dev/null 2>&1
			[[ ${?} == 0 ]] && qcCMD="/usr/bin/sudo -u mfg"
		else
			qcCMD=""
		fi
		return 0
	else
		return 1
	fi

	export qcCMD
}

function YabEnabled() {

	########################################################################
	# Usage
	#	YabEnabled
	#
	# Description
	#	Verify if YAB is installed on this server.
	#
	# Result
	#	0 - Yab is used
	#	1 - Yab is not installed
	#
	# Author
	#	Written by John Brink (j6b@qad.com)
	#
	# Revision history
	#	1.0	09/06/2016 j6b - Initial version
	#	1.1	04/19/2021 j6b - Works fine for user mfg as default permissions
	#						 for yab is 744 and owned by mfg, but will fail
	#						 for any other user.	
	# ----------------------------------------------------------------------

	# v1.1 - Don't look at executable permissions
	#if [[ -x /usr/local/yab/yab ]] ; then
	# End - v1.1
	if [[ -f /usr/local/yab/yab ]] ; then
	        return 0
	else
	        return 1
	fi
}

function BoomiInstalled() {

	########################################################################
	# Usage
	#       BoomiInstalled
	#
	# Description
	#       Verify if Boomi is installed on this server.
	#
	# Result
	#       0 - Boomi is used
	#       1 - Boomi is not installed
	#
	# Author
	#       Written by John Brink (j6b@qad.com)
	#
	# Revision history
	#       1.0     11/14/2018 j6b - Initial version
	# ----------------------------------------------------------------------

	RETVAL=1
	for i in base devl devel ldat migr prod qond test train; do
		for j in /${i}/apps/boomi_qx*; do
			if [[ -x ${j}/bin/atom ]]; then
				RETVAL=0
				break
			fi
		done
		[[ ${RETVAL} = 0 ]] && break
	done
	return ${RETVAL}
}

notifyZabbix() {

	########################################################################
	# Usage
	#       notifyZabbix hostname key status
	#			hostname : name of the host the application is running
	#			key : valid key for zabbix / progress_agent
	#			status : 0 for success, 1 for failure
	#
	# Description
	#       Notify zabbix for success or failure of script
	#
	# Author
	#       Written by John Brink (j6b@qad.com)
	#
	# Revision history
	#       1.0     02/07/2017 j6b - Initial version
	#		1.1		09/08/2017 j6b - Use qcZABBIX if set
	# ----------------------------------------------------------------------

	# Local variables
	local _zabbixprod=216.58.134.132
	local _zabbixnonprod=216.58.134.142

	# Read variables
	if [[ ! ${#} = 3 ]]; then
		logMsg ERR "notifyZabbix - Invalid number of parameters"
	else
		local _hostname=${1}
		local _key=${2}
		local _status=${3}

		# Identify if a cluster and change host to it
		_clustername=`/usr/sbin/clustat 2>/dev/null | grep service | awk '{print $1}' | awk -F: '{print $2}'`
	
		if [[ "${_clustername}" != "" ]]; then
	    	_hostname=${_clustername}
		fi
	
		# Notifify Zabbix about status
		if [[ ! -z ${qcZABBIX} ]] ; then
			/usr/local/bin/zabbix_sender -z ${qcZABBIX} --host ${_hostname} --key ${_key} --value ${_status} 1>/dev/null
		else
			/usr/local/bin/zabbix_sender -z ${_zabbixprod} --host ${_hostname} --key ${_key} --value ${_status} 1>/dev/null
			/usr/local/bin/zabbix_sender -z ${_zabbixnonprod} --host ${_hostname} --key ${_key} --value ${_status} 1>/dev/null
		fi
	fi
# keyname:  oe.db.auditarchive.<qcLOC>.status
# 0 - ok
# 2 - critical
}

get_psc_version() {

	# NAME
	# 	get_psc_version	-	will return the Progress version in the
	#				format: <ver>:<subver>:<SP>:<HF> or -1 if failed
	#
	# Usage:
	#	get_psc_version <progress version>
	#
	# Examples:
	#		get_psc_version('10.2B0881') returns 10:2B:8:81
	#		get_psc_version('11.7.5') returns 11:7:5:0
	#		get_psc_version('12.1') returns 12:1:0:0
	#		get_psc_version('11.6.4.019') returns 11:6:4:19
	#
	# Revision History
	# -----------------------------------------------------------------
	#	1.0 01/23/2020 j6b - Initial version
	#	1.1	04/13/2021 j6b - Support older Progress versions and 
	#						 better error handling
	# -----------------------------------------------------------------

	if [[ ! ${#} = 1 ]] || [[ ! "${1}" =~ "." ]] ; then
		echo -1
	else
		_pversion=${1}

		if [[ ${_pversion:0:1} -ge 6 ]] ; then
			IFS="\." read -a _a <<< "${_pversion}"
			case ${#_a[1]} in
			  4)	# SP installed
			  		echo "${_a[0]}:${_a[1]:0:2}:$((10#${_a[1]:2:2})):0"
					;;
			  *)	# base installation
			  		echo "${_a[0]}:${_a[1]:0:2}:0:0"
					;;
			esac
		elif [[ ${_pversion:0:2} -gt 10 ]] ; then
			IFS="\." read -a _a <<< "${_pversion}"
			case ${#_a[@]} in
			  4)	# HF installed
					echo "${_a[0]}:${_a[1]}:${_a[2]}:$((10#${_a[3]}))"
					;;
			  3)	# SP installed
					echo "${_a[0]}:${_a[1]}:${_a[2]}:0"
					;;
			  *)	# base installation
					echo "${_a[0]}:${_a[1]}:0:0"
					;;
			esac
		elif [[ ${_pversion:0:2} = 10 ]] ; then
			IFS="\." read -a _a <<< "${_pversion}"
			case ${#_a[1]} in
			  6)	# HF installed
			  		echo "${_a[0]}:${_a[1]:0:2}:$((10#${_a[1]:2:2})):$((10#${_a[1]:4}))"
					;;
			  4)	# SP installed
			  		echo "${_a[0]}:${_a[1]:0:2}:$((10#${_a[1]:2:2})):0"
					;;
			  *)	# base installation
			  		echo "${_a[0]}:${_a[1]:0:2}:0:0"
					;;
			esac
		else
			echo -1
		fi
	fi
}

getDC() {

	# NAME
	# 	getDC	-	will return DataCenter based on IP
	#				and information in /usr/remote/odutils/OD-datacenters
	#
	# USAGE:
	#	getDC
	#
	# RETURN:
	#	<name> of DC or "-1" if failed
	#
	# Revision History
	# -----------------------------------------------------------------
	#	1.0 12/16/2020 j6b - Initial version
	# -----------------------------------------------------------------

	_ODFILE=/usr/remote/odutils/OD-datacenters

	if [[ -f ${_ODFILE} ]] ; then

		# Get QAD IP
		_QADIP=`/sbin/ifconfig | grep inet | grep -i mask | awk '{print $2}' | sed 's/addr://' | grep -ve "^127\.0\.0\.1" -e "^172\.18\." -e "^10\." -e "^119\." -e "^168\.1" | head -1`
		
		# Get data center
		_FINDIP=`echo ${_QADIP} | awk -F. '{print $1"."$2"."$3}'`
		_IPNUM=`echo ${_QADIP} | awk -F. '{print $4}'`
		for _ENTRY in $(grep ^${_FINDIP}\. ${_ODFILE} | awk '{print $1","$2","$3}') ; do
			_IPRANGE=`echo ${_ENTRY} | sed "s/,/ /g"`
			_FROMIP=`echo ${_IPRANGE} | awk '{print $1}'`
			_THRUIP=`echo ${_IPRANGE} | awk '{print $2}'`
			_DCTMP=`echo ${_IPRANGE} | awk '{print $3}'`
		
			_FROMNUM=`echo ${_FROMIP} | awk -F. '{print $4}'`
			_THRUNUM=`echo ${_THRUIP} | awk -F. '{print $4}'`
			let "_CHECK1=_IPNUM - _FROMNUM"
			let "_CHECK2=_THRUNUM - _IPNUM"
			if [ ${_CHECK1} -ge 0 ] && [ ${_CHECK2} -ge 0 ] ; then
				_DC=${_DCTMP}
				break
			fi
		done
	fi
	
	if [[ ! -z ${_DC} ]] ; then
		qcDC=$(echo ${_DC} | cut -d'-' -f2)
	else
		qcDC="-1"
	fi
}

checkLK() {

	# NAME
	#	checkLK	-	Check lk-file
	#
	# DESCRIPTION
	#	This function will return 0 if an instance is not running,
	#	it will return 1 if the instance is running and 99 if 
	#	the lockfile doesn't contain any information
	#
	# USAGE
	#	checkLK [-d] lockfile [other parameters]
	#
	# RETURN
	#	0 - Process is not running
	#	1 - Process is already running
	#  98 - Process lockfile is not readable
	#  99 - Something went wrong
	#
	# AUTHOR
	#	written by John Brink <j6b@qad.com>
	#
	# REVISION HISTORY
	# ----------------------------------------------------------------------
	#	0.1	02/10/2021 j6b - Initial Creation
	#	0.2	02/24/2021 j6b - Change grep command
	#	0.3	03/17/2021 j6b - Modify grep command for stranded sessions
	#						 Also try to look at the provided parameters
	# ----------------------------------------------------------------------
	# vim:ts=4
	VERS=0.3   # Version of this script.

	[[ ! ${#} -gt 0 ]] && return 99

	_debug=0
	while getopts d opt; do
		case "${opt}" in
		  d)	_debug=1;;
		esac
	done
	shift $((OPTIND - 1))

	_lkfile=${1}
	shift

	_otherparams=${@}

	[[ ${_debug} = 1 ]] && echo "checkLK - called from ${0}"

	if [[ ! -f ${_lkfile} ]] ; then
		# File doesn't exist, so process not running
		return 0
	elif [[ ! -r ${_lkfile} ]] ; then
		# File exists, but is not readable
		return 98
	else
		# Collect information
		[[ ${_debug} = 1 ]] && echo "${_lkfile} found - continue"
		_content=$(cat ${_lkfile})
		# Layout should be PID [time started] [..]
		_pid=$(echo ${_content}|cut -d' ' -f1)
		_time=$(echo ${_content}|cut -d' ' -f2)

		if [[ ${_debug} = 1 ]] ; then
			echo "\${_content}: ${_content}"
			echo "\${_pid}: ${_pid}"
			echo "\${_time}: ${_time}"
			echo "\${_otherparams}: ${_otherparams}"
		fi

		if [[ -z ${_pid} ]] ; then
			# No pid value found - cannot verify
			return 99
		else
			# Verify if pid is already running
# v0.2 - Changing grep
			#_wc=$(ps -ef | grep ${_pid} | \
			#	grep -e /bin/sh -e /bin/ksh -e /bin/bash -e $(basename ${_lkfile} .lk)| \
			#	grep -v grep | \
			#	wc -l)
# v0.3 - Changing grep
			#	grep -e /bin/sh -e /bin/ksh -e /bin/bash -e $(basename ${0})| \
# End - v0.3
			_wc=$(ps -ef | grep ${_pid} | \
				grep "$(basename ${0}) ${_otherparams}"| \
				grep -v grep | \
				wc -l)
# End - v0.2
			[[ ${_debug} = 1 ]] && echo "\${_wc}: ${_wc}"
			return ${_wc}
		fi
	fi
}

##------------------##
## Global variables ##
##------------------##

PROSQL_LOCKWAIT_TIMEOUT=302
[[ -d /usr/remote/odutils ]] && qcUTILS=/usr/remote/odutils

export PROSQL_LOCKWAIT_TIMEOUT qcUTILS