#!/bin/bash
# #################################################################################
# Exceed TurboX Connection Node
# © Rocket Software, Inc. or its affiliates. All Rights Reserved.
# ROCKET SOFTWARE, INC. CONFIDENTIAL
# #################################################################################
# boottime
# #################################################################################

# Script to support 'otetxcn bootstatus | bootstart | nobootstart'

readonly SUCCESS=0
readonly FAILURE=1

test_ECHO()
{
	${ECHO} -e "test\c" | grep "\-e" > /dev/null
	if [ $? -gt 0 ] ; then
		# -e is supported so add it
		ECHO="${ECHO} -e"
	fi
}

find_good_ECHO()
{
	ECHO=`which echo`
	test_ECHO
	export ECHO
}

test_TR()
{
	echo "aaAA" | ${TR} [:upper:] [:lower:]  > /dev/null 2>&1
	if [ $? -gt 0 ] ; then
		return 1
	else
		return 0
	fi
}

find_good_TR()
{
	TR=`which tr`
	test_TR
	if [ $? -gt 0 ] ; then
		TR=/usr/bin/tr
		test_TR
		if [ $? -gt 0 ] ; then
			TR=/usr/xpg4/bin/tr
			test_TR
			if [ $? -gt 0 ] ; then
				TR=/usr/ucb/tr
				test_TR
				if [ $? -gt 0 ] ; then
					${ECHO} "FATAL ERROR: The tr version on this machine is too old.  It needs to be equivalent to GNU tr."
					cd_and_exit 1
				fi
			fi
		fi
	fi
	export TR
	return
}



read_yesno()
{
# accepts y, n, yes, no; converts input to lowercase
# by default null input is "no", $1 overrides yndefault (n, y, none, block)
# yes: returns 0 (useful for: if read_yesno ; then )
# no: returns 1; Enter: returns default: 0, 1, or 2

	# set default answer if null response
	yndefault=${1:-n}
	
	while true ; do
		
		read answer
		#convert to lowercase
		answer=`${ECHO} ${answer}| ${TR} [:upper:] [:lower:]`
		${ECHO}
		
		case "${answer}" in
			y|yes )
				return 0
				;;
			n|no )
				return 1
				;;
			"" )
				case "${yndefault}" in
					y ) return 0 ;;
					n ) return 1 ;;
					none ) return 2 ;;
					block )
						${ECHO} "Invalid input. Please try again: \c"
						;;
				esac
				;;
			* )
				${ECHO} "Invalid input '${answer}'. Please try again: \c"
				;;
		esac
	
	done
}

read_noyes()
{
# no: returns 0 (useful for: if read_noyes ; then )

	read_yesno $*
	resp=$?
	if [ $resp -eq 0 ] ; then
		return 1
	elif [ $resp -eq 1 ] ; then
		return 0
	else
		return $resp
	fi
}

setBoottimeOps()
{
	addOp="--add"
	delOp="--del"
	statOp="--status"  
}

includeMsgTxt()
{
	MSGTXT="${InstallDir}/bin/sys/msg.txt"
	FATALERRMSG="Please run install/patch script first."
	
	if [ ! -f ${MSGTXT} ] ; then
		${ECHO} "${FATALERRMSG}"
		exit 1
	fi
	
	. ${MSGTXT}
}

getInstallDir()
{
	# resolve links - $0 may be a softlink
	thisPRG="$0"
	
	while [ -h "$thisPRG" ]; do
		ls_ld=`ls -ld "$thisPRG"`
		this_link=`expr "${ls_ld}" : '.*-> \(.*\)$'`
		
		if expr "${this_link}" : '/.*' > /dev/null; then
			thisPRG="${this_link}"
		else
			thisPRG=`dirname "${thisPRG}"`/"${this_link}"
		fi
	done
	
	CDir=`pwd`
	
	thisPrgDirName="`dirname ${thisPRG}`"
	
	cd "${thisPrgDirName}" > /dev/null 2>&1
	cd ../.. > /dev/null 2>&1 
	
	InstallDir=`pwd`

}

resetCDir()
{
	cd "${CDir}" > /dev/null 2>&1
}

init()
{
	if [ -z "$bSilent" ] ; then
	    bSilent=0
	fi
	find_good_ECHO
	find_good_TR
	getInstallDir
	setBoottimeOps  
	includeMsgTxt
	
	if [ "${1}x" = "x" ] ; then
	boottime_usage
	fi
}


isBoottimeOption()
{
	case "$1" in
		--* )
			return 0
			;;
		++* )
			return 2
			;;
		* )
			return 1
			;;
	esac
}

systemdInUse()
{
	local rv=$FAILURE
	if [ -d /run/systemd/system ]; then
		rv=$SUCCESS
	fi
	if [ -n "$OTETX_USE_SYSTEMD" ]; then
		# Invert
		[ "$OTETX_USE_SYSTEMD" = 1 ]
		rv=$?
	fi
	return $rv
}

