#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/sbin/snap/snapcore.sh 1.6.5.4 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2000,2011 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)43        1.6.5.4  src/bos/usr/sbin/snap/snapcore.sh, cmdsnap, bos720 7/14/11 19:42:36
# COMPONENT_NAME: (cmdsnap) 
#
# FUNCTIONS: snapcore.sh
#
# ORIGINS: 27
#
# (C) COPYRIGHT International Business Machines Corp. 1991, 1994
# All Rights Reserved
# Licensed Materials - Property of IBM
#
# US Government Users Restricted Rights - Use, duplication or
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
#
## [End of PROLOG]



# This program gathers all the information required to analyse
# core file sucessfully on any machine.

################################################################################
#	Algorithm:
#		1. Verify core file and exceutable file name passed exists
#		2. Make sure specified core file is created by specified 
#                  executable
#		3. Gather all the libraries used by the program.
#		5. Gather information about installed filesets
#		6. Calculate the space required  
#		7. Archive all the files collected using pax command
#		8. Compress the archive
###############################################################################


# prepend to PATH to prevent unexpected aliasing of commands
export PATH="/usr/bin:/etc:/usr/sbin:$PATH"

#global variables
map_sz=0        	# module information list size 
fileset_sz=0     	# fileset list size 
core_sz=0       	# size in bytes of core file 
exe_sz=0        	# size in bytes of exe 
lib_sz=0          	# module sizes 
total_sz=0      	# total working space required 
errpt_sz=0		# errlog entries for CORE_DUMP
doclean="n" 
dirname="/tmp/snapcore" #location of archive
exename=""
corename=""
l_libs=""



#**********************************************************************
#                   GENERAL UTILITY FUNCTIONS                         * 
#**********************************************************************

Usage () {
	echo "Usage:\n"
	echo "snapcore [-dDir] [-r] <core file  name> [< binary name >]\n"

}

Error() {
	echo "snapcore cannot continue. Exiting.\n"
	cleanUp

}

#follow link until it ends in a file or dir


follow_link()
{
	tmp=$1
	while [ -h $tmp ];do
 		src=`ls -al $tmp |awk '{print $11}'`
 		path=$(cd `dirname $tmp`; cd `dirname $src`;echo $PWD)
		[ $? -ne 0 ] && return 1
 		base=`basename $src`
 		full=$path/$base
 		tmp=$full
	done
	l_source=$tmp
	return 0
}


#go one level down -- /a/b/c -> /a/b and track it in var down
down1()
{
	p1=`dirname $1`
	down="$down/.."
}

#
#go one level up -- /a/b/c -> /a/b/c/d and track it in var up
down2()
{
	up="/`basename $1`$up"
	p2=`dirname $1`
}

#find the no of levels in the path given 
#eg. /a/b/c has 3, / has 0
#
level()
{
lev=0
d=$1
while [ "$d" != "/" ];do
	d="`dirname $d`"
 	lev=$((lev+1))
done
}


