#!/bin/bash -e

function assertTarFileSpecified()
{
    if [ -z "${TARFILE}" ]
    then
        printHelp
        printError "Error: Tar filename has not been specified"
    fi
}

function getAsConfFile()
{
    local _candidates=$(find /etc -name "*.conf"|xargs grep -l "JBOSS_HOME"|xargs grep -l "SNMPAGENT_HOME")
    if [ -z "${_candidates}" ]
    then
        echo
        return
    fi

    ls -1dt ${_candidates}|head -n 1
}

function getMbConfFile()
{
    local _candidates=$(find /etc -name "*.conf"|xargs grep -l "RTPPROXY_HOME")
    if [ -z "${_candidates}" ]
    then
        echo
        return
    fi

    ls -1dt ${_candidates}|head -n 1
}

function getJdkExecutable()
{
    local _list_conf_files=$1
    local _exe_name=$2

    local _jdk_executable=""
    for _conf_file in $_list_conf_files
    do
        if [ -n "${_conf_file}" ] && [ -f "${_conf_file}" ]
        then
            local _java_home=$(grep -i 'JAVA_HOME=' $_conf_file  | cut -f2 -d'=')
            if [ -n "$_java_home" ]
            then
                _jdk_executable=$_java_home/bin/$_exe_name
                break
            fi
        fi
    done

    if [ -z "$_jdk_executable" ]
    then
        printError "Cannot find $_exe_name"
    fi

	if [ ! -x $_jdk_executable ]
	then
        printError "$_jdk_executable is not executable"
	fi

    echo $_jdk_executable
}

function getListOfLogFiles()
{
    local _dir=$1

    local _log_files=""
    local _pwd=$PWD

    if [ -d $_dir ]
    then
        cd $_dir

        baseServerLogDirArray=$(find -name "*.log"  -o \( -path "*/log/*" -name "*.xml" \) | sort -u)

        for i in ${baseServerLogDirArray[@]}
        do
            _log_files=${_log_files}" "$(readlink -f $i)
        done

        cd $_pwd
    fi

    echo ${_log_files}
}

function createOutputDirectory()
{
    mktemp -d --tmpdir logcapture.temp-XXX
}

function createLogFileCapturingDaemons()
{
    local _original_log_dir=$1
    local _captured_dir=$2

    if [ -d "${_original_log_dir}" ]
    then
        local _original_log_files=$(getListOfLogFiles "${_original_log_dir}")
    
        for _original_log in ${_original_log_files}
        do
            local _captured_log_file=${_captured_dir}/$(convertToRelativePath "${_original_log}" "${_original_log_dir}")
            mkdir -p $(dirname ${_captured_log_file})
            tail -n 0 -F ${_original_log} > ${_captured_log_file} &
            CAPTURE_PROC_IDS="${CAPTURE_PROC_IDS} $!"
        done
    fi
}

function copyNewLogFileOutput()
{
    local _output_dir=$(createOutputDirectory)
    local _as_output_dir=${_output_dir}/AS
    local _mb_output_dir=${_output_dir}/MB
    CAPTURE_PROC_IDS=""

    if [ -n "${AS_BASE_DIR}" ]
    then
        createLogFileCapturingDaemons "${AS_BASE_DIR}" "${_as_output_dir}"
    fi

    if [ -n "${MB_HOME_DIR}" ]
    then
        createLogFileCapturingDaemons "${MB_HOME_DIR}" "${_mb_output_dir}"
    fi

    if [ -n "${DO_PCAP}" ]
    then
        local _capture_cmd=""
        local _hostname=$(hostname)
        if [ -x /usr/sbin/tcpdump ]
        then
            _capture_cmd="tcpdump -q -s0 -iany -vvv -tttt -w ${_output_dir}/tcpdump_${_hostname}.pcap"
        elif [ -x /usr/sbin/tshark ]
        then
            _capture_cmd="tshark -w ${_output_dir}/tshark_${_hostname}.pcap"
        fi

        if [ -n "${_capture_cmd}" ]
        then
            ${_capture_cmd} -i any 2>/dev/null &
            CAPTURE_PROC_IDS="${CAPTURE_PROC_IDS} $!"
        fi
    fi

    if [ -n "$DO_VMSTAT" ]
    then
        vmstat 2 > ${_output_dir}/vmstat.out &
        CAPTURE_PROC_IDS="${CAPTURE_PROC_IDS} $!"
    fi

    trap "killCopyingBgroundProcesses \"${CAPTURE_PROC_IDS}\"" INT TERM
    trap "collateAllFiles \"${_output_dir}\" \"${_as_output_dir}\" \"${_mb_output_dir}\" \"${TARFILE}\"" EXIT

    echo "*****************************************************"
    echo "* Capturing files to directory ${_output_dir}  *"
    echo "* Press <CTL>-C when ready to tar up captured files *"
    echo "*****************************************************"
    wait ${CAPTURE_PROC_IDS}
}

