#!/bin/bash
#
#
#   OPEN-XCHANGE legal information
#
#   All intellectual property rights in the Software are protected by
#   international copyright laws.
#
#
#   In some countries OX, OX Open-Xchange, open xchange and OXtender
#   as well as the corresponding Logos OX Open-Xchange and OX are registered
#   trademarks of the Open-Xchange, Inc. group of companies.
#   The use of the Logos is not covered by the GNU General Public License.
#   Instead, you are allowed to use these Logos according to the terms and
#   conditions of the Creative Commons License, Version 2.5, Attribution,
#   Non-commercial, ShareAlike, and the interpretation of the term
#   Non-commercial applicable to the aforementioned license is published
#   on the web site http://www.open-xchange.com/EN/legal/index.html.
#
#   Please make sure that third-party modules and libraries are used
#   according to their respective licenses.
#
#   Any modifications to this package must retain all copyright notices
#   of the original copyright holder(s) for the original code used.
#
#   After any such modifications, the original and derivative code shall remain
#   under the copyright of the copyright holder(s) and/or original author(s)per
#   the Attribution and Assignment Agreement that can be located at
#   http://www.open-xchange.com/EN/developer/. The contributing author shall be
#   given Attribution for the derivative code and a license granting use.
#
#    Copyright (C) 2004-2013 Open-Xchange, Inc.
#    Mail: info@open-xchange.com
#
#
#    This program is free software; you can redistribute it and/or modify it
#    under the terms of the GNU General Public License, Version 2 as published
#    by the Free Software Foundation.
#
#    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 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
#
#

OXFUNCTIONS=/opt/open-xchange/lib/oxfunctions.sh
export PATH="${PATH}:/opt/open-xchange/sbin"

# some defaults
PATH="${PATH}:/opt/open-xchange/sbin"
TMP=/tmp
TMP_LONG=tempdir
LOGDIR=ox_support_infos-$(date +%Y%m%d-%H%M%S)
LOGDIR_LONG=logdir
LOGARC=${TMP}/${LOGDIR}.tar
LOGARC_SUFFIX=".gz"
LOGS_SUFFIX=".bz2"
LOGARC_LONG=log-archive
KEEP_TMP=false
KEEP_TMP_LONG=keep-tmp-files
KEEP_TMP_VALUES="true false"
CREATE_THREAD_DUMP=false
CREATE_THREAD_DUMP_LONG=thread-dump
CREATE_THREAD_DUMP_VALUES="true false"
CREATE_HEAP_DUMP=false
CREATE_HEAP_DUMP_LONG=heap-dump
CREATE_HEAP_DUMP_VALUES="true false"
SKIP_COMPRESSION=false
SKIP_COMPRESSION_LONG=skip-compression
SKIP_COMPRESSION_VALUES="true false"
EXCLUDE_OLDER_LOGS_THAN=disabled
EXCLUDE_OLDER_LOGS_THAN_LONG=exclude-old-logs
EXCLUDE_OLDER_LOGS_THAN_VALUES="0 ... n Days"
EXCLUDE_SECRETS=true
EXCLUDE_SECRETS_LONG=exclude-ox-secrets
EXCLUDE_SECRETS_VALUES="true false"
KEEP_LOGS_ALWAYS="open-xchange.log.0 open-xchange-console.log open-xchange-osgi.log ox-udm-modules-switch.log"
PROCS='showruntimestats -a;showruntimestats -c;showruntimestats -g;route -n;ps faux;top -bHn 1;dmesg;df;mount;netstat -putane;free;report -d;listbundles;listservices;uname -a;ps -eLf;java -version;listconfigdiff'
MUSTOPTS="TMP LOGDIR KEEP_TMP LOGARC"
LONGOPTS='$TMP_LONG:,$LOGDIR_LONG:,$LOGARC_LONG:,$KEEP_TMP_LONG:,$CREATE_THREAD_DUMP_LONG:,$CREATE_HEAP_DUMP_LONG:,$SKIP_COMPRESSION_LONG:,$EXCLUDE_OLDER_LOGS_THAN_LONG:,$EXCLUDE_SECRETS_LONG:'
EARLY_NEEDED_TOOLS="basename cat echo dirname java pwd tr date"
NEEDED_TOOLS="awk bzip2 cat cp df du egrep find grep gzip head mkdir mktemp rm rmdir sed sleep tail tar xargs"
OX_LOG_DIR=/var/log/open-xchange
OX_CONFIG_DIRS="/opt/open-xchange/etc /opt/open-xchange/guard/etc"