initBoottime()
{
	# Check for install first
	isInstall=
	isBoottimeOption $1
	if [ $? -eq 2 ] ; then
		isInstall="1"
		thisOp=${addOp}
	else
		thisOp="${1}"
	fi

	logDirName=installlogs
	logDir=${InstallDir}/${logDirName}
	installLogFile=${logDir}/install.log
	initFile=${logDir}/init.d
	systemdUnitLog="${logDir}/systemd.unit.name"

	servicebase=otetxcn
	if [ "${isInstall}" = "1" ] ; then
		initscriptname="${servicebase}${VERSION}"
	else
		initscriptname=$(getInitScriptBaseName)
	fi
	getPlatformSpecificInfo
	
	cd ${InstallDir}/bin
}

initPlatformSpecificInfo()
{
	thisChkConfig="/sbin/chkconfig"
	
	if [ -x ${thisChkConfig} ] ; then
		hasChkConfig="1"
	fi
	
	thisUpdateRcD="/usr/sbin/update-rc.d"
	
	if [ -x ${thisUpdateRcD} ] ; then
		hasUpdateRcD="1"
	fi
	
	# default link type - soft link 
	linktype="-fs"

	# default messages, initpath and link prefixes
	initpath="/etc/init.d/"
	preStop="K09"
	preStart="S99"
	startmsg="${BOOT_START_MSG}"
	stopmsg="${BOOT_STOP_MSG}"
}

setupForAIX()
{
	initpath="/etc/rc.d/init.d/"
	linkpathStart="/etc/rc.d/rc2.d"
	linkpathrel="../init.d/"
	preStart="S" 
}

setupForHPUX()
{
	initpath="/sbin/init.d/"
	linkpathStop="/sbin/rc1.d"
	linkpathStart="/sbin/rc2.d"
	linkpathrel=${initpath}
	preStop="K090"
	preStart="S910"
	isHpUx="1"
}

setupForSunOS()
{
	linkpathStop="/etc/rc0.d /etc/rcS.d /etc/rc1.d /etc/rc2.d"
	linkpathStart="/etc/rc3.d"
	linkpathrel=${initpath}
	isSunOS="1"
}

setupForDebianUbuntu()
{
	if [ "`uname -a | fgrep -c -i Ubuntu`" = "1" ] ; then
		thisLinux="Ubuntu"
		isUbuntu=1
	else
		thisLinux="Debian"
		isDebian=1
	fi

	killLevels="016"
	startLevels="2345"
	defaultStart="2 3 4 5"
	defaultStop="0 1 6"
	chkconfigParams="345 99 10"
	preStop="K20"
#	preStart="S20"
	linkpathStop="/etc/rc0.d /etc/rc1.d /etc/rc6.d"
	linkpathStart="/etc/rc2.d /etc/rc3.d /etc/rc4.d /etc/rc5.d"
}

setupForSUSE()
{
	thisLinux="SUSE"
	isSUSE=1
	killLevels="2345"
	startLevels="35"
	defaultStart="3 5"
	defaultStop="3 5"
	chkconfigParams="345 99 10"
	
	linkpathStop="/etc/init.d/rc3.d /etc/init.d/rc5.d"
	linkpathStart="/etc/init.d/rc3.d /etc/init.d/rc5.d"
	linkpathrel="../"
	preStop="K01"
#	preStart="S01"
}

setupForRedHat()
{
	thisLinux="RedHat"
	
	killLevels="0126"
	startLevels="345"
	
	defaultStart="2 3 4 5"
	defaultStop="0 1 6"
	chkconfigParams="345 99 10"
	
	initpath="/etc/rc.d/init.d/"
	linkpathStop="/etc/rc.d/rc0.d /etc/rc.d/rc1.d /etc/rc.d/rc2.d /etc/rc.d/rc6.d"
	linkpathStart="/etc/rc.d/rc3.d /etc/rc.d/rc4.d /etc/rc.d/rc5.d"
	linkpathrel="../init.d/"
}

setupForLinux()
{
	isLinux=1
	preStop="K10"
#	preStart="S90"
	findhasquit=" -quit"	
	
	startmsg="${BOOT_START_MSG_LINUX}"
	stopmsg="${BOOT_STOP_MSG_LINUX}"
	
	if [ -x "/etc/rc.d/init.d" ]; then
		setupForRedHat
	elif [ -x "/etc/rc0.d/" ]; then
		setupForDebianUbuntu
	else
		setupForSUSE
	fi

	sysvinitpath=
	isSysvUpgrade=
	if systemdInUse; then
		usingSystemd=1;
		systemdunitdir=/etc/systemd/system
		sysvinitpath=$initpath
		initpath="${InstallDir}/bin/"
		initscriptname=$servicebase
		servicebasewithport=
		setNodePort
		if [ -n "$nodeport" ]; then
			servicebasewithport=${servicebase}_${nodeport}
		fi
	fi
}

getPlatformSpecificInfo()
{
	initPlatformSpecificInfo
	
	case "`uname`" in
		AIX )
			setupForAIX
			;;
		HP-UX )
			setupForHPUX
			;;
		Linux )
			setupForLinux
			;;
		SunOS )
			setupForSunOS
			;;
		* )
			${ECHO} "${SRV_NAME}"
			${ECHO}
			${ECHO} "Sorry, `uname` is not supported."
			${ECHO} "Supported systems are: AIX, HP-UX, Linux and SunOS."
			exit 1
			;;
	esac
	
	initScriptPathName="${initpath}${initscriptname}"
}