# Returns a relative path of the first argument.
# The second argument contains a list of directory names to strip from the first argument
# e.g If the first arg is "/foo/bar/file.txt" and the second argument contained "/foo", the returned string would be
# "bar/file.txt"
function convertToRelativePath()
{
    local _log_file=$1
    local _base_dir_list=$2

    for _dir in ${_base_dir_list}
    do
        if [ -n "${_dir}" ]
        then
            local _base_dir=""
            case ${_dir} in
                */) _base_dir="${_dir}" ;;
                *) _base_dir="${_dir}/" ;;
            esac
            _log_file=${_log_file#${_base_dir}}
        fi
    done

    echo ${_log_file}
}


function killCopyingBgroundProcesses()
{
    local _ppids="$1"

    echo -e "\nKilling daemon processes, please wait..."
    kill ${_ppids}
}

#Gets the PIDs that are children of the AS Process Controller
function getFasChildPids()
{
    local _ps_controller_search_string="-D\\[Process Controller]"
    local _controller_pid=$(ps -ef | grep " ${_ps_controller_search_string}"| grep -vw grep | awk '{print $2}')
    if [ -n "${_controller_pid}" ]
    then
        echo $(ps --no-headers -o pid --ppid ${_controller_pid})
    fi
}

#Gets the PIDs that are children of the MB Manager Process
function getMediaBrokerChildPids()
{
    local _mb_manager_pid=$1
    if [ -n "${_mb_manager_pid}" ]
    then
        echo $(ps --no-headers -o pid --ppid ${_mb_manager_pid})
    fi
}

function collectHostInfo()
{
    local _host_info=${_output_dir}/$(hostname)"_info.txt"

    if [ -w $_host_info ]
    then
        rm -f $_host_info
    fi

    for command in \
"uname -a" \
"cat /proc/meminfo" \
"df -h" \
"ulimit -a" \
"ip addr" \
"cat /etc/hosts" \
"cat /etc/resolv.conf" \
"ip route show" \
"check_service_not_running iptables" \
"check_service_not_running ip6tables" \
"ps -ef" \
"ss -pantu"
    do
        echo "$command" >> $_host_info
        echo $(head -c $(echo -n "$command" | wc -m) < /dev/zero | tr '\0' '=')  >> $_host_info #Underline the command
        $command >> $_host_info
        echo >> $_host_info
    done
}

check_service_not_running()
{
    local _service_name=$1

    local _test_name="Check that ${_service_name} service is not running"
    if [ ! -r /etc/init.d/${_service_name} ]
    then
        echo "${_test_name}: ${_service_name} service not installed"
    else
        set +e
        service ${_service_name} status &>/dev/null
        local _service_running=$?
        set -e
        if [ ${_service_running} -eq 0 ]
        then
            echo "Warning: ${_service_name} is running"
            echo "If not configured correctly, ${_service_name} can cause problems."
            echo "iptables -L"
            echo "==========="
			iptables -L
        else
            echo "Passed: ${_test_name}"
        fi
    fi
    echo
}