###############################################################################
#                                   Functions                                 #
###############################################################################

check_argument() {
    local _pattern=$(echo ${2} | tr [:upper:] [:lower:])
    case ${_pattern} in
        false) _pattern=false ;;
        true) _pattern=true ;;
        yes) _pattern=true ;;
        *) _pattern=invalid ;;
    esac
    if [ 'invalid' == "${_pattern}" ]; then
        usage
        die "Error, option '${1}' requires true or false argument"
    else
        echo ${_pattern}
    fi
}

check_on_needed() {
    local path=$(type -P ${1})
    if [ -z "${path}" ]; then
        die "${0}: Error, missing needed command: ${1}"
    fi
}

clean_up(){
   test "${KEEP_TMP}" = "true" || rm -rf ${TMP}/${LOGDIR}
}

collect_command_output() {
   echo "Collecting output of system tools into $(normalize_path ${TMP}/${LOGDIR}/commands)"
   cd /${TMP}
   OIFS=${IFS}
   IFS=";"
   for i in ${PROCS}; do
       IFS=${OIFS}
       ${i} > "/${TMP}/${LOGDIR}/commands/${i}" 2>&1
       IFS=";"
   done
   IFS=${OIFS}
   cd - > /dev/null 2>&1
}

collect_httpd_configs() {
    # Be very selective on what to add to the archive (Bug#28830)
    for dir in /etc/httpd /etc/apache*; do
        if [ -d "${dir}" ]; then
            local _tmp=$(mktemp)
            local _name=$(basename $(find ${dir} -maxdepth 0 -type d | head -n 1))
            local _tmp_exclude_status=$(normalize_path ${TMP}/${LOGDIR}/${_name}-configs-exclude)
            local _httpd_config_copy=$(normalize_path ${TMP}/${LOGDIR}/${dir})
            echo "Collecting httpd configuration into ${_httpd_config_copy}"
            mkdir -p $(dirname ${_httpd_config_copy})
            cp -aL ${dir} ${_httpd_config_copy}
            # Drop all files without name schema '.conf' or which are stored outside
            # <name>-enabled or <name>-available directories
            find ${_httpd_config_copy} -type f | \
                egrep -v '^.*\.conf$|^.*-(enabled|available)/.*$' | \
                sed 's| |\\ |g' >> ${_tmp}
            # Drop all files with SSL suffix types
            find ${_httpd_config_copy} -type f | \
                egrep '^.*\.crt$|^.*\.key$|^.*\.csr$|^.*\.prm$' | \
                sed 's| |\\ |g' >> ${_tmp}
            # Drop all which could be identified as cert or key file
            find ${_httpd_config_copy} -type f | \
                sed 's| |\\ |g' | \
                xargs -r file | \
                egrep '*[cC]ertificate*|*[kK]ey*' | \
                awk -F ':' '{print $1}' >> ${_tmp}
            cat ${_tmp} | sort -u > ${_tmp_exclude_status}
            rm ${_tmp}
            cat ${_tmp_exclude_status} | xargs -r rm
            # Delete empty directories
            find ${_httpd_config_copy} -depth -type d -empty | \
                sed 's| |\\ |g' | \
                xargs -r rmdir
            print_hint_to_line_end ${_tmp_exclude_status} "removed"
            ALL_EXCLUDED="${_tmp_exclude_status} ${ALL_EXCLUDED}"
        fi
    done
}

collect_logs() {
   local _log_dir=${1}
   local _name=$(basename $(find ${_log_dir} -maxdepth 0 -type d | head -n 1))
   local _bname=$(echo ${_name} | sed 's/\(^.\)/\U\1/' | sed 's|-xchange|-Xchange|g')
   local _logs_archive=$(normalize_path \
       ${TMP}/${LOGDIR}/${_name}-logs.tar)
   local _tmp_exclude_status=$(normalize_path ${TMP}/${LOGDIR}/${_name}-logs-exclude)

   echo "Collecting ${_bname} logs into ${_logs_archive}${LOGS_SUFFIX}"
   if [ 'disabled' == "${EXCLUDE_OLDER_LOGS_THAN}" ]; then
       tar cvf ${_logs_archive} \
           --ignore-failed-read ${_log_dir} > /dev/null 2>&1
   else
       # Find files which should be excluded by --drop-older-logs-than option
       find ${_log_dir} \
           -mtime +${EXCLUDE_OLDER_LOGS_THAN} \
           -type f > ${_tmp_exclude_status}
       # Remove mandatory files from exclusion list if listed
       for keep in ${KEEP_LOGS_ALWAYS}; do
           sed -i "/${keep}$/d" ${_tmp_exclude_status}
       done
       tar cvf ${_logs_archive} \
           --ignore-failed-read ${_log_dir} \
           --exclude-from=${_tmp_exclude_status} > /dev/null 2>&1
       print_hint_to_line_end ${_tmp_exclude_status} "removed"
       ALL_EXCLUDED="${_tmp_exclude_status} ${ALL_EXCLUDED}"
   fi

   if [ 'true' != "${SKIP_COMPRESSION}" ]; then
       bzip2 -9 ${_logs_archive}
   fi
}

