Handy Wrapper Script - Triage of Bootable Kernels

Sometimes, you want to keep older bootable kernels around, either as failsafe or for context-based testing.

At some point, you may want to purge a selection of those older versions. This script only keeps the two most recent bootable kernels and prompts the purge of the other ones.


Script: OS_Admin__bootBinaries_ReviewAndPurge.sh
#!/bin/sh

#23456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+
####################################################################################################
###
###	$Id: OS_Admin__bootBinaries_ReviewAndPurge.sh,v 1.1 2020/09/17 03:11:42 root Exp root $
###
###	Script to identify OS build versions that are installed and purge all except the last 2.
###
####################################################################################################

#DevStat=PROD
TMP=/tmp/tmp.`basename $0 ".sh"`.$$

VERSIONS=0
LATEST=0

sync
testor1="`ps -ef | grep -v 'grep' | grep 'synaptic' | head -1 `"
testor2="`ps -ef | grep -v 'grep' | grep 'dpkg' | head -1 `"
testor3="`ps -ef | grep -v 'grep' | grep 'apt' | head -1 `"

if [ -n "${testor1}" ]
then
	echo "\n Active processes identified:\n\t ${testor1}"
	echo "\n This process may not continue until 'synaptic' is shut down.\n"
	exit 1
else
	if [ -n "${testor2}" ]
	then
		echo "\n Active processes identified:\n\t ${testor2}"
		echo "\n This process may not continue until 'dpkg*' is shut down.\n"
		exit 1
	else
		if [ -n "${testor3}" ]
		then
			echo "\n Active processes identified:\n\t ${testor2}"
			echo "\n This process may not continue until 'apt*' is shut down.\n"
			exit 1
		fi
	fi
fi

echo "\n\t This script identifies build versions that are installed, \n\t does not touch 2 most recent versions, and offers \n\t the remaining list of older builds for selective \n\t direct purge, not an uninstall process ...\n"

#FUTURES:  add logic to do uninstall of items that are chosen for purge.


#/boot/abi-3.13.0-143-generic
#/boot/config-3.13.0-143-generic
#/boot/initrd.img-3.13.0-143-generic
#/boot/retpoline-3.13.0-143-generic
#/boot/System.map-3.13.0-143-generic
#/boot/vmlinuz-3.13.0-143-generic

cd /boot
mode=generic

echo "\n\t Bootable versions ..."
#ls -l vmlinuz* | sort -r --version-sort | awk '{ printf("\t\t%s\n",$0) }'
ls -lt vmlinuz* | awk '{ printf("\t\t %s\n",$0) }'

sleep 4

echo""
for pref in  vmlinuz System.map retpoline initrd.img config abi
do
	echo "\t Getting versions of  '${pref} ..."
	rm -f ${TMP}.${pref}.v

	( ls ${pref}-*-${mode} | sort -n >${TMP}.${pref}.all ) 2>&1 | awk '{ printf("\t\t %s\n", $0 ) }'
	#cat ${TMP}.${pref}.all | cut -f2-3 -d\- | sort -nr --version-sort >${TMP}.${pref}.v
	cat ${TMP}.${pref}.all | cut -f2-3 -d\- | sort -nr --key=2.1,3.0 --field-separator="-" >${TMP}.${pref}.v
done

echo""
for pref in  System.map retpoline initrd.img config abi
do
	echo "\n\t Looking for differences in versions of  '${pref} ..."
	diff ${TMP}.vmlinuz.v ${TMP}.${pref}.v >${TMP}.${pref}.diff

	if [ -s ${TMP}.${pref}.diff ]
	then
	{
		echo " === ${TMP}.${pref}.diff "
		cat ${TMP}.${pref}.diff
	} | awk '{ printf("\t\t %s\n", $0 ) }'
	else
		echo "\t\t Versions match up ... "
	fi
done

#echo here
#cat ${TMP}.vmlinuz.v
#echo there

purgePackageName()
{
	rm -f ${TMP}.purge ${TMP}.RC

	echo "\t\t Purging: ${pkgName} ...\c"
	( dpkg --purge ${pkgName} 2>&1 ; RC=$? ; echo "${RC}" >${TMP}.RC ) >${TMP}.purge

	read RC <${TMP}.RC

	#testor=`grep 'error' ${TMP}.err 2>/dev/null`

	#if [ \( ${RC} -ne 0 \) -o \( -n "${testor}" \) ]
	if [ ${RC} -ne 0 ]
	then
		#echo "\n\t\t Log of attempt [${pkgName}]:"
		echo " FAILED!"
		cat ${TMP}.purge | awk '{ printf("\t\t\t %s\n",$0) }'
		echo "\n\t\t Encountered error during purging attempt.  Abandoning job until issue is resolved.\n" ; exit 1
	else
		#echo " SUCCESS!"
		echo ""
	#	if [ -s ${TMP}.purge ]
	#	then
	#		echo "\n\t\t Log of purge:"
	#		cat ${TMP}.purge | awk '{ printf("\t\t\t %s\n",$0) }'
	#	fi
	fi
}	#purgePackageName()