confirmEnablingBootstart()
{
	if [ "${bSilent}" = "1" ] ; then
		if [ "${bBootStart}" = "1" ] ; then
			return
		else
			exit 0
		fi
	fi
	
	# ETX-26044 - Always install system service in interactive mode
	${ECHO} "${BOOT_INFO_AUTOMATIC}"
	${ECHO}
}

getNextUnusedNumber()
{
	maxNum=1001
	thisNum=0
	
	while [ ! "${thisNum}" = "${maxNum}" ]
	do
		suffix="_${thisNum}"
		
		if [ ! -r "${initScriptPathName}${suffix}" ] ; then
			nextUnusedNumberFound="1"
			break
		fi
		
		thisNum=`expr ${thisNum} + 1`
		
	done
}

getNewScriptName()
{
	if [ -r "${initScriptPathName}" ] ; then
		getNextUnusedNumber
		
		if [ "${nextUnusedNumberFound}x" = "x" ] ; then
			msg1="Init script for ${PROD_NAME} could not be created."
			msg2="Maximum number, ${maxNum}, of supported init scripts had been previouly created."
			msg3="Please review ${initpath} and remove unused scripts."
			msg4="Then run install again."
			
			${ECHO} "${msg1}" 
			${ECHO} "${msg2}"
			${ECHO} "${msg3}"
			${ECHO} "${msg4}"
			${ECHO}
			${ECHO}
			
			logIncident "3" "${installLogFile}" "${msg1}" "${msg2}" "${msg3}" "${msg4}"
			
			exit 1
		fi
		
		initscriptname=${initscriptname}${suffix}
		initScriptPathName="${initpath}${initscriptname}"
	fi
}

addCopyRightNotice()
{
	cat > "${initScriptPathName}" << _ADDCOPYRIGHTNOTICE_
#!/bin/sh
# ####################################################################
# ${SRV_NAME} ${VERSION} 
# © Rocket Software, Inc. or its affiliates. All Rights Reserved.
# ROCKET SOFTWARE, INC. CONFIDENTIAL
# ####################################################################
#
# ${initscriptname} - bootstart script for ${SRV_NAME} 
#   
# ${initScriptPathName}
#
# ####################################################################
_ADDCOPYRIGHTNOTICE_
}

addRH_SpecificHeader()
{
	cat >> "${initScriptPathName}" << _ADDRHSPECIFICHEADER_
${BEGIN_INIT_INFO}
#
# chkconfig: ${chkconfigParams}
# description: ${SRV_NAME}
#
${END_INIT_INFO}

_ADDRHSPECIFICHEADER_
}

addNonRH_SpecificHeader()
{
	cat >> "${initScriptPathName}" << _ADDNONRHSPECIFICHEADER_
${BEGIN_INIT_INFO}
#
# Provides: ${initscriptname}
# Required-Start: ${network} 
# Required-Stop: ${network} 
# Default-Start: ${defaultStart}
# Default-Stop: ${defaultStop}
# Description: ${SRV_NAME}
#
${END_INIT_INFO}
_ADDNONRHSPECIFICHEADER_
}

addLinuxSpecificHeader()
{
	BEGIN_INIT_INFO="### BEGIN INIT INFO"
	END_INIT_INFO="### END INIT INFO"
	
	case "${thisLinux}" in
		SUSE | Ubuntu | Debian )
			network="\$network \$ALL"
			addNonRH_SpecificHeader
			;;
		* )
			addRH_SpecificHeader
			;;
	esac
}

# When we update the otetxcn wrapper while it is running we must ensure that the
# byte position of the old next line lands at a byte position in the new file with
# appropriate commands. In this case, to exit. Spaces are nops here.
addUpdateSafeBlock()
{
	cat >> "${initScriptPathName}" << _ADDBLOCK_EOF_
######## Self-update landing zone #######
#                                                                                                                  exit
#########################################
_ADDBLOCK_EOF_
}

create_bin_otetxcn()
{
	otetxcnWrapper="otetxcn" 
	thisRevision=`grep "Revision" "${sys_otetxcn}"`
	
	cat > "${otetxcnWrapper}" << _CREATEBINOTECS_
#!/bin/sh
# ####################################################################
# ${SRV_NAME} ${VERSION}
# © Rocket Software, Inc. or its affiliates. All Rights Reserved.
# ROCKET SOFTWARE, INC. CONFIDENTIAL
# ####################################################################
# ${otetxcnWrapper}
# ####################################################################

otetxcnWrapper="\`basename \${0}\`"
export otetxcnWrapper

thisInitScript=${initScriptPathName}

if [ -x \${thisInitScript} ] ; then
	\${thisInitScript} \$* 
fi

_CREATEBINOTECS_

	if [ "`groups | grep -c root`" = "1" ] ; then
		change_owner "root:root" "${otetxcnWrapper}"
	elif [ "`groups | grep -c system`" = "1" ] ; then
		change_owner "root:system" "${otetxcnWrapper}"
	fi
	
	chmod 744 ${otetxcnWrapper}
}

