#!/usr/bin/env bash

#### ------------> DO NOT CHANGE VARIABLES IN THIS FILE <------------- ####
#### Create $OPENNMS_HOME/etc/opennms.conf and put overrides in there. ####
#### ------------> DO NOT CHANGE VARIABLES IN THIS FILE <------------- ####

INCOMING_ARGS=("$@")

# Home directory for OpenNMS.
# shellcheck disable=SC2154
OPENNMS_HOME="${install.dir}"

# PID file for OpenNMS.
OPENNMS_PIDFILE="${install.pid.file}"

# Log directory for OpenNMS
LOG_DIRECTORY="${install.logs.dir}"

# Where to redirect "start" output.
REDIRECT="$LOG_DIRECTORY/output.log"

# Number of times to do "opennms status" after starting OpenNMS to see
# if it comes up completely.  Set to "0" to disable.  Between each
# attempt we sleep for STATUS_WAIT seconds.
START_TIMEOUT=0

# Number of seconds to wait between each "opennms status" check when
# START_TIMEOUT > 0.
STATUS_WAIT=5

# Number of times to do "opennms status" after stopping OpenNMS to see
# if it has shut down completely.  Set to "0" to disable.  Between each
# attempt we sleep for STATUS_WAIT seconds.
# Default is 3 minutes (36 * 5 seconds)
STOP_TIMEOUT=36

# Initial heap size (-Xms) in megabytes
JAVA_INITIAL_HEAP_SIZE=""

# Maximum heap size (-Xmx) in megabytes
JAVA_HEAP_SIZE=4096

# Additional options passed to Java when starting OpenNMS.
# ADDITIONAL_MANAGER_OPTIONS=()

# Use incremental garbage collection.
USE_INCGC=""

# Use the Java Hotspot server VM.
HOTSPOT=""

# Enable verbose garbage collection debugging.
VERBOSE_GC=""

# Whether we're in the test framework
OPENNMS_UNIT_TEST=0

# Additional options to pass to runjava.
RUNJAVA_OPTIONS=""

# JMX URL that this script uses to communicate with a running OpenNMS daemon
# when it cannot connect using the Attach API (automatically or by PID)
JMX_URL="service:jmx:rmi:///jndi/rmi://127.0.0.1:1099/jmxrmi"

# Whether to include a thread dump before we start shutting down
THREAD_DUMP=1

# Whether to enable the Pyroscope agent
[ -z "$PYROSCOPE_AGENT_ENABLED" ] && PYROSCOPE_AGENT_ENABLED=0

# JaCoCo Code Coverage agent, used in smoke tests
[ -z "$JACOCO_AGENT_ENABLED" ] && JACOCO_AGENT_ENABLED=0

# The user that OpenNMS needs to run as.
[ -z "$RUNAS" ] && RUNAS=opennms

# Maximum file Descriptors to be setted by ulimit -n
MAXIMUM_FILE_DESCRIPTORS=20480

# maximum size of stack segment (in kbytes) to be setted by ulimit -s
MAXIMUM_SIZE_STACK_SEGMENT=8192

ADDITIONAL_CONTROLLER_OPTIONS=""
ADDITIONAL_MANAGER_OPTIONS=""
APP_PARMS_CONTROLLER=""
APP_PARMS_AFTER=""

export OPENNMS_EXECUTION_ENVIRONMENT="${OPENNMS_EXECUTION_ENVIRONMENT:-bareMetalOrVm}"

#### ------------> DO NOT CHANGE VARIABLES IN THIS FILE <------------- ####
#### Create $OPENNMS_HOME/etc/opennms.conf and put overrides in there. ####
#### ------------> DO NOT CHANGE VARIABLES IN THIS FILE <------------- ####

OVERRIDEABLE_ARRAYS=(RUNJAVA_OPTIONS ADDITIONAL_CONTROLLER_OPTIONS ADDITIONAL_MANAGER_OPTIONS APP_PARMS_CONTROLLER APP_PARMS_AFTER)

# shellcheck disable=SC1090,SC1091
. "${OPENNMS_HOME}/bin/_lib.sh"

# Load opennms.conf, if it exists, to override above configuration options.
if [ -f "${OPENNMS_HOME}/etc/opennms.conf" ]; then
	# shellcheck disable=SC1090
	__onms_read_conf "${OPENNMS_HOME}/etc/opennms.conf"
fi

# Load ~/.opennms-dev/opennms.conf if it exists, to override above configuration options.
if [ -f "${HOME}/.opennms-dev/opennms.conf" ]; then
	# shellcheck disable=SC1090
	__onms_read_conf "${HOME}/.opennms-dev/opennms.conf"
fi

for __overrideable in "${OVERRIDEABLE_ARRAYS[@]}"; do
	__onms_convert_to_array "${__overrideable}"
done

CONTROLLER_OPTIONS=("-Dopennms.home=$OPENNMS_HOME")
#CONTROLLER_OPTIONS+=("-Dlog4j.configuration=log4j.properties")

COMMAND=""

