From 8007883986715b1c4b21e7f81fab1001677cbe64 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 18 Jul 2024 09:27:57 -0400 Subject: [PATCH] swupdate-ab: add A/B scripts recipe for swupdate Recipe to deploy script used to support A/B update mechanism. Link: https://groups.google.com/g/swupdate/c/85ALYtCt7Pc/m/DAJkUr5lDgAJ --- .../images/include/image-hvmpd-common.inc | 2 + recipes-core/images/include/update-common.inc | 3 +- .../images/include/update/sw-description | 8 +- recipes-core/images/include/update/update.sh | 90 ----------- .../swupdate/swupdate-ab/update.footer.sh | 140 ++++++++++++++++++ .../swupdate/swupdate-ab/update.header.sh | 38 +++++ recipes-support/swupdate/swupdate-ab_0.1.bb | 42 ++++++ 7 files changed, 227 insertions(+), 96 deletions(-) delete mode 100644 recipes-core/images/include/update/update.sh create mode 100644 recipes-support/swupdate/swupdate-ab/update.footer.sh create mode 100644 recipes-support/swupdate/swupdate-ab/update.header.sh create mode 100644 recipes-support/swupdate/swupdate-ab_0.1.bb diff --git a/recipes-core/images/include/image-hvmpd-common.inc b/recipes-core/images/include/image-hvmpd-common.inc index ddee2a4..e9ade7a 100644 --- a/recipes-core/images/include/image-hvmpd-common.inc +++ b/recipes-core/images/include/image-hvmpd-common.inc @@ -46,6 +46,8 @@ IMAGE_INSTALL:append = " \ swupdate-progress \ " +EXTRA_IMAGEDEPENDS += "swupdate-ab" + # Remove swupdate-www (package with the website, that you can customize with # your own logo, template and style). IMAGE_INSTALL:remove = " \ diff --git a/recipes-core/images/include/update-common.inc b/recipes-core/images/include/update-common.inc index ed80f58..eb9a50f 100644 --- a/recipes-core/images/include/update-common.inc +++ b/recipes-core/images/include/update-common.inc @@ -4,11 +4,10 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/update:" SRC_URI = " \ file://sw-description \ - file://update.sh \ " # Image(s) and files that will be included in the .swu image -SWUPDATE_IMAGES = "${IMAGE_DEPENDS}" +SWUPDATE_IMAGES = "${IMAGE_DEPENDS} swupdate-ab.sh" UBOOT_PART_VAR ?= "mmcpart" diff --git a/recipes-core/images/include/update/sw-description b/recipes-core/images/include/update/sw-description index 78d91e9..647f14b 100644 --- a/recipes-core/images/include/update/sw-description +++ b/recipes-core/images/include/update/sw-description @@ -31,10 +31,10 @@ software = { ); scripts: ( { - filename = "update.sh"; + filename = "swupdate-ab.sh"; type = "shellscript"; data = "@@ROOT_PART_PREFIX@@@@ROOT_PART_A_ID@@"; /* Destination partition ID */ - sha256 = "$swupdate_get_sha256(update.sh)"; + sha256 = "$swupdate_get_sha256(swupdate-ab.sh)"; } ); bootenv: ( @@ -62,10 +62,10 @@ software = { ); scripts: ( { - filename = "update.sh"; + filename = "swupdate-ab.sh"; type = "shellscript"; data = "@@ROOT_PART_PREFIX@@@@ROOT_PART_B_ID@@"; /* Destination partition ID */ - sha256 = "$swupdate_get_sha256(update.sh)"; + sha256 = "$swupdate_get_sha256(swupdate-ab.sh)"; } ); bootenv: ( diff --git a/recipes-core/images/include/update/update.sh b/recipes-core/images/include/update/update.sh deleted file mode 100644 index 6efa218..0000000 --- a/recipes-core/images/include/update/update.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/sh -set -e - -# Shell scripts are called via system command. SWUpdate scans for all scripts -# and calls them before and after installing the images. SWUpdate passes -# ‘preinst’ or ‘postinst’ as first argument to the script. If the data attribute -# is defined, its value is passed as the last argument(s) to the script. - -FSTYPE="ext4" - -echo "${0}: arguments = \"${*}\"" - -if [ $# -lt 2 ]; then - exit 1; -fi - -# L'environnement U-Boot doit être valide pour utiliser ce script. -# Après la programmation initiale avec uuu, il faut effectuer la sauvegarde -# de l'environnement en mémoire non-volatile (ex: eMMC) dans U-boot avec -# 'saveenv'. Sinon, fw_saveenv va utiliser l'environnement par défaut contenu -# dans /etc/u-boot-initial-env -if fw_printenv 2>&1 | grep -q 'Cannot read environment'; then - echo "Warning: U-Boot environment cannot be read. Make sure you save the" - echo " default environment to flash using these U-Boot commands:" - echo " $> env default -a" - echo " $> saveenv" -fi - -do_preinst() -{ - # Find internal parent kernel device name of rootfs. - # For example: - # - /dev/sda1 --> /dev/sda - # - /dev/mmcblk2p2 --> /dev/mmcblk2 - disk="/dev/$(lsblk -ndo pkname $(findmnt -n -o SOURCE /))" - - if [ ! -b "${disk}" ]; then - echo "Error: disk \"${disk}\" not found" - exit 1 - fi - - update_dev="${disk}${next_part}" - - if [ ! -b "${update_dev}" ]; then - echo "Error: destination partition \"${update_dev}\" not found" - exit 1 - fi - - echo "${0}: destination device = ${update_dev}" - - # Create temporary mount point: - tmp_file=$(mktemp -q -d /tmp/swupdate-mount.XXXXXX) - if [ ${?} -ne 0 ]; then - echo "Error: cannot create temporary file" - exit 1 - fi - - # Mount update partition: - mount -t ${FSTYPE} ${update_dev} ${tmp_file} - - # Remove old data: - echo "${0}: erasing old data..." - rm -rf ${tmp_file}/* - - # Finish - umount ${tmp_file} - rmdir ${tmp_file} - - exit 0 -} - -do_postinst() -{ - exit 0 -} - -next_part=${2} - -case "$1" in - preinst) - do_preinst - ;; - postinst) - do_postinst - ;; - *) - echo "unsupported install mode: \"${1}\"" - exit 1 - ;; -esac diff --git a/recipes-support/swupdate/swupdate-ab/update.footer.sh b/recipes-support/swupdate/swupdate-ab/update.footer.sh new file mode 100644 index 0000000..6c8e394 --- /dev/null +++ b/recipes-support/swupdate/swupdate-ab/update.footer.sh @@ -0,0 +1,140 @@ + +# L'environnement U-Boot doit être valide pour utiliser ce script. +# Après la programmation initiale avec uuu, il faut effectuer la sauvegarde +# de l'environnement en mémoire non-volatile (ex: eMMC) dans U-boot avec +# 'saveenv'. Sinon, fw_saveenv va utiliser l'environnement par défaut contenu +# dans /etc/u-boot-initial-env +if fw_printenv 2>&1 | grep -q 'Cannot read environment'; then + echo "Warning: U-Boot environment cannot be read. Make sure you save the" + echo " default environment to flash using these U-Boot commands:" + echo " $> env default -a" + echo " $> saveenv" +fi + +# Return the partition device +get_destination_partition_device() +{ + # Find internal parent kernel device name of rootfs. + # For example: + # - /dev/sda1 --> /dev/sda + # - /dev/mmcblk2p2 --> /dev/mmcblk2 + disk="/dev/$(lsblk -ndo pkname $(findmnt -n -o SOURCE /))" + + if [ ! -b "${disk}" ]; then + swulog "Error: disk \"${disk}\" not found" + return 1 + fi + + update_dev="${disk}${next_part}" + + if [ ! -b "${update_dev}" ]; then + swulog "Error: destination partition \"${update_dev}\" not found" + return 1 + fi + + echo "${update_dev}" + return 0 +} + +mount_destination_partition() +{ + update_dev=$(get_destination_partition_device) + + # Create temporary mount point: + mnt_point=$(mktemp -q -d /tmp/swupdate-mount.XXXXXX) + if [ ${?} -ne 0 ]; then + swulog "Error: cannot create temporary file" + exit 1 + fi + + # Mount update partition: + mount -t ${FSTYPE} ${update_dev} ${mnt_point} +} + +unmount_destination_partition() +{ + if mount | grep -q ${mnt_point}; then + umount ${mnt_point} + fi + + if [ -d ${mnt_point} ]; then + rmdir ${mnt_point} + fi +} + +do_preinst() +{ + update_dev=$(get_destination_partition_device) + swulog "Reformat destination partition: ${update_dev}" + + label="$(blkid -s LABEL -o value ${update_dev})" + + # Reformat destination partition: + mkfs.${FSTYPE} ${update_dev} -L "${label}" + + exit 0 +} + +do_postinst() +{ + mount_destination_partition + + # Perform migration of selected files/folders... + swulog "migrating existing data..." + + # Migrate files: + for f in ${SWU_PRESERVE_FILES}; do + if [ ! -f ${f} ]; then + swulog "warning: missing source file: ${f} (skipping)" + continue + fi + + dst_folder="$(dirname ${mnt_point}/${f})" + + # Do not copy file if destination folder doesn't exist. + # The destination folder need to be created in your new SWU archive + # with the proper ownership and permissions (can be empty) + if [ ! -d ${dst_folder} ]; then + swulog "warning: missing destination folder for file: ${f} (skipping)" + continue + fi + + # Copy old file to new partition: + cp -a ${f} ${mnt_point}/${f} + done + + # Migrate folders: + for d in ${SWU_PRESERVE_FOLDERS}; do + if [ ! -d ${d} ]; then + swulog "warning: missing source folder: ${d} (skipping)" + continue + fi + + if [ -d ${mnt_point}/${d} ]; then + # Remove folder if it exists in new partition: + rm -rf ${mnt_point}/${d} + fi + + # Copy old folder to new partition: + cp -a ${d} ${mnt_point}/${d} + done + + unmount_destination_partition + + exit 0 +} + +next_part=${2} + +case "$1" in + preinst) + do_preinst + ;; + postinst) + do_postinst + ;; + *) + swulog "unsupported install mode: \"${1}\"" + exit 1 + ;; +esac diff --git a/recipes-support/swupdate/swupdate-ab/update.header.sh b/recipes-support/swupdate/swupdate-ab/update.header.sh new file mode 100644 index 0000000..50af79c --- /dev/null +++ b/recipes-support/swupdate/swupdate-ab/update.header.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -e + +# Shell scripts are called via system command. SWUpdate scans for all scripts +# and calls them before and after installing the images. SWUpdate passes +# ‘preinst’ or ‘postinst’ as first argument to the script. If the data attribute +# is defined, its value is passed as the last argument(s) to the script. + +trap 'catch $?' EXIT + +catch() +{ + if [ "$1" != "0" ]; then + # Error handling goes here + echo "Error $1 occurred" + + if mount | grep -q ${mnt_point}; then + unmount_destination_partition + fi + fi +} + +# Arg1: log message/string +swulog() +{ + echo "$(basename ${0}): ${1}" +} + +FSTYPE="ext4" + +swulog "arguments = \"${*}\"" + +if [ $# -lt 2 ]; then + exit 1; +fi + +alias cp=cp + diff --git a/recipes-support/swupdate/swupdate-ab_0.1.bb b/recipes-support/swupdate/swupdate-ab_0.1.bb new file mode 100644 index 0000000..5a51648 --- /dev/null +++ b/recipes-support/swupdate/swupdate-ab_0.1.bb @@ -0,0 +1,42 @@ +SUMMARY = "Deploy SWUpdate shell scripts to support A/B update mechanism" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI = " \ + file://update.header.sh \ + file://update.footer.sh \ +" + +inherit deploy + +# Default list of files/folders to preserve between updates, can be extended/overriden by BSP: +SWU_PRESERVE_FILES ?= "\ + /etc/hostname \ + /etc/localtime \ +" +SWU_PRESERVE_FOLDERS ?= "" + +DEST_SCR = "${DEPLOYDIR}/${PN}.sh" + +do_deploy() { + cat ${WORKDIR}/update.header.sh > ${DEST_SCR} + + # Insert our variable(s) between header and footer: + echo "SWU_PRESERVE_FILES=\"\\" >> ${DEST_SCR} + for f in ${SWU_PRESERVE_FILES}; do + echo " ${f} \\" >> ${DEST_SCR} + done + echo "\"" >> ${DEST_SCR} + + echo "SWU_PRESERVE_FOLDERS=\"\\" >> ${DEST_SCR} + for f in ${SWU_PRESERVE_FOLDERS}; do + echo " ${f} \\" >> ${DEST_SCR} + done + echo "\"" >> ${DEST_SCR} + + cat ${WORKDIR}/update.footer.sh >> ${DEST_SCR} +} + +addtask deploy after do_install -- 2.20.1