createInitdScriptFile()
{
	addCopyRightNotice
	
	if [ "${isLinux}" = "1" ] ; then
		if [ "$usingSystemd" = "1" ]; then
			addUpdateSafeBlock
		else
			addLinuxSpecificHeader
		fi
	fi
	
	sys_otetxcn="sys/otetxcn_functions"
	
	nlines_otetxcn_header=7
	nlines_otetxcn_all=`wc -l < ${sys_otetxcn}`
	nlines_otetxcn_body=`expr ${nlines_otetxcn_all} - ${nlines_otetxcn_header}`
	
	tail -${nlines_otetxcn_body} ${sys_otetxcn} >> "${initScriptPathName}"
	
	${ECHO} "" >> "${initScriptPathName}"
	${ECHO} "otetxcn \"${InstallDir}\" \"\$1\"  \"\$2\" \"\$3\"" >> "${initScriptPathName}"
	${ECHO} "exit \$?" >> "${initScriptPathName}"
	${ECHO} "" >> "${initScriptPathName}"
	
	if [ "${isSunOS}" = "1" ] ; then
		perms="755"
	elif [ "${isHpUx}" = "1" ] ; then
		perms="555"
	else
		perms="755"
	fi
	
	chmod ${perms} ${initScriptPathName}
	
	if [ "$usingSystemd" = "1" ]; then
		selinuxenabled 2>/dev/null
		if [ $? = 0 ]; then
			chcon -t bin_t ${initScriptPathName}
		fi
	else
		create_bin_otetxcn
	fi
	
	msg1="${initScriptPathName} script has been created."
	msg2="${initScriptPathName} script has been updated."
	
	if [ "${updateInitScriptOnly}x" = "x" ] ; then
		if [ ! "${bSilent}" = "1" ] ; then
			${ECHO} "${msg1}"
			${ECHO}
		fi
		logIncident "1" "${installLogFile}" "${msg1}"
	else
		if [ ! "${bSilent}" = "1" ] ; then
			${ECHO} "${msg2}"
			${ECHO}
		fi
		
		logIncident "1" "${installLogFile}" "${msg2}"
	fi
}

checkAndFixupInitScript()
{
	if [ ! -r "${initScriptPathName}" ] ; then
		if [ ! "${bSilent}" = "1" ] ; then
			${ECHO} "${BOOT_CANT_CREATE} ${initScriptPathName}"
		fi
		exit 1
	fi
	
	chmod u+x "${initScriptPathName}"
}

checkStartPathLink()
{
	if [ "${linkpathStart}x" = "x" -a ! "${isLinux}" = "1" ] ; then
		exit 0
	fi
}

delStopSymLinks()
{
	thisKLink="${preStop}${initscriptname}"
	
	for i in ${linkpathStop};
	do
	        if [ ! -d "${i}" ] ; then
		    continue
		fi

		cd ${i}
		if [ -r "${thisKLink}" ] ; then
			rm -f "${thisKLink}"
		fi
	done
}

createStopSymLinks()
{
	thisKLink="${preStop}${initscriptname}"
	
	for i in ${linkpathStop};
	do
	        if [ ! -d "${i}" ] ; then
		    continue
		fi

		cd ${i}
		
		if [ -r "${thisKLink}" ] ; then
			rm -f "${thisKLink}"
		fi
		
		ln "${linktype}" "${linkpathrel}${initscriptname}" "${thisKLink}"
		
		if [ ! -r "${thisKLink}" ] ; then
			if [ ! "${bSilent}" = "1" ] ; then
				${ECHO} "${BOOT_CANT_CREATE} ${i}/${thisKLink}"
			fi
			exit 1
		fi
	done
}

delStartSymLinks()
{
	thisSLink="${preStart}${initscriptname}"
	
	for i in ${linkpathStart};
	do
	        if [ ! -d "${i}" ] ; then
		    continue
		fi

		cd ${i}
		
		if [ -r "${thisSLink}" ] ; then
			rm -f "${thisSLink}"
		fi
	done
}

createStartSymLinks()
{
	thisSLink="${preStart}${initscriptname}"
	
	for i in ${linkpathStart} ; do
	        if [ ! -d "${i}" ] ; then
		    continue
		fi

		cd ${i}
		
		if [ -r "${thisSLink}" ] ; then
			rm -f "${thisSLink}"
		fi
		
		ln "${linktype}" "${linkpathrel}${initscriptname}" "${thisSLink}"
		
		if [ ! -r "${thisSLink}" ] ; then
			${ECHO} "${BOOT_CANT_CREATE} ${i}/${thisSLink}"
			exit 1
		fi
	done
}

createRunControlLinks()
{
	if [ "${hasChkConfig}" = "1" ] ; then
		${thisChkConfig} --add "${initscriptname}" > /dev/null 2>&1
		
		if [ ! "${isSUSE}" = "1" ] ; then
			${thisChkConfig} --level ${killLevels} "${initscriptname}" off > /dev/null 2>&1
			${thisChkConfig} --level ${startLevels} "${initscriptname}" on > /dev/null 2>&1 
		fi
	elif [ "${hasUpdateRcD}" = "1" ] ; then
		${thisUpdateRcD} "${initscriptname}" defaults > /dev/null 2>&1
	else
		createStopSymLinks
		createStartSymLinks
	fi
}