collect_other_configs() {
    if [ -n "${OTHER_CONFIGS}" ]; then
        echo "Collecting other system configurations into $(normalize_path ${TMP}/${LOGDIR}/etc)"
        for config in ${OTHER_CONFIGS}; do
            mkdir -p ${TMP}/${LOGDIR}/$(dirname ${config})
            cp -aL ${config} ${TMP}/${LOGDIR}/$(dirname ${config})
        done
    fi
}

collect_ox_configs() {
    for ox_config_dir in ${OX_CONFIG_DIRS}; do
        if [ -d ${ox_config_dir} ]; then
            local _ox_config_copy=$(normalize_path ${TMP}/${LOGDIR}/${ox_config_dir})
            echo "Collecting Open-Xchange configurations into ${_ox_config_copy}"
            mkdir -p $(dirname ${_ox_config_copy})
            if [ "$(normalize_path ${ox_config_dir})" == "$(normalize_path ${_ox_config_copy})" ]; then
                die "${0}: Internal Tool Error, tried to modify original data: ${ox_config_dir}"
            fi
            cp -rL ${ox_config_dir} ${_ox_config_copy}
            if [ 'true' == "${EXCLUDE_SECRETS}" ]; then
                echo "Removing secrets from Open-Xchange configurations: ${_ox_config_copy}"
                exclude_secret_ox_properties_entries ${_ox_config_copy}
                exclude_secret_ox_config_files ${_ox_config_copy}
            fi
        fi
    done
}

create_excluded_and_modified_overview() {
# Create an overview of excluded, modified files and remove fragmented exclude
# files
    local _overview_file=$(normalize_path /${TMP}/${LOGDIR}/${1})
    echo "Creating an overview about modified and excluded files: ${_overview_file}"
    cat ${ALL_EXCLUDED} | sed "s|$(normalize_path ${TMP}/${LOGDIR})||g" | \
    sed 's|^/||g' | \
    sort -u > ${_overview_file}
    rm -f ${ALL_EXCLUDED}
}

