#!/bin/sh

# **********************************************************************************
# * (c) Copyright 2021 NetIQ Corporation, a Micro Focus company. All Rights Reserved.
# * THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,
# * PROPRIETARY AND TRADE SECRET INFORMATION OF NETIQ. ACCESS TO
# * THIS WORK IS RESTRICTED TO (I) NETIQ EMPLOYEES WHO HAVE A NEED
# * TO KNOW HOW TO PERFORM TASKS WITHIN THE SCOPE OF THEIR ASSIGNMENTS AND
# * (II) ENTITIES OTHER THAN NETIQ WHO HAVE ENTERED INTO
# * APPROPRIATE LICENSE AGREEMENTS. NO PART OF THIS WORK MAY BE USED,
# * PRACTICED, PERFORMED, COPIED, DISTRIBUTED, REVISED, MODIFIED,
# * TRANSLATED, ABRIDGED, CONDENSED, EXPANDED, COLLECTED, COMPILED,
# * LINKED, RECAST, TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN
# * CONSENT OF NETIQ. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
# * AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND CIVIL
# * LIABILITY.
# *
# *
# * ========================================================================
# ***********************************************************************************

#  Author: Paul Hardwick (PAH)
# Company: Novell, Inc.
# Purpose: To create and use "core" file bundles with a common interface
#          across platforms.
#
#Code Standard: To allow the script to run on multiplatforms, ensure that
#		functions follow the following standard:
#
#		# Comment describing what the function does
#		functionName()
#		{
#		doActionsInsideFunction
#		}
#
#  $Id: novell-getcore 8 2012-05-22 14:35:17Z phardwick $
##### Start of general script variables #####
setVars_General()
{
	# Script version and title
	UTILITY_VERSION_MAJOR=1
	UTILITY_VERSION_MINOR=2
	UTILITY_VERSION_REVISION=09
	UTILITY_VERSION="$UTILITY_VERSION_MAJOR.$UTILITY_VERSION_MINOR.$UTILITY_VERSION_REVISION"
	UTILITY_TITLE="NetIQ GetCore Utility"
	UTILITY_COPYRIGHT="Copyright (c) 2013 NetIQ Corporation and its affiliates. All Rights Reserved."

	# Supported Operating Systems for this script
	SUPPORTED_OS="Linux SunOS AIX"

	# Null Device
	NULLDEV=/dev/null

	# Default CMD Variables
	CMD_HOSTOS="uname -s"

	# Save output from a command into this file.  The output can then be used later on in tracking and displaying errors.
	COMMAND_OUTPUT_TEMP_FILE="/tmp/novell-getcore_gdb_command_output.$$"

	# GDB command file
	GDB_COMMAND_FILE="/tmp/novell-getcore_core_gdb_commands.$$"

	# GDB command file to open core
	GDB_COMMAND_OPENCORE_FILE="./opencore.ini"

	# Script to open the core file with GDB
	OPENCORE_FILE="./opencore.sh"

	# Command to cat the output from the generic output temp file
	CMD_COMMAND_OUTPUT="cat $COMMAND_OUTPUT_TEMP_FILE"

	# Temp file for storing information extracted from GDB
	GDB_EXTRACTED_DATA_TEMP_FILE="/tmp/novell-getcore_extracted_data.$$"

	# Command to get the current user's ID
	CMD_USER_ID="id"

	# Package temp info file
	PKG_TEMP_INFO_FILE="/tmp/novell-getcore_packages.$$"

	# Shared lib temp file
	GDB_SHARED_LIB_TEMP_FILE="/tmp/novell-getcore_sharedlibs.$$"

	# List of temp files that need to be cleaned up
	TEMP_FILES="$PKG_TEMP_INFO_FILE $PKGFILE_TEMP_INFO_FILE $COMMAND_OUTPUT_TEMP_FILE $COMMAND_OUTPUT_TEMP_FILE.tmp $GDB_COMMAND_FILE $GDB_COMMAND_OPENCORE_FILE $OPENCORE_FILE $GDB_EXTRACTED_DATA_TEMP_FILE $GDB_SHARED_LIB_TEMP_FILE"

	# List of the signals that can be trapped by the script eg: If someone hits CTRL-C then we want to be able to clean up the temp files etc.
	TRAP_SIGNALS="1 2 3 5"

	# Script configuration file
	CFG_FILE="./install.cfg"

	# Script keyword deliminator
	CFG_FILE_DELIM="="

	# List of terminal types that should have color OFF by default
	TERMINAL_TYPES="xterm"

	# List of operations
	OP_BUNDLE_CREATE=1

	# List of utilities that need to be found in current path
	REQUIRED_UTILS="gdb gzip tar ls cut expr tr cat grep"

	# Type of parameters
	PARM_REQ=0
	PARM_OPT=1

	# Initialize the values for "YES", "NO", "INVALID", "TRUE" and "FALSE"
	TRUE=0; FALSE=1; YES=$TRUE; NO=$FALSE; INVALID=999

	# Is the Twirler enabled?
	TWIRL_ENABLED=$TRUE

	# End of line marker
	MARK_EOL="ENDOFLINE"
	
	# Kernel Info
	KERNEL_INFO=`uname -a`

	# Debug on or off
	#DEBUG="$TRUE"
}

