SWUpdate — это агент обновлений Linux, целью которого является эффективный и безопасный способ обновления встроенной системы. SWUpdate поддерживает локальные и удаленные обновления, включает несколько стратегий обновления, и его можно легко интегрировать в систему сборки Yocto, подключив слой meta-swupdate.

В статье описывается как можно настроить режим обновления в Yocto наиболее простым способом для платы Orange Pi R1, таким же образом вы можете настроить и обновление для самой распространенной платы Raspberry Pi.

Образ Yocto для которого будет подготовлено обновление наследуется от core-image-minimal и выполняет функцию простого маршрутизатора, используя DHCP и Shorewall.

Статья из серии: если хочешь что то обновить, сделай это сам.

Назначение и возможности SWUpdate

SWUpdate позволяет настроить обновление ПО для встраиваемых систем наиболее надежным способом. SWUpdate поддерживает распространенные носители на встроенных устройствах, такие как: флеш память NAND, eMMC, SD карты памяти, а также SPI-NOR.

В SWUpdate существует возможность расширить процедуру обновления с помощью сценариев обновления, которые можно запустить до или после установки. Сценарии могут быть написаны на языке LUA. Пакет обновления описывается в файле sw-description с использованием синтаксиса libconfig или JSON.

Для развертывания программного обеспечения можно использовать:

  • Локальное хранилище;

  • Интегрированный веб-сервер;

  • Получение с удаленного сервера по протоколам http и https;

  • C помощью бэкенда т.е. обновление загружается с удаленного сервера (режим suricatta). Текущая версия SWUpdate поддерживает сервер hawkBit.

Обновление программного обеспечения поставляется в виде образов, tar-архивов, сжатых с помощью gzip.

Обновление ПО на встраиваемой системе невозможно без непосредственного взаимодействия с загрузчиком ОС. SWUpdate поддерживает загрузчики U-Boot и Grub. Для встраиваемых систем по большей части используется U-Boot, так как он поддерживает наибольшее количество оборудования.

В большинстве случаев требуется обновить ядро и корневую файловую систему, с сохранением пользовательских данных. И лишь в крайне редких случаях требуется обновить загрузчик. Обновление загрузчика может привести к выходу из строя платы, и в этом случае возможно придется отправить плату обратно производителю.

Стратегия обновления SWUpdate

В зависимости от ресурсов системы могут использоваться разные стратегии обновлении, вот две из них:

  • Единая копия с использованием отдельных образов;

  • Стратегия двойного копирования.

Основная концепция единой копии заключается в том, что разработчик поставляет один большой образ, в котором все отдельные образы упакованы в формате CPIO, вместе с дополнительным файлом (sw-description), который содержит мета информацию о каждом отдельном образе.

Стратегия двойного копирования используется в том случае, если в хранилище достаточно места для сохранения двух копий всего программного обеспечения. В этом случае можно гарантировать, что всегда будет рабочая копия, даже если в процессе обновления что то пошло не так, например произошел сбой из за отключения питания.

Каждая копия содержит ядро, файл дерева устройств и корневую файловую систему. Для этой стратегии SWUpdate запускается в качестве прикладного программного обеспечения для обновления резервной копии, оставляя текущую копию ПО не тронутой.

Основным носителем информации для платы Orange Pi R1 является microSD карта памяти и для обновления программного обеспечения я буду использовать стратегию с двойной копией.

Примечание:

SD карты часто сыпятся, являются крайне не надежными, но их очень легко заменить, и в идеале для увеличения срока службы карты памяти файловая система rootfs должна быть использована в режиме только чтение.

Подключение механизма обновления к своему дистрибутиву в Yocto

Тот образ дистрибутива, для которого мне необходимо обновление, наследуется от core-image-minimal. Дистрибутив собирается для платы Orange Pi R1, она такая же, как и Orange Pi Zero только с двумя сетевыми интерфейсами.

В Yocto для сборки я буду использовать параметр MACHINE = "orange-pi-zero".

В первую очередь необходимо подключить слой meta-swupdate в build/conf/blayers.conf:

...
${BSPDIR}/sources/meta-swupdate \
...

После этого необходимо добавить расширение рецепта для сборки SWUpdate:

── recipes-support
    └── swupdate
        ├── swupdate
        │   └── orange-pi-zero
        │       ├── 09-swupdate-args
        │       ├── defconfig
        │       └── swupdate.cfg
        └── swupdate_%.bbappend

Расширение включает три файла:

  • 09-swupdate-args: скрипт для формирования аргументов с которыми запускается программа SWUpdate;

  • defconfig: конфигурация для сборки программы, используется процедура сборки Kbuild;

  • swupdate.cfg: конфигурация для запуска программы SWUpdate.

Содержимое скрипта 09-swupdate-args:

   rootfs=`mount | grep "on / type" | cut -d':' -f 2 | cut -d' ' -f 1`

   if [ $rootfs == '/dev/mmcblk0p2' ];then
      selection="-e stable,copy2"
   else
      selection="-e stable,copy1"
   fi

   state=`fw_printenv ustate | cut -f 2 -d'='`
   if [ "$state" == "1" ];then
      SWUPDATE_SURICATTA_ARGS="-c 2"
   else
      SWUPDATE_SURICATTA_ARGS=""
   

   if [ -e /media/etc/swupdate.cfg ];then
      CFGFILE="/media/etc/swupdate.cfg"
   else
      CFGFILE="/etc/swupdate.cfg"
   fi

   SWUPDATE_ARGS="-H orange-pi-zero:1.0 ${selection} -f ${CFGFILE}"

Скрипт 09-swupdate-args используется для определения текущей корневой файловой системы (переменная "selection"). Это позволяет выбрать на какой носитель в стратегии двойного копирования будет записываться обновление. Для загрузки с /dev/mmcblk0p2 (rootfs1) обновление будет записано на следующий раздел диска (копия 2), а для загрузки с /dev/mmcblk0p3 (rootfs2) обновление будет записано на предыдущий раздел диска (копия 1).

Параметр из переменных загрузчика u-boot "ustate == 1" определяет будет ли запущен режим обновления с использованием сервера HawkBit (режим suricatta).

Скрипт также формирует аргументы запуска SWUpdate с указанием названием компьютера "-H orange-pi-zero" и версией аппаратной платформы "1.0". Этот идентификатор должен совпасть со значением указанным в конфигурационном файле sw-description, иначе обновление не установиться.

Конфигурационный файл swupdate.cfg также содержит общие настройки обновления, настройки загрузки, настройки Suricatta и настройки локального Web Сервера.

Содержимое файла swupdate.cfg:
   globals :
   {

      verbose = true;
      loglevel = 5;
      syslog = true;
      /* public-key-file = "test.pem";*/
   };

   download :
   {
      retries = 3;
      timeout = 1800;
   };

   identify : (
      { name = "orange-pi-zero"; value = "1.0"; }
   );

   suricatta :
   {

      tenant		= "default";
      id		= "orange-pi-zero";
      confirm 	= 0;
      url 		= "http://paperina2:8280";
      polldelay	= 60;
      nocheckcert	= true;
      retry		= 4;
      retrywait	= 200;
      loglevel	= 10;
      userid		= 0;
      groupid		= 0;
      max_artifacts	= 1;
   /*
      cafile		= "/etc/ssl/cafile";
      sslkey		= "/etc/ssl/sslkey";
      sslcert		= "/etc/ssl/sslcert";
   */
   };


   webserver :
   {
      document_root = "/www";
      userid		= 0;
      groupid		= 0;
   };

Для подключения механизма обновления в конфигурационном файле слоя conf/layer.conf, который будет использовать слой meta-swupdate необходимо добавить секцию:

   # libubootenv — это библиотека, которая обеспечивает независимый 
   # от аппаратного обеспечения способ доступа к среде U-Boot. 
   # позволяет выполнять команды "fw_printenv / fw_setenv"
   PREFERRED_PROVIDER_u-boot-fw-utils = "libubootenv"

   # ядро Linux, которое содержит дополнительную служебную информацию для U-Boot
   KERNEL_IMAGETYPE = "uImage"
   # для обновления используется сжатая версия корневой файловой системы rootfs (ext4)
   # образ дистрибутива подготавливается с использованием механизма wic
   IMAGE_FSTYPES += " wic ext4.gz"

   # указание wic сценария для разбиения на логические разделы
   WKS_FILES_orange-pi-zero = "sunxi-mmc-spl.wks"