create_heap_dump() {
# Most JVM's are working with jmap to create heap dumps but some distributions like
# Redhead have issues with '-F' option on Java 1.7.0. The sudo command is needed
# to have the right permissions and is somehow much faster as '-F', calling as root
# will fail on some systems.
    echo "Creating Java heap dump of Open-Xchange groupware..."
    if [ -f "/opt/open-xchange/sbin/heapdump" -a 'ibm' != "${JAVA_VM_TYPE}" ]; then
        local _heap_dump="${TMP}/${LOGDIR}/commands/heapdump.bin"
        local _heap_dump_cmd="heapdump -f ${_heap_dump}"
        local _heap_dump_log="${TMP}/${LOGDIR}/commands/heapdump -f heapdump.bin"

        # Grant write permissions for ox user
        chmod a+w ${TMP}/${LOGDIR}/commands

        ${_heap_dump_cmd} 2>&1 | tee "${_heap_dump_log}"

        if [ 'true' != "${SKIP_COMPRESSION}" ]; then
            echo "Compressing Java heap dump: ${_heap_dump}${LOGS_SUFFIX}"
            bzip2 "${_heap_dump}"
        fi
    elif [ 'ibm' != "${JAVA_VM_TYPE}" ]; then
        local _heap_dump_cmd="jmap -dump:file"
        local _heap_dump_log="${TMP}/${LOGDIR}/commands/${_heap_dump_cmd}"
        local _heap_dump="${_heap_dump_log} (heap-dump)"
        local _heap_histo_cmd="jmap -histo"
        local _heap_histo_log="${TMP}/${LOGDIR}/commands/${_heap_histo_cmd}"
        local _heap_histo_dump="${TMP}/${LOGDIR}/commands/${_heap_histo_cmd} (heap-dump)"

        # Grant write permissions for ox user
        chmod a+w ${TMP}/${LOGDIR}/commands
        sudo -u "#$(get_ox_user_ID)" ${_heap_histo_cmd} $(get_ox_java_pid) \
            > "${_heap_histo_dump}" 2> "${_heap_histo_log}"
        sudo -u "#$(get_ox_user_ID)" \
            ${_heap_dump_cmd}=${TMP}/${LOGDIR}/commands/"${_heap_dump_cmd} (heap-dump)" \
            $(get_ox_java_pid) 2>&1 | \
            tee "${_heap_dump_log}" | sed 's|^|  |g'
        if [ 'true' != "${SKIP_COMPRESSION}" ]; then
            echo "Compressing Java heap dump: ${_heap_dump}${LOGS_SUFFIX}"
            bzip2 "${_heap_dump}"
        fi
   else
        echo -n "  Terminating 'open-xchange' service for heap dump creation"
        cd ${TMP}
        # This will create the heap dump on ibm java
        kill -ABRT "$(get_ox_java_pid)"
        # Wait until all dumps are written, which will inditified by stopped ox java process
        while [ -n "$(get_ox_java_pid)" ]; do
            echo -n .
            sleep 1
        done
        echo
        OX_JVM_TERMINATED=true
        identify_and_move_ibm_dumps "dump written to "
        local _heap_dumps=$(find ${TMP}/${LOGDIR} -name "core.*.dmp")

        echo "  Processing heap dumps..."
        if [ 'true' != "${SKIP_COMPRESSION}" ]; then
            local _jextract_cmd="jextract"
        else
            local _jextract_cmd="jextract -nozip"
        fi
        for heap_dump in ${_heap_dumps}; do
            local _heap_dump_log="${TMP}/${LOGDIR}/commands/${_jextract_cmd} $(basename ${heap_dump}) (heap-dump)"
                ${_jextract_cmd} ${heap_dump} 2>&1 | \
                    tee "${_heap_dump_log}" | \
                    egrep -v '^.*-->$|^Adding .*'| \
                    sed 's|^|    |g'
                if [ 'true' != "${SKIP_COMPRESSION}" ]; then
                    # the heap dump is included in the jextract zip archive
                    rm ${heap_dump}
                fi
        done
    fi
}

create_thread_dumps() {
# By kill -3 on the java pid the JVM creates a thread dump. The top output
# is needed to identiefy long running tasks. On IBM Java the JVM stores the
# dumps seperatly and not into the open-xchange-console.log.
    local _delay=3
    local _count=5
    local _top_cmd="top -d ${_delay} -bHn 5"
    ${_top_cmd} >> /${TMP}/${LOGDIR}/commands/"${_top_cmd} (thread-dump)" &
    echo -n "Creating Java thread dumps: "
    for i in $(seq ${_count}); do
        echo -n "${i} "
        kill -3 $(get_ox_java_pid)
        sleep ${_delay}
    done
    echo
    if [ 'ibm' == "${JAVA_VM_TYPE}" ]; then
        identify_and_move_ibm_dumps "dump written to "
    fi
}

distribution_check() {
# Distribution depending staff should go here
    ox_system_type
    DIST=${?}

     if [ $(( ${DIST} & ${DEBIAN} )) -eq ${DEBIAN} ]; then
         echo "Debian system detected"
         OTHER_CONFIGS="${OTHER_CONFIGS} /etc/debian_version"
         PROCS=${PROCS}';dpkg -l'
         test -f /etc/mysql/my.cnf && OTHER_CONFIGS="${OTHER_CONFIGS} /etc/mysql/my.cnf"
     elif [ $(( ${DIST} & ${UCS} )) -eq ${UCS} ]; then
         echo "Univention system detected"
         UNIVENTION_LOG_DIR="/var/log/univention*"
         PROCS=${PROCS}';dpkg -l;ucr dump'
         test -f /etc/mysql/my.cnf && OTHER_CONFIGS="${OTHER_CONFIGS} /etc/mysql/my.cnf"
     elif [ $(( ${DIST} & ${SUSE} )) -eq ${SUSE} ]; then
         echo "SuSE system detected"
         OTHER_CONFIGS="${OTHER_CONFIGS} /etc/SuSE-release"
         PROCS=${PROCS}';rpm -qa --last'
         test -f /etc/my.cnf && OTHER_CONFIGS="${OTHER_CONFIGS} /etc/my.cnf"
     elif [ $(( ${DIST} & ${REDHAT} )) -eq ${REDHAT} ]; then
         echo "Redhat system detected"
         OTHER_CONFIGS="${OTHER_CONFIGS} /etc/*-release"
         PROCS=${PROCS}';rpm -qa --last'
         test -f /etc/my.cnf && OTHER_CONFIGS="${OTHER_CONFIGS} /etc/my.cnf"
     else
         echo "Distribution not detected"
     fi
}