# Set static message string variables
setVars_Messages()
{
	# Message strings
	MSG_REQ_COREFILE="Must provide core file. eg: /var/nds/dib/core"
	MSG_GDB_IS_NOT_A_CORE="is not a core dump"
	MSG_GDB_NO_SHARED_LIBS_LOADED="No shared libraries loaded at this time"
	MSG_GDB_CORE_WAS_GENERATED_BY="Core was generated by"
	MSG_GDB_PARSING_OUTPUT="Parsing GDB output..."
	MSG_GDB_PREPROCESSING_OUTPUT="PreProcessing GDB output..."
	MSG_CORE_GENERATED_BY="Core generated by:"
	MSG_SHARED_LIBS_OBTAIN="Obtaining names of shared libraries listed in core..."
	MSG_SHARED_LIBS_COUNT="Counting number of shared libraries listed in core..."
	MSG_SHARED_LIBS_TOTAL="Total number of shared libraries listed in core: "
	MSG_GDB_GEN_GDBINIT="Generating GDBINIT commands to open core remotely..."
	MSG_GDB_GEN_OPENCORE="Generating ./opencore.sh..."
	MSG_PKG_INFO="Gathering package info..."
	MSG_IGNORING_BINARY_TYPE="User requested that 'file' checking of the binary type to be ignored..."
	MSG_OPTION_ONLY_CORE="Option enabled: Only bundle core file"
	MSG_OPTION_ONLY_STACK="Option enabled: Only bundle GDB stack output"

	# GDB Command strings
	GDB_CMD_START="# GDBCMDSTART"
	GDB_CMD_END="# GDBCMDEND"
	GDB_CMD_INFO="info"
	GDB_CMD_INFOSHAREDLIBS="$GDB_CMD_INFO sharedlibrary"
	GDB_CMD_INFOTHREADS="$GDB_CMD_INFO threads"
	GDB_CMD_INFOALLREGS="$GDB_CMD_INFO all-registers"
	GDB_CMD_INFOARGS="$GDB_CMD_INFO args"
	GDB_CMD_INFOFRAME="$GDB_CMD_INFO frame"
	GDB_CMD_INFOLOCALS="$GDB_CMD_INFO locals"
	GDB_CMD_BT="bt"
	GDB_CMD_THREADAPPLYALL="thread apply all "
}

##### End of general script variables #####

##### Start of Host OS specific Variables #####
# Set the Linux specific variables
setVars_Linux()
{
	FULLHOSTNAME="`hostname --long`"
	SHORTHOSTNAME="`hostname --short`"
	CMD_LIST_ALL_PACKAGES="rpm -qa"
	EXTRA_FILES="/etc/SuSE-release /etc/redhat-release /etc/debian_version"
        
	# Debian commands 
	if [ -f "/etc/debian_version" ]
	then
		CMD_LIST_ALL_PACKAGES="dpkg --list"
	fi
}

# Set the SunOS specific variables
setVars_SunOS()
{
	FULLHOSTNAME="`hostname`.`domainname`"
	SHORTHOSTNAME="`hostname`"
	CMD_LIST_ALL_PACKAGES="pkginfo -x"
	EXTRA_FILES=
}

# Set the HP-UX specific variables
setVars_HPUX()
{
	FULLHOSTNAME="`hostname`.`domainname`"
	SHORTHOSTNAME="`hostname`"
	CMD_LIST_ALL_PACKAGES="swlist -a product"
	EXTRA_FILES=
}

# Set the AIX specific variables
setVars_AIX()
{
	FULLHOSTNAME="`hostname`.`domainname`"
	SHORTHOSTNAME="`hostname`"
	CMD_LIST_ALL_PACKAGES="lslpp -l -c"
	EXTRA_FILES=
}
##### End of Host OS Variables #####

# Enable the colors but not if an XTERM
enableColorsIfNeeded()
{
	for TERM_TYPE in $TERMINAL_TYPES
	do
		if [ "$TERM_TYPE" = "$TERM" ]
		then
			setANSIColours "OFF"
			return 0
		fi
	done

	setANSIColours "ON"
}

##### Set the ANSI colours #####
# Enable or disable ANSI colours
# Input: $1="ON" or "OFF"
setANSIColours()
{
	if [ -z "$1" ]
	then
		dispError "Internal Error:  setANSIColours() was not passed a parameter."
		abortScript "1"
	else
		case $1 in
		"on" |"ON")
			enableANSIColours;;
		"off" |"OFF")
			disableANSIColours;;
		*)
			dispError "Internal Error:  setANSIColours() was passed an invalid parameter of '$1'."
			abortScript "1"
		esac
	fi
}

enableANSIColours()
{
	esc=""

	fgblack="${esc}[30m";	fgred="${esc}[31m";	fggreen="${esc}[32m";
	fgyellow="${esc}[33m";	fgblue="${esc}[34m";	fgpurple="${esc}[35m";
	fgcyan="${esc}[36m";	fgwhite="${esc}[37m";
  
	bgblack="${esc}[40m";	bgred="${esc}[41m";	bggreen="${esc}[42m";
	bgyellow="${esc}[43m";	bglue="${esc}[44m";	bgpurple="${esc}[45m";
	bgcyan="${esc}[46m";	bgwhite="${esc}[47m";

	boldon="${esc}[1m";	boldoff="${esc}[22m";
	italicson="${esc}[3m";	italicsoff="${esc}[23m";
	ulon="${esc}[4m";	uloff="${esc}[24m";
	invon="${esc}[7m";	invoff="${esc}[27m";

	reset="${esc}[0m";
}

# Disable the ANSI colours
disableANSIColours()
{
	fgblack="";	fgred="";	fggreen="";
	fgyellow="";	fgblue="";	fgpurple="";
	fgcyan="";	fgwhite="";
  
	bgblack="";	bgred="";	bggreen="";
	bgyellow="";	bglue="";	bgpurple="";
	bgcyan="";	bgwhite="";

	boldon="";	boldoff="";
	italicson="";	italicsoff="";
	ulon="";	uloff="";
	invon="";	invoff="";

	reset="";
}

# Reset the ANSI colours on the terminal back to normal
resetANSIcolours()
{
	echo ${reset}
}
##### End of the ANSI colours #####


##### Start of Auxillary Functions #####
# Determine which OS the script is being run on
# Output: OS contains operating system
getHostOS()
{
	OS="`$CMD_HOSTOS`"

	# Convert HP-UX to HPUX as function or variable names don't like hyphens in them
	if [ "$OS" = "HP-UX" ]
	then
		OS="HPUX"
	fi
}

# Verify the the host OS that this script is executing on is supported
# by the script
verifySupportedOS()
{
	for CHECKOS in $SUPPORTED_OS
        do
		if [ "$OS" = "$CHECKOS" ]
		then
			return 1
		fi
	done
	echo "Sorry, the \"$OS\" operating system is not supported by this utility."
	exit 1
}