function collateAllFiles()
{
    local _output_dir=$1
    local _as_output_dir=$2
    local _mb_output_dir=$3

    echo "Collating information, please wait..."

    if [ -n "$DO_THREADS" ] || [ -n "$DO_MEMORY" ]
    then
        local _ps_server_search_string_as="-D\\[Server:"
        local _as_server_candidates=$(getFasChildPids)
        if [ -n "${_as_server_candidates}" ]
        then
            for _pid in ${_as_server_candidates}
            do
                local _as_server_name=$(ps --no-headers -o command --pid ${_pid}| grep " ${_ps_server_search_string_as}" | awk '{print $2}')
                # ^^^ $1 is "/.../java", $2 is "-D[Server:...]" (output from the ps command)
                if [ -n "${_as_server_name}" ]
                then
                    _as_server_name=${_as_server_name#${_ps_server_search_string_as}} #Strip the "-D[Server:" from the beginning
                    _as_server_name=${_as_server_name%]} #Strip the trailing ']'

                    set +e
                    [ -n "$DO_THREADS" ] && $JSTACK_EXE $FORCE $_pid > ${_as_output_dir}/servers/${_as_server_name}/thread.dump
                    [ -n "$DO_MEMORY" ] && $JMAP_EXE $FORCE -dump:live,format=b,file=${_as_output_dir}/servers/${_as_server_name}/heap.bin $_pid >/dev/null
                    set -e
                fi
            done
        fi

        local _mb_manager_pid=$(ps -ef | grep ' -jar multi-rtp-proxy-manager.jar'| grep -v grep | awk '{print $2}')
        local _mb_manager_logname=multi-rtp-proxy-manager
        if [ -z "$_mb_manager_pid" ]
        then
            _mb_manager_pid=$(ps -ef | grep ' -jar rtp-proxy.jar'| grep -v grep | awk '{print $2}')
            _mb_manager_logname=rtp-proxy
            # ^^^ Legacy, single process media broker
        fi

        if [ -n "$_mb_manager_pid" ]
        then
            if [ -n "$DO_THREADS" ]
            then
                [ -d "${_mb_output_dir}" ] || mkdir -p "${_mb_output_dir}"
                set +e
                $JSTACK_EXE $FORCE $_mb_manager_pid > ${_mb_output_dir}/${_mb_manager_logname}-thread.dump
                set -e
            fi
            if [ -n "$DO_MEMORY" ]
            then
                [ -d "${_mb_output_dir}" ] || mkdir -p "${_mb_output_dir}"
                set +e
                $JMAP_EXE $FORCE -dump:live,format=b,file=${_mb_output_dir}/${_mb_manager_logname}-heap.bin $_mb_manager_pid
                set -e
            fi
        
            local _mb_child_candidates=$(getMediaBrokerChildPids "$_mb_manager_pid")
            if [ -n "$_mb_child_candidates" ]
            then
                local _ps_server_search_string_mb="-D\\[Proxy:"
                for _pid in ${_mb_child_candidates}
                do
                    local _mb_child_name=$(ps --no-headers -o command --pid ${_pid}| grep " ${_ps_server_search_string_mb}" | awk '{print $2}')
                    # ^^^ $1 is "/.../java", $2 is "-D[Proxy:...]" (output from the ps command)
                    if [ -n "${_mb_child_name}" ]
                    then
                        _mb_child_name=${_mb_child_name#${_ps_server_search_string_mb}} #Strip the "-D[Proxy:" from the beginning
                        _mb_child_name=${_mb_child_name%]} #Strip the trailing ']'

                        set +e
                        [ -n "$DO_THREADS" ] && $JSTACK_EXE $FORCE $_pid > ${_mb_output_dir}/rtp-proxy-instances/${_mb_child_name}/thread.dump
                        [ -n "$DO_MEMORY" ] && $JMAP_EXE $FORCE -dump:live,format=b,file=${_mb_output_dir}/rtp-proxy-instances/${_mb_child_name}/heap.bin $_pid >/dev/null
                        set -e
                    fi
                done
            fi
        fi
    fi

    if [ -n "$DO_CONFIG" ]
    then
        if [ -n "$AS_CONF_DIR" ]
        then
            mkdir -p ${_as_output_dir}/configuration/
            local _conf_file_list=$(find ${AS_CONF_DIR} -maxdepth 1 -type f)
            cp ${_conf_file_list} ${_as_output_dir}/configuration/
        fi

	if [ -n "$MB_HOME_DIR" ]
	then
		if [ -d ${_mb_output_dir} ]
		then
			cp -r ${MB_HOME_DIR}/*.properties ${_mb_output_dir}/
		fi
	fi
	
	collectHostInfo
    fi

    echo "Creating tar file ${TARFILE} from directory ${_output_dir}"
    mkdir -p $(dirname ${TARFILE})
    tar -C ${_output_dir} -cf ${TARFILE} .

    if [ -z "${DO_NOT_CLEAN}" ]
    then
        rm -rf ${_output_dir}
    fi
}

function printError()
{
    echo
    echo "Error: " $* 1>&2
    exit 1
}

function confirmIfForceFlagUsed()
{
    if [ -n "${DO_FORCE}" -a \( -n "$DO_MEMORY" -o -n "$DO_THREADS" \) ]
    then
        read -p "The -F flag can cause disruption on production systems, are you sure you wish to continue?: "
        case "$REPLY" in
            y|Y) return;;
            *)  exit 1;;
        esac
    fi
}

function printHelp()
{
    cat << EOF

Usage: $(basename $0) [-a] [-m] [-n] [-p] [-t] [-v] [-F] -f tarfile
    Archives log files to the specified tar file

    -f, --tar-file	   	The name of the tar file
Optional:
    -c, --config        Optional: This will include configuration files
    -t, --threads       Optional: This will include thread dumps of AS and MB in the tar file
    -m, --memory        Optional: This will include heap memory dumps of AS and MB in the tar file
    -n, --do-not-clean  Optional: Do NOT clean up output directory at end of run
    -p, --capture-pcap  Optional: Capture network traffic in a pcap file
    -v, --vmstat        Optional: This will include vmstat output in the tar file
    -a, --all           Optional: All optional options
    -F, --force         Optional: Uses the -F option of jmap and jstack if memory and/or heap dumps
                                  are being performed. Only meaningful for -t -m -a
    -h, --help          Display this help message
EOF
}

############################
#          Main            #
############################
progname=$(basename $0)

TEMP=$(getopt -o Fhcmnptavf: --long force,help,config,tar-file,memory,do-not-clean,capture-pcap,threads,all,vmstat: \
     -n ${progname} -- "$@")

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"
while true ; do
	case "$1" in
	    -F|--force)         DO_FORCE=true; shift 1;;
		-c|--config)		DO_CONFIG=true;	shift 1;;
		-f|--tar-file)		TARFILE=$2;	shift 2;;
		-t|--threads)		DO_THREADS=true;	shift 1;; 
		-m|--memory)		DO_MEMORY=true;	shift 1;; 
		-n|--do-not-clean)	DO_NOT_CLEAN=true;	shift 1;; 
		-p|--capture-pcap)	DO_PCAP=true;	shift 1;; 
		-v|--vmstat)		DO_VMSTAT=true;	shift 1;; 
		-a|--all)			DO_MEMORY=true;	DO_THREADS=true; DO_VMSTAT=true; DO_PCAP=true; shift 1;; 
		-h|--help)			help=true;	shift 1;; 
		--) shift; break;;
		*) echo "Internal error!"; exit 1;;
	esac
done

if [ "${help}" != "" ]
then
    printHelp
    exit 1
fi

assertTarFileSpecified
confirmIfForceFlagUsed

AS_CONF=$(getAsConfFile)
if [ -n "${AS_CONF}" ] && [ -f "${AS_CONF}" ]
then
    AS_HOME_DIR=$(grep -i 'JBOSS_HOME=' $AS_CONF  | cut -f2 -d'=')
    if [ -n "$AS_HOME_DIR" ] && [ -d "$AS_HOME_DIR" ]
    then
        AS_BASE_DIR=$AS_HOME_DIR/domain
        AS_CONF_DIR=${AS_BASE_DIR}/configuration
    fi  
fi

MB_CONF=$(getMbConfFile)
if [ -n "${MB_CONF}" ] && [ -f ${MB_CONF} ]
then
    . $MB_CONF
    MB_HOME_DIR=$RTPPROXY_HOME



fi

if [ -z "${AS_CONF}" ] && [ -z "${MB_CONF}" ]
then
    DO_MEMORY=
    DO_THREADS=
fi

[ -n "$DO_MEMORY" ] && JMAP_EXE=$(getJdkExecutable "$AS_CONF $MB_CONF" jmap)
[ -n "$DO_THREADS" ] && JSTACK_EXE=$(getJdkExecutable "$AS_CONF $MB_CONF" jstack)
[ -n "${DO_FORCE}" ] && FORCE="-F"

copyNewLogFileOutput

