#!/bin/bash

SVER='1.53.02'
SDATE="2021 11 17"
SNAME=$(basename $0)

# --------------------------------------------------------------------- #
#  getappcore - Creates an archive of all things required to analyze an #
#  application crash.                                                   #
#  usage: see show_help(), or call with -h                              #
#                                                                       #
#  Please submit bug fixes or comments via:                             #
#    http://en.opensuse.org/Supportutils#Reporting_Bugs                 #
#                                                                       #
#  Copyright (C) 2007-2022 SUSE LINUX GmbH, Nuernberg, Germany          #
#                                                                       #
# --------------------------------------------------------------------- #
#                                                                       #
#  This program is free software; you can redistribute it and/or modify #
#  it under the terms of the GNU General Public License as published by #
#  the Free Software Foundation; version 2 of the License.              #
#                                                                       #
#  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, see <http://www.gnu.org/licenses/>. #
#                                                                       #
#  Authors/Contributors:                                                #
#     Mike Latimer <mlatimer@suse.com>                                  #
#     Jason Record <jason.record@suse.com>                              #
#                                                                       #
#  Additional credit to Paul Hardwick (phardwick@novell.com) for his    #
#  novell-getcore, which was inspiration for this (more generic) script #
# --------------------------------------------------------------------- #
#                                                                       #
# Overview:                                                             #
#                                                                       #
# This script is intended to ease the process of getting application    #
# cores to an analyzable state by SUSE Technical Support.               #
#                                                                       #
# This script creates a tarball containing the specified application    #
# core, and all libraries required to run the core'ing binary. This     #
# script also executes chkbin against the problem binary, and captures  #
# the output of that check, along with a log detailing the RPM versions #
# required to fully analyze the application core.                       #
#                                                                       #
# Changes: Moved to the supportutils changelog                          #
# --------------------------------------------------------------------- #


ARCHIVE_PATH="/var/log"
BASE_TMP_DIR="/tmp"
GETAPPCORE_CONF="/etc/getappcore.conf"
GETAPPCORE_TMP_DIR=$(/bin/mktemp -d ${BASE_TMP_DIR}/getappcore_tmp_XXXXXXXX)
GETAPPCORE_LOG="$GETAPPCORE_TMP_DIR/getappcore.log"
GDBLIBS_CMD=$GETAPPCORE_TMP_DIR/.gdblibs.cmd
SERVER_NAME=$(/bin/uname -n)
SUSE_RELEASE="/etc/SuSE-release"
OS_RELEASE="/etc/os-release"
ARCHIVE_PREFIX='scc_'
JOURNAL_PID=''
CONTAINER_IMAGE=''
PODMAN_BIN='/usr/bin/podman'
TAR_BIN_OPTIONS=''
TAR_BIN_EXT=''
UPLOAD=0
VERBOSE=0
CLEANUP_CORE=0

SUSE_UPLOAD_NA_ASERVER='support-ftp.us.suse.com'
SUSE_UPLOAD_EMEA_ASERVER='support-ftp.emea.suse.com'
SUSE_UPLOAD_NA_HTTPS="https://${SUSE_UPLOAD_NA_ASERVER}/incoming/upload.php?appname=supportconfig&file={tarball}"
SUSE_UPLOAD_NA_FTPES="ftps://${SUSE_UPLOAD_NA_ASERVER}/incoming"
SUSE_UPLOAD_EMEA_HTTPS="https://${SUSE_UPLOAD_EMEA_ASERVER}/incoming/upload.php?appname=supportconfig&file={tarball}"
SUSE_UPLOAD_EMEA_FTPES="ftps://${SUSE_UPLOAD_EMEA_ASERVER}/incoming"
DEFAULT_HTTPS="${SUSE_UPLOAD_NA_HTTPS}"
DEFAULT_FTPES="${SUSE_UPLOAD_NA_FTPES}"
UPLOAD_TARGET="${DEFAULT_HTTPS}"