# Delete the temp files that have been created.  Ensure that any temp file that needs to be deleted is in $TEMP_FILES from setVars_General().
removeTempFiles()
{
	for TEMP_FILE_NAME in $TEMP_FILES
	do
		debug "removeTempFiles() Removing $TEMP_FILE_NAME"
		rm -rf $TEMP_FILE_NAME
		
	done
}

# Calculates the length of a string
# Input: $1=string to calculate
# Output: LENGTH= number of chars in string from $1
length()
{
	LENGTH="`echo $1| wc -c`"
	# remove one from the total due to the CR at the end
	LENGTH="`expr $LENGTH - 1`"
}

# Count the number of items in a list
# Input: $1=list of items eg: "One Two Three"
# Output: RC=number of items
countListItems()
{
	TOTAL_LIST_ITEMS=0
	for LIST_ITEM in $1
	do
		TOTAL_LIST_ITEMS=`expr $TOTAL_LIST_ITEMS + 1`
	done

	return $TOTAL_LIST_ITEMS
}

# Register for signal trapping.  This is used so that the temp files can be cleaned up.
registerSignalTraps()
{
	trap 'abortScriptFromUser' $TRAP_SIGNALS
}

# Aborts the script because the user requested to. This function is what the signal traps are registered for.
abortScriptFromUser()
{
	echo
	abortScript "99" "YES"
}

# Abort the script.  
# Input:  $1=OPTIONAL! exit code 
#         $2=OPTIONAL! (requires $1) "YES" = Really abort - ignore whatever the FORCE switch is set to 
abortScript()
{
	SCRIPT_ABORTED_MSG="${fgred}Utility execution has been aborted.${reset}"
	SCRIPT_NOT_ABORTED_MSG="${boldon}${fgred}Utility was not aborted due to FORCE installation enabled on the command line.${reset}"

	if [ -z "$1" ]
	then
		EXIT_CODE=0
	else
		EXIT_CODE=$1
	fi

	if [ "$FORCE" = "true" ]
	then
		if [ "$2" != "YES" ]
		then
			echo $SCRIPT_NOT_ABORTED_MSG
		else
			removeTempFiles
			exit $EXIT_CODE
		fi
	else
		removeTempFiles
		exit $EXIT_CODE
	fi
}

# Display the script title
dispTitle()
{
	echo "${boldon}${fgred}$UTILITY_TITLE ${fgwhite}$UTILITY_VERSION${fgred} [${fgwhite}$OS${fgred}]"
	echo "${fgred}$UTILITY_COPYRIGHT${reset}"
}

# Display an error message. It will 'bordered' by equal signs (eg: ====). 
# If "NO_ERRORS" is true then don't display the error. Not really a good idea
# as you won't know why the error occured.
dispError()
{
	if [ "$NO_ERRORS" != "true" ]
	then
		printf "${boldon}${fgred}Error: $1${reset}\n"
	fi
}

# Display a status message
# Input $1=Status message
dispStatus()
{
	echo "[${fggreen}*${reset}] $1"
}

# Get the output from a command from the $COMMAND_OUTPUT_TEMP_FILE 
# Output: ERROR_OUTPUT= text inside $COMMAND_OUTPUT_TEMP_FILE
getCommandOutput()
{
	ERROR_OUTPUT="`$CMD_COMMAND_OUTPUT`"
}

# Debug Error Message
debug()
{
	if [ "$DEBUG" = "$TRUE" ]
	then
		printf "DBG: $1\n"
	fi
}

##### End of Auxillary Functions #####

##### Start of Display Functions #####
# Initialize the twirler
initTwirler()
{
	if [ "$TWIRL_ENABLED" = "$TRUE" ]
	then
		TWIRL_TYPE=0
		TWIRL_SPACE=" "

		# TWIRL_MAX_POS: Reflects the maximum number of possible char positions.
		#                This MUST match the case/esac statement in dispTwirler()
		TWIRL_MAX_POS=16
	fi  
}

# Display a twirling blob
dispTwirler()
{
	TWIRL_TYPE=`expr $TWIRL_TYPE + 1`
	if [ $TWIRL_TYPE -gt $TWIRL_MAX_POS ]
	then
		TWIRL_TYPE=1
	fi
        
	case $TWIRL_TYPE in
		1)  TWIRL_CHAR="|" ;;
		2)  TWIRL_CHAR="|" ;;
		3)  TWIRL_CHAR="/" ;;
		4)  TWIRL_CHAR="/" ;;
		5)  TWIRL_CHAR="-" ;;
		6)  TWIRL_CHAR="-" ;;
		7)  TWIRL_CHAR="\\" ;;
		8)  TWIRL_CHAR="\\" ;;
		9)  TWIRL_CHAR="|" ;;
		10) TWIRL_CHAR="|" ;;
		11) TWIRL_CHAR="/" ;;
		12) TWIRL_CHAR="/" ;;
		13) TWIRL_CHAR="-" ;;
		14) TWIRL_CHAR="-" ;;
		15) TWIRL_CHAR="\\" ;;
		16) TWIRL_CHAR="\\" ;;
	esac

	printf "\b${boldon}${fgyellow}$TWIRL_CHAR${reset}"
}

# Finish the twirler. Essentially sets the TWIRL_TYPE to 1 as it loops through the rest.
dispTwirlerFinish()
{
	TWIRL_TYPE=0
	printf "\b"
}

# Display the result on the right hand side of the screen - but on the left
# Input: $1=Info to display - MAX Chars=13
dispResult()
{
	printf "${boldon}[${fggreen}%-13s${reset}${boldon}]${reset}\n" "$1"
}

# Display the result of the right hand side of the screen - but on the right
# Input: $1=Info to display - MAX Chars=13. The colour is Green for good.
dispResultNumber()
{
	printf "${boldon}[${fggreen}%13s${reset}${boldon}]${reset}\n" "$1"
}