verifyVersionPurge()
{
	echo "\n\t Verifying PURGE action .."

	partialPurge=0
	for pkgName in `cat ${TMP}.thisVersion `
	do
		#echo "\n [VERIFY] pkgName= ${pkgName}"
		rm -f ${TMP}.RC

		( dpkg -l ${pkgName} ; RC=$? ; echo "${RC}" >${TMP}.RC ) 2>&1 | grep ${pkgName} >>/dev/null

		read RC <${TMP}.RC

		if [ ${RC} -eq 0 ]
		then
			echo "\t\t Purge of ${pkgName} FAILED or SKIPPED ..."
			partialPurge=1
		else
			echo "\t\t PURGED: ${pkgName} ..."
		fi
	done

	if [ ${partialPurge} -eq 0 ]
	then
		echo "\n\t Version ${version} package grouping PURGED.  Doing sanity check ..."

		find / -xdev -print >z ; grep "${version}" z | sort >${admin}/OS_KernelPurge_${version}.list
		if [ -s "${admin}/OS_KernelPurge_${version}.list" ]
		then
			echo "\n\t WARNING:  Verify listing in '${admin}/OS_KernelPurge_${version}.list' for possible leftovers from purge .."
		else
			echo "\t\t Sanity check identified no overlooked items ..."
		fi
	else
		echo "\n\t WARNING:  PARTIAL purge of version ${version} package grouping ..."
	fi
}	#verifyVersionPurge()

if [ -n "`tail -n +3 ${TMP}.vmlinuz.v`" ]
then
	tail -n +3 ${TMP}.vmlinuz.v | sort |
	while read version
	do
		echo "\n\n Boot-related files for ${version} KERNEL:\n"
		ls -l *-${version}-${mode} 2>&1 | awk '{ printf("\t\t %s\n", $0 ) }'

		echo "\n\n Packages related to ${version} KERNEL:\n"

		rm -f ${TMP}.thisVersion*
		dpkg -l | grep "${version}" | awk '{ print $2 }' | grep '^linux-' | sort -r >${TMP}.thisVersion.tmp
		{
			grep 'linux-headers-' ${TMP}.thisVersion.tmp
			grep 'linux-hwe-' ${TMP}.thisVersion.tmp | grep 'headers-'
			grep 'linux-modules-extra-' ${TMP}.thisVersion.tmp
			grep 'linux-image-' ${TMP}.thisVersion.tmp
			grep 'linux-modules-' ${TMP}.thisVersion.tmp | grep -v 'modules-extra'
		} >${TMP}.thisVersion

		cat ${TMP}.thisVersion | awk '{ printf("\t\t %s\n",$0) }'

		echo "\n\t Purge all packages for this version ? [y|N] => \c"
		read ans <&2
	
		case "$ans" in
			y* | Y* )
				#echo "\n\t Removing files for version ${version} ..."
				#for pref in vmlinuz System.map retpoline initrd.img config abi
				#do
				#	ls -l ${pref}-${version}-${mode} 2>&1
				#	rm -fv ${pref}-${version}-${mode} 
				#done | awk '{ printf("\t\t %s\n",$0) }'
				echo "\n\t\t ARE YOU SURE YOU WANT TO DO THAT ? [y|N] => \c"
				read vans <&2

				case "${vans}" in
					y* | Y* )
						echo "\n\t Purging packages for version ${version} ...\n"

						for pkgName in `cat ${TMP}.thisVersion `
						do
							purgePackageName
						done

						verifyVersionPurge
						;;
					* )
						echo "\t Leaving version ${version} untouched ..."
						;;
				esac
				;;
			* )
				echo "\n Purge any of those ${version} packages ? [y|N] => \c"
				read nans <&2

				case "${nans}" in
					y* | Y* )
						for pkgName in `cat ${TMP}.thisVersion `
						do
							echo "\t Purge  ${pkgName} ? [y|N] => \c"
							read mans <&2

							case "${mans}" in
								y* | Y* )
									purgePackageName
									;;
								* )
									echo "\t\t Leaving ${pkgName}  untouched ..."
									;;
							esac
						done

						verifyVersionPurge
						;;
					* )
						echo "\n\t Leaving version ${version} untouched ..."
						;;
				esac
				;;
		esac

		echo "\n\n\t\t#############################################\n\n"
	done
else
	echo "\n\t Only 2 bootable KERNEL versions remaining as desired."
fi

echo "\n\t Remaining bootable KERNEL versions:"
#ls -l vmlinuz* | sort -r --version-sort | awk '{ printf("\t\t%s\n",$0) }'
ls -lt vmlinuz* 2>&1 | awk '{ printf("\t\t %s\n",$0) }'
echo "\n Bye!\n"


exit 0
exit 0
exit 0