#Required Binaries
AWK_BIN=/usr/bin/awk
BASENAME_BIN=/usr/bin/basename
CAT_BIN=/bin/cat
CHKBIN_BIN=/sbin/chkbin
CHMOD_BIN=/bin/chmod
COREDUMP_BIN=/usr/bin/coredumpctl
CURL_BIN=/usr/bin/curl
CUT_BIN=/usr/bin/cut
DATE_BIN=/bin/date
DOS2UNIX_BIN=/usr/bin/dos2unix
DIRNAME_BIN=/usr/bin/dirname
FIND_BIN=/usr/bin/find
GDB_BIN=/usr/bin/gdb
GREP_BIN=/usr/bin/grep
HEAD_BIN=/usr/bin/head
LN_BIN=/bin/ln
MKDIR_BIN=/bin/mkdir
MV_BIN=/bin/mv
READLINK_BIN=/usr/bin/readlink
RM_BIN=/bin/rm
RMDIR_BIN=/bin/rmdir
RPM_BIN=/bin/rpm
SED_BIN=/usr/bin/sed
SORT_BIN=/usr/bin/sort
TAR_BIN=/bin/tar
TEE_BIN=/usr/bin/tee
TR_BIN=/usr/bin/tr
UNAME_BIN=/bin/uname
UNIQ_BIN=/usr/bin/uniq
WC_BIN=/usr/bin/wc
WHICH_BIN=/usr/bin/which
XARGS_BIN=/usr/bin/xargs
ALL_BINS="$AWK_BIN $BASENAME_BIN $CAT_BIN $CHKBIN_BIN $CHMOD_BIN $COREDUMP_BIN $CUT_BIN $DATE_BIN $DIRNAME_BIN $FIND_BIN \
	$CURL_BIN $GDB_BIN $GREP_BIN $HEAD_BIN $LN_BIN $MKDIR_BIN $MV_BIN $READLINK_BIN $RM_BIN $RMDIR_BIN $RPM_BIN \
	$SED_BIN $SORT_BIN $TAR_BIN $TEE_BIN $TR_BIN $UNAME_BIN $UNIQ_BIN $WC_BIN $WHICH_BIN $XARGS_BIN"


# --------------------------------------------------------- #
# show_title ()                                             #
# Display title banner, showing environment and core info   #
# --------------------------------------------------------- #
show_title() {
	echo "####################################################################" | $TEE_BIN $GETAPPCORE_LOG
	echo "Get Application Core Tool, v$SVER"                                    | $TEE_BIN -a $GETAPPCORE_LOG
	echo "Date:     $($DATE_BIN +'%D, %T')"                                     | $TEE_BIN -a $GETAPPCORE_LOG
	echo "Server:   $SERVER_NAME"                                               | $TEE_BIN -a $GETAPPCORE_LOG
	echo "OS:       $OS_VERSION $OS_PATCHLEVEL"	                            | $TEE_BIN -a $GETAPPCORE_LOG
	echo "Kernel:   $($UNAME_BIN -r) ($($UNAME_BIN -i))"                        | $TEE_BIN -a $GETAPPCORE_LOG
	echo "Corefile: $COREFILE_STR"                                              | $TEE_BIN -a $GETAPPCORE_LOG
	echo "####################################################################" | $TEE_BIN -a $GETAPPCORE_LOG
	echo
}

# --------------------------------------------------------- #
# show_help ()                                              #
# Display program help screen                               #
# --------------------------------------------------------- #
show_help() {
	echo "Usage: $0 [OPTION] -j PID | /path/to/corefile"
	echo
	echo "$SNAME creates an archive containing an application core, and all files"
	echo "required to analyze the application core - including the binary which"
	echo "created the core, and all required shared libraries. Included in the"
	echo "archive is a logfile containing RPM version information for further"
	echo "investigation by SUSE."
	echo
	echo "Required parameter:"
	echo
	echo "  -j <PID>        PID of coredump from 'coredumpctl list' to extract"
	echo 
	echo "  or" 
	echo
	echo "  COREFILE        The application core file"
	echo "                  Note: the corefile must be provided uncompressed."
	echo "                  Since systemd creates compressed core files by default,"
	echo "                  you should almost always prefer '-j <PID>' nowadays."
	echo "                  In pre-systemd times, the core files could be found in the"
	echo "                  working directory of the application, or /."
	echo
	echo "Optional parameters:"
	echo
	echo "  -h              This screen"
	echo "  -b <BINARY>     Binary which generated <COREFILE>, unnecessary with '-j'"
	echo "  -r <SR Number>  SUSE Service Request number associated with this issue"
	echo "  -u              Automatically upload archive to the SUSE FTP server using HTTPS"
	echo "  -f              Automatically upload archive to the SUSE FTP server using FTPES"
	echo "  -c [IMAGE]      Runs inside the container image [IMAGE]"
	echo "  -v              Enable verbose messages"
	echo
	echo "For example:"
	echo
	echo "   $SNAME -ur 00123456 -j 2344"
	echo "   $SNAME -ur 00123456 -b /bin/rpm /core.15832"
	echo
	echo "$SNAME version $SVER ($SDATE)"
	echo
	show_journal_coredumps
}

# --------------------------------------------------------- #
# verbose ()                                                #
# Enable vebose messages                                    #
# --------------------------------------------------------- #
verbose() {
	if [[ $VERBOSE -eq '1' ]]; then
		[[ -z $1 ]] && echo || echo "-- $1"
	fi
}

