#!/bin/sh

# Wrapper script for making a thread dump from a running OpenNMS
# Copyright (c) 2018-2021 The OpenNMS Group
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by # the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# For more information contact:
#      OpenNMS Licensing       <license@opennms.org>
#      http://www.opennms.org/
#      http://www.opennms.com/
#
# put an '@' here so we don't break maven substition

# shellcheck disable=SC2016
opennms_home="${OPENNMS_HOME}"
if [ -d '${install.dir}' ]; then
    # shellcheck disable=SC2153
    opennms_home='${install.dir}'
fi
OPENNMS_HOME="${opennms_home}"

default_date_pattern="%Y%m%d_%H%M%S"
default_oldest_survivor="120"
default_output_dir="${opennms_home}/logs"
default_base_filename="opennms-thread-dump"

basename="$(basename "$0")"

die(){
    echo "${basename}: $*" >&2
    exit 1
}
warn(){
    echo "${basename}: $*" >&2
}
tell(){
    echo "${basename}: $*" >&3
}

#
# Function: printHelp()
# Give the user some basic help.
#
printHelp() {
    echo "usage: $basename [-d] [-b] [-p] [-o]"
    echo ""
    echo "Options:"
    echo ""
    echo "  -d  Write the thread dump into this directory, instead"
    echo "      of ${default_output_dir}."
    echo ""
    echo "  -b  Use a different base filename for the output files, instead"
    echo "      of ${default_base_filename}"
    echo ""
    echo "  -p  Use a custom date pattern for the date / time portion of"
    echo "      output filenames. The default is ${default_date_pattern}."
    echo "      See the date(1) man page for format options."
    echo ""
    echo "  -o  Age, in minutes, of the oldest thread dump file that should"
    echo "      be allowed to survive the cleanup step which runs at the"
    echo "      start of each run of this command. Default is ${default_oldest_survivor}."
    echo ""
    echo ""
}


find_jstack_bin() {
    if [ ! -e "${opennms_home}/etc/java.conf" ]; then
        die "FATAL: File ${opennms_home}/etc/java.conf does not exist."
    fi
    java_bin="$(head -n 1 "${opennms_home}/etc/java.conf")"
    java_bin_actual="$(readlink -f "${java_bin}")"
    echo "${java_bin_actual}" | grep -q 'jre/bin/java'
    # shellcheck disable=SC2181
    if [ "$?" -eq 0 ]; then
        java_home_actual="$(readlink -f "${java_bin_actual}/../..")"
    else
        java_home_actual="$(readlink -f "${java_bin_actual}/..")"
    fi
    if [ ! -x "${java_home_actual}/bin/jstack" ]; then
        die "FATAL: ${java_home_actual}/bin/jstack does not exist or is not executable."
    fi
}

find_opennms_pid() {
    if [ -r "${opennms_home}/logs/opennms.pid" ]; then
        opennms_pid="$(head -n 1 "${opennms_home}/logs/opennms.pid")"
    elif [ -r "${opennms_home}/logs/karaf.pid" ]; then
        opennms_pid="$(head -n 1 "${opennms_home}/logs/karaf.pid")"
    else
        die "FATAL: Unable to determine OpenNMS PID. Tried opennms.pid and karaf.pid."
    fi
}

parseArgs() {
    output_dir="${default_output_dir}"
    base_filename="${default_base_filename}"
    date_pattern="${default_date_pattern}"
    oldest_survivor="${default_oldest_survivor}"

    while getopts hd:b:p:o: c
    do
      case "$c" in
          d)
              output_dir="$OPTARG"
              ;;

          b)
              base_filename="$OPTARG"
              ;;
    
          p)
              date_pattern="$OPTARG"
              ;;

          o)
              oldest_survivor="$OPTARG"
              ;;

          h)
              printHelp
              exit 0
              ;;
          *)
              echo "FATAL: unknown option: $c"
              exit 1
              ;;
      esac
    done
    shift "$((OPTIND - 1))"
}

deleteOldThreadDumps() {
    find "${output_dir}/" -name "${base_filename}".'*' -a -mmin "+${oldest_survivor}" -delete
}

writeThreadDump() {
    dump_file="${output_dir}/${base_filename}.$(date "+${date_pattern}")"
    "${java_home_actual}/bin/jstack" -l "${opennms_pid}" > "${dump_file}" || die "FATAL: Failed to write thread dump. Exiting with no cleanup."
    chmod 0666 "${dump_file}"
}

[ -z "$RUNAS" ] && RUNAS=opennms
# Load opennms.conf, if it exists, to override configuration options.
if [ -f "${opennms_home}/etc/opennms.conf" ]; then
    # shellcheck disable=SC1090,SC1091
    . "${opennms_home}/etc/opennms.conf"
fi

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

parseArgs "$@"

myuser="$(id -u -n)"
if [ "$myuser" = "$RUNAS" ]; then
    true # all is well
else
    echo "ERROR: you should run this script as ${RUNAS}, not '${myuser}'." >&2
    echo "       Create or edit ${opennms_home}/etc/opennms.conf and set 'RUNAS=${myuser}'" >&2
    echo "       if you wish for OpenNMS to run as ${myuser} instead." >&2
    exit 4 # According to LSB: 4 - user had insufficient privileges
fi

find_jstack_bin
find_opennms_pid
writeThreadDump
deleteOldThreadDumps