# Display the result of the right hand side of the screen - but on the right
# Input: $1=Info to display - MAX Chars=13.  The colour is Red for Failed.
dispResultNumberFailed()
{
	printf "${boldon}[${fgred}%13s${reset}${boldon}]${reset}\n" "$1"
}

# Display the item of the left hand side of the screen
# Input: $1=Item to display
#        $2=OPTIONAL! Extra info to display if it is given
dispItem()
{
	if [ -z "$2" ]
	then
		printf "${boldon}| %-63s${reset}" "$1"
	else
		printf "${boldon}| %-38s%-25s${reset}" "$1" "| $2" 
	fi
}
##### End of Display Functions #####

##### Start of Various Action Functions #####
# Read a keyword from the configuration file and return
# the value(s) in $CFG_FILE_RESULT
# Input:  $1 = Keyword
# Output: $CFG_FILE_RESULT = Data after the keyword. If blank then no data or keyword not found
readCFGFile()
{
	if [ -z "$1" ]
	then
		dispError "Internal Error:  readCFGFile() was not passed a parameter."
		abortScript "1"
	fi

	CFG_FILE_RESULT=
	# As long as the configuration file exists then read it
	if [ -f "$CFG_FILE" ]
	then
		CFG_FILE_DATA="`cat $CFG_FILE | grep $1`"

		if [ ! -z "$CFG_FILE_DATA" ]
		then
			CFG_FILE_RESULT="`echo $CFG_FILE_DATA | cut -d $CFG_FILE_DELIM -f 2`"
		fi
	fi
}

##### End of Action Functions #####

##### Start of Operation Functions #####
# Display the type of operation
dispOperation()
{
	OP_DESC=

	case $OPERATION in
		"1") OP_DESC="Create Bundle" ;;
		*)
			debug "OPERATION=$OPERATION OP_DESC=$OP_DESC"
			dispError "Internal Error:  dispOperation() Unknown operation '$OPERATION'."
			abortScript "1"
	esac
	debug "dispOperation() Operation is \"$OP_DESC\" ($OPERATION)."
	debug "dispOperation() Operation parameters are: $OPERATION_PARAMS"

	if [ "$OPTION_ONLY_CORE" = "$TRUE" ]
	then
		dispStatus "$MSG_OPTION_ONLY_CORE"
	fi 

	if [ "$OPTION_ONLY_STACK" = "$TRUE" ]
	then
		dispStatus "$MSG_OPTION_ONLY_STACK"
	fi 

}

# Perform the requested operation
doOperation()
{
	if [ -z "$1" ]
	then
		dispError "Internal Error:  doOperation() was not passed a parameter."
		abortScript "1"
	fi

	case $1 in
		"$OP_BUNDLE_CREATE")    getParameter 0 $PARM_REQ "$MSG_REQ_COREFILE"; 
					COREFILE="$PARM";
					getParameter 1 $PARM_OPT ""; 
					COREFILE_GENERATED_BY="$PARM";
					opBundleCreate;;
					
		*)
			dispError "Internal Error:  doOperation() Unknown operation '$1'."
			abortScript "1"
	esac
}

# Process GDB output for Shared Libs SunOS
# Input: CURRENT_LINE=line data
# Output: SHARED_LIB=shared lib file
processGDBOutput_SharedLibs_SunOS()
{
	IGNORE_LINES="Read sharedlibrary"
	SHARED_LIB="`echo "$CURRENT_LINE" | egrep "Yes|No" | awk -F" " '{print $NF}'`"
	for LINE in $IGNORE_LINES
	do
		if [ "$SHARED_LIB" = "$LINE" ]
		then
			SHARED_LIB=
		fi	
	done
	debug "processGDBOutput_SharedLibs_$OS SHARED_LIB=$SHARED_LIB"
}

# Process GDB output for Shared Libs AIX
# Input: CURRENT_LINE=line data
# Output: SHARED_LIB=shared lib file
processGDBOutput_SharedLibs_AIX()
{
	IGNORE_LINES="open Range sharedlibrary"
	SHARED_LIB="`echo "$CURRENT_LINE" | egrep "Yes|No" | awk -F" " '{print $NF}'`"
	for LINE in $IGNORE_LINES
	do
		if [ "$SHARED_LIB" = "$LINE" ]
		then
			SHARED_LIB=
		fi	
	done
	debug "processGDBOutput_SharedLibs_$OS() SHARED_LIB=$SHARED_LIB"
}

# Process GDB output for Shared Libs HPUX
# Input: CURRENT_LINE=line data
# Output: SHARED_LIB=shared lib file
processGDBOutput_SharedLibs_HPUX()
{
	IGNORE_LINES="Shared flags # dstart sharedlibrary"
	SHARED_LIB="`echo "$CURRENT_LINE" | egrep "Yes|No" | awk -F" " '{print $NF}'`"
	for LINE in $IGNORE_LINES
	do
		if [ "$SHARED_LIB" = "$LINE" ]
		then
			SHARED_LIB=
		fi	
	done
	debug "processGDBOutput_SharedLibs_$OS() SHARED_LIB=$SHARED_LIB"
}

# Process GDB output for Shared Libs Linux
# Input: CURRENT_LINE=line data
# Output: SHARED_LIB=shared lib file
processGDBOutput_SharedLibs_Linux()
{
	IGNORE_LINES="Read sharedlibrary"
	SHARED_LIB="`echo "$CURRENT_LINE" | egrep "Yes|No" | awk -F" " '{print $NF}'`"
	for LINE in "$IGNORE_LINES"
	do
		if [ "$SHARED_LIB" = "$LINE" ]
		then
			SHARED_LIB=
		fi	
	done
	debug "processGDBOutput_SharedLibs_$OS() SHARED_LIB=$SHARED_LIB"
}