# --------------------------------------------------------- #
# get_server_release ()                                     #
# Determine the server versio and patch level               #
# --------------------------------------------------------- #
get_server_release() {
	if [[ -s $OS_RELEASE ]]; then
		PRETTY_NAME=$(grep PRETTY_NAME $OS_RELEASE 2>/dev/null | cut -d= -f2)
		VERSION_ID=$(grep VERSION_ID $OS_RELEASE 2>/dev/null | cut -d= -f2)
		PRETTY_NAME=${PRETTY_NAME//\"/}
		VERSION_ID=${VERSION_ID//\"/}
		OS_VERSION=${PRETTY_NAME// SP*/}
		VER=${VERSION_ID//.*/}
		REL=${VERSION_ID//*./}
		[[ $VER == $REL ]] && REL=0
		OS_PATCHLEVEL="SP${REL}"
	else
		OS_VERSION=$($HEAD_BIN -1 $SUSE_RELEASE)
		OS_VERSION=${OS_VERSION%(*)}
		OS_PATCHLEVEL=SP$($CAT_BIN $SUSE_RELEASE | $GREP_BIN PATCHLEVEL | $AWK_BIN '{print $3}')
	fi
}

# --------------------------------------------------------- #
# check_binaries ()                                         #
# Check for all required binaries prior to execution        #
# --------------------------------------------------------- #
check_binaries() {
	for BINARY in $ALL_BINS
	do
		verbose "Checking $BINARY"
		if ! [ -e $BINARY ]; then
			if [ -z "$MISSING_BINS" ]; then
				MISSING_BINS=$BINARY
			else
				MISSING_BINS="$BINARY $MISSING_BINS"
			fi
		fi
	done
	if [[ -x /usr/bin/xz ]]; then
		TAR_BIN_OPTIONS='-Jhvcvf'
		TAR_BIN_EXT='txz'
	elif [[ -x /usr/bin/bzip2 ]]; then
		TAR_BIN_OPTIONS='-jhvcvf'
		TAR_BIN_EXT='tbz'
	elif [[ -x /usr/bin/gzip ]]; then
		TAR_BIN_OPTIONS='-zhvcvf'
		TAR_BIN_EXT='tgz'
	else
		MISSING_BINS="/usr/bin/xz or /usr/bin/bzip2 or /usr/bin/gzip $MISSING_BINS"
	fi
	if ! [ -z "$MISSING_BINS" ]; then
		echo "The following required binaries were not found!"
		echo
		for BIN in $MISSING_BINS; do
			echo "   $BIN"
		done
		echo
		echo "Please install the required package(s) and try again."
		echo
		cleanup
		exit -1
	fi
}

# --------------------------------------------------------- #
# create_filename ()                                        #
# Create the archive filename                               #
# --------------------------------------------------------- #
create_filename() {
	CORE_ARCHIVE_NAME="$SERVER_NAME"_"$($BASENAME_BIN $COREFILE_BIN)_$($DATE_BIN +%y%m%d_%H%M%S)_appcore"
	if [ -z "$SR_NUM" ]; then
		CORE_ARCHIVE_NAME="${ARCHIVE_PREFIX}${CORE_ARCHIVE_NAME}"
	else
		CORE_ARCHIVE_NAME="${ARCHIVE_PREFIX}SR${SR_NUM}_${CORE_ARCHIVE_NAME}"
	fi
	verbose "Using archive name $CORE_ARCHIVE_NAME"
	OPENCORE_INI=$GETAPPCORE_TMP_DIR/$CORE_ARCHIVE_NAME/opencore.ini
	OPENCORE_SH=$GETAPPCORE_TMP_DIR/$CORE_ARCHIVE_NAME/opencore.sh
	LOG_DIR=$GETAPPCORE_TMP_DIR/$CORE_ARCHIVE_NAME/logs
	verbose "Making the log directory $LOG_DIR"
	$MKDIR_BIN -p $LOG_DIR
}

# ----------------------------------------------------------------- #
# create_opencoreini ()                                             #
# Create the opencore.ini file required for opening the core in gdb #
# ----------------------------------------------------------------- #
create_opencoreini() {
	# Strip double slashes and ././ from COREFILE and COREFILE_BIN
	verbose "Creating file: $OPENCORE_INI"
	COREFILE_BIN_TMP=`echo "./${COREFILE_BIN#./}" | $SED_BIN s#//*#/#g`
	COREFILE_TMP=`echo "./${COREFILE#./}" | $SED_BIN s#//*#/#g`
	echo "# Generated by $SNAME v$SVER"				>  $OPENCORE_INI
	echo "# Command line used: $CMDLINE"				>> $OPENCORE_INI
	echo "# GDB environment:"					>> $OPENCORE_INI
	echo "set solib-absolute-prefix ./"				>> $OPENCORE_INI
	echo "set solib-search-path ./:$GDB_SOLIB_SEARCH_PATH"		>> $OPENCORE_INI
	# Add substitute-path to access src code from debuginfo packages
	echo "set substitute-path /usr/src/debug ./usr/src/debug"	>> $OPENCORE_INI
	echo "set debug-file-directory ./usr/lib/debug"			>> $OPENCORE_INI
	echo "set print max-symbolic-offset 1"				>> $OPENCORE_INI
	echo "set prompt #"						>> $OPENCORE_INI
	echo "set height 0"						>> $OPENCORE_INI
	echo ""								>> $OPENCORE_INI
	echo "# Core file:"						>> $OPENCORE_INI
	echo "file $COREFILE_BIN_TMP"					>> $OPENCORE_INI
	echo "core $COREFILE_TMP"					>> $OPENCORE_INI
	echo ""								>> $OPENCORE_INI
}

# --------------------------------------------------------- #
# create_opencoresh ()                                      #
# Create the opencore.sh script at the root of the archive  #
# --------------------------------------------------------- #
create_opencoresh() {
	verbose "Creating file: $OPENCORE_SH"
	echo "#!/bin/sh"								>  $OPENCORE_SH
	echo "gdb --command=./$($BASENAME_BIN $OPENCORE_INI)"				>> $OPENCORE_SH
	echo ""										>> $OPENCORE_SH
	$CHMOD_BIN u+x $OPENCORE_SH
}


# --------------------------------------------------------- #
# cleanup ()                                                #
# Remove temporary directory, and all files                 #
# --------------------------------------------------------- #
cleanup() {
	echo -n "Removing required files and directories ... "
	verbose ""
	for FILE in $OPENCORE_INI $OPENCORE_SH $GETAPPCORE_LOG $CHKBIN_LOG; do
		verbose "Removing file: $FILE"
		$RM_BIN $FILE
	done
	if (( $CLEANUP_CORE )); then
		verbose "Removing file: $COREFILE"
		$RM_BIN $COREFILE
	fi
	for FILE in $REQUIRED_LIBRARIES $COREFILE $COREFILE_BIN; do
		SYMLINK=$GETAPPCORE_TMP_DIR/$CORE_ARCHIVE_NAME/$FILE
		SYMLINK=${SYMLINK/\/\//\/}
		if [ -L "$SYMLINK" ]; then
			verbose "Removing symlink: $SYMLINK"
			$RM_BIN $SYMLINK
		fi
	done
	for DIR in $($FIND_BIN $GETAPPCORE_TMP_DIR -type d | sort -r); do
		verbose "Removing directory: $DIR"
		$RMDIR_BIN $DIR
	done
	echo "Done"
}


# --------------------------------------------------------------- #
# create_symlinks ()                                              #
# Create symlinks to all required files, which will be            #
# followed during archive creation to include all required files  #
# --------------------------------------------------------------- #
create_symlinks() {
	for REQUIRED_FILE in $COREFILE $COREFILE_BIN $REQUIRED_LIBRARIES; do
		REQUIRED_FILE_LINK=$GETAPPCORE_TMP_DIR/$CORE_ARCHIVE_NAME/$REQUIRED_FILE
		REQUIRED_FILE_LINK=${REQUIRED_FILE_LINK/\/\//\/}
		$MKDIR_BIN -p $(dirname $REQUIRED_FILE_LINK)
		verbose "Creating symlink: $REQUIRED_FILE_LINK --> $REQUIRED_FILE"
		$LN_BIN -s $REQUIRED_FILE $REQUIRED_FILE_LINK
	done
}

# ------------------------------------------------------------ #
# create_archive ()                                            #
# Create the archive containg the core, and all required files #
# ------------------------------------------------------------ #
create_archive() {
	echo -n "Creating core archive... "
	ORIGINAL_DIR=$PWD
	cd $GETAPPCORE_TMP_DIR
	FINAL_ARCHIVE_FILE="${CORE_ARCHIVE_NAME}.${TAR_BIN_EXT}"
	FINAL_ARCHIVE_NAME="${ARCHIVE_PATH}/${FINAL_ARCHIVE_FILE}"
	verbose ''
	verbose "Executing: $TAR_BIN $TAR_BIN_OPTIONS $FINAL_ARCHIVE_NAME $CORE_ARCHIVE_NAME 1>/dev/null 2>&1"
	$TAR_BIN $TAR_BIN_OPTIONS $FINAL_ARCHIVE_NAME $CORE_ARCHIVE_NAME 1>/dev/null 2>&1
	if [[ $? -eq 0 ]]; then
		echo "Done"
		verbose "Created archive as:  $FINAL_ARCHIVE_NAME"
	else
		echo "ERROR"
		echo "+ Unable to create $FINAL_ARCHIVE_NAME!"
		exit 5
	fi
	cd $ORIGINAL_DIR
}

# --------------------------------------------------------- #
# show_journal_coredumps()                                   #
# Shows the number of coredumps in the journal              #
# --------------------------------------------------------- #
show_journal_coredumps() {
	echo -n "Number of coredumps in the journal... "
	CORENUM=$($COREDUMP_BIN list --no-legend --no-pager | wc -l)
	echo $CORENUM
	if (( $CORENUM )); then
		echo
		if (( $VERBOSE )); then
			$COREDUMP_BIN list
		else
			echo
			echo "Most recent coredump:"
			$COREDUMP_BIN list -1
			echo
			echo "Use '$COREDUMP_BIN list' to see more."
		fi
	fi
	echo
}

# --------------------------------------------------------- #
# get_journal_coredump()                                    #
# Gets the coredump from the journal using the PID          #
# --------------------------------------------------------- #
get_journal_coredump() {
	JPID=$1
	echo -n "Retrieving core file with PID ${JPID}... "
	COREFILE="${ARCHIVE_PATH}/core.${JPID}"
	COREFILE_BIN=$($COREDUMP_BIN dump -o ${COREFILE} ${JPID} 2>/dev/null | grep -i 'Executable:' | $AWK_BIN '{print $NF}')
	#Only extracted core files are cleaned up.
	CLEANUP_CORE=1
	if [[ -s $COREFILE ]]; then
		echo Done
	else
		echo "ERROR"
		echo "Coredump not found - PID ${JPID}"
		echo
		echo "Use '$COREDUMP_BIN list' to see coredumps."
		echo
		cleanup
		exit 10
	fi
	verbose "$($COREDUMP_BIN info $JPID)"
	echo >> $GETAPPCORE_LOG
	$COREDUMP_BIN info $JPID >> $GETAPPCORE_LOG
	echo >> $GETAPPCORE_LOG
	verbose "Extracted corefile:    $COREFILE"
	verbose "Associated executable: $COREFILE_BIN"
}

# --------------------------------------------------------- #
# upload_archive ()                                         #
# Upload the appcore archive to the designated ftp server   #
# --------------------------------------------------------- #
upload_archive() {
	echo "Uploading Archive... ${FINAL_ARCHIVE_FILE}"
	UPLOAD_SERVICE=$(echo ${UPLOAD_TARGET} | cut -d: -f1)
	ERRNO=255
	verbose "UPLOAD_TARGET      = ${UPLOAD_TARGET}"
	verbose "FINAL_ARCHIVE_FILE = ${FINAL_ARCHIVE_FILE}"
	verbose "FINAL_ARCHIVE_NAME = ${FINAL_ARCHIVE_NAME}"
	case $UPLOAD_SERVICE in
	https)
		echo "Protocol... HTTPS"
		UPLOAD_URL=$($SED_BIN -e "s/{[Tt][Aa][Rr][Bb][Aa][Ll][Ll]}/${FINAL_ARCHIVE_FILE}/g" <<< ${UPLOAD_TARGET})
		verbose "UPLOAD_URL         = $UPLOAD_URL"
		verbose "Executing: $CURL_BIN -v -L -A SupportConfig -T ${FINAL_ARCHIVE_NAME} ${UPLOAD_URL}"
		if (( $VERBOSE )); then
			$CURL_BIN -v -L -A SupportConfig -T "${FINAL_ARCHIVE_NAME}" "${UPLOAD_URL}"
			ERRNO=$?
		else
			$CURL_BIN -v -L -A SupportConfig -T "${FINAL_ARCHIVE_NAME}" "${UPLOAD_URL}" >> $GETAPPCORE_LOG 2>&1
			ERRNO=$?
		fi
		;;
	ftps|ftpes)
		echo "Protocol... FTPES"
		UPLOAD_URL=$($SED_BIN -e 's/ftpes:/ftp:/g;s/ftps:/ftp:/g' <<< ${UPLOAD_TARGET})
		verbose "UPLOAD_URL         = $UPLOAD_URL"
		FTPES_OPTIONS="--ssl-reqd"
		verbose "Executing: $CURL_BIN -#T ${FINAL_ARCHIVE_NAME} ${FTPES_OPTIONS} ${UPLOAD_URL}/${FINAL_ARCHIVE_FILE}"
		if (( $VERBOSE )); then
			$CURL_BIN -#T ${FINAL_ARCHIVE_NAME} ${FTPES_OPTIONS} ${UPLOAD_URL}/${FINAL_ARCHIVE_FILE}
			ERRNO=$?
		else
			$CURL_BIN -#T ${FINAL_ARCHIVE_NAME} ${FTPES_OPTIONS} ${UPLOAD_URL}/${FINAL_ARCHIVE_FILE} >> $GETAPPCORE_LOG 2>&1
			ERRNO=$?
		fi
		;;
	esac
	echo
	if (( $ERRNO )); then
		echo "Upload status... FAILED"
		echo
		echo "Please upload the archive manually."
	else
		echo "Upload status... Success!"
		echo
		echo "   Please contact SUSE Technical Support for assistance analyzing this core."
		echo "   If you already have an open Service Request, please update it with the"
		echo "   archive name below."
	fi
	echo
}

# --------------------------------------------------------- #
# prepare_run_script_for_container ()                       #
# Creates a temporary script for running getappcore inside  #
# a container.                                              #
# It installs the getappcore basic dependencies and runs    #
# getappcore with the given core file                       #
# --------------------------------------------------------- #
prepare_run_script_for_container() {
cat <<- EOF > ${GETAPPCORE_TMP_DIR}/runincontainer
#!/bin/bash
COREFILE=$1
zypper in -y supportutils gdb curl file systemd-coredump
/sbin/getappcore $COREFILE
EOF
chmod +x ${GETAPPCORE_TMP_DIR}/runincontainer
}

# --------------------------------------------------------- #
# run_in_container ()                                       #
# Creates a temporary script for running getappcore inside  #
# a container.                                              #
# It installs the getappcore basic dependencies and runs    #
# getappcore with the given core file                       #
# --------------------------------------------------------- #
run_in_container() {
	if ! [ -e $PODMAN_BIN ]; then
		echo "podman is required for running getappcore inside a container."
		echo "Please install podman and try again."
		exit 1
	fi
	CONTAINER_IMAGE=$1
	[[ $JOURNAL_PID ]] && get_journal_coredump $JOURNAL_PID
	if [ ! -z $COREFILE -a -e $COREFILE ]; then
		prepare_run_script_for_container
		$PODMAN_BIN run -it --entrypoint=/tmp/sbin/runincontainer -v ${ARCHIVE_PATH}:${ARCHIVE_PATH} -v ${GETAPPCORE_TMP_DIR}:/tmp/sbin $CONTAINER_IMAGE $COREFILE
	fi
}

# --------------------------------------------------------- #
# main ()                                                   #
# Main script function                                      #
# --------------------------------------------------------- #
while getopts b:hr:ufvj:c: opt
do
	case $opt in
	\?)
		get_server_release
		show_title
		show_help
		cleanup
		exit 0
		;;
	b)
		COREFILE_BIN=$OPTARG
		;;
	r)
		SR_NUM=$OPTARG
		;;
	u)
		UPLOAD=1
		UPLOAD_TARGET="${DEFAULT_HTTPS}"
		;;
	f)
		UPLOAD=1
		UPLOAD_TARGET="${DEFAULT_FTPES}"
		;;
	v)
		VERBOSE=1
		;;
	j)
		JOURNAL_PID=$OPTARG
		;;
	c)
		CONTAINER_IMAGE=$OPTARG
		;;
	h)
		get_server_release
		show_title
		show_help
		cleanup
		exit 0
		;;
	esac