exclude_secret_ox_properties_entries() {
# This function only works on .properies* files and replace secret enties with an
# '<REMOVED BY OXSYSREPORT>' marker on a working copy.
# _file=/tmp/ox_support_infos-20140526-165503/opt/open-xchange/etc/msnoauth.properties
# _original_line=com.openexchange.oauth.msn.apiSecret=secret
# _plain_property=com.openexchange.oauth.msn.apiSecret=
# _secret_part=secret
# _modified_line=com.openexchange.oauth.msn.apiSecret=<REMOVED BY OXSYSREPORT>
    local _ox_config_copy=${1}
    local _tmp_exclude_status=$(normalize_path ${TMP}/${LOGDIR}/ox-configs-secret-exclude)
    local _whitelist='enabledMailCapableKey|UserAttributePassword|^[[:blank:]]*#'
    local _blacklist='[pP]assword[[:blank:]]*=|[sS]ecret[[:blank:]]*=|[kK]ey[[:blank:]]*=|secretSource[[:blank:]]*=|secretRandom[[:blank:]]*=|[sS]alt[[:blank:]]*=|SSLKey(Pass|Name)[[:blank:]]*='
    find ${_ox_config_copy} -type f -name "*.properties*" | \
        xargs egrep -HrIv "${_whitelist}" | \
        egrep "${_blacklist}" >> ${_tmp_exclude_status}

    while read line; do
        local _file=$(echo ${line} | awk -F ':' '{print $1}')
        local _original_line=$(echo ${line} | awk -F '\\.properties.*:' '{print $2}')
        local _plain_property=$(echo ${_original_line} | sed 's|=.*$|=|g')
        local _secret_part=$(echo ${_original_line} | \
            sed "s|${_plain_property}||g" | \
            sed 's|password=||g' )
        if [ -n "${_secret_part}" ]; then
            local _modified_line=$(echo ${_original_line} | \
                sed "s|${_secret_part}|<REMOVED BY OXSYSREPORT>|g")
        else
            local _modified_line="${_original_line}<REMOVED BY OXSYSREPORT>"
        fi
        sed -i "s|^${_original_line}$|${_modified_line}|g" ${_file}
        sed -i "s|:${_original_line}$|:${_modified_line}|g" ${_tmp_exclude_status}
    done < ${_tmp_exclude_status}
    print_hint_to_line_end ${_tmp_exclude_status} "modified"
    ALL_EXCLUDED="${_tmp_exclude_status} ${ALL_EXCLUDED}"
}

exclude_secret_ox_config_files() {
# This function removes every file which is not whitelisted.
    local _ox_config_copy=${1}
    local _tmp_exclude_status=$(normalize_path ${TMP}/${LOGDIR}/ox-configs-exclude)
    local _whitelist='\.properties.*$|\.yml$|\.xml$|\.yaml$|\.sh$|\.ccf$|\.types$|\.sql$|\.txt$|\.name$|\.perfMap$|folder-reserved-names'
    find ${_ox_config_copy} -type f | \
        egrep -v "${_whitelist}" | \
        sed 's| |// |g' >> ${_tmp_exclude_status}
    cat ${_tmp_exclude_status} | \
        xargs -r rm -f
    print_hint_to_line_end ${_tmp_exclude_status} "removed"
    ALL_EXCLUDED="${_tmp_exclude_status} ${ALL_EXCLUDED}"
}

fix_log_archive_tarball_name() {
# Drop --log-archive '.tar*' user input end replace it like defined
    local _name="$(echo ${1} | \
    sed 's|.tar\\.*||g' | \
    sed 's|.tar||g' | \
    sed 's|$|.tar|g' | \
    sed 's|.gz||g')${LOGARC_SUFFIX}"
    echo ${_name}
}