# Append commands to the GDB command file
# Input: $1=Commands
gdbCommandFileAppend()
{
	echo "echo " >> $GDB_COMMAND_FILE
	echo "echo $GDB_CMD_START $1 $MARK_EOL \n" >> $GDB_COMMAND_FILE
	echo "echo \n" >> $GDB_COMMAND_FILE
	echo "$1" >> $GDB_COMMAND_FILE
	echo "echo $GDB_CMD_END $1 $MARK_EOL \n" >> $GDB_COMMAND_FILE
	echo "echo \n" >> $GDB_COMMAND_FILE
}

# Generate the gdbinit command file that will allow the
# core to be opened remotely
genFileGDBInit()
{
	echo "# Generated by $UTILITY_TITLE v$UTILITY_VERSION"	>  $GDB_COMMAND_OPENCORE_FILE
	echo "# Command line used: $GETCORE_CMDLINE"		>> $GDB_COMMAND_OPENCORE_FILE
	echo "# Setup the GDB environment" 			>> $GDB_COMMAND_OPENCORE_FILE
	echo "set solib-absolute-prefix /fakedir"		>> $GDB_COMMAND_OPENCORE_FILE
	echo "set solib-search-path ./ $SHARED_LIB_PATHS"	>> $GDB_COMMAND_OPENCORE_FILE
	echo "set print max-symbolic-offset 1"			>> $GDB_COMMAND_OPENCORE_FILE
	echo "set prompt #"					>> $GDB_COMMAND_OPENCORE_FILE
	echo "set height 0"					>> $GDB_COMMAND_OPENCORE_FILE
	echo ""							>> $GDB_COMMAND_OPENCORE_FILE
	echo "# Load the core file"				>> $GDB_COMMAND_OPENCORE_FILE
	echo "file ./$COREFILE_BIN"				>> $GDB_COMMAND_OPENCORE_FILE
	echo "core ./$COREFILE" 				>> $GDB_COMMAND_OPENCORE_FILE
	echo ""							>> $GDB_COMMAND_OPENCORE_FILE
}

# Generate the opencore script to open core
genFileOpencore()
{
	echo "#!/bin/sh" 				  >  $OPENCORE_FILE
	echo "gdb --command=./$GDB_COMMAND_OPENCORE_FILE" >> $OPENCORE_FILE
	echo "" 					  >> $OPENCORE_FILE
	chmod u+x ./$OPENCORE_FILE
}

# Generate the package info
genFilePackageInfo()
{
	$CMD_LIST_ALL_PACKAGES 1>$PKG_TEMP_INFO_FILE 2>$NULLDEV
}