confirmMovedHome()
{
	if [ "${bSilent}" = "1" ] ; then
		updateInitScriptOnly="1"
		return
	fi
	
	${ECHO} "It appears that the previous installation was at:"
	${ECHO} "    ${lastHome}"
	${ECHO}
	${ECHO} "That install directory was then moved to:"
	${ECHO} "    ${InstallDir}"
	${ECHO}
	${ECHO} "Is this correct? [y]es or [n]o (<Enter> for no): \c"
	
	if read_yesno ; then
		updateInitScriptOnly="1"
	fi
}

getInitScriptBaseName()
{
	if [ "$usingSystemd" = "1" ]; then
		echo $servicebase
	else
		echo `cat ${initFile}`
	fi
}

checkInstalledInitScript()
{
	if [ -r ${initFile} ] ; then 
		initscriptname=$(getInitScriptBaseName)
		initScriptPathName="${initpath}${initscriptname}"
		if [ -r ${initScriptPathName} ] ; then 
			lastHome=`${initScriptPathName} lastHome`
			
			if [ "${lastHome}" = "${InstallDir}" ] ; then
				updateInitScriptOnly="1"
			elif [ "${lastHome}" = "${migrateHome}" ] ; then
				updateInitScriptOnly="1"
			else
				confirmMovedHome
			fi
			
			if [ "${updateInitScriptOnly}" = "1" ] ; then
				return 1
			fi
		fi
		
		initscriptname="otetxcn${VERSION}"
		initScriptPathName="${initpath}${initscriptname}"
	fi
	
	return 0
}

installInitdScriptFile()
{
	checkInstalledInitScript
	
	if [ "${updateInitScriptOnly}x" = "x" ] ; then
		getNewScriptName
	fi
	
	createInitdScriptFile
	checkAndFixupInitScript
	${ECHO} "${initscriptname}" > ${initFile}
}

addRunControlLinks()
{
	if [ "${isInstall}" = "1" ] ; then
		confirmEnablingBootstart
	fi
	
	if [ ! "${isLinux}" = "1" ] ; then
		checkStartPathLink
	fi
	
	createRunControlLinks
}

confirmDeletingBootstart()
{
	if [ "${bSilent}" = "1" ] ; then
		return
	fi
	
	${ECHO} "Would you like to remove bootstart files for installation at ${InstallDir}?"
	${ECHO} "${BOOT_CONFIRM}"
	
	if read_noyes ; then
		exit 0
	fi
}

delRunControlLinks()
{
	if [ "$1" != "noconfirm" ]; then
		confirmDeletingBootstart
	fi
	
	if [ "${hasChkConfig}" = "1" ] ; then
		${thisChkConfig} --del "${initscriptname}" > /dev/null 2>&1
	elif [ "${hasUpdateRcD}" = "1" ] ; then
		${thisUpdateRcD} -f "${initscriptname}" remove > /dev/null 2>&1
	else
		delStartSymLinks
		delStopSymLinks
	fi
}

showBoottimeStatus()
{
	bootStatus="off"
	if [ "$usingSystemd" = "1" ]; then
		if [ "$(systemdGetCurrentUnit)" != "" ]; then
			if systemdGetUnitForInstallPath "${InstallDir}"; then
				if [ "$existingunit" = "$(systemdGetCurrentUnit)" ]; then
					bootStatus="on"
				fi
			fi
		fi
	else
		for i in ${linkpathStart};
		do
			findresult=`find ${i} -name \*${initscriptname} -print ${findhasquit}`
			if [ -n "${findresult}" ] ; then
				bootStatus="on"
				break
			fi
		done
	fi
	
	${ECHO} "${bootStatus}"
}

serviceIsRunning()
{
	if [ "${isInstall}" = "1" ] ; then
                # pm will print error if not installed yet
		return $FAILURE
	fi
	"${InstallDir}/bin/etxpmctl" status >/dev/null
	return $?
}

setNodePort()
{
	local port
	nodeport=
	if [ "$isInstall" = "1" -a -n "$ListenPort" ]; then
		port=$ListenPort
	else
		if [ -f "${InstallDir}/runtime/etxpm/etxpmdb" ]; then
			port=$(echo "SELECT val from config where key='ListenPort';" | "${InstallDir}/bin/etxpmctl" -s dbshell)
		fi
	fi
	# Test if number
	if [ "$port" -eq "$port" ] 2>/dev/null; then
		nodeport=$port
	fi
}

systemdUnitProperty()
{
	local lunit="$1"
	local lproperty="$2"
	systemctl show "$lunit" --no-pager --all "--property=$lproperty" 2>/dev/null | \
		sed "s/^$lproperty=//" 2>/dev/null
}