Для того, чтобы включить механизм обновления в уже готовый образ Yocto Project вы можете добавить файл рецепта update-image.bb, который наследуется от класса swupdate, c указанием трех основных переменных:

- IMAGE_DEPENDS: образ от которого зависит сборка обновления;

- SWUPDATE_IMAGES: образ файлы которого будут включены в сборку обновления;

- SWUPDATE_IMAGES_FSTYPES: тип и формат файловой системы, в котором зависимый образ будет включен в состав обновления.

Файл рецепта update-image.bb:

    DESCRIPTION = "Example image demonstrating how to build SWUpdate compound image"

    LICENSE = "MIT"
    LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

    inherit swupdate

    SRC_URI = "\
       file://emmcsetup.lua \
       file://sw-description \
    "

    # images to build before building swupdate image
    IMAGE_DEPENDS = "router-bs-image"

    # images and files that will be included in the .swu image
    SWUPDATE_IMAGES = "router-bs-image"

    SWUPDATE_IMAGES_FSTYPES[router-bs-image] = ".ext4.gz"

Описание образа обновления и сценарий развертывания обновления включаются в update-image.bb в виде двух файлов sw-description и emmcsetup.lua.

Файл sw-description:

   software =
   {
      version = "0.1.0";

      orange-pi-zero = {
         hardware-compatibility: [ "1.0"];
         stable : {
            copy1 : {
               images: (
                  {
                     filename = "router-bs-image-orange-pi-zero.ext4.gz";
                     type = "raw";
                     compressed = true;
                     device = "/dev/mmcblk0p2";
                  }
               );
               scripts: (
                  {
                     filename = "emmcsetup.lua";
                     type = "lua";
                  }
               );
               uboot: (
                  {
                     name = "opipart";
                     value = "2";
                  }
               );
            };
            copy2 : {
               images: (
                  {
                     filename = "router-bs-image-orange-pi-zero.ext4.gz";
                     type = "raw";
                     compressed = true;
                     device = "/dev/mmcblk0p3";
                  }
               );
               scripts: (
                  {
                     filename = "emmcsetup.lua";
                     type = "lua";
                  }
               );
               uboot: (
                  {
                     name = "opipart";
                     value = "3";
                  }
               );
            };
         };
      }
   }

Файл sw-description описывает аппаратную платформу для которой используется обновление "orange-pi-zero", и идентификатор "hardware-compatibility" для указания номера версии этой платформы.

Секцию с названием дистрибутива обновления "stable" и разделением в соответствии со стратегией обновления "двойная копия" с подсекциями "copy1" и "copy2", каждая из которых содержит название образа обновления "filename", с указанием формата образа и логическим разделом на который будет записано обновление.

Здесь также указывается скрипт, отвечающий за развертывания обновления и переменная загрузчика uboot, которая будет изменена после успешного завершения обновления. Переменная "opipart" сохраняется в файле настроек /boot/uboot.env и будет содержать логический номер раздела с которого будет загружаться ядро и корневая файловая система.

Примечание:

Физически переменные среды загрузчика uboot располагаются на первом логическом разделе диска /dev/mmcblk0p1, об этом я напишу подробней чуть ниже.