# Create a bundle containing the core file and required libs
# Input:  $1=corefile
#         $2=corebundle
opBundleCreate()
{
	# Ensure core file exists
	if [ ! -f "$COREFILE" ]
	then
		dispError "Core file ${fgwhite}$COREFILE${fgred} does not exist."
		abortScript "1"
	fi

	# We can't rely on 'file' giving us useful information about the binary that caused the crash.
	# Let's just try to open it with gdb, extract the file that generated it then do a 'which' on it. If the full path
	# is specified to 'which' then it will echo back out the file. If just the file name is given then it will try to find
	# it in the path.

	# If all else fails, the user can specify the binary on the command line.
	if [ ! -z "$COREFILE_GENERATED_BY" ]
	then
		COREFILE_BIN=$COREFILE_GENERATED_BY
		dispStatus "User specified binary that generated core: $COREFILE_BIN"
	else
		# Guess which file generated the core?
		COREFILE_GEN=`gdb --core=$COREFILE --batch 2>$NULLDEV |grep "generated" |cut -d '\`'  -f 2|cut -d " " -f 1 | cut -d "'" -f 1`
		dispStatus "Determined binary that generated core to be: $COREFILE_GEN"

		if [ ! -z "$COREFILE_GEN" ]
		then
			COREFILE_BIN=`which $COREFILE_GEN 2>$NULLDEV`
			if [ -z "$COREFILE_BIN" ]
			then
			    COREFILE_BIN=$COREFILE_GEN
			fi
		fi
	fi

	if [ ! -f "$COREFILE_BIN" ]
	then
		dispError "Unable to find \"$COREFILE_BIN\" on the system.\nPlease specify the binary file explicitly on command line and ensure that it exists."
		abortScript "1"
	fi
	debug "opBundleCreate() Guessing that the binary that cored was: $COREFILE_BIN"

	# Remove the GDB command file
	rm -rf $COMMAND_OUTPUT_TEMP_FILE 1>$NULLDEV 2>$NULLDEV

	# Append the commands that should be executed in GDB, to the command file
	gdbCommandFileAppend "$GDB_CMD_INFOSHAREDLIBS"
	gdbCommandFileAppend "$GDB_CMD_BT"
	gdbCommandFileAppend "$GDB_CMD_THREADAPPLYALL $GDB_CMD_BT"
	gdbCommandFileAppend "$GDB_CMD_INFOTHREADS"
	gdbCommandFileAppend "$GDB_CMD_INFOALLREGS"
	gdbCommandFileAppend "$GDB_CMD_INFOARGS"
	gdbCommandFileAppend "$GDB_CMD_INFOFRAME"
	gdbCommandFileAppend "$GDB_CMD_INFOLOCALS"

	# Process the core with GDB, execute commands then exit
	debug "Executing gdb --version >>$COMMAND_OUTPUT_TEMP_FILE 2>>$COMMAND_OUTPUT_TEMP_FILE"
	gdb --version >>$COMMAND_OUTPUT_TEMP_FILE 2>>$COMMAND_OUTPUT_TEMP_FILE
	RC=$?
	if [ "$RC" != "0" ]
	then
		dispError "Required utility '${fggreen}gdb${fgred}' not in path"
		abortScript "1"
	fi

	dispStatus "Processing '$COREFILE' with GDB..."
        debug "Executing gdb --nx --batch --core=$COREFILE --command=$GDB_COMMAND_FILE $COREFILE_BIN 1>>$COMMAND_OUTPUT_TEMP_FILE 2>>$NULLDEV"
        gdb --nx --batch --core=$COREFILE --command=$GDB_COMMAND_FILE $COREFILE_BIN 1>>$COMMAND_OUTPUT_TEMP_FILE 2>>$COMMAND_OUTPUT_TEMP_FILE
	TMP=`$CMD_COMMAND_OUTPUT` 1>$NULLDEV 2>$NULLDEV

	if [ -z "$TMP" ]
	then
		dispError "No output from GDB using $GDB_COMMAND_FILE was found!"
		abortScript "1"
	fi
	unset TMP
	TMP=

	if [ "$OPTION_ONLY_CORE" != "$TRUE" -a "$OPTION_ONLY_STACK" != "$TRUE" ]
	then
		dispStatus "$MSG_GDB_PREPROCESSING_OUTPUT"
		touch $GDB_EXTRACTED_DATA_TEMP_FILE
		while read CURRENT_LINE
		do
			dispTwirler
			echo "$CURRENT_LINE" | grep $MARK_EOL 1>$NULLDEV 2>$NULLDEV
			RC=$?
			if [ "$RC" = "0" ]
			then
				CURRENT_LINE="`echo $CURRENT_LINE | sed s/$MARK_EOL/!/g | tr ! \\\\n`"
			fi
			echo "$CURRENT_LINE" >> $GDB_EXTRACTED_DATA_TEMP_FILE
		done < $COMMAND_OUTPUT_TEMP_FILE
		dispTwirlerFinish
		rm -rf $COMMAND_OUTPUT_TEMP_FILE
		mv $GDB_EXTRACTED_DATA_TEMP_FILE $COMMAND_OUTPUT_TEMP_FILE
		
		# Read in the GDB output line by line and process it
		PROCESS_GDB_OUTPUT=0
		GDB_PROCESS_FINDSHAREDLIBS=1
		GDB_PROCESS_SHAREDLIBS=2
		GDB_PROCESS_END=99
		dispStatus "$MSG_GDB_PARSING_OUTPUT"
		SHARED_LIBS=
		SHARED_LIB_PATHS=
		while read CURRENT_LINE
		do
			debug "opBundleCreate() PROCESS_GDB_OUTPUT=$PROCESS_GDB_OUTPUT CURRENT_LINE=$CURRENT_LINE"
			dispTwirler
			# Process each line (mainly this is just to get the libs)
			case $PROCESS_GDB_OUTPUT in
				0)      echo "$CURRENT_LINE" | grep "$MSG_GDB_CORE_WAS_GENERATED_BY" 1>$NULLDEV 2>$NULLDEV
					RC=$?
					if [ "$RC" = "0" ]
					then
						dispTwirlerFinish
						dispStatus "Core file $COREFILE is a valid $OS core"
						if [ -z "$COREFILE_BIN" ]
						then
							COREFILE_BIN=`echo $CURRENT_LINE | awk -F"'" '{print $1}'`
						fi
						dispStatus "$MSG_CORE_GENERATED_BY $COREFILE_BIN"
		
						# Verify that the core is a binary executable as long as 
						# the user hadn't requested that this is ignored.
						if [ "$IGNORE_BINARY_TYPE" != "$TRUE" ]
						then
						    file "$COREFILE_BIN" | grep "executable" 1>$NULLDEV 2>$NULLDEV
						    RC=$?
						    if [ "$RC" != "0" ]
						    then
							    dispError "${fgwhite}$COREFILE_BIN${fgred} is not a binary executable.\n       Please specify valid executable for core via the command line.\n       If this is truely a valid binary then rerun using\n the --ignorebinarytype switch."
							    abortScript "1"
						    fi
						else
						    dispStatus "$MSG_IGNORING_BINARY_TYPE"
						fi
		
						PROCESS_GDB_OUTPUT=$GDB_PROCESS_FINDSHAREDLIBS
						dispStatus "$MSG_SHARED_LIBS_OBTAIN"
						continue
					fi;;
		
				1)      if [ "$CURRENT_LINE" = "$GDB_CMD_START $GDB_CMD_INFOSHAREDLIBS" ]
					then
						PROCESS_GDB_OUTPUT=$GDB_PROCESS_SHAREDLIBS
						continue
					fi;;
		
				2)      if [ "$CURRENT_LINE" = "$GDB_CMD_END $GDB_CMD_INFOSHAREDLIBS" ]
					then
						PROCESS_GDB_OUTPUT=$GDB_PROCESS_END
					fi
					processGDBOutput_SharedLibs_$OS $CURRENT_LINE
					
					if [ ! -z "$SHARED_LIB" ]
					then
						if [ "$SHARED_LIB" != "linux-gate.so.1" -a "$SHARED_LIB" != "linux-vdso.so.1" ]
						then
							if [[ "$SHARED_LIB" != *"liblz4-java"* ]]
							then
								SHARED_LIBS="$SHARED_LIBS $SHARED_LIB"
								echo ./`echo "$SHARED_LIB" | awk -F/ '{for (i=1; i<NF-1 ;i++) {printf("%s/",$i)};printf("%s\n",$(NF-1))}'` >> $GDB_SHARED_LIB_TEMP_FILE
							fi
						fi
					fi
		
					if [ ! -z "$SHARED_LIB" ]
					then
						if [ ! -f "$SHARED_LIB" -a "$SHARED_LIB" != "linux-gate.so.1" -a "$SHARED_LIB" != "linux-vdso.so.1" ]
						then
							if [[ "$SHARED_LIB" != *"liblz4-java"* ]]
							then
								dispError "${fgred} The file ${fgwhite}\"$SHARED_LIB\"${fgred} could not be found."
								abortScript "1"
							fi
						fi
					fi;;
					
		
				99)     echo SHARED_LIBS=\"$SHARED_LIBS\" > $GDB_EXTRACTED_DATA_TEMP_FILE
					echo PROCESS_GDB_OUTPUT=$PROCESS_GDB_OUTPUT >> $GDB_EXTRACTED_DATA_TEMP_FILE
					echo COREFILE_BIN=\"$COREFILE_BIN\" >> $GDB_EXTRACTED_DATA_TEMP_FILE
					break;;
		
				*)
					dispError "Internal Error: Unknown value \"$PROCESS_GDB_OUTPUT\" for PROCESS_GDB_OUTPUT"
					abortScript "1";;
			esac
		done < $COMMAND_OUTPUT_TEMP_FILE
		dispTwirlerFinish

		# Ensure that the required data has been parsed from the GDB output
		if [ ! -f "$GDB_EXTRACTED_DATA_TEMP_FILE" ]
		then
        	    dispError "No data was extracted from the GDB output. A possible reason is the specified binary ($COREFILE_BIN) did not actually create the specified core ($COREFILE)."
		    abortScript "1"
		fi

		# Set the folowing vars to nothing, and then source in new values from the extracted / parsed GDB output
		SHARED_LIBS=
		PROCESS_GDB_OUTPUT=
		COREFILE_BIN=
		. $GDB_EXTRACTED_DATA_TEMP_FILE

		debug "SHARED_LIBS should contain data: SHARED_LIBS=$SHARED_LIBS"
		debug "PROCESS_GDB_OUTPUT should contain data: PROCESS_GDB_OUTPUT=$PROCESS_GDB_OUTPUT"
		debug "COREFILE_BIN should contain data: COREFILE_BIN=$COREFILE_BIN"

		# Ensure that the required env vars are now set.
		if [ -z "$SHARED_LIBS" -o -z "$PROCESS_GDB_OUTPUT" -o -z "$COREFILE_BIN" ]
		then
		    dispError "Required data was not found when parsing the GDB output."
		    abortScript "1"
		fi
		
		# Count the number of shared libs 
		dispStatus "$MSG_SHARED_LIBS_COUNT"
		COUNT=0
		for lib in $SHARED_LIBS
		do
			dispTwirler
			COUNT=`expr $COUNT + 1`
			debug "opBundleCreate() Shared Library $COUNT=$lib"
		done
		dispTwirlerFinish
		dispStatus "$MSG_SHARED_LIBS_TOTAL $COUNT"
		
		# Process the share lib paths
		SHARED_LIB_PATHS_TMP=`cat $GDB_SHARED_LIB_TEMP_FILE | sort | uniq`
		for sharedLibPath in $SHARED_LIB_PATHS_TMP
		do
			SHARED_LIB_PATHS=$SHARED_LIB_PATHS:$sharedLibPath
		done
		
		echo SHARED_LIB_PATHS=\"$SHARED_LIB_PATHS\" >> $GDB_EXTRACTED_DATA_TEMP_FILE
		debug "GDB_SHARED_LIB_TEMP_FILE=$GDB_SHARED_LIB_TEMP_FILE"
		debug "SHARED_LIB_PATHS for opencore.ini is: $SHARED_LIB_PATHS"
	fi

	# Determine corefile bundle filename
	if [ "$OPTION_ONLY_CORE" = "$TRUE" ]
	then
	    COREBUNDLE_FILE_EXTRA="only"
	fi

	if [ "$OPTION_ONLY_STACK" = "$TRUE" ]
	then
	    COREBUNDLE_FILE_EXTRA="stack"
	fi
	
	COREBUNDLE_FILE="core`echo $COREBUNDLE_FILE_EXTRA`_`date +%Y%m%d_%H%M%S`_`echo $OS`_"
	COREBUNDLE_FILE="$COREBUNDLE_FILE`echo $COREFILE_BIN|awk -F/ '{print $NF}'`_"
	COREBUNDLE_FILE="$COREBUNDLE_FILE`echo $SHORTHOSTNAME|awk -F. '{print $1}'`"
	COREBUNDLE_FILE="`echo $COREBUNDLE_FILE | tr '[:upper:]' '[:lower:]'`"
	dispStatus "Corefile bundle: $COREBUNDLE_FILE"

	# Ensure corefile bundle does not already exist
	if [ -f "$COREBUNDLE_FILE.tar.gz" ]
	then
		dispError "$COREBUNDLE_FILE.tar.gz already exists"
		abortScript "1"
	fi

	if [ "$OPTION_ONLY_CORE" != "$TRUE" -a "$OPTION_ONLY_STACK" != "$TRUE" ]
	then
		dispStatus "$MSG_GDB_GEN_GDBINIT"
		genFileGDBInit

		dispStatus "$MSG_GDB_GEN_OPENCORE"
		genFileOpencore
	fi

	dispStatus "$MSG_PKG_INFO"
	genFilePackageInfo

	FILES="$COMMAND_OUTPUT_TEMP_FILE $COREFILE_BIN $COREFILE $SHARED_LIBS $GDB_COMMAND_OPENCORE_FILE $OPENCORE_FILE $PKG_TEMP_INFO_FILE"

	if [ "$OPTION_ONLY_CORE" = "$TRUE" ]
	then
		FILES="$COMMAND_OUTPUT_TEMP_FILE $COREFILE $PKG_TEMP_INFO_FILE"
	fi

	if [ "$OPTION_ONLY_STACK" = "$TRUE" ]
	then
		FILES="$COMMAND_OUTPUT_TEMP_FILE $PKG_TEMP_INFO_FILE"
	fi

	# Include extra files (if they exist) in the core bundle
	for FILE in $EXTRA_FILES
	do
		if [ -f "$FILE" ]
		then
			dispStatus "Including file: $FILE"
			FILES="$FILES $FILE"
		fi
	done
    
	# Now create the final tar from the temp directory
	dispStatus "Creating $COREBUNDLE_FILE.tar..."
	debug "Executing tar -hvcvf $COREBUNDLE_FILE.tar $FILES 1>$NULLDEV 2>$NULLDEV"
	tar -hvcvf $COREBUNDLE_FILE.tar $FILES 1>$NULLDEV 2>$NULLDEV
	RC=$?
	if [ "$RC" != "0" ]
	then
		dispError "Unable to create $COREBUNDLE_FILE.tar"
		abortScript "1"
	fi

	# GZIP tar file
	dispStatus "GZipping ./$COREBUNDLE_FILE.tar..."
	debug "Executing gzip ./$COREBUNDLE_FILE.tar"
	gzip ./$COREBUNDLE_FILE.tar
	RC=$?
	if [ "$RC" != "0" ]
	then
		dispError "Unable to gzip $COREBUNDLE_FILE.tar"
		abortScript "1"
	fi

	dispStatus "${fggreen}${boldon}Done${reset}. Corefile bundle is ${fgyellow}${boldon}./$COREBUNDLE_FILE.tar.gz${reset}"
}