systemdRemoveUnit()
{
	local unit="$1"
	local fragmentpath="$2"
	local startpath="$3"
	local stopmode="$4"
	local stopped=
	local removedsomething=

	# If unit was created by systemd-sysv-generator then disable will remove any RC links
	if [ -f "$fragmentpath" ]; then
		# Even if no process is running, stop is needed to make list-units drop this unit
		if [ "${bSilent}" != "1" -a "$stopmode" = "AskToStop" ] ; then
			${ECHO} "${BOOT_STOP_UNIT_ON_REMOVE}"
			${ECHO} "  Unit file:   $fragmentpath"
			${ECHO} "${BOOT_CONFIRM_Y}"
			if read_yesno y; then
				systemctl stop "$unit" 2>/dev/null
				stopped=y
			fi
		fi
		systemctl disable "$unit" 2>/dev/null
		rmlog "$fragmentpath" 2> /dev/null
		${ECHO} "Removed '${unit}'"
		removedsomething=y
	fi
	if [ "${startpath/init.d/}" != "$startpath" ]; then
		rmlog "$startpath" 2> /dev/null
		${ECHO} "Removed '${startpath}'"
		removedsomething=y
	fi
	if [ -z "$stopped" -a -n "$removedsomething" ]; then
		if serviceIsRunning; then
			${ECHO} "${BOOT_DISABLED_NOT_STOPPED}"
		fi
	fi
}

systemdDebug()
{
	local msg="DEBUG: $1"
	local mode="${ETX_SYSTEMD_DEBUG:-log}"

	[ "${mode/con/}" != "$mode" ] && ${ECHO} "$msg"
	if [ "${mode/log/}" != "$mode" ]; then
		mkdir -p "$(dirname $installLogFile)"
		${ECHO} "$msg" >> "$installLogFile"
	fi
}

logFunc()
{
	systemdDebug "ENTER ${FUNCNAME[1]}"
}

rmlog()
{
	local file="$1"
	systemdDebug "rm $file"
	"rm" "$file"
}

mvlog()
{
	local src="$1"
	local dst="$2"
	systemdDebug "mv $src $dst"
	"mv" "$src" "$dst"
}

systemdForEachUnit()
{
	local mode="$1"
	local matchpath="$2"
	local instdir
	local unit

	systemdDebug "systemdForEachUnit($mode)"
	for unit in "${servicebase}" "${servicebasewithport}" \
			$(systemctl list-units --no-legend ${servicebase}*.service 2>/dev/null \
				| sed 's/.service.*//'); do
		local fragmentpath="$(systemdUnitProperty "$unit" FragmentPath)"
		local sourcepath="$(systemdUnitProperty "$unit" SourcePath)"
		local execstart="$(systemdUnitProperty "$unit" ExecStart)"
		local startpath="$(echo "$execstart"|sed 's/.* path=\([^;]*\) ;.*/\1/')"
		instdir="$(dirname "$(dirname "$startpath")")"
		local sysv=
		if [ -z "$fragmentpath" -o -z "$instdir" ]; then
			continue
		fi
		# Point to real node install dir if this unit was generated from SYSV
		if [ "${sourcepath/init.d/}" != "$sourcepath" ]; then
			sysv=1
			instdir=$(sed -n "s/^$servicebase \"\([^ ]*\)\" .*/\1/p" "$startpath" 2>/dev/null)
		fi
		systemdDebug "    [unit $unit] = instdir $instdir"
		if [ "$mode" = "exists" ]; then
			if [ "$instdir" = "$matchpath" ]; then
				existingunit=$unit
				break
			fi
		elif [ "$mode" = "upgrade_sysv" ]; then
			if [ -n "$sysv" -a "$instdir" = "$matchpath" ]; then
				# Must remove RC links before init.d script or os utilities will not delete anything
				delRunControlLinks noconfirm
				rmlog "$startpath"
				rmlog "$fragmentpath"
				existingunit=$unit
				break
			fi
		elif [ "$mode" = "remove" ]; then
			# Remove units pointing to non-existent install paths
			if [ ! -d "$instdir" ]; then
				if [ "${bSilent}" != "1" ] ; then
					${ECHO} "${BOOT_REMOVE_ORPHANED}"
					${ECHO} "  Install dir: $instdir"
					${ECHO} "  Unit file:   $fragmentpath"
					${ECHO} "${BOOT_CONFIRM_Y}"
					if read_yesno y; then
						systemdRemoveUnit "$unit" "$fragmentpath" "$startpath" AskToStop
					fi
				fi
			fi
			if [ "$instdir" = "$matchpath" ]; then
				systemdRemoveUnit "$unit" "$fragmentpath" "$startpath" NoAskToStop
			fi
		fi
	done
}

systemdGetCurrentUnit()
{
	local rv=
	if [ -f "$systemdUnitLog" ]; then
		rv="$(cat "$systemdUnitLog" 2>/dev/null)"
	fi
	echo "$rv"
}

systemdGetUnitForInstallPath()
{
	logFunc
	local rv=$FAILURE
	local instdir="$1"
	existingunit=
	systemdForEachUnit exists "$instdir"
	if [ -n "$existingunit" ]; then
		rv=$SUCCESS
	fi
	return $rv
}