get_java_vm_type() {
# The java vm type is defines in third line of 'java -version' output
   local _java_vm_type=$(java -version 2>&1 | \
       head -n 3  | \
       tail -n 1 | \
       sed 's|(.*||g' | \
       tr [:upper:] [:lower:])
       case ${_java_vm_type} in
           *openjdk*) _java_vm_type=openjdk ;;
           *ibm*) _java_vm_type=ibm ;;
           *hotspot*) _java_vm_type=hotspot ;;
           *sun*) _java_vm_type=sun ;;
           *oracle*) _java_vm_type=oracle ;;
           *) _java_vm_type=unknown ;;
       esac
       echo ${_java_vm_type}
}

get_ox_java_pid() {
    local _ox_pid=$(ps ax | \
        grep 'open-xchange' | \
        grep 'java' | \
        head -n 1 | \
        awk '{print $1}')
    echo "${_ox_pid}"
}

get_ox_user_ID() {
    local _ox_user=$(ps axu | \
        grep 'open-xchange' | \
        grep 'java' | \
        head -n 1 | \
        awk '{print $1}')
    echo "${_ox_user}"
}

identify_and_move_ibm_dumps() {
# The IBM Java VM is storing the dumps separately in the filesystem, we search
# for the storage location within the open-xchange-console.log and move them
# into the support tarball. The filesystem storage location can be indified
# by enties like below:
# JVMDUMP010I Java dump written to /tmp/javacore.20140522.122625.3824.0005.txt
# JVMDUMP010I Snap dump written to /tmp/Snap.20140522.122628.3824.0008.trc
# JVMDUMP010I System dump written to /tmp/core.20140522.122628.3824.0006.dmp
        echo "  Moving separate stored IBM Java dumps:"
        local _pattern=${1}
        local _dump_files=$(cat ${OX_LOG_DIR}/open-xchange-console.log | \
            grep "${_pattern}" | \
            sed "s|.*${_pattern}||g")
        for file in ${_dump_files}; do
            if [ -f "${file}" ]; then
                mkdir -p ${TMP}/${LOGDIR}/$(dirname ${file})
                mv -v ${file} $( normalize_path ${TMP}/${LOGDIR}/${file}) | \
                sed 's|???||g' | \
                sed 's|???||g' | \
                sed 's|^|  |g'
            fi
        done
}

is_ox_running() {
    if [ -n "$(get_ox_java_pid)" ]; then
        echo "true"
    else
        echo "false"
    fi
}

normalize_path() {
# Add a leading and remove dublicated or more slashes
    echo ${@} | sed 's|^|/|g' | sed 's|//*|/|g'
}

print_hint_to_line_end() {
    sed -i "s|$| (${2})|g" ${1}
}

usage() {
    echo "Usage: $(basename ${0})  [<OPTION>  <VALUE>] ..."
    echo
    echo "${0} currently knows the following parameters:"
    echo
    local _lopts=$(echo ${LONGOPTS} | sed -e 's/[:,]/ /g')
    printf '%-22s | %-45s | %-s\n' "Option" "Default value" "Possible values"
    echo "--------------------------------------------------------------------------------------------"
    for opt in ${_lopts}; do
        local _rvar=${opt%_LONG}
        GLOBIGNORE='*'
        local _possible=$(eval echo ${opt%_LONG}_VALUES)
        local _lopt=$(eval echo ${opt})
        if [ "${LOGARC_LONG}" == "${_lopt}" ]; then
            local _default="$(eval echo "${_rvar}")"
            local _default=$(fix_log_archive_tarball_name ${_default})
        else
            local _default="$(eval echo "${_rvar}")"
        fi
#        echo ${_opt} ${_rvar} ${_default} ${_lopt}
        printf '%-22s | %-45s | %-s\n' "--${_lopt}" ${_default} "${_possible}"
        GLOBIGNORE=
    done
cat<<EOF

Examples:

  ${0}

Drop all log files which are not modified since two days:
  ${0} --${EXCLUDE_OLDER_LOGS_THAN_LONG} 2

use -D for Debug mode

Please see for further information, requirements and examples following documentation:
http://oxpedia.org/wiki/index.php?title=AppSuite:Oxsysreport

EOF
}