show_help () {
	cat <<END
Usage: $0 [-f] [-n] [-t] [-p] [-o] [-c timeout] [-v] [-Q] [-s] <command> [<service>]

  command options: start|stop|restart|status|ssh|check|pause|resume|kill|restart-fast

  service options: all|<a service id from the etc/service-configuration.xml>
      defaults to all

  The following options are available:

      -f  Foreground mode. Don't fork & execute.
      -n  "No execute" mode.  Don't call Java to do anything.
      -t  Test mode.  Enable JPDA on port 8001.
      -T  Test mode.  Enable JPDA on port 8001 and suspend until a debugger is attached.
      -p  Enable TIJMP profiling
      -o  Enable OProfile profiling
      -c  Controller HTTP connection timeout in seconds.
      -v  Verbose mode.  When used with the "status" command, gives the
          results for all ${install.package.description} services.
          When used with "start", enables some verbose debugging, such
          as details on garbage collection.
      -Q  Quick mode.  Don't wait for ${install.package.description} to start up.
          Useful if you want to watch the logs while ${install.package.description} starts up
          without wanting to open another terminal window.
      -s  systemd mode. The foreground mode option, -f, is ignored when this option is used.

END
	return
}

isRunning() {
	[ "$(/bin/ps -o pid= -o comm= -p "$1" | wc -l)" -gt 0 ];
}

getPid(){
	local __pid

	if [ -f "$OPENNMS_PIDFILE" ]; then
		__pid="$(cat "$OPENNMS_PIDFILE")"
	fi
	if [[ "$__pid" =~ ^[0-9]+$ ]] && [ "$__pid" -gt 0 ]; then
		echo "$__pid"
		return
	fi

	echo -1
}

findPid() {
	local __matched_pid;

	__matched_pid="$(pgrep -f _OPENNMS_LAUNCH 2>/dev/null || :)"
	if [[ "$__matched_pid" =~ ^[0-9]+$ ]] && [ "$__matched_pid" -gt 0 ]; then
		echo "$__matched_pid"
		return
	fi

	echo -1
}

getTempFile(){
	mktemp="$(command -v mktemp)"

	test -z "$TMPDIR" && TMPDIR=/tmp

	__TEMPFILE=""
	test -n "$mktemp" && {
		__TEMPFILE="$(mktemp "$TMPDIR/opennmsXXXXXX")"
	}
	test -z "$mktemp" && {
		__TEMPFILE="$TMPDIR/xmllint.$RANDOM.$RANDOM.$RANDOM.$$"
	}

	rm -f "$__TEMPFILE"
	(umask 077 && touch "$__TEMPFILE")
	echo "$__TEMPFILE"
}

checkXmlFiles(){
	XMLLINT="$(command -v xmllint || :)"

	if [ -n "$XMLLINT" ] && [ -x "$XMLLINT" ]; then
		TEMPFILE="$(getTempFile)"
		find -L "$OPENNMS_HOME/etc" -type f -name \*.xml 2>/dev/null | while read -r "FILE"; do
			if ! "$XMLLINT" "$FILE" >/dev/null 2>&1; then
				echo "ERROR: XML validation failed: $FILE"
				echo "	   run '$XMLLINT $FILE' for details"
			fi
		done >"$TEMPFILE"
		cat "$TEMPFILE" >&2
		FAILCOUNT="$(grep -c 'XML validation failed' < "$TEMPFILE")"
		rm -f "$TEMPFILE"
		if [ "$FAILCOUNT" -gt 0 ]; then
			echo "Validation failed on $FAILCOUNT XML files.  Exiting." >&2
			case "$COMMAND" in
				status)
					# when calling `opennms status` return 3 for "stopped"
					return 3	# From LSB: 3 - program is stopped
					;;
				*)
					# any other init command should return 6 on error
					return 6	# From LSB: 6 - program is not configured
					;;
			esac
		fi
	fi

	TEMPFILE="$(getTempFile)"
	find -L "$OPENNMS_HOME/etc/imports" -type f -name \*.xml 2>/dev/null | while read -r "FILE"; do
		if grep "non-ip-snmp-primary" "$FILE" >/dev/null 2>&1 || grep "non-ip-interfaces" "$FILE" >/dev/null 2>&1; then
			echo "ERROR: Old attributes found: $FILE" >&2
		fi
	done >/dev/null 2>"$TEMPFILE"

	FAILCOUNT="$(grep -c 'Old attributes found' < "$TEMPFILE")"
	if [ "$FAILCOUNT" -gt 0 ]; then
		cat <<END

WARNING!  The following file(s) contain the
'non-ip-snmp-primary' or 'non-ip-interfaces' attributes,
which no longer exist.

Please check your import files and remove the deprecated
attributes, and restart.

END
		cat "$TEMPFILE"
		echo ""
		rm -f "$TEMPFILE"

		return 6 # From LSB: 6 - program is not configured
	fi
	rm -f "$TEMPFILE"

	return 0
}

