#!/bin/bash
#
# Copyright (c) 2018-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# Utility for adding patches to an unpatched ISO
#
source "$(dirname $0)/image-utils.sh"
if [ -z "${MY_REPO}" ]; then
echo "Required environment variable MY_REPO is not set"
exit 1
fi
if [ -z "${MY_WORKSPACE}" ]; then
echo "Required environment variable MY_WORKSPACE is not set"
exit 1
fi
STX_DIR=${MY_REPO}/stx
SETUP_PATCH_REPO=${STX_DIR}/update/extras/scripts/setup_patch_repo.sh
if [ ! -x ${SETUP_PATCH_REPO} ]; then
echo "Cannot find or execute ${SETUP_PATCH_REPO}"
exit 1
fi
# Create temp dir if necessary
export TMPDIR="$MY_WORKSPACE/tmp"
mkdir -p $TMPDIR
REPO_UPGRADES_DIR=${STX_DIR}/metal/bsp-files/upgrades
RELEASE_INFO="$(get_release_info)"
if [ $? -ne 0 ]; then
echo "ERROR: failed to find a release info file."
exit 1
fi
PLATFORM_RELEASE=$(source $RELEASE_INFO && echo $PLATFORM_RELEASE)
function usage() {
echo ""
echo "Usage: "
echo " $(basename $0) -i -o [ -u ] ..."
echo " -i : Specify input ISO file"
echo " -o : Specify output ISO file"
echo " -u : Update with upgrades files from ${REPO_UPGRADES_DIR}"
echo ""
}
function extract_pkg_from_patch_repo() {
local repodir=${BUILDDIR}/patches
local pkgname=$1
local pkgfile=$(repoquery --disablerepo=* --repofrompath local,${repodir} --enablerepo=local --location -q ${pkgname})
if [ -z "${pkgfile}" ]; then
return 1
fi
rpm2cpio ${pkgfile/file://} | cpio -idmv
if [ $? -ne 0 ]; then
echo "Failed to extract $pkgname files from ${pkgfile/file://}"
exit 1
fi
}
declare INPUT_ISO=
declare OUTPUT_ISO=
declare ORIG_PWD=$PWD
declare DO_UPGRADES=1
while getopts "i:o:u" opt; do
case $opt in
i)
INPUT_ISO=$OPTARG
;;
o)
OUTPUT_ISO=$OPTARG
;;
u)
DO_UPGRADES=0
;;
*)
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
shift $((OPTIND-1))
if [ $# -le 0 ]; then
usage
exit
fi
for pf in $@; do
if [ ! -f $pf ]; then
echo "Patch file $pf does not exist"
exit 1
fi
if [[ ! $pf =~ \.patch$ ]]; then
echo "Specified file $pf does not have .patch extension"
exit 1
fi
done
declare MNTDIR=
declare BUILDDIR=
declare WORKDIR=
function check_requirements {
local -a required_utils=(
rsync
mkisofs
isohybrid
implantisomd5
)
if [ $UID -ne 0 ]; then
# If running as non-root user, additional utils are required
required_utils+=(
guestmount
guestunmount
)
fi
local -i missing=0
for req in ${required_utils[@]}; do
which ${req} >&/dev/null
if [ $? -ne 0 ]; then
echo "Unable to find required utility: ${req}" >&2
let missing++
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
\rm -rf $BUILDDIR
fi
if [ -n "$WORKDIR" -a -d "$WORKDIR" ]; then
\rm -rf $WORKDIR
fi
}
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 ${MNTDIR}/ ${BUILDDIR}/
rc=$?
if [ $rc -ne 0 ]; then
echo "Call to rsync ISO content. Aborting..."
exit $rc
fi
unmount_iso
# Setup the patch repo
${SETUP_PATCH_REPO} -o ${BUILDDIR}/patches $@
rc=$?
if [ $rc -ne 0 ]; then
echo "Call to $(basename ${SETUP_PATCH_REPO}) failed with rc=$rc. Aborting..."
exit $rc
fi
# Look for components that need modification
#extract_pkg_from_patch_repo
WORKDIR=$(mktemp -d -p $PWD patchiso_work_XXXXXX)
if [ -z "${WORKDIR}" -o ! -d ${WORKDIR} ]; then
echo "Failed to create workdir. Aborting..."
exit $rc
fi
\cd ${WORKDIR}
\mkdir extract
\cd extract
# Changes to copied files here must also be reflected in build-iso
extract_pkg_from_patch_repo platform-kickstarts
if [ $? -eq 0 ]; then
# Replace files
\rm -f ${BUILDDIR}/*ks.cfg &&
\cp --preserve=all www/pages/feed/rel-*/*.cfg ${BUILDDIR}/ &&
\cp --preserve=all ${BUILDDIR}/controller_ks.cfg ${BUILDDIR}/ks.cfg
if [ $? -ne 0 ]; then
echo "Failed to copy extracted kickstarts"
exit 1
fi
fi
\cd ${WORKDIR}
\rm -rf extract
\mkdir extract
\cd extract
extract_pkg_from_patch_repo platform-kickstarts-pxeboot
if [ $? -eq 0 ]; then
# Replace files
\rm -f ${BUILDDIR}/pxeboot/pxeboot_controller.cfg \
${BUILDDIR}/pxeboot/pxeboot_smallsystem.cfg \
${BUILDDIR}/pxeboot/pxeboot_smallsystem_lowlatency.cfg &&
\cp --preserve=all pxeboot/* ${BUILDDIR}/pxeboot/
if [ $? -ne 0 ]; then
echo "Failed to copy extracted pxeboot kickstarts"
exit 1
fi
fi
\cd ${WORKDIR}
\rm -rf extract
\mkdir extract
\cd extract
extract_pkg_from_patch_repo pxe-network-installer
if [ $? -eq 0 ]; then
# Replace files
\rm -f ${BUILDDIR}/pxeboot/pxelinux.0 \
${BUILDDIR}/pxeboot/menu.c32 \
${BUILDDIR}/pxeboot/chain.c32 &&
\cp --preserve=all pxeboot/pxelinux.0 pxeboot/menu.c32 pxeboot/chain.c32 ${BUILDDIR}/pxeboot/
if [ $? -ne 0 ]; then
echo "Error: Could not copy all files from installer"
exit 1
fi
\rm -f ${BUILDDIR}/LiveOS/squashfs.img &&
\cp --preserve=all www/pages/feed/rel-*/LiveOS/squashfs.img ${BUILDDIR}/LiveOS/
if [ $? -ne 0 ]; then
echo "Error: Could not copy squashfs from LiveOS"
exit 1
fi
# Replace vmlinuz and initrd.img with our own pre-built ones
\rm -f \
${BUILDDIR}/vmlinuz \
${BUILDDIR}/images/pxeboot/vmlinuz \
${BUILDDIR}/initrd.img \
${BUILDDIR}/images/pxeboot/initrd.img &&
\cp --preserve=all pxeboot/rel-*/installer-bzImage_1.0 \
${BUILDDIR}/vmlinuz &&
\cp --preserve=all pxeboot/rel-*/installer-bzImage_1.0 \
${BUILDDIR}/images/pxeboot/vmlinuz &&
\cp --preserve=all pxeboot/rel-*/installer-intel-x86-64-initrd_1.0 \
${BUILDDIR}/initrd.img &&
\cp --preserve=all pxeboot/rel-*/installer-intel-x86-64-initrd_1.0 \
${BUILDDIR}/images/pxeboot/initrd.img
if [ $? -ne 0 ]; then
echo "Error: Failed to copy installer images"
exit 1
fi
fi
\cd ${WORKDIR}
\rm -rf extract
\mkdir extract
\cd extract
extract_pkg_from_patch_repo grub2-efi-x64-pxeboot
if [ $? -eq 0 ]; then
# Replace files
\rm -f ${BUILDDIR}/pxeboot/EFI/grubx64.efi &&
\cp --preserve=all pxeboot/EFI/grubx64.efi ${BUILDDIR}/pxeboot/EFI/
if [ $? -ne 0 ]; then
echo "Error: Failed to copy grub2-efi-x64-pxeboot files"
exit 1
fi
fi
\cd ${WORKDIR}
\rm -rf extract
\mkdir extract
\cd extract
extract_pkg_from_patch_repo grub2-common
if [ $? -eq 0 ]; then
# Replace files
for f in usr/lib/grub/x86_64-efi/*; do
f_base=$(basename $f)
\rm -f ${BUILDDIR}/pxeboot/EFI/$f_base &&
\cp --preserve=all ${f} ${BUILDDIR}/pxeboot/EFI/
if [ $? -ne 0 ]; then
echo "Error: Failed to copy grub2-common files"
exit 1
fi
done
fi
\cd ${WORKDIR}
\rm -rf extract
\mkdir extract
\cd extract
extract_pkg_from_patch_repo grub2-efi-x64-modules
if [ $? -eq 0 ]; then
# Replace files
for f in usr/lib/grub/x86_64-efi/*; do
f_base=$(basename $f)
\rm -f ${BUILDDIR}/pxeboot/EFI/$f_base &&
\cp --preserve=all ${f} ${BUILDDIR}/pxeboot/EFI/
if [ $? -ne 0 ]; then
echo "Error: Failed to copy grub2-efi-x64-modules files"
exit 1
fi
done
fi
\cd ${WORKDIR}
\rm -rf extract
\cd ${ORIG_PWD}
if [ ${DO_UPGRADES} -eq 0 ]; then
# Changes to copied files here must also be reflected in build-iso
echo "Updating upgrade support files"
ISO_UPGRADES_DIR="${BUILDDIR}/upgrades"
\rm -rf ${ISO_UPGRADES_DIR}
\mkdir ${ISO_UPGRADES_DIR}
\cp ${REPO_UPGRADES_DIR}/* ${ISO_UPGRADES_DIR}
sed -i "s/xxxSW_VERSIONxxx/${PLATFORM_RELEASE}/g" ${ISO_UPGRADES_DIR}/metadata.xml
chmod +x ${ISO_UPGRADES_DIR}/*.sh
# Write the version out (used in upgrade scripts - this is the same as SW_VERSION)
echo "VERSION=$PLATFORM_RELEASE" > ${ISO_UPGRADES_DIR}/version
fi
# Rebuild the ISO
mkisofs -o ${OUTPUT_ISO} \
-R -D -A 'oe_iso_boot' -V 'oe_iso_boot' \
-quiet \
-b isolinux.bin -c boot.cat -no-emul-boot \
-boot-load-size 4 -boot-info-table \
-eltorito-alt-boot \
-e images/efiboot.img \
-no-emul-boot \
${BUILDDIR}
isohybrid --uefi ${OUTPUT_ISO}
implantisomd5 ${OUTPUT_ISO}
# Sign the .iso with the developer private key
# Signing with the formal key is only to be done for customer release
# and is a manual step afterwards, as with the GA ISO
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}"