warning_heap_dump() {
cat << EOF
###############################################################################
                                  WARNING
While creating Java heap dumps your JVM will be stopped and the OX Service is
not functional. This step may take several minutes and it could happen that you
need to restart the open-xchange service afterwards manually again.

Please also make sure that you have enough free disk space for processing the
data in directories which are defined by '--tempdir' and '--log-archive',
depending on your ox-scriptconf.sh '-Xmx' size of your groupware Java virtual
machine and amount of logs, several gigabyte could be needed!

System Info:
Groupware Max Heap Size: $(cat /opt/open-xchange/etc/ox-scriptconf.sh | \
    grep -A 1 'groupware Java virtual machine' | \
    sed 's|.*-Xmx|-Xmx|g' | \
    sed 's|"||g' | \
    sed 's| .*||g' | \
    tail -n 1)
OX Log Storage: $(du -h ${OX_LOG_DIR} | sort | tail -n 1)

$(df -h ${TMP})    Processing data within: $(normalize_path ${TMP}/${LOGDIR})
$(df -h $(dirname ${LOGARC}) | tail -n 1)    Final tarball storage: ${LOGARC}

###############################################################################
EOF
read -p "Press ENTER to continue or [ctrl+c] to cancel..."
echo
}

###############################################################################
#                              Runtime Checks                                 #
###############################################################################

test -f ${OXFUNCTIONS} || {
    echo "missing common shell functions file"
    exit 1
}

source ${OXFUNCTIONS}

for tool in ${EARLY_NEEDED_TOOLS}; do
    check_on_needed ${tool} >/dev/null
done

JAVA_VM_TYPE=$(get_java_vm_type)
TEMP=$(POSIXLY_CORRECT=true getopt -o s:i:l:hD --long "$(eval echo ${LONGOPTS}),help" -- "${@}") \
    || die "exiting"

eval set -e -- "${TEMP}"

for argument in ${@}; do
    case "${1}" in
        --${TMP_LONG})
            if [ -d "${2}" ]; then
                cd ${2}
                TMP=$(pwd)
                cd - &> /dev/null
            else
                usage
                die "Error, option --${TMP_LONG} directory do not exist: ${2}"
            fi
            shift 2
        ;;
        --${LOGDIR_LONG})
            LOGDIR=${2}
            shift 2
        ;;
        --${KEEP_TMP_LONG})
            KEEP_TMP=$(check_argument ${1} ${2})
            shift 2
        ;;
        --${LOGARC_LONG})
            if [ -d "$(dirname ${2})" ]; then
                cd $(dirname ${2})
                LOGARC=$(pwd)/$(basename ${2})
            else
                usage
                die "Error, option --${LOGARC_LONG} path do not exist: $(dirname ${2})"
            fi
            LOGARC=$(fix_log_archive_tarball_name ${LOGARC})
            if [ "${2}" != "${LOGARC})" ]; then
                echo
                echo "Info, changing tarball name --${LOGARC_LONG} from ${2} to ${LOGARC}"
                echo
            fi
            if [ -e "${LOGARC}" ]; then
                usage
                die "Error, option --${LOGARC_LONG} is pointing to an existing file: ${LOGARC}"
            elif [ -e "${LOGARC%%.gz}" ]; then
                usage
                die "Error, option --${LOGARC_LONG} is pointing uncompressed to an existing file: ${LOGARC%%.gz}"
            fi
            unset TMP_LOGARC_TRABALL
            shift 2
        ;;
        --${CREATE_THREAD_DUMP_LONG})
            CREATE_THREAD_DUMP=$(check_argument ${1} ${2})
            NEEDED_TOOLS="kill top ${NEEDED_TOOLS}"
            shift 2
            if [ 'false' == "$(is_ox_running)" ]; then
                die "${0}: Error, open-xchange service is not running"
            fi
        ;;
        --${CREATE_HEAP_DUMP_LONG})
            CREATE_HEAP_DUMP=$(check_argument ${1} ${2})
            if [ -f "/opt/open-xchange/sbin/heapdump" -a 'ibm' != "${JAVA_VM_TYPE}" ]; then
                NEEDED_TOOLS="heapdump tee ${NEEDED_TOOLS}"
            elif [ 'ibm' == "${JAVA_VM_TYPE}" ]; then
                NEEDED_TOOLS="kill service jextract tee ${NEEDED_TOOLS}"
            else
                NEEDED_TOOLS="jmap sudo tee ${NEEDED_TOOLS}"
            fi
            shift 2
            if [ 'false' == "$(is_ox_running)" ]; then
                die "${0}: Error, open-xchange service is not running"
            fi
        ;;
        --${SKIP_COMPRESSION_LONG})
            SKIP_COMPRESSION=$(check_argument ${1} ${2})
            shift 2
            unset LOGARC_SUFFIX
            unset LOGS_SUFFIX
        ;;
        --${EXCLUDE_OLDER_LOGS_THAN_LONG})
            if [ '0' -le "${2}" ] 2> /dev/null ; then
                EXCLUDE_OLDER_LOGS_THAN=${2}
            else
                usage
                die "Error, option '${1}' requires positive numbers: ${2}"
            fi
            shift 2
       ;;
        --${EXCLUDE_SECRETS_LONG})
            EXCLUDE_SECRETS=$(check_argument ${1} ${2})
            shift 2
       ;;
        -h|--help)
            usage
            exit 0
        ;;
        -D)
            set -x
            shift
        ;;
        --)
            shift
            break
        ;;
        *)
            die "${0}: Internal Error!"
        ;;
    esac