# Ensure required utils exist
ensureUtilsExist()
{
	for UTIL in $REQUIRED_UTILS
	do
		# Obviously requires 'which' to be installed and in 
		# current path.
		debug "ensureUtilsExit() Checking for '$UTIL'"
		debug "Executing which $UTIL 1>$NULLDEV 2>$NULLDEV"
		which $UTIL 1>$NULLDEV 2>$NULLDEV
		RC=$?
		debug "Result code: $RC"

		if [ "$RC" != "0" ]
		then
			dispError "Required utility '${fggreen}$UTIL${fgred}' not in path"
			abortScript "1"
		fi
		debug "ensureUtilsExist() Found '$UTIL'!"
	done
}

# Get a specified parameter 
# Input: $1=parameter number
# Output: $PARM=contains value of parameter number
getParameter()
{
	debug "getParameter() Looking for parameter $1: OPERATION_PARAMS=$OPERATION_PARAMS"
	PARM_NUM=$1
	PARM_TYPE=$2
	PARM_ERR_MSG="$3"
	PARM_COUNTER=0
	for OP_PARM in $OPERATION_PARAMS
	do
		debug "getParameter() Parm $PARM_COUNTER = $OP_PARM"
		if [ "$PARM_COUNTER" = "$PARM_NUM" ]
		then
			PARM=$OP_PARM
			debug "getParameter() Found parm $PARM_COUNTER! PARM=$PARM"
			return
		fi
		PARM_COUNTER=`expr $PARM_COUNTER + 1`
	done
	PARM=

	# If the parameter is required then abort the script and display
	# an error message.
	if [ "$PARM_TYPE" = "$PARM_REQ" ]
	then
		dispCommandlineHelp "$PARM_ERR_MSG"
		abortScript "1"
	else
		debug "getParameter() Did not find optional parameter $1"
	fi
}