Файл emmcsetup.lua:
   function os.capture(cmd)
      local f = assert(io.popen(cmd, 'r'))
      local s = assert(f:read('*a'))
      f:close()
      return s
   end

   function file_exists(name)
      local f=io.open(name,"r")
      if f~=nil then io.close(f) return true else return false end
   end

   function cmdexec(cmd)
      local ret, s, status = os.execute(cmd)
      if (status ~= 0) then
         return false, cmd .. " return with error"
      end

      return true,""
   end

   function preinst()
      local out
      local s1
      local ret

      local log = os.tmpname()

      local eMMC = "/dev/mmcblk0"
      ret = file_exists("/dev/mmcblk0")

      if (ret == false) then
         return false, "Cannot fine eMMC"
      end

      cmdexec("/usr/sbin/sfdisk -d " .. eMMC .. "> /tmp/dumppartitions")

      -- check if there are two identical partitions
      -- and create the second one if no available
      f = io.input("/tmp/dumppartitions")
      fo = io.output("/tmp/partitions")
      t = f:read()
      found = false
      while (t ~= nil) do
         j=0
         j=string.find(t, "/dev/mmcblk0p3")
         fo:write(t .. "\n")
         if (j == 1) then
            found=true
            break
         end
         j=string.find(t, "/dev/mmcblk0p2")
         if (j == 1) then
            start, size = string.match(t, "%a+%s*=%s*(%d+), size=%s*(%d+)")
         end
         t = f:read()
      end

      if (found) then
         f:close()
         fo:close()
         return true, out
      end

      start=start+size
      partitions = eMMC .. "p3 : start=    " .. string.format("%d", start) .. ", size=  " .. size .. ", type=83\n"

      fo:write(partitions)
      fo:close()
      f:close()

      out = os.capture("/usr/sbin/sfdisk --force " .. eMMC .. " < /tmp/partitions")

      -- use partprobe to inform the kernel of the new partitions
      
      cmdexec("/usr/sbin/partprobe " .. eMMC)

      return true, out
   end

   function postinst()
      local out = "Post installed script called"

      return true, out
   end

Использование WIC для создания логических разделов

Для того, чтобы программа SWUpdate смогла успешно накатить обновление в соответствии с мета информацией файла sw-description и выбранной стратегией обновления, необходимо записать исходный образ дистрибутива на файловый носитель в определенном формате, с разбиением на несколько логических разделов.

Для упрощения этой процедуры используется механизм WIC и в этом случае структура исходного raw образа описывается о одном файле sunxi-mmc-spl.wks

следующего содержания:

part u-boot --source rawcopy --sourceparams="file=u-boot-sunxi-with-spl.bin" --ondisk mmcblk0 --no-table --align 8
part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 1024 --fixed-size 16
part /              --source rootfs --ondisk mmcblk0 --fstype=ext4 --label rootfs1 --align 1024 --fixed-size 256
part                --source rootfs --ondisk mmcblk0 --fstype=ext4 --label rootfs2 --align 1024 --fixed-size 256
part --ondisk mmcblk0 --fstype=ext4 --label data --align 1024 --fixed-size 512

механизм подключается в конфигурационном файле слоя:

   IMAGE_FSTYPES += " wic"
   WKS_FILES_orange-pi-zero = "sunxi-mmc-spl.wks"

В первой строке файла "part u-boot ..." указывается размещение вторичного загрузчика (SPL) в начале дискового носителя без таблицы разделов. Сразу после включения питания система еще ничего не может, обычно в самом начале работает только одно процессорное ядро и доступна статическая память на кристалле очень ограниченного размера, поэтому процесс загрузки сильно фрагментирован и в нем обычно участвуют несколько загрузчиков.

Первичный загрузчик размещен в ПЗУ процессора и он умеет работать с памятью процессора SRAM и основной его задачей является загрузка в память и передача управления вторичному загрузчику, который входит в состав образа "u-boot-sunxi-with-spl.bin".

Файл загрузчика должен быть размещен по фиксированным адресам на одном из поддерживаемых носителях, в данном случае в начале SD карты памяти по смещению 8Kбайт.

Одноплатный компьютер Orange Pi R1(Zero) включает процессор "Allwinner H2(+) quad core Cortex A7". Информацию о загрузке с карты памяти SD, а именно стартовый адрес по которому должен располагаться загрузчик SPL, вы можете посмотреть здесь. Вторичный загрузчик уже умеет работать с динамической памятью DRAM и его задача скопировать и запустить из этой памяти полноценный загрузчик U-Boot, который также входит в состав интегрированного образа "u-boot-sunxi-with-spl.bin".

