#!/bin/bash
#set -x

# Verify consistency of BuildID between vmlinux, image (vmlinuz etc - if 
# supported) and vmlinux.debug (debuginfo). 
# Tony Jones <tonyj@suse.de>, May 2018

# This script uses following build environment vars:
# -PNAME (package name)
# -BUILD_DEBUG (are debuginfo's being built? aka osc build -d?)

trap '[ -d "${_tmpdir}" ] && rm -rf ${_tmpdir}' EXIT
function warn() { echo "... $*" >&2; }
function err() { warn $*; echo "Giving up" >&2 ; exit 0; }
function buildid() { eu-readelf --notes $1 | awk '/Build ID:/ {print $3}'; }
function have_image { [ -n "${image_name}" ]; }
function have_debuginfo { [ ${have_debugi} -eq 1 ]; }

# Find the version#s for kernel and kernel rpm
# This should be consistent across branches
function findversion() {
	local _rpm 

	[ -d ${rpms} ] || err "Unable to find rpmbuild dir ${rpms}"

	# find the "main" kernel rpm for $PNAME
	_rpm=`cd ${rpms}; compgen -G "${PNAME}-[0-9]*.${arch}.rpm"`

	[ -z "${_rpm}" -o ! -f "${rpms}/${_rpm}" ] && err "Unable to find base kernel rpm for ${PNAME}"

	kernversion=`rpm -qp --provides ${rpms}/${_rpm} | awk '/^kernel-base =/ {print $3}'`
	rpmversion=`rpm -qp --provides ${rpms}/${_rpm} | awk "/^${PNAME}\\(${rpm_prov_arch}\\) =/ {print \\$3}"`

	[ -z "${kernversion}" -o -z "${rpmversion}" ] && err "Unable to validate kernel build versions from ${_rpm}"; 
}

TOPDIR=/usr/src/packages
cpioflags="-icd --quiet"
have_debugi=1
image_name=""
vmlinux_compression_suffix=""
warnonly=0

# only check for these flavors
case "${PNAME}" in
       kernel-default|kernel-debug|kernel-vanilla) 
		flavor=${PNAME#kernel-};;
       *) exit 0;;
esac

have_debugi=${BUILD_DEBUG}
[ -z "${BUILD_DEBUG}" ] && have_debugi=0

[ -h ${BUILD_ROOT}/.build.packages ] && TOPDIR=${BUILD_ROOT}/`readlink ${BUILD_ROOT}/.build.packages`

[ -f ${TOPDIR}/SOURCES/IGNORE_BUILDID_MISMATCH ] && warnonly=1

# look for which arch was built, since this is kernel specific, there are
# no biarch issues
karchs="i586 i686 x86_64 ppc ppc64 ppc64le s390 s390x armv6l armv7l aarch64"
rpm_prov_arch=""
for arch in ${karchs}; do
	if [ -d ${TOPDIR}/RPMS/${arch} ] ;then
		case ${arch} in
			"i586"|"i686")    rpm_prov_arch="x86-32"
				   vmlinux_compression_suffix=".gz"
				   image_name="vmlinuz";;
			"x86_64")  rpm_prov_arch="x86-64";
				   vmlinux_compression_suffix=".gz"
				   image_name="vmlinuz";;
			"ppc")     rpm_prov_arch="ppc-32";;
			"ppc64"|"ppc64le")   rpm_prov_arch="ppc-64";;
			"s390")    rpm_prov_arch="s390-32"
				   vmlinux_compression_suffix=".gz"
				   #has Image but not ELF
				   ;;
			"s390x")   rpm_prov_arch="s390-64"
				   vmlinux_compression_suffix=".gz"
				   #has Image but not ELF
				   ;;
			"armv6l") rpm_prov_arch="armv6hl-32"
				   vmlinux_compression_suffix=".gz"
				   #has Image but not ELF
				   ;;
			"armv7l") rpm_prov_arch="armv7hl-32"
				   vmlinux_compression_suffix=".gz"
				   #has Image but not ELF
				   ;;
			"aarch64") rpm_prov_arch="aarch-64"
				   vmlinux_compression_suffix=".gz"
				   #has Image but not ELF
				   ;;
			*) err "karchs does not match case statement, please fixme!" ;;
		esac
		break
	fi