done

LOGARC=$(fix_log_archive_tarball_name ${LOGARC})

if [ $(id -u) -ne 0 ]; then
    die "${0}: Error, need to be root in order to collect all needed data"
fi

if [ -d "${TMP}/${LOGDIR}" ]; then
    usage
    die "Error, '--${TMP_LONG}' and '--${LOGDIR_LONG}' directory already exists: $(normalize_path ${TMP}/${LOGDIR})"
fi

for tool in ${NEEDED_TOOLS}; do
    check_on_needed ${tool}
done

if [ 'true' == "${CREATE_HEAP_DUMP}" ]; then
    warning_heap_dump
fi

# Generic parameter checking
for opt in ${MUSTOPTS}; do
    opt_var=$(eval echo \$${opt})
    opt_var_long=$(eval echo \$${opt}_LONG)
    if [ -z "${opt_var}" ]; then
    usage;
         die "${0}: Error, missing required option --${opt_var_long}"
    fi
done

# Generic option checking
ALLOPTS=$(echo ${LONGOPTS} | sed -e 's/[$:,]/ /g' -e 's/_LONG//g')
for opt in ${ALLOPTS}; do
    opt_var=$(eval echo \$${opt})
    opt_var_long=$(eval echo \$${opt}_LONG)
    opt_var_values=$(eval echo \$${opt}_VALUES)
    if [ -n "${opt_var_values}" ]; then
        found=
        for val in ${opt_var_values}; do
            if [ "${val}" == "${opt_var}" ]; then
                found=${val}
            fi
            # Accept any value in case '...' is defined in this option <name>_VALUES
            if [ '...' == "${val}" ]; then
                found=${opt_var}
            fi
        done
        if [ -z "${found}" ]; then
            die "${0}: Error, \"${opt_var}\" is not a valid option to --${opt_var_long}"
        fi
    fi
done

###############################################################################
#                                Runtime Main                                 #
###############################################################################
# The tool has passed all sanity checks and now it is required not to break on
# futher errors, except critical internal tool errors.
set +e

trap "rm -f ${LOGARC}; clean_up; exit 1" SIGINT SIGSEGV SIGQUIT SIGTERM

if [ 'ibm' == "${JAVA_VM_TYPE}" ]; then
    echo "IBM Java Virtual Machine detected"
fi
distribution_check
echo "Creating temporary log directory: $(normalize_path ${TMP}/${LOGDIR})"
mkdir -p ${TMP}/${LOGDIR}/commands/
collect_command_output
collect_httpd_configs
collect_ox_configs
collect_other_configs
if [ 'true' == "${CREATE_THREAD_DUMP}" ]; then
    create_thread_dumps
fi
if [ 'true' == "${CREATE_HEAP_DUMP}" ]; then
    create_heap_dump
fi
collect_logs ${OX_LOG_DIR}
if [ -n "${UNIVENTION_LOG_DIR}" ]; then
    collect_logs ${UNIVENTION_LOG_DIR}
fi
create_excluded_and_modified_overview modified-and-excluded-files.txt
echo "Creating final archive: $(normalize_path ${LOGARC})"
tar cvf ${LOGARC%%.gz}  -C ${TMP} ${LOGDIR} > /dev/null 2>&1
if [ 'true' != "${SKIP_COMPRESSION}" ]; then
    gzip ${LOGARC%%.gz}
fi
if [ 'true' == "${OX_JVM_TERMINATED}" ]; then
    service open-xchange start
fi
clean_up