Загрузчик ищет стартовый конфигурационный сценарий boot.scr на первом логическом разделе ("part /boot ...") и запускает его. Образ bootimg-partition также содержит linux ядро uImage и файл дерева устройств sun8i-h2-plus-orangepi-zero.dtb. В дальнейшем, после первого запуска на этом разделе будут храниться текущие переменные среды u-boot, сохраненные командой saveenv в файле uboot.env. После загрузки системы, раздел монтируется в fstab, и файл настроек будет доступен по пути /boot/uboot.env. Размер первого логического раздела 16 Мбайт.

Следующие две записи создают две одинаковые копии корневой файловой системы rootfs, размещаемые на втором логическом разделе "/dev/mmcblk0p2" и на третьем логическом разделе "/dev/mmcblk0p3" соответственно, при этом для SWUpdate используется стратегия двойного копирования. Размер разделов фиксированный и равен 256 Мб.

Примечание:

Чем больше размер разделов, тем больше будет размер исходного raw образа для первоначального копирования на карту памяти SD. RAW образ c расширением .wic копируется командой dd. Размер подбирается из предпосылок будущей функциональности дистрибутива. При превышении этого размера вы не сможете обновить образ.

И наконец четвертым разделом будет являться раздел с данными, которые необходимо хранить отдельно от корневой файловой системы. Это позволяет сохранить пользовательские данные после завершения процесса обновления.

Подключение файловой системы overlayfs

Диск будет подключен с помощью механизма overlayfs в точку монтирования /etc в качестве файлового слоя верхнего уровня и это позволит все изменения существующих конфигурационных файлов, а также новых файлов в каталоге /etc хранить на этом логическом разделе.

 ── recipes-core
      └── overlayfs
         ├── etc-overlay.bb
         └── files
             ├── etc.mount
             └── overlays-etc-dirs.service

В рецепте используется следующий скрипт Systemd для монтирования диска:

[Unit]
Description=Overlay /etc/
Requires=overlays-etc-dirs.service
After=overlays-etc-dirs.service

[Mount]
What=overlay
Where=/etc
Type=overlay
Options=lowerdir=/etc,upperdir=/media/overlayfs/etc/upper,workdir=/media/overlayfs/etc/work

[Install]
WantedBy=local-fs.target

Доступ к переменным среды загрузчика U-Boot

В процессе обновления программа SWUpdate использует библиотеку libubootenv для доступа к переменным загрузчика U-Boot (команды "fw_printenv / fw_setenv"). Эта библиотека ищет информацию о месте нахождения переменных среды в файле /etc/fw_env.config, и для каждой платы эти адреса могут различаться. Поэтому следующим шагом является дополнение для рецепта libubootenv_%.bbappend:

└── recipes-bsp
     └── libubootenv
         ├── files
         │   ├── fw_env.config
         │   └── orange-pi-zero
         │       └── fw_env.config
         └── libubootenv_%.bbappend  

Содержание дополнения рецепта libubootenv_%.bbappend:

   FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

   SRC_URI:append:class-target = " file://fw_env.config"

   do_install:append:class-target() {
      install -d ${D}${sysconfdir}
      install -m 644 ${WORKDIR}/fw_env.config ${D}${sysconfdir}
   }

   FILES:${PN}:append:class-target = " ${sysconfdir}"

Конфигурация "/etc/fw_env.config" для платы Orange Pi Zero:

/boot/uboot.env 0x0000    0x20000

Здесь указывается начальный адрес и размер выделяемый для переменных среды т.е. 128 kБайт.

Стартовый конфигурационный сценарий U-Boot

Для самого загрузчика U-Boot необходимо добавить стартовый конфигурационный сценарий boot.cmd, это текстовый файл который будет преобразован в файл boot.scr, размещаемый на первом логическом разделе.

 └── dynamic-layers
      └── orange
          └── recipes-bsp
              └── u-boot
                   ├── files
                   │    └── boot.cmd
                   └── u-boot_%.bbappend