systemdUpgradeSysvUnitForInstallPath()
{
	logFunc
	local rv=$FAILURE
	local instdir="$1"
	existingunit=
	systemdForEachUnit upgrade_sysv "$instdir"
	if [ -z "$existingunit" ]; then
		if [ ! -f "$systemdUnitLog" ]; then
			# systemctl couldn't see it yet, but we are a sysv upgrade
			local etcinit="$(sed -n "s|^thisInitScript=||p" ${initScriptPathName} 2>/dev/null)"
			if [ -f "$etcinit" ]; then
				rmlog "$etcinit"
			fi
			existingunit="${etcinit##/*/}"
		fi
	fi
	if [ -n "$existingunit" ]; then
		rv=$SUCCESS
	fi

	return $rv
}

systemdRemoveUnitsForInstallPath()
{
	logFunc
	local instdir="$1"
	systemdForEachUnit remove "$instdir"
}

systemdCreateServiceFile()
{
	logFunc
	local unitfile="$1"
	local portphrase=""
	if [ "$unitfile" != "${unitfile/_/}" -a -n "$nodeport" ]; then
		portphrase=" port $nodeport"
	fi
	cat > "$unitfile" << _EOF_SYSTEMD_UNIT_
[Unit]
Description=Rocket Exceed TurboX Connection Node${portphrase}
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
GuessMainPID=no
KillMode=none
ExecStart=${initScriptPathName} start
ExecStop=${initScriptPathName} stop
Environment=ETX_SYSTEMD_LAUNCH=1
Restart=no
TasksMax=90%

[Install]
WantedBy=multi-user.target

_EOF_SYSTEMD_UNIT_
}

systemdUnitExists()
{
	local searchunit="$1"
	local u
	for u in $(systemctl list-units --no-legend "${searchunit}*.service" 2>/dev/null | sed 's/.service.*//'); do
		if [ "$u" = "$searchunit" ]; then
			# Check actual backing files exist; list-units will show units that are removed if
			# the service process is still running.
			local fragmentpath="$(systemdUnitProperty "$u" FragmentPath)"
			if [ -f "$fragmentpath" ]; then
				return $SUCCESS
			fi
		fi
	done
	return $FAILURE
}

systemdSetUnitName()
{
	logFunc
	sdunit=
	for u in $servicebase $servicebasewithport ${servicebase}_{1..10}; do
		if ! systemdUnitExists "$u"; then
			if [ ! -f "${systemdunitdir}/${sdunit}.service" ]; then
				sdunit=$u
				return $SUCCESS
			fi
		fi
	done
	return $FAILURE
}

# Systems using update_rc.d (Debian) will fail to parse malformed LSB Init headers,
# leaving the systemd-sysv-generator unit in a weird state that can't be upgraded properly.
fixLSBInitHeaders()
{
	logFunc
	local rv=$FAILURE
	local f
	for f in ${sysvinitpath}${initscriptname}*; do
		if [ -f "$f" ]; then
			if grep "^### BEGIN INIT INFO ###" "$f" > /dev/null ; then
				${ECHO} "${BOOT_FIXING_INIT_HEADERS} $f"
				sed -i 's/^### \(BEGIN INIT INFO\|END INIT INFO\) #.*/### \1/' "$f"
				rv=$SUCCESS
			fi
		fi
	done
	return $rv
}

removeOrphaned()
{
	logFunc
	local instdir="$1"
	local glob="$2"
	local f
	local dir
	local msg

	if [ "${bSilent}" = "1" ] ; then
		return
	fi

	for f in $glob; do
		if [ ! -f "$f" ]; then
			continue
		fi
		if [ "${f/.service/}" != "$f" ]; then
			dir=$(sed -n 's|ExecStart=\(.*\)/bin/otetxcn start$|\1|p' "$f" 2>/dev/null)
		else
			dir=$(sed -n 's|^otetxcn "\(/[^ ]*\)" .*|\1|p' "$f" 2>/dev/null)
		fi
		msg=
		if [ ! -d "$dir" ]; then
			msg="${BOOT_REMOVE_ORPHANED}"
		elif [ "$dir" = "$instdir" ]; then
			msg="${BOOT_REMOVE_EXTRA}"
		fi
		if [ -n "$msg" ]; then
			${ECHO} "$msg"
			${ECHO} "  Init file:   $f"
			${ECHO} "  Install dir: $dir"
			${ECHO} "${BOOT_CONFIRM_Y}"
			if read_yesno y; then
				rmlog "$f"
			fi
		fi
	done
}

delSystemd()
{
	logFunc
	confirmDeletingBootstart
	systemdRemoveUnitsForInstallPath "${InstallDir}"
	if [ -f "$systemdUnitLog" ]; then
		rmlog "$systemdUnitLog" 2>/dev/null
	fi
	systemctl daemon-reload
}