#find the relative path of dir2 wrt dir1
#for eg., to go from /a/b/c -> /a/b/d/e - ../d/e
# to go from /a/b/c to /a/b/c/d/e - ./d/e
#first find the common path by traversing down each path and comparing
#then go up the extra dirs if any or .. to go backwards
#dir1 and dir2 have to exist
#
abs2rel()
{
if [ $# -ne 2 ];then
 	exit 1
fi

dir1=$1
dir2=$2
down="."
up=""
abs_rel=""

if [ ! -d $dir1 ];then
	exit 1
fi

if [ ! -d $dir2 ];then
	exit 1
fi

dir1=$(cd $dir1;echo $PWD)
dir2=$(cd $dir2;echo $PWD)

#get no of levels
level $dir1
n1=$lev

level $dir2
n2=$lev

p1=$dir1
p2=$dir2

if [ $n1 -gt $n2 ];then
	count=$((n1-n2))
	while [ $count -ne 0 ];do
		down1 $p1
		count=$((count-1))
	done
 
elif [ $n1 -lt $n2 ];then
	count=$((n2-n1))
	while [ $count -ne 0 ];do
		down2 $p2
		count=$((count-1))
	done
fi

 
#now find the common dir

while true; do
	if [ "$p1" = "$p2" ];then
 		break
	fi
	down1 $p1
	down2 $p2
done

abs_rel=$down$up
return
}


# Check available space
checkSpace () { 
	freespace=`df -k $dirname` 
	freespace=`echo $freespace | awk '{print $11}'`

        if [ $freespace -lt $total_sz ]    
	then
		echo "\t\tAvailable space is $freespace kbytes.\n"
        	echo "ERROR: Not enough space Increase the file system size.\n" 
                exit 4; 
	fi
	echo "\t\tAvailable space is $freespace kbytes\n"

} 

#calculate space required for libraries and executable
libSize () { 

	count=`wc -l $mapfile | awk '{print $1}'`
	if [ $? != 0 ] 
	then
		echo "Error executing command wc"
		exit 3;
	fi	
	
	let libcount=$count-1

	l_libs=`head -$libcount $mapfile`
	if [ $? != 0 ] 
	then
		echo "Error executin command head:"
		exit 3;
	fi	

	for a in $l_libs
        do 
        	a_sz=`ls -alL $a | awk '{print $5}'` 
		let lib_sz=$lib_sz+$a_sz

        done 

	# update the map file wih full pathnames and link files

	echo $l_libs > $mapfile

	#resolve links and include destination file in the list to archive
	for a in $l_libs $exename
	do
		if [ -h $a ]
		then
			follow_link $a
			if [ $? -ne 0 ];then
				echo "error when following link for $a"
				exit 3
			fi

			echo $l_source >> $mapfile
		fi
	done

	# get updated list of libraries and exe 
	l_libs=`cat $mapfile`

	# get executable file size
	exe_sz=`ls -alL $exename | awk '{print $5}'`
} 

# remove all temporary files 
cleanUp () { 
	rm -f $mapfile 
        rm -f $fileset 
        rm -f $readme 
	rm -f $errpt
	rm -fr $dummydir
} 


# compute space required 
pass1 () { 

	echo "pass1() in progress .... \n"

	# parse snapcore_$pid.map and calculate total library sizes 
	echo "\t\tCalculating space required . \n"
        libSize

        fileset_sz=`lslpp -l | wc -c | awk '{print $1}'` 
	errpt_sz=`errpt -aJ CORE_DUMP | wc -c | awk '{print $1}'`
        core_sz=`ls -alL $corename | awk '{print $5}'` 
        let total_sz=$lib_sz+$fileset_sz+$core_sz+$exe_sz+$errpt_sz

	let total_sz=$total_sz/1024 	#size in KB
	echo "\t\tTotal space required is $total_sz kbytes .. \n"

	echo "\t\tChecking for available space ...\n"
        checkSpace
	echo "pass1 complete.\n"
}

#collect all the information 
pass2() { 

	echo "pass2() in progress ....\n"

	echo "\t\tCollecting fileset information . \n"
	#collect fileset info in to snapcore_$pid.lvl 
        lslpp -l > $fileset 
        if [ $? != 0 ] 
	then
       		echo " Warning: Could not collect fileset information." 
        fi 

	echo "\t\tCollecting error report of CORE_DUMP errors ..\n"
	errpt -aJ CORE_DUMP > $errpt
        if [ $? != 0 ] 
	then
       		echo " Warning: Can't collect error report."
        fi 

	echo "\t\tCreating readme file .. \n"
        #create a README file  in to snapcore_$pid.read 
	createReadme

	echo "\t\tCreating archive file ...\n"

	#create actual archive
	createArchive
	echo "pass2 completed.\n"
} 

# General instructions on extracting archive 
createReadme() { 
          cat << END >$readme 
General Instructions:

The archive generated by snapcore contains all the libraries, including libc.a!
It is therefore necessary for the machine where the core file is to be analyzed
to be at the same level as the machine where the core file was generated.
If, for example, you replace your libc.a with a libc.a that doesn't match
your kernel, your machine will not boot!

You may wish to examine the snapcore archive before restoring it,
and only restore selected files from it.

You will need to be the super-user, root, to replace most libraries.
END


} 

#create Archive 
createArchive() {
        #Refer defect 751630 
	#fix to make the pax one shot.
        flist=""
        slist=""


	#Add core file to the archive 
	abspath=$(cd `dirname $corename`;echo $PWD);
	src=`basename $corename`
	abssrc=$abspath/$src
	if [ `echo $abspath | grep ^$PWD` ];then        #file in or below current dir
	         flist="$flist $corename"
        else                                            #file is above current dir
	        flist="$flist $corename"
                slist="$slist -s?$corename?.$corename?"
        fi


        #Add README file 
        flist="$flist $readme"
        slist="$slist -s?$readme?README?"

        #Add fileset info 
        flist="$flist $fileset"
        slist="$slist -s?$fileset?lslpp.out?"

        #Add fileset info 
        flist="$flist $errpt"
        slist="$slist -s?$errpt?errpt.out?"


	count=0
        #Add executable and libraries listed in map file 
        for a in $exename $l_libs
        do 
		base_name=`basename $a`
		if [ -h $a ];then
			follow_link $a
			if [ $? -ne 0 ];then
				echo "error when following link for $a"
				exit 2
			fi
			abspath=`dirname $l_source`
			src=`basename $l_source`
			abssrc=$abspath/$src
			abslpath=$(cd `dirname $a`;echo $PWD)
			abslink=$abslpath/`basename $a`
			dummydir=$dirname/dummy_$$

			if [ ! -f $abssrc ] ;then		
				echo "source for link $a ($abssrc) is not a file "
				cleanUp
				exit 1
			fi


			[ ! -d $dummydir ] && mkdir $dummydir

			if [ `echo $abspath | grep ^$PWD` ];then 	#src at or below current dir
				abs2rel $PWD $abspath
				newspath=$abs_rel	
			else 						#src above current dir
				newspath=.$abspath
			fi


			if [ `echo $abslpath | grep ^$PWD` ];then 	#link below current dir
				abs2rel $PWD $abslpath
				newlpath=$abs_rel	
			else 						#link is above currnet dir
				newlpath=.$abslpath
			fi

			
			curdir=$PWD	
			cd $dummydir
			[ "$newspath" != "." ] && mkdir -p $newspath
			touch $newspath/$src
			[ "$newlpath" != "." ] && mkdir -p $newlpath

			abs2rel $newlpath $newspath
			cd $newlpath
		
			ln -s $abs_rel/$src ./$base_name
			cd - >/dev/null
			flist="$flist $dummydir/$newlpath/$base_name"
                        slist="$slist -s?$dummydir/$newlpath/$base_name?$newlpath/$base_name?"


			cd $curdir
		else 		#not a link


			absdir=`dirname $a`
			abspath=$(cd $absdir;echo $PWD)
			abssrc=$abspath/`basename $a`
			
			if [ `echo $abspath | grep ^$PWD` ];then 	#file below current dir
				abs2rel $PWD $abspath
                		relsrc=$abs_rel/`basename $a`
                                flist="$flist $a"
                                slist="$slist -s?$a?$relsrc?"
			else
                                flist="$flist $a"
                                slist="$slist -s?$a?.$abssrc?"
			fi
		fi
			


        done 
       
        # pax all the files from the list using flist and slist
        pax  -xpax $slist -wf $snapcore $flist
        if [ $? != 0 ]
        then
        echo "Problem encountered while creating archive \n"
        Error
        exit 5
        fi
 

	echo "\t\tCompressing archive file ....\n"
        #compress the archive 
        compress  $snapcore 
	if [ $? != 0 ]
	then
		echo "Problem encountered while compressing $snapcore." 
	fi
                 
} 
                 

#parse Arguments, do clean up if -r specified 
parseArgs () { 

	set -- `getopt rd: $*` 

        if [ "$?" != 0 ] 
        then 
		Usage 
		Error
                exit 1 
        fi 

        while [ "$1" != -- ] 
        do 

        	case $1 in 
		-r )	doclean=y 
                	shift;; 
		-d )	dirname=$2 
                  	shift;shift;; 

		 * ) 	echo "Error: Invalid argument" ; Usage; Error;exit 1; 
                esac 

	done 

       	shift #skip -- 

	# Do the cleanup first. It might free some space
       	if [ "$doclean" = "y" ] 
       	then 
		echo "\nCleaning up in progress  .. \n"
		rm $dirname/snapcore_* 2>/dev/null
	fi 

	if [ $# = 0 ] && [ "$doclean" = "y" ] 
	then 
       		echo "snapcore completed successfully.\n"  
      		exit 0 
	else 
		if [ $# = 0 ] && [ "$doclean" = "n" ]
		then
			Usage
			Error
			exit 1
		fi
	fi 

                 
       	corename=$1		#initialize core file name 
	if [ ! -f $corename ]
	then
		echo "Specifiled core file ($corename) does not exist.\n"
		Usage
		Error
		exit 1
	fi
       	if [ $#  -lt 2 ]	#initiliaze executable name 
       	then 
       		exename="" 
       	else  
       		exename=$2 
       	fi 
                 
	# creating destination directory
	if [ ! -d $dirname ]
	then
		echo "Creating directory $dirname ...\n"
		mkdir -p $dirname
		if [ $? != 0 ]
		then
			echo "Cannot create directory $dirname\n"
			exit 1;
		fi
	fi
	#get full path of snap output dir
	dirname=$(cd $dirname;echo $PWD)


        #intialize archive filenames 
        snapcore=$dirname/snapcore_$pid.pax #end Archive 
        #initialize temporary filenames 
        mapfile=$dirname/snapcore_$pid.map          # library list 
        fileset=$dirname/snapcore_$pid.lvl          # lslpp output 
        readme=$dirname/snapcore_$pid.readme        # general instructions 
	errpt=$dirname/snapcore_$pid.errpt          # CORE_DUMP errors

       	# Read core file info in to $mapfile 
       	/usr/lib/ras/check_core $corename > $mapfile 2>/dev/null # validate core and binary 
       	if [ $? != 0 ] 
       	then 
       		echo "Invalid core file name specified." 
		Usage
		Error
                exit 1; 
       	fi 

       	if [ ! -s $mapfile ] 
       	then 
       		echo "Could not write data in to $mapfile. Check for filesystem space."
		Error 
       		exit 1; 
	fi 

	# last line in the module list is exe name 
        tmp_exename=`tail -1 $mapfile`  

	if [ $? != 0 ]
        then
                echo "Invalid  core file."
		Usage
		Error
                exit 1;
        fi

	echo "\nCore file \"$corename\" created by \"$tmp_exename\"\n"
	
	#locate the program name 
        if [  "$exename" != "" ] 
        then 
        	base_exename=`basename $exename` 
       	else 
                exename=`which $tmp_exename`  
                base_exename=$tmp_exename 
       	fi  

       	# make sure specified program and the name contained in core are same. 
       	if [ $tmp_exename != $base_exename ]  ||  [ ! -f $exename ]
       	then 
       		echo "Specified program file($exename) is not valid.\n" 
		Usage
		Error
                exit 1; 

	fi 
}



#main routine 

	# set LANG nvironment
	export LC_MESSAGES=C

	# CDPATH environment variable is unset, because it was counter-productive here.
	unset CDPATH

	# parse arguments, do cleanup and collect core file information. 
        pid=$$ 

	#parse arguments and initialization
	parseArgs $@
                 
	#calculate space required
        pass1 

	#Gather all information
        pass2  

	#remove temporary file
        cleanUp 

        echo "Snapcore completed successfully. Archive created in $dirname.\n" 
        exit 0