В Yocto можно выборочно подключать определенные рецепты, при работе с разными аппаратными конфигурациями (BSP слои). Для этого используется динамический слой, который подключается по имени коллекции, т.е. рецепты слоя будут подключены только в том случае, если в списке слоев будет слой с BBFILE_COLLECTIONS+=meta-sunxi.

Это позволяет в одном слое совмещать разную функциональность, которую в противном случае пришлось бы хранить в разных слоях.

Слой подключается в layer.conf следующим образом:

BBFILES_DYNAMIC += " \
      meta-sunxi:${LAYERDIR}/dynamic-layers/orange/*/*/*.bb \
      meta-sunxi:${LAYERDIR}/dynamic-layers/orange/*/*/*.bbappend \
   "

Содержание дополнения рецепта u-boot_%.bbappend:

   ###https://stackoverflow.com/questions/58610052/how-to-get-thisdir-inside-do-unpack-append-in-bbappend-file
   FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
   SRC_URI_append += " file://boot.cmd"
   SAVED_DIR := "${THISDIR}/files"

   do_unpack_append(){
      bb.build.exec_func('replace_file', d)
   }

   replace_file(){
      cp -f ${SAVED_DIR}/boot.cmd ${WORKDIR}/boot.cmd
   }

Стартовый конфигурационный сценарий boot.cmd:

 saveenv
 if env exists opipart;then echo Booting from mmcblk0p${opipart};else setenv opipart 2;echo opipart not set, default to ${opipart};fi

 setenv bootargs console=${console} console=tty1 root=/dev/mmcblk0p${opipart} rootwait panic=10 ${extra}
 load mmc 0:${opipart} ${fdt_addr_r} ${fdtfile} || load mmc 0:${opipart} ${fdt_addr_r} boot/${fdtfile}
 load mmc 0:${opipart} ${kernel_addr_r} zImage || load mmc 0:${opipart} ${kernel_addr_r} boot/zImage || load mmc 0:${opipart} ${kernel_addr_r} uImage || load mmc 0:${opipart} ${kernel_addr_r} boot/uImage
 bootz ${kernel_addr_r} - ${fdt_addr_r} || bootm ${kernel_addr_r} - ${fdt_addr_r}

В том случае, если переменная выбора корневой файловой системы "opipart" не установлена, то по умолчанию используется второй логический раздел диска.

Когда SWUpdate успешно завершает обновление резервной копии корневой файловой системы, он переключает эту переменную т.е. меняет основную и резервную rootfs местами для следующей загрузки.

Это делается например так, командой:

fw_setenv opipart 3
Примечание:

Таким же образом вы можете вручную сменить текущую версию системы, переключив ее на предыдущую, без использования SWUpdate. Если текущая функциональность вас каким либо образом не устроила, это быстро и требует только перезагрузки.

В начале команда "load mmc 0:${opipart} ${fdt_addr_r} boot/${fdtfile}" загружает файл дерева устройств для платы в память, где:

0 - номер диска, нумерация дисков начинается с 0;

opipart - номер раздела, задается начиная с 1. Номер раздела 0 указывает, что все устройство должно использоваться как один «раздел».

Затем команда "load mmc 0:${opipart} ${kernel_addr_r} boot/uImage" загружает ядро в память и передает управления загруженному ядру "bootm ${kernel_addr_r} - ${fdt_addr_r}".

Для корректного выполнения обновления через SWUpdate на работающей системе Linux должны быть подключены дополнительные дисковые разделы, указанные выше.

В первую очередь это загрузочный раздел и раздел с данными. Дополнение для рецепта base-files_%.bbappend:

   └── recipes-core
       └── base-files
           ├── base-files
           │   └── fstab
           └── base-files_%.bbappend

Содержание файла fstab:

# stock fstab - you probably want to override this with a machine specific one
/dev/root            /                    auto       defaults              1  1
proc                 /proc                proc       defaults              0  0
devpts               /dev/pts             devpts     mode=0620,ptmxmode=0666,gid=5      0  0
tmpfs                /run                 tmpfs      mode=0755,nodev,nosuid,strictatime 0  0
tmpfs                /var/volatile        tmpfs      defaults              0  0