done

[ -n "${rpm_prov_arch}" ] || { warn "No valid build arch in ${TOPDIR}/RPMS"; exit 0; }

! have_image && ! have_debuginfo  && { warn "No BuildID consistency to verify (debuginfo disabled and arch has no kernel image fmt)"; exit 0; }

rpm -q --quiet elfutils || { warn "Unable to verify BuildID (no elfutils). Add 'BuildRequires: elfutils' to package"; exit 0; }

rpms=${TOPDIR}/RPMS/${arch}
findversion

echo "... Verifying kernel build-ids for ${PNAME} ${kernversion} ${arch}"

echo "... Processing kernel rpms in '${rpms}'"

krpm=${rpms}/${PNAME}-${rpmversion}.${arch}.rpm
[ -f ${krpm} ] || err "Unable to find kernel rpm '${krpm}'"

_tmpdir=`mktemp -d`
[ -d "${_tmpdir}" ] || err "Unable to make tempdir"

vmlinux="./boot/vmlinux-${kernversion}-${flavor}${vmlinux_compression_suffix}"
image=""
have_image && image="./boot/${image_name}-${kernversion}-${flavor}"
rpm2cpio ${krpm} | (cd ${_tmpdir} ; cpio ${cpioflags} ${vmlinux} ${image})

[ ! -f ${_tmpdir}/${vmlinux} ] && err "Unable to extract ${vmlinux} from ${krpm}"
vmlinux_id=`buildid ${_tmpdir}/${vmlinux}`

mismatch=0
if have_image ;then
	[ ! -f ${_tmpdir}/${image} ] && err "Unable to extract ${image} from ${krpm}"
	image_id=`buildid ${_tmpdir}/${image}`

	if [ ${vmlinux_id} == "${image_id}" ] ;then
		echo "... BuildID vmlinux/${image_name} OK - ${vmlinux_id}"
	else
		warn "BuildID Mismatch vmlinux=${vmlinux_id} ${image_name}=${image_id}" ; mismatch=1
	fi
fi

if have_debuginfo ;then
	kdbgi_rpm=${rpms}/${PNAME}-debuginfo-${rpmversion}.${arch}.rpm
	[ ! -f ${kdbgi_rpm} ] && err "Unable to find kernel debuginfo rpm '${kdbgi_rpm}'"

	vmlinux_dbgi="./usr/lib/debug/boot/vmlinux-${kernversion}-${flavor}.debug"
	rpm2cpio ${kdbgi_rpm} | (cd ${_tmpdir} ; cpio ${cpioflags} ${vmlinux_dbgi})

	[ ! -f ${_tmpdir}/${vmlinux_dbgi} ] && err "Unable to extract ${vmlinux} from ${kdbgi_rpm}"
	vmlinux_dbgi_id=`buildid ${_tmpdir}/${vmlinux_dbgi}`

	if [ ${vmlinux_id} == "${vmlinux_dbgi_id}" ] ;then
		echo "... BuildID vmlinux/vmlinux_debuginfo OK - ${vmlinux_id}"
	else
		warn "BuildID Mismatch vmlinux=${vmlinux_id} vmlinux_debuginfo=${vmlinux_dbgi_id}" ; mismatch=1
	fi
fi

[ ${warnonly} -eq 1 -a ${mismatch} -eq 1 ] && { mismatch=0; warn "Ignoring BuildID mismatch (IGNORE_BUILDID_MISMATCH exists in kernel source dir)"; }
exit ${mismatch}
