69aadbb3c0
With this change, if 'xmllint' is not available, the script will try to use python3's xml default module Other changes: - Corrected misleading logs regarding what info is extracted from the patch xmls - Added support for interchangeable requirements Test Plan: pass - Execute snippet with the changes and confirm ostree commit was properly extracted Story: 2011098 Task: 49860 Change-Id: I13c2440814de337049c0af84f20b83bf524a05ea Signed-off-by: Leonardo Fagundes Luz Serrano <Leonardo.FagundesLuzSerrano@windriver.com>
312 lines
7.9 KiB
Bash
Executable File
312 lines
7.9 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2023 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# Utility for adding patch metadata to the iso
|
|
# Debian patches are sequential and uses ostree
|
|
# so any patch will produce an updated iso, this
|
|
# utility injects the patch metadata into the iso.
|
|
# During install the kickstart will copy the metadata
|
|
# into the right location and the sw-patch query
|
|
# command will output the correct patch level
|
|
#
|
|
|
|
source "$(dirname $0)/image-utils.sh"
|
|
|
|
if [ -z "${STX_BUILD_HOME}" ]; then
|
|
echo "Required environment variable STX_BUILD_HOME is not set"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "${MY_REPO}" ]; then
|
|
echo "Required environment variable MY_REPO is not set"
|
|
exit 1
|
|
fi
|
|
|
|
DEPLOY_DIR="${STX_BUILD_HOME}/localdisk/deploy"
|
|
OSTREE_REPO="${DEPLOY_DIR}/ostree_repo"
|
|
|
|
function usage() {
|
|
echo ""
|
|
echo "Usage: "
|
|
echo " $(basename $0) -i <input filename.iso> -o <output filename.iso> [ -p ] <patch> ..."
|
|
echo " -i <file>: Specify input ISO file"
|
|
echo " -o <file>: Specify output ISO file"
|
|
echo " -p <file>: Patch files. You can call it multiple times."
|
|
echo ""
|
|
}
|
|
|
|
function extract_ostree_commit_from_metadata_xml() {
|
|
local XML_PATH=$1
|
|
local XPATH="//contents/ostree/commit1/commit"
|
|
|
|
# Check if xmllint is available. Otherwise, use python's xml standard lib
|
|
if (which xmllint &>/dev/null); then
|
|
xmllint --xpath "string(${XPATH})" ${XML_PATH}
|
|
else
|
|
python3 -c "import xml.etree.ElementTree as ET ; print(ET.parse('${XML_PATH}').find('.${XPATH}').text, end='')"
|
|
fi
|
|
}
|
|
|
|
function extract_metadata() {
|
|
local patchesdir=${BUILDDIR}/patches
|
|
local patchfile=$1
|
|
local patchid=$(basename $patchfile .patch)
|
|
local ostree_log=$(ostree --repo=${OSTREE_REPO} log starlingx)
|
|
|
|
echo "Extracting ${patchfile}"
|
|
# Extract it
|
|
tar xf ${patchfile} -O metadata.tar | tar x -O > ${patchesdir}/${patchid}-metadata.xml
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to extract metadata from ${patchfile}"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify if top commit from metadata exist in ostree log
|
|
ostree_commit=$(extract_ostree_commit_from_metadata_xml ${patchesdir}/${patchid}-metadata.xml)
|
|
if [[ "$ostree_log" != *"$ostree_commit"* ]]; then
|
|
echo "Error: Patch ${patchid} ostree commit does not exist in ISO ostree log."
|
|
echo "patch's ostree commit: ${ostree_commit}"
|
|
echo "ISO ostree log:"
|
|
ostree --repo=${BUILDDIR}/ostree_repo log starlingx
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
declare INPUT_ISO=
|
|
declare OUTPUT_ISO=
|
|
declare ORIG_PWD=$PWD
|
|
declare DO_UPGRADES=1
|
|
|
|
while getopts "i:o:p:" opt; do
|
|
case $opt in
|
|
i)
|
|
INPUT_ISO=$OPTARG
|
|
;;
|
|
o)
|
|
OUTPUT_ISO=$OPTARG
|
|
;;
|
|
p)
|
|
PATCH_FILES+=($OPTARG)
|
|
;;
|
|
*)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$INPUT_ISO" -o -z "$OUTPUT_ISO" ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f ${INPUT_ISO} ]; then
|
|
echo "Input file does not exist: ${INPUT_ISO}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f ${OUTPUT_ISO} ]; then
|
|
echo "Output file already exists: ${OUTPUT_ISO}"
|
|
exit 1
|
|
fi
|
|
|
|
for PATCH in "${PATCH_FILES[@]}";
|
|
do
|
|
if [ ! -f ${PATCH} ]; then
|
|
echo "Patch file dos not exists: ${PATCH}"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! ${PATCH} =~ \.patch$ ]]; then
|
|
echo "Specified file ${PATCH} does not have .patch extension"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
shift $((OPTIND-1))
|
|
|
|
declare MNTDIR=
|
|
declare BUILDDIR=
|
|
|
|
function check_requirements {
|
|
|
|
# Declare "require reqA or reqB" as "reqA__reqB"
|
|
local -a required_utils=(
|
|
rsync
|
|
mkisofs
|
|
isohybrid
|
|
implantisomd5
|
|
ostree
|
|
xmllint__python3
|
|
)
|
|
if [ $UID -ne 0 ]; then
|
|
# If running as non-root user, additional utils are required
|
|
required_utils+=(
|
|
guestmount
|
|
guestunmount
|
|
)
|
|
fi
|
|
|
|
local -i missing=0
|
|
local reqA
|
|
local reqB
|
|
|
|
for req in ${required_utils[@]}; do
|
|
if [[ "$req" = *"__"* ]]; then
|
|
reqA="${req%__*}" # select everything before "__"
|
|
reqB="${req#*__}" # select everything after "__"
|
|
|
|
if ! (which ${reqA} &>/dev/null) && ! (which ${reqB} &>/dev/null); then
|
|
echo "Unable to find required utility: either ${reqA} or ${reqB}" >&2
|
|
let missing++
|
|
fi
|
|
|
|
else
|
|
if ! (which ${req} &>/dev/null); then
|
|
echo "Unable to find required utility: ${req}" >&2
|
|
let missing++
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ ${missing} -gt 0 ]; then
|
|
echo "One or more required utilities are missing. Aborting..." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function mount_iso {
|
|
if [ $UID -eq 0 ]; then
|
|
# Mount the ISO
|
|
mount -o loop ${INPUT_ISO} ${MNTDIR}
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to mount ${INPUT_ISO}" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
# As non-root user, mount the ISO using guestmount
|
|
guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR}
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
# Add a retry
|
|
echo "Call to guestmount failed with rc=$rc. Retrying once..."
|
|
|
|
guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR}
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
echo "Call to guestmount failed with rc=$rc. Aborting..."
|
|
exit $rc
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function unmount_iso {
|
|
if [ $UID -eq 0 ]; then
|
|
umount ${MNTDIR}
|
|
else
|
|
guestunmount ${MNTDIR}
|
|
fi
|
|
rmdir ${MNTDIR}
|
|
}
|
|
|
|
function cleanup() {
|
|
if [ -n "$MNTDIR" -a -d "$MNTDIR" ]; then
|
|
unmount_iso
|
|
fi
|
|
|
|
if [ -n "$BUILDDIR" -a -d "$BUILDDIR" ]; then
|
|
chmod -R +w $BUILDDIR
|
|
\rm -rf $BUILDDIR
|
|
fi
|
|
|
|
}
|
|
|
|
check_requirements
|
|
|
|
trap cleanup EXIT
|
|
|
|
MNTDIR=$(mktemp -d -p $PWD patchiso_mnt_XXXXXX)
|
|
if [ -z "${MNTDIR}" -o ! -d ${MNTDIR} ]; then
|
|
echo "Failed to create mntdir. Aborting..."
|
|
exit $rc
|
|
fi
|
|
|
|
BUILDDIR=$(mktemp -d -p $PWD patchiso_build_XXXXXX)
|
|
if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then
|
|
echo "Failed to create builddir. Aborting..."
|
|
exit $rc
|
|
fi
|
|
|
|
# Mount the ISO
|
|
mount_iso
|
|
|
|
rsync -a --exclude 'ostree_repo' ${MNTDIR}/ ${BUILDDIR}/
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
echo "Call to rsync ISO content. Aborting..."
|
|
exit $rc
|
|
fi
|
|
|
|
unmount_iso
|
|
|
|
# Fix for permission denied if not running as root
|
|
chmod +w ${BUILDDIR}
|
|
chmod -R +w ${BUILDDIR}/isolinux
|
|
|
|
# Create the directory where metadata will be stored
|
|
mkdir -p ${BUILDDIR}/patches
|
|
chmod -R +w ${BUILDDIR}/patches
|
|
|
|
echo "Copying only the latest commit from ostree_repo..."
|
|
ostree --repo=${BUILDDIR}/ostree_repo init --mode=archive-z2
|
|
ostree --repo=${BUILDDIR}/ostree_repo pull-local --depth=0 ${OSTREE_REPO} starlingx
|
|
ostree --repo=${BUILDDIR}/ostree_repo summary --update
|
|
echo "Updated iso ostree commit:"
|
|
ostree --repo=${BUILDDIR}/ostree_repo log starlingx
|
|
|
|
echo "Extracting patch metadata"
|
|
for PATCH in "${PATCH_FILES[@]}";
|
|
do
|
|
extract_metadata $PATCH
|
|
done
|
|
|
|
echo "Packing iso..."
|
|
# get the install label
|
|
ISO_LABEL=$(grep -ri instiso "${BUILDDIR}"/isolinux/isolinux.cfg | head -1 | xargs -n1 | awk -F= /instiso/'{print $2}')
|
|
if [ -z "${ISO_LABEL}" ] ; then
|
|
echo "Error: Failed to get iso install label"
|
|
fi
|
|
echo "ISO Label: ${ISO_LABEL}"
|
|
|
|
# Repack the ISO
|
|
mkisofs -o "${OUTPUT_ISO}" \
|
|
-A "${ISO_LABEL}" -V "${ISO_LABEL}" \
|
|
-quiet -U -J -joliet-long -r -iso-level 2 \
|
|
-b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \
|
|
-boot-load-size 4 -boot-info-table \
|
|
-eltorito-alt-boot \
|
|
-e efi.img \
|
|
-no-emul-boot \
|
|
"${BUILDDIR}"
|
|
|
|
isohybrid --uefi ${OUTPUT_ISO}
|
|
implantisomd5 ${OUTPUT_ISO}
|
|
|
|
# Sign the .iso with the developer private key
|
|
openssl dgst -sha256 \
|
|
-sign ${MY_REPO}/build-tools/signing/dev-private-key.pem \
|
|
-binary \
|
|
-out ${OUTPUT_ISO/%.iso/.sig} \
|
|
${OUTPUT_ISO}
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
echo "Call to $(basename ${SETUP_PATCH_REPO}) failed with rc=$rc. Aborting..."
|
|
exit $rc
|
|
fi
|
|
|
|
echo "Patched ISO: ${OUTPUT_ISO}"
|