/dev/mmcblk0p1       /boot                vfat       defaults              0  0
/dev/mmcblk0p4       /media               ext4       defaults              0  0

Таким образом fstab добавляется вручную, так как он должен присутствовать как в rootfs (wic), так и в update-image (ext4.gz) и чтобы исключить обновление файла fstab (дублирование строк) при создании wic образа в layer.conf используется запись:

WIC_CREATE_EXTRA_ARGS = "--no-fstab-update"

После запуска встроенной системы, проще всего проверить механизм обновления через встроенный Web сервер, который доступен на 8080 порту:

http://ip_address:8080

Web интерфейс крайне минималистичен, достаточно выбрать файл с обновлением и расширением swu, а затем нажать кнопку загрузки. Интерфейс позволяет посмотреть текущий лог процесса обновления.

После успешного обновления плату можно перезагрузить вручную, подключившись по ssh протоколу и выполнив команду reboot.

Размер файла обновления с ядром и корневой файловой системой rootfs у меня занимает ~44 Мбайта (update-image-orange-pi-zero-20221017123455.swu).

Suricatta - режим демона SWUpdate, при котором он регулярно опрашивает удаленный сервер на наличие обновлений, загружает и устанавливает их, а затем перегружает систему. Для этого вначале необходимо установить и запустить сервер обновления hawkBit, например на Desktop компьютере с Ubuntu:

   git clone https://github.com/eclipse/hawkbit
   cd hawkbit
   git checkout 0.3.0M7

   # собрать через Maven
   sudo apt install maven
   mvn clean install

   # и запустить
   java -jar ./hawkbit-runtime/hawkbit-update-server/target/hawkbit-update-server-0.3.0-SNAPSHOT.jar

По умолчанию настройка сервера доступна через http://localhost:8080, пользователь admin, пароль admin.

Конфигурация для работы SWUpdate c hawkbit в swupdate.cfg:

suricatta :
   {

      tenant		= "default";
      id		= "orange-pi-zero";
      confirm 	= 0;
      url 		= "http://paperina2:8280";
      polldelay	= 60;
      nocheckcert	= true;
      retry		= 4;
      retrywait	= 200;
      loglevel	= 10;
      userid		= 0;
      groupid		= 0;
      max_artifacts	= 1;
   };

Режим включается в том случае, если установлена переменная u-boot "ustate = 1".

Для того, чтобы не заморачиваться с перенастройкой, можно проверить взаимодействие с HawkBit вручную запустив команду на встраиваемой системе:

swupdate -H orange-pi-zero:1.0 -e stable,copy2 -f /etc/swupdate.cfg -l 5 -u '-t DEFAULT -u http://ip_address:8080 -i orange-pi-zero'

Указав порт по умолчанию 8080 и ip адрес вашего Desktop компьютера, на котором запущен hawkBit. HawkBit позволяет управлять обновлениями достаточно сложным образом, но это уже совсем другая история.

Зависимости образа дистрибутива, для которого нужно создать обновление update-image у меня следующие:

   IMAGE_INSTALL += " \
                     base-files \
                     base-passwd \
                     busybox \
                     mtd-utils \
                     mtd-utils-ubifs \
                     libconfig \
                     util-linux-sfdisk \
                     libubootenv-bin \
                     swupdate \
                     swupdate-www \
                     etc-overlay \
                  "

Инструкция по сборке образа

   mkdir yo-swupdate
   cd yo-swupdate
   repo init -u https://github.com/berserktv/bs-manifest -m orange/hardknott/router-bs-swupdate-0.7.6.xml
   repo sync

   ./shell.sh
   bitbake update-image

На мой взгляд SWUpdate очень классный проект, который хорошо документирован и который позволяет настроить процесс обновления достаточно надежным способом, и вы всегда можете увеличить эту надежность подобрав и соответствующую аппаратную платформу, а проверить этот механизм можно на простых платах прототипирования типа Orange Pi R1.

Комментарии (1)


  1. zedroid
    31.10.2022 21:39

    Очень крутая статья! Очень подробно расписано как включить в релиз йокты