addSystemd()
{
	logFunc
	if [ "${isInstall}" = "1" ] ; then
		confirmEnablingBootstart
	fi

	# The old SysV install may never have properly registered with systemd because of
	# malformed LSB Init headers.  Fix them first so other systemd calls can see the unit.
	if fixLSBInitHeaders; then
		systemctl daemon-reload
	fi

	needRestart=
	sdunit=
	if systemdUpgradeSysvUnitForInstallPath "${InstallDir}"; then
		sdunit="$existingunit"
		isSysvUpgrade=1
		${ECHO} "${BOOT_UPGRADE_SYSV} $sdunit"

		if [ -f "${systemdunitdir}/${sdunit}.service" ]; then
			systemdSetUnitName
			if [ "$sdunit" != "$existingunit" ]; then
				${ECHO} "${BOOT_UPGRADE_SYSV_RENAME} $sdunit"
			else
				local src="${systemdunitdir}/${sdunit}.service"
				mvlog "$src" "$src.$(date +%Y-%m-%d).disable"
				${ECHO} "${BOOT_UPGRADE_SYSV_CLOBBER} $sdunit"
			fi
		fi
		installInitdScriptFile
	elif systemdGetUnitForInstallPath "${InstallDir}"; then
		sdunit="$existingunit"
		if [ -n "$sdunit" -a "$sdunit" = "$(systemdGetCurrentUnit)" ]; then
			# It might exist but be disabled
			systemctl enable "$sdunit"
			${ECHO} "${BOOT_ALREADY_ENABLED} $sdunit"
			return
		else
			${ECHO} "${BOOT_UPDATE_EXISTING} $sdunit"
		fi
	else
		systemdRemoveUnitsForInstallPath "${InstallDir}"
		removeOrphaned "${InstallDir}" "${systemdunitdir}/${servicebase}*.service"
		removeOrphaned "${InstallDir}" "${sysvinitpath}${initscriptname}*"
		systemdSetUnitName

		# If etxpm is already running but not under systemd, we need to stop and restart under systemd later
		if serviceIsRunning; then
			${ECHO} "${BOOT_RESTART_EXISTING}"
			${ECHO} "${BOOT_CONFIRM_Y}"
			if read_noyes y; then
				${ECHO} "${BOOT_NO_RESTART_EXISTING}"
				return
			fi
			needRestart=1
		fi
	fi

	if [ -z "$sdunit" ]; then
		${ECHO} "${BOOT_CANT_GET_UNIT_NAME}"
	else
		if [ "$needRestart" = "1" ]; then
			"${InstallDir}/bin/etxpmctl" stop
		fi
		systemdCreateServiceFile "${systemdunitdir}/${sdunit}.service"
		${ECHO} "${BOOT_CREATED_UNIT} '${sdunit}.service'"
		echo "${sdunit}"> "$systemdUnitLog"
		systemctl daemon-reload
		if [ "$isSysvUpgrade" = "1" ]; then
			if [ "${isInstall}" = "1" ] ; then
				# The PM isn't initialized yet, we can't do the stop/start routine
				${ECHO} "${BOOT_NEED_RESTART_FOR_SYSTEMD}"
			else
				# Force unit reload
				systemctl disable "$sdunit"
				systemctl enable "$sdunit"
				# Run etxpm under new unit so systemctl status works
				${ECHO} "${BOOT_RESTART_FOR_SYSTEMD}"
				${ECHO} "${BOOT_CONFIRM_Y}"
				if read_yesno y; then
					ETX_SYSTEMD_LAUNCH=1 "${initScriptPathName}" stop
					systemctl start "$sdunit"
					systemctl status "$sdunit"
				fi
			fi
		else
			systemctl enable "$sdunit"
		fi
		if [ "$needRestart" = "1" ]; then
			systemctl start "$sdunit"
		fi

	fi

}

handleBootstart_systemd()
{
	if [ "${thisOp}" = "${delOp}" ] ; then
		if [ "$(systemdGetCurrentUnit)" = "" ]; then
			delRunControlLinks
		else
			delSystemd
		fi
	elif [ "${thisOp}" = "${addOp}" ] ; then
		addSystemd
	elif [ "${thisOp}" = "${statOp}" ] ; then
		showBoottimeStatus
	elif [ ! "${thisOp}x" = "x" ] ; then
		boottime_usage
	fi
}

handleBootstart_sysv()
{
	if [ "${thisOp}" = "${delOp}" ] ; then
		delRunControlLinks
	elif [ "${thisOp}" = "${addOp}" ] ; then
		addRunControlLinks
	elif [ "${thisOp}" = "${statOp}" ] ; then
		showBoottimeStatus
	elif [ ! "${thisOp}x" = "x" ] ; then
		boottime_usage
	fi
}

handleBootstart()
{
	if [ "$usingSystemd" = "1" ] ; then
		handleBootstart_systemd
	else
		handleBootstart_sysv
	fi
}

doBootTime()
{
	thisScriptName=${1}
	migrateHome=${3}
	
	init ${2}
	initBoottime "${2}"
	
	if [ "${isInstall}" = "1" ] ; then
		installInitdScriptFile
	fi
	
	if [ -r "${initScriptPathName}" ] ; then
		if [ "${updateInitScriptOnly}x" = "x" ] ; then
			handleBootstart
		fi
	else
		${ECHO}
		${ECHO} "${thisScriptName}: ${initScriptPathName} not found."
		${ECHO}
	fi
	resetCDir
	exit 0
}

boottime_usage()
{
	${ECHO}
	${ECHO} "Usage: ${thisScriptName} ${addOp} | ${delOp} | ${statOp}"
	${ECHO}
	exit 1
}

doBootTime "`basename ${0}`" "${1}" "${2}"