checkRpmFiles(){
	CHECKDIRS=("$OPENNMS_HOME/etc")

	for dir in "$OPENNMS_HOME"/*webapps/*; do
		if [ -d "$dir/WEB-INF" ]; then
			CHECKDIRS+=("$dir/WEB-INF")
		fi
	done

	for EXTENSION in rpmnew rpmsave dpkg-dist; do
		for CHECKDIR in "${CHECKDIRS[@]}"; do
			if [ "$(find -L "$CHECKDIR" -name \*."$EXTENSION" 2>/dev/null | wc -l)" -gt 0 ]; then
				cat <<END >&2

WARNING!  You have files that end in .$EXTENSION in your
OPENNMS_HOME ($OPENNMS_HOME) directory.

The format of the original files may have changed since
you modified them before installing a new version.
Please double-check that your configuration files are
up-to-date and delete any leftover .$EXTENSION files or
${install.package.description} will not start.

END
				case "$COMMAND" in
					status)
						# when calling `opennms status` return 3 for "stopped"
						return 3	# From LSB: 3 - program is stopped
						;;
					*)
						# any other init command should return 6 on error
						return 6	# From LSB: 6 - program is not configured
						;;
				esac
			fi
		done
	done

	return 0
}

checkLogDir(){
	local LOGDIR="$OPENNMS_HOME/logs"

	if [ ! -e "$LOGDIR" ]; then
		mkdir -p "$LOGDIR"
	fi

	return 0
}

checkConfigured() {
	if [ ! -f "$OPENNMS_HOME/etc/configured" ]; then
		cat <<END >&2
$0: ${install.package.description} not configured.
$OPENNMS_HOME/etc/configured does not exist.

You need to run the installer to set up the database.  In most
cases, it is enough to run:

  $OPENNMS_HOME/bin/install -dis

For details, see the install guide at:

http://www.opennms.org/index.php/QuickStart#Initialize_OpenNMS_and_the_Database

END

		case "$COMMAND" in
			status)
				# when calling `opennms status` return 3 for "stopped"
				return 3	# From LSB: 3 - program is stopped
				;;
			*)
				# any other init command should return 6 on error
				return 6	# From LSB: 6 - program is not configured
				;;
		esac
	fi
}

runCmd(){
	if [ "$NOEXECUTE" -eq 1 ]; then
		__tmp_array=()
		for ARG in "$@"; do
			__tmp_array+=("'$ARG'")
		done
		echo "Skipping execution:" "${__tmp_array[@]}"
	else
		"$@"
	fi
}

clearPids(){
	for PIDFILE in \
		"${OPENNMS_PIDFILE}" \
		"/karaf.pid" \
		"${LOG_DIRECTORY}/karaf.pid" \
	; do
		if [ -n "${PIDFILE}" ] && [ -r "${PIDFILE}" ]; then
			rm -f "${PIDFILE}"
		fi
	done
}

doStart(){
	checkConfigured || return 1
	checkXmlFiles   || return 1
	checkRpmFiles   || return 1
	checkLogDir     || return 1

	doStatus "$@"
	status=$?
	case $status in
		0)
			echo "${install.package.description} is already running." >&2
			return 0
			;;

		160)
			echo "${install.package.description} is partially running." >&2
			echo "If you have just attempted starting ${install.package.description}, please try again in a few" >&2
			echo "moments, otherwise, at least one service probably had issues starting." >&2
			echo "Check your logs in $LOG_DIRECTORY for errors." >&2
			return 1
			;;

		3)
			# don't do anything, it isn't running, which is good
			# because we are going to start it. :-)
			true
			;;

		*)
			echo "Unknown value return from doStatus: $status" >&2
			return 1
	esac


	if [ "$NOEXECUTE" -eq 0 ] && ! "${RUNJAVA_CMD[@]}" "${JAVA_CMD[@]}" "-Dopennms.home=$OPENNMS_HOME" -jar "$BOOTSTRAP" check; then
		echo "${install.package.description} was unable to connect to the 'opennms' database configured in opennms-datasources.xml." >&2
		return 1
	fi

	# make sure the directory exists for writing a pidfile
	local _piddir
	_piddir="$(dirname "${OPENNMS_PIDFILE}")"
	if [ ! -e "${_piddir}" ]; then
		mkdir -p "${_piddir}"
	fi

	if [ -e "${OPENNMS_PIDFILE}" ]; then
		echo "WARNING: starting OpenNMS, but PID file ${OPENNMS_PIDFILE} already exists."
		rm "${OPENNMS_PIDFILE}"
	fi

	##########################################################################
	# Run opennms with the "-t" option to enable the Java Platform Debugging
	# Architecture. This will open a server socket on port 8001 that can be
	# connected to by a remote java debugger. A good choice is JSwat which can
	# be found at http://www.bluemarsh.com
	###########################################################################
	if [ "$TEST" -gt 0 ]; then
		echo "- enabling JPDA debugging on port 8001" >&2
		JPDA=("-agentlib:jdwp=transport=dt_socket,server=y,address=${JPDA_ADDRESS},suspend=${JPDA_SUSPEND}")
	fi

	# See: http://www.khelekore.org/jmp/tijmp/
	if [ "$TPROFILE" -gt 0 ]; then
		echo "- enabling TIJMP Profiling" >&2
		JPDA=("-Dtijmp.jar=/usr/share/java/tijmp-0.6.jar" "-agentlib:tijmp" "${JPDA[@]}")
	fi

	# See: http://oprofile.sourceforge.net/doc/setup-jit.html
	if [ "$OPROFILE" -gt 0 ]; then
		echo "- enabling OProfile support" >&2
		JPDA=("-agentpath:/usr/lib/oprofile/libjvmti_oprofile.so" "${JPDA[@]}")
	fi

	if [ "$SERVICE" = "" ]; then
		APP_VM_PARMS=("${JPDA[@]}" "${MANAGER_OPTIONS[@]}")
		APP_PARMS_BEFORE=("start")
	else
		APP_VM_PARMS=("${CONTROLLER_OPTIONS[@]}")
		APP_PARMS_BEFORE=(start "$SERVICE")
	fi

	JAVA_EXE="$("$OPENNMS_HOME/bin/runjava" -c -v)"
	JAVA_EXE_BINDIR="$(dirname "$JAVA_EXE")"
	if [ ! -x "$JAVA_EXE_BINDIR"/javac ]; then
		# this is a JRE, try to use ECJ for Jetty compilation instead
		APP_VM_PARMS=("-Dorg.apache.jasper.compiler.disablejsr199=true" "${APP_VM_PARMS[@]}")
	fi
	# work around some jar files that are not compatible with updated JDK validation
	APP_VM_PARMS=("-Djdk.util.zip.disableZip64ExtraFieldValidation=true" "${APP_VM_PARMS[@]}")

	CMD=("${RUNJAVA_CMD[@]}" "-D_OPENNMS_LAUNCH" "${JAVA_CMD[@]}" "-Dopennms.pidfile=${OPENNMS_PIDFILE}" "${APP_VM_PARMS[@]}" -jar "$BOOTSTRAP" "${APP_PARMS_CONTROLLER[@]}" "${APP_PARMS_BEFORE[@]}" "$@" "${APP_PARMS_AFTER[@]}")
	if [ "$SYSTEMD" = 1 ]; then
		if [ "$BACKGROUND" = 0 ]; then
			echo "WARNING: -s (systemd) was passed, -f (foreground) is ignored" >&2
		fi
		echo "running ulimit -a"
		ulimit -a
		runCmd "${CMD[@]}" &
	else
		if [ "$BACKGROUND" = 1 ]; then
			# shellcheck disable=SC2129
			{
				echo "------------------------------------------------------------------------------"
				date
				echo "begin ulimit settings:"
				ulimit -a
				echo "end ulimit settings"
				echo "Executing command:" "${CMD[@]}"
			} >> "$REDIRECT"

			runCmd "${CMD[@]}" >> "$REDIRECT" 2>&1 &
		else
			echo "running ulimit -a"
			ulimit -a
			runCmd "${CMD[@]}"
			exit $?
		fi
	fi

	if [ "$START_TIMEOUT" -eq 0 ]; then
		# don't wait for startup
		printf "(not waiting for startup) "
		return 0
	fi

	# wait for startup
	STATUS_ATTEMPTS=0
	while [ "$STATUS_ATTEMPTS" -lt "$START_TIMEOUT" ]; do
		sleep $STATUS_WAIT
		if doStatus "$@"; then
			return 0
		fi
		if isRunning "$(getPid)"; then
			true		# Java process is still running... don't do anything
		else
			echo "Started ${install.package.description}, but it stopped running: for details see $REDIRECT" >&2
			return 1
		fi
		((STATUS_ATTEMPTS++))
	done

	echo "Started ${install.package.description}, but it has not finished starting up" >&2
	return 1
}

doPause(){
	if doStatus "$@"; then
		# If there is a PID file for OpenNMS, specify the PID
		PID="$(getPid)"
		if [ "$PID" -gt 0 ]; then
			PARM_PID=("-p" "$PID")
		else
			PARM_PID=()
		fi

		APP_VM_PARMS=("${CONTROLLER_OPTIONS[@]}")
		APP_PARMS_BEFORE=("${PARM_PID[@]}" -u "$JMX_URL" pause)
		if [ -n "$SERVICE" ]; then
			APP_PARMS_BEFORE+=("$SERVICE")
		fi
		runCmd "${RUNJAVA_CMD[@]}" "${JAVA_CMD[@]}" "${APP_VM_PARMS[@]}" -jar "$BOOTSTRAP" "${APP_PARMS_CONTROLLER[@]}" "${APP_PARMS_BEFORE[@]}" "$@" "${APP_PARMS_AFTER[@]}"
	else
		echo "${install.package.description} is not running." >&2
	fi
}

doResume(){
	if doStatus "$@"; then
		# If there is a PID file for OpenNMS, specify the PID
		PID="$(getPid)"
		if [ "$PID" -gt 0 ]; then
			PARM_PID=("-p" "$PID")
		else
			PARM_PID=()
		fi

		APP_VM_PARMS=("${CONTROLLER_OPTIONS[@]}")
		APP_PARMS_BEFORE=("${PARM_PID[@]}" -u "$JMX_URL" resume)
		if [ -n "$SERVICE" ]; then
			APP_PARMS_BEFORE+=("$SERVICE")
		fi
		runCmd "${RUNJAVA_CMD[@]}" "${JAVA_CMD[@]}" "${APP_VM_PARMS[@]}" -jar "$BOOTSTRAP" "${APP_PARMS_CONTROLLER[@]}" "${APP_PARMS_BEFORE[@]}" "$@" "${APP_PARMS_AFTER[@]}"
	else
		echo "${install.package.description} is not running." >&2
	fi
}

doCheck() {
	if doStatus "$@"; then
		# do nothing.. it's running
		exit 0
	fi

	echo "${install.package.description} is not running... Restarting" >&2
	"$OPENNMS_HOME/bin/opennms" start

	exit 0
}

doStop() {
	doStatus "$@"
	if [ "$?" -eq 3 ]; then
		(sleep 1; echo "Trying to stop ${install.package.description} but it's already stopped." >&2)
		return 0   # LSB says: stopping when stopped is successful
	fi

	PID="$(getPid)"
	if [ -z "$THREAD_DUMP" ] || [ "$THREAD_DUMP" -eq 0 ]; then
		echo "=== Skipping Complimentary Thread Dump ===" >> "$REDIRECT"
	elif [ "$PID" -gt 0 ]; then
		echo "=== ${install.package.description} Complimentary Thread Dump ===" >> "$REDIRECT"
		kill -3 "$PID" >> "$REDIRECT" 2>&1
	else
		echo "=== WARNING: Unable to Perform Complimentary Thread Dump ===" >> "$REDIRECT"
	fi

	if [ "$PID" -gt 0 ]; then
		# OpenNMS registers a shutdown hook that calls `stop` directly
		# there's no reason to do it programmatically unless we can't reach it any other way
		kill "$PID"
	else
		APP_VM_PARMS=("${CONTROLLER_OPTIONS[@]}")
		APP_PARMS_BEFORE=(-u "$JMX_URL" stop)
		if [ -n "$SERVICE" ]; then
			APP_PARMS_BEFORE+=("$SERVICE")
		fi
		runCmd "${RUNJAVA_CMD[@]}" "${JAVA_CMD[@]}" "${APP_VM_PARMS[@]}" -jar "$BOOTSTRAP" "${APP_PARMS_CONTROLLER[@]}" "${APP_PARMS_BEFORE[@]}" "$@" "${APP_PARMS_AFTER[@]}"
	fi

	STATUS_ATTEMPTS=0
	while [ "$STATUS_ATTEMPTS" -lt "$STOP_TIMEOUT" ]; do
		doStatus "$@"
		if [ "$?" -eq 3 ]; then
			clearPids
			return 0
		fi

		sleep $STATUS_WAIT
		((STATUS_ATTEMPTS++))
	done

	echo "WARNING: timed out waiting for OpenNMS to stop"
	return 1
}

doKill(){
	if doStatus "$@"; then
		# If there is a PID file for OpenNMS, specify the PID
		PID="$(getPid)"
		if [ "$PID" -gt 0 ]; then
			PARM_PID=("-p" "$PID")
		else
			PARM_PID=()
		fi

		APP_VM_PARMS=("${CONTROLLER_OPTIONS[@]}")
		APP_PARMS_BEFORE=("${PARM_PID[@]}" -u "$JMX_URL" exit)
		runCmd "${RUNJAVA_CMD[@]}" "${JAVA_CMD[@]}" "${APP_VM_PARMS[@]}" -jar "$BOOTSTRAP" "${APP_PARMS_CONTROLLER[@]}" "${APP_PARMS_BEFORE[@]}" "$@" "${APP_PARMS_AFTER[@]}"
	fi

	PID="$(getPid)"
	if [ "$PID" -gt 0 ]; then
		if isRunning "$PID"; then
			kill -9 "$PID" > /dev/null 2>&1
			return $?
		fi
	fi

	clearPids
	return 0
}

doStatus(){
	if [ $OPENNMS_UNIT_TEST -eq 1 ] && [ -n "$OPENNMS_UNIT_TEST_STATUS" ]; then
		eval "return $OPENNMS_UNIT_TEST_STATUS"
		return $?
	fi

	_status_filename="${LOG_DIRECTORY}/status-check.txt"
	_status_temp_filename="${_status_filename}.$$"

	PID="$(getPid)"
	MATCHED_PID="$(findPid)"

	if [ "${PID}" -gt 0 ]; then
		# If we found a PID, validate it then trigger a status check

		if [ "${MATCHED_PID}" -le 0 ]; then
			# we didn't find -D_OPENNMS_LAUNCH running, assume the
			# PID file is old and that OpenNMS is not running
			return 3
		fi

		if [ "${PID}" -ne "${MATCHED_PID}" ]; then
			echo "WARNING: 'pgrep -f _OPENNMS_LAUNCH' matched a different PID (${MATCHED_PID}) than found in ${OPENNMS_PIDFILE} (${PID}); something is not right"
			return 1
		fi

		# First, we touch a temporary file to compare to when the status file is written out.
		# We could use `find -newermt <datestamp>`, but this requires a new enough GNU find
		# that I'm afraid to rely on it... This algorithm should work on any POSIX system.

		touch "${_status_temp_filename}"

		# [ file1 -nt file2 ] only has second-level granularity, make sure
		# we have at least one second between the touch and the kill
		sleep 1

		# see org.opennms.netmgt.vmmgr.Invoker; we register a custom
		# signal handler to trigger writing a current status update

		kill -USR1 "$PID"

		# wait up to 30 seconds for a new status-check.txt file to have been written
		SLEEP_COUNT=0
		while [ "${SLEEP_COUNT}" -lt 30 ] && \
			[ "${_status_temp_filename}" -nt "${_status_filename}" ]
		do
			sleep 1
			((SLEEP_COUNT++))
		done

		# if a new status check file gets written, print the contents if $VERBOSE=1,
		# and then parse the output to determine which exit code to use

		if [ "${_status_filename}" -nt "${_status_temp_filename}" ]; then
			if [ "$VERBOSE" -gt 0 ]; then
				cat "${_status_filename}"
			fi

			_running_count="$(grep -i -c -E ' running$' "${_status_filename}")"
			_starting_count="$(grep -i -c -E ' (start_pending|starting)$' "${_status_filename}")"
			_total_count="$(wc -l < "${_status_filename}")"

			if [ "${_total_count}" -eq 0 ]; then
				rm "${_status_temp_filename}"

				echo "WARNING: failed to determine running services from status check"
				return 1
			elif [ "${_starting_count}" -gt 0 ] || [ "${_running_count}" -lt "${_total_count}" ]; then
				rm "${_status_temp_filename}"

				# LSB: reserved for app-specific values
				# we use this to indicate "partially running"
				return 160
			fi

			rm "${_status_temp_filename}"
			return 0
		fi

		rm "${_status_temp_filename}"
	elif [ "${MATCHED_PID}" -gt 0 ]; then
		# we did not get a PID file through normal means, but we found a running OpenNMS anyway
		# try to use the attach API and see if we can retrieve the status

		echo "WARNING: PID file was missing but OpenNMS was found at ${MATCHED_PID}"

		if [ "${VERBOSE}" -gt 0 ]; then
			PARM_VERBOSE=("-v")
		else
			PARM_VERBOSE=()
		fi

		APP_VM_PARMS=("${CONTROLLER_OPTIONS[@]}")
		APP_PARMS_BEFORE=(-p "${MATCHED_PID}" -u "${JMX_URL}" "${PARM_VERBOSE[@]}" status)
		runCmd "${RUNJAVA_CMD[@]}" "${JAVA_CMD[@]}" "${APP_VM_PARMS[@]}" -jar "${BOOTSTRAP}" "${APP_PARMS_CONTROLLER[@]}" "${APP_PARMS_BEFORE[@]}" "$@" "${APP_PARMS_AFTER[@]}"
	fi

	# if we make it this far, we couldn't find any PID, assume OpenNMS is not running

	# LSB - service not running
	return 3
}

FUNCTIONS_LOADED=0

if [ -f /etc/SuSE-release ]; then
	# shellcheck disable=SC1091
	. /etc/rc.status
	rc_reset
else
	# Source function library.
	for dir in /etc /etc/rc.d; do
		if [ -f "$dir/init.d/functions" ] && [ "$FUNCTIONS_LOADED" -eq 0 ]; then
			# shellcheck disable=SC1090,SC1091
			. "$dir/init.d/functions"
			FUNCTIONS_LOADED=1
		fi
	done
fi

ulimit -s $MAXIMUM_SIZE_STACK_SEGMENT > /dev/null 2>&1
ulimit -n $MAXIMUM_FILE_DESCRIPTORS > /dev/null 2>&1
if [ "$(uname)" = "Darwin" ]; then
	for flag in "-d" "-f" "-l" "-m" "-n" "-u" "-v"; do
		ulimit $flag unlimited >/dev/null 2>&1
	done
fi

umask 002

# XXX is this needed?  maybe we should "cd $OPENNMS_HOME/logs" so hotspot
# XXX error files go somewhere reasonable
cd "$OPENNMS_HOME" || { echo "could not \"cd $OPENNMS_HOME\"" >&2; exit 1; }

# define needed for grep to find opennms easily
RUNJAVA_CMD=("$OPENNMS_HOME/bin/runjava" "-r" "${RUNJAVA_OPTIONS[@]}" "--")
JAVA_CMD=()
BOOTSTRAP="$OPENNMS_HOME/lib/opennms_bootstrap.jar"

MANAGER_OPTIONS=()
MANAGER_OPTIONS+=("-Dopennms.home=$OPENNMS_HOME")
#MANAGER_OPTIONS+=("-Djcifs.properties=$OPENNMS_HOME/etc/jcifs.properties")
MANAGER_OPTIONS+=("-XX:+HeapDumpOnOutOfMemoryError")


if [ -n "$JAVA_INITIAL_HEAP_SIZE" ];then
 if [[ "$JAVA_INITIAL_HEAP_SIZE" =~ [0-9] ]];then
    if [ "$JAVA_INITIAL_HEAP_SIZE" -gt 0 ];then
      MANAGER_OPTIONS+=("-Xms${JAVA_INITIAL_HEAP_SIZE}m")
    fi
 else
      MANAGER_OPTIONS+=("-Xms${JAVA_INITIAL_HEAP_SIZE}")
 fi
fi


#if [ -z "$JAVA_HEAP_SIZE" ]; then
#  JAVA_HEAP_SIZE=4096
#fi
if [[ "$JAVA_HEAP_SIZE" =~ ^[0-9]+$ ]]; then
   MANAGER_OPTIONS+=("-Xmx${JAVA_HEAP_SIZE}m")
 else
   MANAGER_OPTIONS+=("-Xmx${JAVA_HEAP_SIZE}")
fi

# https://pyroscope.io/docs/java-tracing/#running-as-otel-java-instrumentation-extension
if [ -n "$PYROSCOPE_AGENT_ENABLED" ] && [ "$PYROSCOPE_AGENT_ENABLED" -gt 0 ]; then
	export PYROSCOPE_APPLICATION_NAME="${PYROSCOPE_APPLICATION_NAME:=OpenNMS}"
	export PYROSCOPE_SERVER_ADDRESS="${PYROSCOPE_SERVER_ADDRESS:=http://localhost:4040}" # so we don't get a warning

	MANAGER_OPTIONS+=("-javaagent:${OPENNMS_HOME}/agent/pyroscope-agent.jar")
fi

# https://www.jacoco.org/jacoco/trunk/doc/agent.html
if [ -n "$JACOCO_AGENT_ENABLED" ] && [ "$JACOCO_AGENT_ENABLED" -gt 0 ]; then
	MANAGER_OPTIONS+=("-javaagent:${OPENNMS_HOME}/agent/jacoco-agent.jar=output=none,jmx=true,excludes=org.drools.*")
fi

JAVA_VERSION="$("$OPENNMS_HOME/bin/runjava" -p -f 2> /dev/null)"
JAVA_SHORT_VERSION="$(echo "$JAVA_VERSION" | cut -d. -f1 | cut -d- -f1)"
if [ -z "$JAVA_SHORT_VERSION" ]; then
	JAVA_SHORT_VERSION=0
fi


KARAF_HOME="${OPENNMS_HOME}"
JPDA_ADDRESS="*:8001"
JPDA_SUSPEND="n"
if [ "$JAVA_SHORT_VERSION" -lt 9 ]; then
	# Java 8, use the endorsed directory
	JAVA_CMD=("${JAVA_CMD[@]}" "-Djava.endorsed.dirs=$OPENNMS_HOME/lib/endorsed")
	JPDA_ADDRESS="8001"
else
	# Java 9+, add required modules
	JAVA_CMD=("${JAVA_CMD[@]}" $("${OPENNMS_HOME}/bin/_module_opts.sh"))

	# extra options that aren't shared with Minion and Sentinel
	JAVA_CMD+=("--add-exports" "org.apache.karaf.specs/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED")

	# see NMS-12261 -- use backwards-compatible locale provider by default
	JAVA_CMD+=("-Djava.locale.providers=CLDR,COMPAT")
fi

if [ -n "$USE_INCGC" ] && [ "$USE_INCGC" = true ] ; then
	MANAGER_OPTIONS+=("-Xincgc")
fi

#if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Dcom.sun.management.jmxremote.port=)" -eq 0 ]; then
#	ADDITIONAL_MANAGER_OPTIONS+=("-Dcom.sun.management.jmxremote.port=18980")
#fi

#if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Dcom.sun.management.jmxremote.ssl=)" -eq 0 ]; then
#	ADDITIONAL_MANAGER_OPTIONS+=("-Dcom.sun.management.jmxremote.ssl=false")
#fi

if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Dcom.sun.management.jmxremote.authenticate=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-Dcom.sun.management.jmxremote.authenticate=true")
fi

if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Dcom.sun.management.jmxremote.login.config=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-Dcom.sun.management.jmxremote.login.config=opennms")
fi

if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Dcom.sun.management.jmxremote.access.file=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-Dcom.sun.management.jmxremote.access.file=$OPENNMS_HOME/etc/jmxremote.access")
fi

#if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Djava.security.debug=)" -eq 0 ]; then
#	ADDITIONAL_MANAGER_OPTIONS+=("-Djava.security.debug=all")
#fi

# see NMS-13437
if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Djdk.attach.allowAttachSelf=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-Djdk.attach.allowAttachSelf=true")
fi

if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -DisThreadContextMapInheritable=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-DisThreadContextMapInheritable=true")
fi

# Fix for NMS-8125: Groovy meta-space leak
if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Dgroovy.use.classvalue=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-Dgroovy.use.classvalue=true")
fi

if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c MaxMetaspaceSize=)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-XX:MaxMetaspaceSize=512m")
fi

if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c -- -Djava.io.tmpdir=)" -eq 0 ]; then
	if [ ! -d "$OPENNMS_HOME/data/tmp" ]; then
		mkdir -p "$OPENNMS_HOME/data/tmp"
	fi
	ADDITIONAL_MANAGER_OPTIONS+=("-Djava.io.tmpdir=$OPENNMS_HOME/data/tmp")
fi

# Fix for Attach API listener sometimes going AWOL
if [ "$(echo "${ADDITIONAL_MANAGER_OPTIONS[@]}" | grep -c StartAttachListener)" -eq 0 ]; then
	ADDITIONAL_MANAGER_OPTIONS+=("-XX:+StartAttachListener")
fi

if (( ${#ADDITIONAL_MANAGER_OPTIONS[@]} )); then
	MANAGER_OPTIONS+=("${ADDITIONAL_MANAGER_OPTIONS[@]}")
fi


if [ -n "$HOTSPOT" ] && [ "$HOTSPOT" = true ] ; then
	JAVA_CMD+=("-server")
fi

if (( ${#ADDITIONAL_CONTROLLER_OPTIONS} )); then
	CONTROLLER_OPTIONS+=("${ADDITIONAL_CONTROLLER_OPTIONS[@]}")
fi

TEST=0
TPROFILE=0
OPROFILE=0
NOEXECUTE=0
VERBOSE=0
BACKGROUND=1
SYSTEMD=0
SMALLTFLAG=0
BIGTFLAG=0

NAME="opennms"

while getopts c:fsntTvpoQ c; do
	case $c in
		c)
			APP_PARMS_CONTROLLER+=("-t" "$OPTARG")
			;;
		f)
			BACKGROUND=0
			;;
		s)
			SYSTEMD=1
			;;
		n)
			NOEXECUTE=1
			;;
		Q)
			START_TIMEOUT=0
			;;
		t)
			TEST=1
			SMALLTFLAG=1
			;;
		T)
			TEST=1
			JPDA_SUSPEND=y
			BIGTFLAG=1
			;;
		p)
			TPROFILE=1
			;;
		o)
			OPROFILE=1
			;;
		v)
			VERBOSE=1
			VERBOSE_GC=1
			;;

		"?")
			show_help
			exit 1
			;;
	esac
done
shift "$((OPTIND - 1))"

if [ "$#" -eq 0 ]; then
	show_help
	exit 1
else
	COMMAND="$1"; shift
fi

if [ "$#" -gt 0 ]; then
	SERVICE="$1"; shift
else
	SERVICE=""
fi

if [ "$#" -gt 0 ]; then
	show_help
	exit 1
fi

if [ "$SERVICE" = "all" ]; then
	SERVICE=""
fi

if [ "$VERBOSE_GC" != "" ]; then
	MANAGER_OPTIONS+=("-verbose:gc")
fi

RUNUSER="$(command -v runuser 2>/dev/null || which runuser 2>/dev/null || :)"
myuser="$(id -u -n)"
if [ "$myuser" != "$RUNAS" ]; then
	if [ "$myuser" = "root" ] && [ -x "$RUNUSER" ]; then
		echo "WARNING: relaunching as $RUNAS" >&2
		_cmd=("$RUNUSER" "-u" "$RUNAS" -- "$0" "${INCOMING_ARGS[@]}");
		exec "${_cmd[@]}"
	fi
	echo "ERROR: you should run this script as ${RUNAS}, not '${myuser}'." >&2
	# shellcheck disable=SC2145
	echo "       To correct this, try 'sudo -u $RUNAS $0 $@'" >&2
	echo "       If you wish for OpenNMS to run as ${myuser} instead," >&2
	echo "       create or edit ${OPENNMS_HOME}/etc/opennms.conf and set 'RUNAS=${myuser}'" >&2
	echo "       If you have already edited opennms.conf, make sure that 'User=' is also set" >&2
	echo "       in the OpenNMS systemd service." >&2
	exit 4 # According to LSB: 4 - user had insufficient privileges
fi

if [ "$SYSTEMD" -ne 1 ]; then
	SYSTEMCTL="$(command -v systemctl 2>/dev/null || which systemctl 2>/dev/null || :)"
	if [ -x "$SYSTEMCTL" ]; then
		# shellcheck disable=SC2016
		echo 'WARNING: `systemctl` was found, but OpenNMS is not being run in Systemd mode.'
		echo 'You will not be able to use systemctl to manage an OpenNMS started manually from the CLI.'
	fi
fi

case "$COMMAND" in
	start|spawn)
		printf 'Starting %s: ' "${install.package.description}"

		doStart "$@"
		__ret="$?"

		if [ -f /etc/SuSE-release ]; then
			# Remember status and be verbose
			rc_status -v
		elif [ "$FUNCTIONS_LOADED" -ne 0 ]; then
			if [ "$__ret" -eq 0 ]; then
				echo_success
				if [ -w /var/lock/subsys ]; then
					touch /var/lock/subsys/${NAME}
				fi
			else
				echo_failure
			fi
			echo ""
		else
			if [ "$__ret" -eq 0 ]; then
				echo "ok"
			else
				echo "failed"
			fi
		fi
		exit "$__ret"
		;;

	stop)
		printf 'Stopping %s: ' "${install.package.description}"

		doStop "$@"
		__ret="$?"

		if [ "$__ret" -gt 0 ]; then
			doKill "$@"
			__ret="$?"
		fi

		if [ -f /etc/SuSE-release ]; then
			# Remember status and be verbose
			rc_status -v
		elif [ "$FUNCTIONS_LOADED" -ne 0 ]; then
			if [ "$__ret" -eq 0 ]; then
				echo_success
			else
				echo_failure
			fi
			if [ -w /var/lock/subsys ]; then
				rm -f /var/lock/subsys/${NAME}
			fi
			echo ""
		else
			if [ "$__ret" -eq 0 ]; then
				echo "stopped"
			else
				echo "failed"
			fi
		fi

		if [ "$__ret" -eq 0 ]; then
			clearPids
		fi

		exit "$__ret"
		;;

	restart)
		## Stop the service and regardless of whether it was
		## running or not, start it again.
		__opennms_cmd=("$OPENNMS_HOME/bin/opennms");
		if [ "$BIGTFLAG" -eq 1 ]; then
			__opennms_cmd+=("-T")
		fi
		if [ "$SMALLTFLAG" -eq 1 ]; then
			__opennms_cmd+=("-t")
		fi
		if [ "$NOEXECUTE" -eq 1 ]; then
			__opennms_cmd+=("-n")
		fi
		if [ "$BACKGROUND" -eq 0 ]; then
			__opennms_cmd+=("-f")
		fi
		"${__opennms_cmd[@]}" "stop" && \
		"${__opennms_cmd[@]}" "start"
		;;

	restart-fast)
		## Kill the service and regardless of whether it was
		## running or not, start it again.
		__opennms_cmd=("$OPENNMS_HOME/bin/opennms");
		if [ "$BIGTFLAG" -eq 1 ]; then
			__opennms_cmd+=("-T")
		fi
		if [ "$SMALLTFLAG" -eq 1 ]; then
			__opennms_cmd+=("-t")
		fi
		if [ "$NOEXECUTE" -eq 1 ]; then
			__opennms_cmd+=("-n")
		fi
		if [ "$BACKGROUND" -eq 0 ]; then
			__opennms_cmd+=("-f")
		fi
		"${__opennms_cmd[@]}" "kill" && \
		"${__opennms_cmd[@]}" "start"
		;;

	status)
		if [ -f /etc/SuSE-release ]; then
			printf 'Checking for %s: ' "${install.package.description}"
			if [ "$VERBOSE" -gt 0 ]; then
				echo ""
			fi
			doStatus "$@"

			# Remember status and be verbose
			rc_status -v
		else
			doStatus "$@"
			__ret="$?"
			case "$__ret" in
				0)
					echo "${NAME} is running"
					;;

				3)
					echo "${NAME} is stopped"
					;;

				160)
					echo "${NAME} is partially running"
					;;

				*)
					echo "Unknown return code from doStatus: $__ret" >&2
			esac
		fi
                exit "$__ret"
		;;

	configtest)
		printf "Validating XML files: "
		if checkXmlFiles; then
			echo "PASSED"
		fi
		;;

	ssh)
		karafSshPort="$(grep "^sshPort" "${OPENNMS_HOME}/etc/org.apache.karaf.shell.cfg" | cut -d= -f2)"
		karafUser="$(grep "^karaf.local.user" "${OPENNMS_HOME}/etc/system.properties" | cut -d= -f2 | tr -d "[:blank:]")"
		printf 'Trying to establish SSH connection to OpenNMS Karaf Shell (%s@localhost:%s)... ' "$karafUser" "$karafSshPort"
		ssh -o NoHostAuthenticationForLocalhost=yes -o HostKeyAlgorithms=+ssh-dss -l "$karafUser" -p "$karafSshPort" localhost
		;;

	pause)
		doPause "$@"
		;;

	check)
		doCheck "$@"
		;;

	resume)
		doResume "$@"
		;;

	kill)
		doKill "$@"
		;;

	*)
		echo ""
		echo "ERROR: unknown command \"$COMMAND\""
		show_help
		exit 2
		;;
esac
