swupdate-ab: add A/B scripts recipe for swupdate
authorHugo Villeneuve <hvilleneuve@dimonoff.com>
Thu, 18 Jul 2024 13:27:57 +0000 (09:27 -0400)
committerHugo Villeneuve <hugo@hugovil.com>
Tue, 24 Sep 2024 19:46:47 +0000 (15:46 -0400)
Recipe to deploy script used to support A/B update mechanism.

Link: https://groups.google.com/g/swupdate/c/85ALYtCt7Pc/m/DAJkUr5lDgAJ
recipes-core/images/include/image-hvmpd-common.inc
recipes-core/images/include/update-common.inc
recipes-core/images/include/update/sw-description
recipes-core/images/include/update/update.sh [deleted file]
recipes-support/swupdate/swupdate-ab/update.footer.sh [new file with mode: 0644]
recipes-support/swupdate/swupdate-ab/update.header.sh [new file with mode: 0644]
recipes-support/swupdate/swupdate-ab_0.1.bb [new file with mode: 0644]

index ddee2a4..e9ade7a 100644 (file)
@@ -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 = " \
index ed80f58..eb9a50f 100644 (file)
@@ -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"
 
index 78d91e9..647f14b 100644 (file)
@@ -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 (file)
index 6efa218..0000000
+++ /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 (file)
index 0000000..6c8e394
--- /dev/null
@@ -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 (file)
index 0000000..50af79c
--- /dev/null
@@ -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 (file)
index 0000000..5a51648
--- /dev/null
@@ -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