# Check the command line to see if any command line switches were provided
checkCommandline()
{
	# Require an operation of some sort
	if [ ! -z "$1" ]
	then 
		# Loop through the command line params
		while [ "$#" -gt "0" ]
		do
			case $1 in
				"-h" | "--help")		dispTitle;
								dispCommandlineHelp;
								exit 0;;
				"-v" | "--version")		dispTitle;
								exit 0;;
				"-d" | "--debug" )		DEBUG="$TRUE" ;;
				"-i" | "--ignorebinarytype" )	IGNORE_BINARY_TYPE="$TRUE" ;;
				"-n" | "--noansi")      	setANSIColours "OFF";;
				"-b" | "--createbundle")	OPERATION=$OP_BUNDLE_CREATE ;;
				"-c" | "--coreonly")    	OPTION_ONLY_CORE="$TRUE" ;;
				"-s" | "--stackonly")   	OPTION_ONLY_STACK="$TRUE" ;;

				*)
					# If a param on the command line is unknown to us
					# then it could be a parameter(s) for a switch
					# so check if an $OPERATION has been specified
					if [ -z "$OPERATION" ]
					then
						dispTitle
						dispCommandlineHelp
					else
						OPERATION_PARAMS="$OPERATION_PARAMS $1"
					fi
			esac
			shift
		done     
	else
		dispTitle
		dispCommandlineHelp
	fi

	# We haven't been given an operation, and if we are at this point
	# then the command line params didn't take care of the action
	# so either no op was given or just some other general params
	# on the command line was given.
	if [ "$OPERATION" = "" ]
	then
		dispCommandlineHelp "No operation was specified on the command line."
	fi

	if [ "$OPTION_ONLY_CORE" = "$TRUE" -a "$OPTION_ONLY_STACK" = "$TRUE" ]
	then
		dispCommandlineHelp "Must specify either -c (--coreonly) OR -s (--stackonly)."
	fi

}

# Display the command line help
dispCommandlineHelp()
{
	setANSIColours "OFF"
	echo "Usage: $0 <operation> [options]"
	echo "Where <operation> is the following:"
	echo
	echo "  -b   --createbundle <core> [core'd binary]"
	echo "         Bundle up specified core file"
	echo
	echo "Where <options> can be one or more of the following:"
	echo
	echo "  -i   --ignorebinarytype"
	echo "         Ignore the file type of core'd binary"
	echo
	echo "  -c   --coreonly"
	echo "         Include only the core file in the bundle"
	echo "         (Can not be used with --stackonly)"
	echo
	echo "  -s   --stackonly"
	echo "         Include only the core stack information in the bundle"
	echo "         (Can not be used with --coreonly)"
	echo
	echo "  -d   --debug"
	echo "         Enable debug output"
	echo
	echo "  -v   --version"
	echo "         Display version information"
	echo
	echo "  -h   --help"
	echo "         Display this command line help"
	echo
	echo "Example:"
	echo "        To bundle only the stack information extracted from the core:"
	echo "        $0 -b /var/nds/dib/core.1234 -s"
	echo 
	echo "        To bundle only the core (minus the libraries and binary files):"
	echo "        $0 -b /var/nds/dib/core.1234 -c"
	echo
	echo "        To bundle everything:"
	echo "        $0 -b /var/nds/dib/core.1234"
	echo

	if [ ! -z "$1" ]
	then
		dispError "$1"
	fi

	exit 2
}

# Main function
main()
{
	GETCORE_CMDLINE="$0 $*"
	setVars_General
	registerSignalTraps
	getHostOS
	verifySupportedOS
	setVars_$OS
	setVars_Messages
	initTwirler
	enableColorsIfNeeded

	checkCommandline $*

	dispTitle
	echo
	dispOperation
	ensureUtilsExist
	doOperation $OPERATION

	echo "${reset}"
	removeTempFiles
}

main $*