done

eval COREFILE=\$$OPTIND
CMDLINE="$0 $*"
[[ $JOURNAL_PID ]] && COREFILE_STR="PID $JOURNAL_PID" || COREFILE_STR=${COREFILE}

get_server_release
show_title
check_binaries

# Check for valid configuration file
if [[ -s ${GETAPPCORE_CONF} ]]; then
	echo "Local configuration file... ${GETAPPCORE_CONF}"
	if [[ -x $DOS2UNIX_BIN ]]; then
		$DOS2UNIX_BIN $GETAPPCORE_CONF &>/dev/null
	else
		$SED_BIN -i -e 's/\r//g' $GETAPPCORE_CONF &>/dev/null
	fi
	. ${GETAPPCORE_CONF}
else
	echo "Local configuration file... None"
fi

[[ -d ${ARCHIVE_PATH} ]] || mkdir -p ${ARCHIVE_PATH}

if [[ ! -z $CONTAINER_IMAGE ]]; then
	echo "Running getappcore in container image: ${CONTAINER_IMAGE}"
	run_in_container $CONTAINER_IMAGE
	exit 0
fi

[[ $JOURNAL_PID ]] && get_journal_coredump $JOURNAL_PID

if [ ! -z $COREFILE -a -e $COREFILE ]; then
	echo -n "Validating core file... "
	COREFILE=$($READLINK_BIN -f $COREFILE)
	verbose ''
	MIME_TYPE=$(file --mime-type --brief $COREFILE)
	TYPE=$(cut -d/ -f2 <<< $MIME_TYPE)
	ERROR=1
	CCMD=''
	verbose "Core file path: $COREFILE"
	verbose "File type: $MIME_TYPE"
	case $TYPE in
	x-coredump) echo Done; ERROR=0 ;;
	x-zstd|zstd) CCMD='zstd -d'; PKG='zstd' ;;
	x-lz4) CCMD='lz4 -d'; PKG='lz4' ;;
	x-xz) CCMD='xz -d'; PKG='xz' ;;
	x-bzip2) CCMD='bzip2 -d'; PKG='bzip2' ;;
	x-gzip|gzip) CCMD='gzip -d'; PKG='gzip' ;;
	esac
	if (( $ERROR )); then
		if [[ -n $CCMD ]]; then
			echo "ERROR: Core file compressed -- $MIME_TYPE"
			echo
			if rpm -q $PKG &> /dev/null; then
				echo "  Try: $CCMD $COREFILE"
			else
				echo "  Try: zypper in $PKG; $CCMD $COREFILE"
			fi
		else
			echo "ERROR: Invalid core file type -- $MIME_TYPE"
		fi
		echo
		cleanup
		exit 5
	fi
	echo -n "Validating binary file... "
	verbose ''
	if [ -z $COREFILE_BIN ]; then
		verbose "Extracting binary file from core file"
		COREFILE_BIN=`$GDB_BIN --core=$COREFILE --batch 2>/dev/null | $GREP_BIN "generated" | $CUT_BIN -d '\`'  -f 2| $CUT_BIN -d " " -f 1 | $CUT_BIN -d "'" -f 1 | $CUT_BIN -d ":" -f 1`
		if [ ! -z "$COREFILE_BIN" ]; then
			COREFILE_BIN=`$WHICH_BIN $COREFILE_BIN 2>/dev/null`
		fi
		verbose "Extracted file: $COREFILE_BIN"
	fi
	if ! [[ -x $COREFILE_BIN ]]; then
		echo
		echo "Unable to determine the binary which generated $COREFILE!"
		echo "  ($COREFILE_BIN does not seem to be an executable!)"
		echo "Please manually determine the binary, and execute the script again"
		echo "using the '-b [CORE_BINARY] parameter."
		echo
		cleanup
		exit -1
	else
		COREFILE_BIN=$($READLINK_BIN -f $COREFILE_BIN)
		verbose "$(file $COREFILE_BIN)"
		echo "Done"

		# Build full name of directory and tarball
		create_filename

		echo -n "Checking Source Binary with chkbin... "
		CHKBIN_LOG=$($CHKBIN_BIN $COREFILE_BIN | $GREP_BIN "Log File" 2>/dev/null | $AWK_BIN '{print $3}')
		CHKBIN_RESULT=$($CAT_BIN $CHKBIN_LOG | $GREP_BIN STATUS | $AWK_BIN '{print $2}')
		echo "Done"

		echo "Crashing binary: $COREFILE_BIN       chkbin result: $CHKBIN_RESULT" >> $GETAPPCORE_LOG 2>&1
		echo >> $GETAPPCORE_LOG
		verbose "Crashing binary: $COREFILE_BIN"
		verbose "Chkbin result:   $CHKBIN_RESULT"

		# Use GDB to build list of required libraries
		echo -n "Building list of required libraries... "
		echo "Libraries:" >> $GETAPPCORE_LOG
		echo "----------" >> $GETAPPCORE_LOG
		# Create temporary GDB command file
		echo "info shared" > $GDBLIBS_CMD
		$GDB_BIN -q --batch -x $GDBLIBS_CMD $COREFILE_BIN $COREFILE 2>/dev/null | $GREP_BIN "^0x" | $AWK_BIN '{print $NF}' | $SORT_BIN | $UNIQ_BIN >> $GETAPPCORE_LOG
		echo >> $GETAPPCORE_LOG
		# Remove temporary GDB command file
		$RM_BIN $GDBLIBS_CMD
		echo "Done"

		echo -n "Building list of required RPMs... "
		echo "RPMs:" >> $GETAPPCORE_LOG
		echo "-----" >> $GETAPPCORE_LOG
		RPM_LIST=$($RPM_BIN -qf --queryformat "%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n" $COREFILE_BIN | $GREP_BIN -v "not owned")
		RPM_LIST="$RPM_LIST $($CAT_BIN $GETAPPCORE_LOG | $GREP_BIN -i "^/.*\.so.*" | \
			$XARGS_BIN $RPM_BIN -qf --queryformat "%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n" | $GREP_BIN -v "not owned")"
		echo $RPM_LIST | $TR_BIN " " \\n | $SORT_BIN | $UNIQ_BIN >> $GETAPPCORE_LOG
		echo "Done"
		echo >> $GETAPPCORE_LOG

		(( $VERBOSE )) && echo "Building list of debuginfo RPMs... " || echo -n "Building list of debuginfo RPMs... "
		echo "DEBUG RPMs:" >> $GETAPPCORE_LOG
		echo "-----------" >> $GETAPPCORE_LOG
		verbose ""
		GUESSED=0
		GUESSED_BEFORE=0
		while read LIBRARY_RPM
		do
			if [ "${LIBRARY_RPM:(-4)}" = ".rpm" ]; then
				verbose "Checking ${LIBRARY_RPM%.*.*}"
				RPM_INFO=($($RPM_BIN -q --queryformat "%{NAME} %{SOURCERPM} %{VERSION}-%{RELEASE} %{ARCH}" ${LIBRARY_RPM%.*.*}))
				if ! [ "${LIBRARY_RPM%.*.*}" = "${RPM_INFO[1]%.*.rpm}" ]; then
					# Since RPM and (NO)SRC RPMs do not match, check to see if versions match.
					# If so, building the debuginfo name is easy
					RPM_VER="-${RPM_INFO[2]}.*.rpm"
					SRC_NAME=${RPM_INFO[1]/$RPM_VER/}
					verbose "${SRC_NAME} =? ${RPM_INFO[1]}"
					if [ "${#SRC_NAME}" -lt "${#RPM_INFO[1]}" ]; then
						DEBUG_RPM=$SRC_NAME-debuginfo-${RPM_INFO[2]}.${RPM_INFO[3]}.rpm
					else
						# Since version numbers didn't match, we have to guess at the split between
						# the SRC rpm name and version number
						SRC_RPM_NAME=${RPM_INFO[1]%%-[0-9]*}
						SRC_RPM_VER=${RPM_INFO[1]/$SRC_RPM_NAME/}
						SRC_RPM_VER=${SRC_RPM_VER%.*.rpm}
						DEBUG_RPM=$SRC_RPM_NAME-debuginfo$SRC_RPM_VER.${RPM_INFO[3]}.rpm
						GUESSED=1
					fi
				else
					DEBUG_RPM="${RPM_INFO[0]}-debuginfo-${RPM_INFO[2]}.${RPM_INFO[3]}.rpm"
				fi
				verbose "     $LIBRARY_RPM   -->   $DEBUG_RPM"
				if (( $GUESSED )); then
					(( $GUESSED_BEFORE )) || echo Warning
					echo " + ${RPM_INFO[0]}: RPM and SRC version did not match. Debuginfo name may not be reliable! --"
					GUESSED=0
					GUESSED_BEFORE=1
				fi
				# Build list of debug RPMs
				DBG_LIST="$DBG_LIST $DEBUG_RPM"
			fi
		done < $GETAPPCORE_LOG
		# Parse list of debug RPMs and add to log
		echo $DBG_LIST | $TR_BIN " " \\n | $SORT_BIN | $UNIQ_BIN >> $GETAPPCORE_LOG
		if (( $VERBOSE )); then
			echo
			echo "                               ... Done"
		else
			echo " Done"
		fi
		echo >> $GETAPPCORE_LOG

		# Build list of libraries and library directories for gdb
		echo -n "Setting gdb environment variables... "
		while read LIBRARY
		do
			IS_LIB=$(echo $LIBRARY | $GREP_BIN -i  "^/.*\.so.*" | $WC_BIN -l)
			if [ "$IS_LIB" = "1" ]; then
				if [ -z "$REQUIRED_LIBRARIES" ]; then
					REQUIRED_LIBRARIES="$LIBRARY"
					LIBRARY_DIRS=$($DIRNAME_BIN $LIBRARY)
				else
					REQUIRED_LIBRARIES="$REQUIRED_LIBRARIES $LIBRARY"
					LIBRARY_DIRS="${LIBRARY_DIRS} $($DIRNAME_BIN $LIBRARY)"
				fi
			fi
		done < $GETAPPCORE_LOG

		LIBRARY_DIRS=$(echo $LIBRARY_DIRS | $TR_BIN " " "\n" | $SORT_BIN | $UNIQ_BIN)

		for LIBRARY_DIR in $LIBRARY_DIRS
		do
			if [ -z "$GDB_SOLIB_SEARCH_PATH" ]; then
				GDB_SOLIB_SEARCH_PATH=".$LIBRARY_DIR"
			else
				GDB_SOLIB_SEARCH_PATH="$GDB_SOLIB_SEARCH_PATH:.$LIBRARY_DIR"
			fi
		done
		echo "Done"

		# Move chkbin and getappcore log to GETAPPCORE_TMP_DIR to be included in tar archive
		if [[ -s $GETAPPCORE_CONF ]]; then
			echo "${GETAPPCORE_CONF}:" >> $GETAPPCORE_LOG
			echo "-----------" >> $GETAPPCORE_LOG
			$CAT_BIN ${GETAPPCORE_CONF} >> $GETAPPCORE_LOG
			echo >> $GETAPPCORE_LOG
		fi
		$MV_BIN $CHKBIN_LOG $GETAPPCORE_LOG $LOG_DIR
		CHKBIN_LOG=$LOG_DIR/$(basename $CHKBIN_LOG)
		GETAPPCORE_LOG=$LOG_DIR/$(basename $GETAPPCORE_LOG)

		(( $VERBOSE )) && { echo "Creating gdb startup files... "; echo; } || echo -n "Creating gdb startup files... "
		create_opencoreini
		create_opencoresh
		create_symlinks
		(( $VERBOSE )) && { echo "                          ... Done"; echo; } || echo "Done"

		create_archive

		(( $UPLOAD )) && upload_archive
		echo | $TEE_BIN -a $GETAPPCORE_LOG
		echo "Affected Binary: ${COREFILE_BIN}" | $TEE_BIN -a $GETAPPCORE_LOG
		echo "Coredump File:   ${COREFILE}" | $TEE_BIN -a $GETAPPCORE_LOG
		echo "Archive Name:    ${FINAL_ARCHIVE_NAME}" | $TEE_BIN -a $GETAPPCORE_LOG
		(( $UPLOAD )) && echo "Upload URL:      ${UPLOAD_URL}" | $TEE_BIN -a $GETAPPCORE_LOG
		cleanup
	fi
else
	if [ -z $COREFILE ]; then
		echo "Required parameter -j PID or COREFILE missing!"
	else
		echo
		echo "ERROR: Missing valid corefile!"
		echo
	fi
	cleanup
	show_help
	exit -1
fi

echo
echo "Finished!"
